patchcord 0.5.69 → 0.5.70

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/bin/patchcord.mjs +78 -29
  2. package/package.json +1 -1
package/bin/patchcord.mjs CHANGED
@@ -709,13 +709,56 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
709
709
  let wasPluginInstalled = false;
710
710
  const { readFileSync, writeFileSync, unlinkSync, rmSync, chmodSync, copyFileSync } = await import("fs");
711
711
 
712
+ // JSONC-safe comment stripper that preserves content inside string literals.
713
+ // Prevents corrupting URLs like "https://..." which contain "//".
714
+ function _stripJsoncOutsideStrings(str) {
715
+ let out = "";
716
+ let i = 0;
717
+ let inStr = false;
718
+ let strCh = "";
719
+ let prev = "";
720
+ while (i < str.length) {
721
+ const c = str[i];
722
+ if (inStr) {
723
+ out += c;
724
+ if (c === strCh && prev !== "\\") inStr = false;
725
+ prev = c;
726
+ i++;
727
+ continue;
728
+ }
729
+ if (c === '"' || c === "'") {
730
+ inStr = true;
731
+ strCh = c;
732
+ out += c;
733
+ prev = c;
734
+ i++;
735
+ continue;
736
+ }
737
+ if (c === "/" && str[i + 1] === "/") {
738
+ while (i < str.length && str[i] !== "\n") i++;
739
+ continue;
740
+ }
741
+ if (c === "/" && str[i + 1] === "*") {
742
+ i += 2;
743
+ while (i < str.length && !(str[i] === "*" && str[i + 1] === "/")) i++;
744
+ i += 2;
745
+ continue;
746
+ }
747
+ out += c;
748
+ prev = c;
749
+ i++;
750
+ }
751
+ return out.replace(/,(\s*[}\]])/g, "$1");
752
+ }
753
+
712
754
  function safeReadJson(filePath) {
713
755
  try {
714
- let content = readFileSync(filePath, "utf-8");
715
- // Strip JSONC comments (Zed, Gemini use JSONC)
716
- content = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
717
- content = content.replace(/,\s*([}\]])/g, "$1");
718
- return JSON.parse(content);
756
+ const raw = readFileSync(filePath, "utf-8");
757
+ // Try strict JSON first (avoids corrupting URLs with //)
758
+ try { return JSON.parse(raw); } catch {}
759
+ // Fall back to JSONC stripping for Zed/Gemini-style configs
760
+ const cleaned = _stripJsoncOutsideStrings(raw);
761
+ return cleaned.trim() ? JSON.parse(cleaned) : {};
719
762
  } catch { return null; }
720
763
  }
721
764
 
@@ -729,24 +772,28 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
729
772
  if (existsSync(filePath)) {
730
773
  const raw = readFileSync(filePath, "utf-8");
731
774
  try {
732
- // JSONC-tolerant: Zed/Gemini settings allow //, /* */, trailing commas.
733
- const cleaned = raw
734
- .replace(/\/\/.*$/gm, "")
735
- .replace(/\/\*[\s\S]*?\*\//g, "")
736
- .replace(/,\s*([}\]])/g, "$1");
737
- const result = cleaned.trim() ? JSON.parse(cleaned) : {};
738
- // null / arrays / primitives aren't config objects we can merge into;
739
- // refuse rather than blow them away.
740
- if (result === null || typeof result !== "object" || Array.isArray(result)) {
741
- console.log(`\n ${yellow}⚠${r} Skipped ${filePath} existing file is not a JSON object.`);
742
- console.log(` Replace it with a valid object ({...}) or remove it, then re-run.`);
775
+ // Try strict JSON first (most configs are plain JSON). This avoids
776
+ // corrupting URLs like "https://..." which contain "//".
777
+ parsed = JSON.parse(raw);
778
+ } catch {
779
+ // Fall back to JSONC stripping for Zed/Gemini-style configs.
780
+ // _stripJsoncOutsideStrings preserves content inside string literals
781
+ // so URLs like "https://..." survive.
782
+ try {
783
+ const cleaned = _stripJsoncOutsideStrings(raw);
784
+ parsed = cleaned.trim() ? JSON.parse(cleaned) : {};
785
+ } catch {
786
+ console.log(`\n ${yellow}⚠${r} Skipped ${filePath} — could not parse existing JSON.`);
787
+ console.log(` Fix the file by hand and re-run the installer. We will not`);
788
+ console.log(` overwrite it: that would erase your other servers/settings.`);
743
789
  return false;
744
790
  }
745
- parsed = result;
746
- } catch {
747
- console.log(`\n ${yellow}⚠${r} Skipped ${filePath} could not parse existing JSON.`);
748
- console.log(` Fix the file by hand and re-run the installer. We will not`);
749
- console.log(` overwrite it: that would erase your other servers/settings.`);
791
+ }
792
+ // null / arrays / primitives aren't config objects we can merge into;
793
+ // refuse rather than blow them away.
794
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
795
+ console.log(`\n ${yellow}⚠${r} Skipped ${filePath} existing file is not a JSON object.`);
796
+ console.log(` Replace it with a valid object ({...}) or remove it, then re-run.`);
750
797
  return false;
751
798
  }
752
799
  }
@@ -1747,12 +1794,14 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1747
1794
  // Install alias for per-project --mcp-config-file
1748
1795
  const aliasLine = `alias kimi-pc='kimi --mcp-config-file .kimi/mcp.json'`;
1749
1796
  const aliasComment = "# Patchcord Kimi alias — loads per-project .kimi/mcp.json";
1750
- const shellRcFiles = [
1751
- join(HOME, ".bashrc"),
1752
- join(HOME, ".zshrc"),
1753
- ];
1797
+ const isZsh = (process.env.SHELL || "").includes("zsh");
1798
+ const shellRcCandidates = isZsh
1799
+ ? [join(HOME, ".zshrc"), join(HOME, ".bashrc")]
1800
+ : [join(HOME, ".bashrc"), join(HOME, ".zshrc")];
1801
+ const primaryRc = shellRcCandidates[0];
1802
+ const primaryName = basename(primaryRc);
1754
1803
  let aliasInstalled = false;
1755
- for (const rcFile of shellRcFiles) {
1804
+ for (const rcFile of shellRcCandidates) {
1756
1805
  if (!existsSync(rcFile)) continue;
1757
1806
  const rcContent = readFileSync(rcFile, "utf-8");
1758
1807
  if (rcContent.includes("alias kimi-pc=")) continue; // already present
@@ -1763,12 +1812,12 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1763
1812
  aliasInstalled = true;
1764
1813
  }
1765
1814
  if (aliasInstalled) {
1766
- console.log(`\n ${green}✓${r} Alias installed in shell config`);
1815
+ console.log(`\n ${green}✓${r} Alias installed in ${primaryName}`);
1767
1816
  console.log(` ${dim}Run ${bold}kimi-pc${r}${dim} instead of ${bold}kimi${r}${dim} in project directories.${r}`);
1768
- console.log(` ${dim}Reload your shell or run ${cyan}source ~/.bashrc${r}${dim} (or ~/.zshrc) to use it now.${r}`);
1817
+ console.log(` ${dim}Reload your shell or run ${cyan}source ~/${primaryName}${r}${dim} to use it now.${r}`);
1769
1818
  } else {
1770
1819
  console.log(`\n ${yellow}⚠${r} Could not auto-install alias.`);
1771
- console.log(` ${dim}Add this to your ~/.bashrc or ~/.zshrc manually:${r}`);
1820
+ console.log(` ${dim}Add this to your ~/${primaryName} manually:${r}`);
1772
1821
  console.log(` ${cyan}${aliasLine}${r}`);
1773
1822
  }
1774
1823
  } else if (isVSCode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.69",
3
+ "version": "0.5.70",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",