@rely-ai/caliber 1.48.0 → 1.48.2

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.
Files changed (2) hide show
  1. package/dist/bin.js +84 -24
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -276,6 +276,21 @@ var init_resolve_caliber = __esm({
276
276
  }
277
277
  });
278
278
 
279
+ // src/utils/windows.ts
280
+ function quoteForWindows(arg) {
281
+ if (!arg) return '""';
282
+ if (!/[ \t\n\v"]/.test(arg)) return arg;
283
+ return '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, "$1$1") + '"';
284
+ }
285
+ function bashPath(p) {
286
+ return p.replace(/\\/g, "/");
287
+ }
288
+ var init_windows = __esm({
289
+ "src/utils/windows.ts"() {
290
+ "use strict";
291
+ }
292
+ });
293
+
279
294
  // src/llm/types.ts
280
295
  var types_exports = {};
281
296
  __export(types_exports, {
@@ -961,10 +976,20 @@ function openDiffsInEditor(editor, files) {
961
976
  try {
962
977
  const leftPath = file.originalPath ?? getEmptyFilePath(file.proposedPath);
963
978
  if (IS_WINDOWS5) {
964
- const quote = (s) => `"${s}"`;
965
- spawn4([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
979
+ spawn4(
980
+ [
981
+ quoteForWindows(cmd),
982
+ "--diff",
983
+ quoteForWindows(leftPath),
984
+ quoteForWindows(file.proposedPath)
985
+ ].join(" "),
986
+ { shell: true, stdio: "ignore", detached: true }
987
+ ).unref();
966
988
  } else {
967
- spawn4(cmd, ["--diff", leftPath, file.proposedPath], { stdio: "ignore", detached: true }).unref();
989
+ spawn4(cmd, ["--diff", leftPath, file.proposedPath], {
990
+ stdio: "ignore",
991
+ detached: true
992
+ }).unref();
968
993
  }
969
994
  } catch {
970
995
  continue;
@@ -975,6 +1000,7 @@ var IS_WINDOWS5, DIFF_TEMP_DIR;
975
1000
  var init_editor = __esm({
976
1001
  "src/utils/editor.ts"() {
977
1002
  "use strict";
1003
+ init_windows();
978
1004
  IS_WINDOWS5 = process.platform === "win32";
979
1005
  DIFF_TEMP_DIR = path25.join(os6.tmpdir(), "caliber-diff");
980
1006
  }
@@ -2622,12 +2648,8 @@ function estimateTokens(text) {
2622
2648
  return Math.ceil(text.length / 4);
2623
2649
  }
2624
2650
 
2625
- // src/utils/windows.ts
2626
- function quoteForWindows(arg) {
2627
- if (!arg) return '""';
2628
- if (!/[ \t\n\v"]/.test(arg)) return arg;
2629
- return '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, "$1$1") + '"';
2630
- }
2651
+ // src/llm/cursor-acp.ts
2652
+ init_windows();
2631
2653
 
2632
2654
  // src/lib/subprocess-sentinel.ts
2633
2655
  var CALIBER_SUBPROCESS_ENV = "CALIBER_SUBPROCESS";
@@ -2977,6 +2999,7 @@ function isCursorLoggedIn() {
2977
2999
  // src/llm/claude-cli.ts
2978
3000
  import fs7 from "fs";
2979
3001
  import { spawn as spawn2, execSync as execSync7, execFileSync as execFileSync2 } from "child_process";
3002
+ init_windows();
2980
3003
  var DEFAULT_TIMEOUT_MS3 = 10 * 60 * 1e3;
2981
3004
  var IS_WINDOWS2 = process.platform === "win32";
2982
3005
  function candidateClaudePaths() {
@@ -3027,12 +3050,18 @@ function cleanClaudeEnv() {
3027
3050
  function spawnClaude(args) {
3028
3051
  const bin = resolveClaudeBin();
3029
3052
  const env = withCaliberSubprocessEnv(cleanClaudeEnv());
3030
- return IS_WINDOWS2 ? spawn2([bin, ...args].join(" "), {
3031
- cwd: process.cwd(),
3032
- stdio: ["pipe", "pipe", "pipe"],
3033
- env,
3034
- shell: true
3035
- }) : spawn2(bin, args, {
3053
+ return IS_WINDOWS2 ? (
3054
+ // Windows path: shell:true skips Node's exe quoting, so paths like
3055
+ // `C:\Program Files\caliber\claude.cmd` (with spaces) would be parsed by
3056
+ // cmd.exe as multiple words. Quote each piece explicitly. Mirrors the
3057
+ // pattern used in cursor-acp.ts and the learn-finalize background spawn.
3058
+ spawn2([quoteForWindows(bin), ...args.map(quoteForWindows)].join(" "), {
3059
+ cwd: process.cwd(),
3060
+ stdio: ["pipe", "pipe", "pipe"],
3061
+ env,
3062
+ shell: true
3063
+ })
3064
+ ) : spawn2(bin, args, {
3036
3065
  cwd: process.cwd(),
3037
3066
  stdio: ["pipe", "pipe", "pipe"],
3038
3067
  env
@@ -4607,6 +4636,7 @@ function getCursorConfigDir() {
4607
4636
 
4608
4637
  // src/lib/hooks.ts
4609
4638
  init_resolve_caliber();
4639
+ init_windows();
4610
4640
  import fs11 from "fs";
4611
4641
  import path10 from "path";
4612
4642
  import { execSync as execSync10 } from "child_process";
@@ -4788,28 +4818,58 @@ var installNotificationHook = notificationHook.install;
4788
4818
  var removeNotificationHook = notificationHook.remove;
4789
4819
  var PRECOMMIT_START = "# caliber:pre-commit:start";
4790
4820
  var PRECOMMIT_END = "# caliber:pre-commit:end";
4821
+ function tryWindowsDirectNodeInvocation(cmd) {
4822
+ if (process.platform !== "win32") return null;
4823
+ if (!/\.cmd$/i.test(cmd)) return null;
4824
+ const npmDir = path10.dirname(cmd);
4825
+ const binJs = path10.join(npmDir, "node_modules", "@rely-ai", "caliber", "dist", "bin.js");
4826
+ if (!fs11.existsSync(binJs)) return null;
4827
+ let nodePath;
4828
+ try {
4829
+ const out = execSync10("where node", {
4830
+ encoding: "utf-8",
4831
+ stdio: ["pipe", "pipe", "pipe"]
4832
+ }).trim();
4833
+ nodePath = pickExecutable(out);
4834
+ if (!nodePath) return null;
4835
+ } catch {
4836
+ return null;
4837
+ }
4838
+ const fwdNode = nodePath.replace(/\\/g, "/");
4839
+ const fwdBin = binJs.replace(/\\/g, "/");
4840
+ return `"${fwdNode}" "${fwdBin}"`;
4841
+ }
4791
4842
  function getPrecommitBlock() {
4792
4843
  const cmd = resolveCaliber();
4793
4844
  const npx = isNpxResolution();
4794
4845
  let guard;
4795
4846
  let invoke;
4796
4847
  if (npx) {
4797
- const npxBin = cmd.split(" ")[0];
4798
- if (npxBin.startsWith("/")) {
4848
+ const npxBinRaw = cmd.split(" ")[0];
4849
+ const npxBin = bashPath(npxBinRaw);
4850
+ if (path10.isAbsolute(npxBinRaw)) {
4799
4851
  guard = `[ -x "${npxBin}" ]`;
4800
- const npxArgs = cmd.slice(npxBin.length);
4852
+ const npxArgs = cmd.slice(npxBinRaw.length);
4801
4853
  invoke = `"${npxBin}"${npxArgs}`;
4802
4854
  } else {
4803
4855
  guard = "command -v npx >/dev/null 2>&1";
4804
4856
  invoke = cmd;
4805
4857
  }
4806
4858
  } else {
4807
- if (cmd.startsWith("/")) {
4808
- guard = `[ -x "${cmd}" ]`;
4859
+ const directNode = tryWindowsDirectNodeInvocation(cmd);
4860
+ if (directNode) {
4861
+ const nodeBin = directNode.match(/^"([^"]+)"/)?.[1] ?? "";
4862
+ guard = `[ -x "${nodeBin}" ]`;
4863
+ invoke = directNode;
4809
4864
  } else {
4810
- guard = `[ -x "${cmd}" ] || command -v "${cmd}" >/dev/null 2>&1`;
4865
+ const cmdBash = bashPath(cmd);
4866
+ if (path10.isAbsolute(cmd)) {
4867
+ guard = `[ -x "${cmdBash}" ]`;
4868
+ } else {
4869
+ guard = `[ -x "${cmdBash}" ] || command -v "${cmdBash}" >/dev/null 2>&1`;
4870
+ }
4871
+ invoke = `"${cmdBash}"`;
4811
4872
  }
4812
- invoke = `"${cmd}"`;
4813
4873
  }
4814
4874
  return `${PRECOMMIT_START}
4815
4875
  if ${guard}; then
@@ -13454,6 +13514,7 @@ async function configCommand() {
13454
13514
  }
13455
13515
 
13456
13516
  // src/commands/learn.ts
13517
+ init_windows();
13457
13518
  import fs48 from "fs";
13458
13519
  import path39 from "path";
13459
13520
  import chalk23 from "chalk";
@@ -15223,10 +15284,9 @@ function parseAgentOption(value) {
15223
15284
  )
15224
15285
  ];
15225
15286
  if (agents.length === 0) {
15226
- console.error(
15287
+ throw new Error(
15227
15288
  `Invalid agent "${value}". Choose from: claude, cursor, codex, opencode, github-copilot (comma-separated for multiple)`
15228
15289
  );
15229
- process.exit(1);
15230
15290
  }
15231
15291
  return agents;
15232
15292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.48.0",
3
+ "version": "1.48.2",
4
4
  "description": "AI context infrastructure for coding agents — keeps CLAUDE.md, Cursor rules, and skills in sync as your codebase evolves",
5
5
  "type": "module",
6
6
  "bin": {