patchcord 0.5.69 → 0.5.71
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/bin/patchcord.mjs +87 -31
- 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
|
-
|
|
715
|
-
//
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
//
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
//
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
console.log(
|
|
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,15 +1794,21 @@ 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
|
|
1751
|
-
|
|
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
|
-
|
|
1804
|
+
let aliasAlreadyPresent = false;
|
|
1805
|
+
for (const rcFile of shellRcCandidates) {
|
|
1756
1806
|
if (!existsSync(rcFile)) continue;
|
|
1757
1807
|
const rcContent = readFileSync(rcFile, "utf-8");
|
|
1758
|
-
if (rcContent.includes("alias kimi-pc="))
|
|
1808
|
+
if (rcContent.includes("alias kimi-pc=")) {
|
|
1809
|
+
aliasAlreadyPresent = true;
|
|
1810
|
+
continue;
|
|
1811
|
+
}
|
|
1759
1812
|
writeFileSync(
|
|
1760
1813
|
rcFile,
|
|
1761
1814
|
rcContent.trimEnd() + `\n\n${aliasComment}\n${aliasLine}\n`
|
|
@@ -1763,12 +1816,15 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1763
1816
|
aliasInstalled = true;
|
|
1764
1817
|
}
|
|
1765
1818
|
if (aliasInstalled) {
|
|
1766
|
-
console.log(`\n ${green}✓${r} Alias installed in
|
|
1819
|
+
console.log(`\n ${green}✓${r} Alias installed in ${primaryName}`);
|
|
1820
|
+
console.log(` ${dim}Run ${bold}kimi-pc${r}${dim} instead of ${bold}kimi${r}${dim} in project directories.${r}`);
|
|
1821
|
+
console.log(` ${dim}Reload your shell or run ${cyan}source ~/${primaryName}${r}${dim} to use it now.${r}`);
|
|
1822
|
+
} else if (aliasAlreadyPresent) {
|
|
1823
|
+
console.log(`\n ${green}✓${r} Alias already present in shell config`);
|
|
1767
1824
|
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}`);
|
|
1769
1825
|
} else {
|
|
1770
|
-
console.log(`\n ${yellow}⚠${r}
|
|
1771
|
-
console.log(` ${dim}Add this to your
|
|
1826
|
+
console.log(`\n ${yellow}⚠${r} No shell config found (~/.bashrc or ~/.zshrc).`);
|
|
1827
|
+
console.log(` ${dim}Add this to your ~/${primaryName} manually:${r}`);
|
|
1772
1828
|
console.log(` ${cyan}${aliasLine}${r}`);
|
|
1773
1829
|
}
|
|
1774
1830
|
} else if (isVSCode) {
|