patchcord 0.3.99 → 0.4.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.
Files changed (2) hide show
  1. package/bin/patchcord.mjs +87 -18
  2. package/package.json +1 -1
package/bin/patchcord.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { existsSync, mkdirSync, cpSync, readdirSync } from "fs";
4
- import { join, dirname } from "path";
4
+ import { join, dirname, basename } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { execSync } from "child_process";
7
7
  import { homedir } from "os";
@@ -80,7 +80,8 @@ if (cmd === "plugin-path") {
80
80
  if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd === "--no-browser" || cmd === "--server") {
81
81
  const flags = cmd?.startsWith("--") ? process.argv.slice(2) : process.argv.slice(3);
82
82
  const fullStatusline = flags.includes("--full");
83
- const { readFileSync, writeFileSync, unlinkSync } = await import("fs");
83
+ let wasPluginInstalled = false;
84
+ const { readFileSync, writeFileSync, unlinkSync, rmSync } = await import("fs");
84
85
 
85
86
  function safeReadJson(filePath) {
86
87
  try {
@@ -115,11 +116,18 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
115
116
  // Claude Code
116
117
  const hasClaude = run("which claude");
117
118
  if (hasClaude) {
119
+ // Remove legacy npm-cache install (pre-marketplace era) — causes duplicate /patchcord commands.
120
+ const npmCachePatchcord = join(HOME, ".claude", "plugins", "npm-cache", "node_modules", "patchcord");
121
+ if (existsSync(npmCachePatchcord)) {
122
+ try { rmSync(npmCachePatchcord, { recursive: true, force: true }); } catch {}
123
+ }
124
+
118
125
  // Always re-add marketplace (copies fresh files from this npx package)
119
126
  // and install/update plugin. Claude Code's built-in plugin update
120
127
  // doesn't detect new versions from local sources (#37252).
121
128
  run(`claude plugin marketplace add "${pluginRoot}"`);
122
129
  const installed = run(`claude plugin list`)?.includes("patchcord");
130
+ wasPluginInstalled = !!installed;
123
131
  if (installed) {
124
132
  run(`claude plugin update patchcord@patchcord-marketplace`);
125
133
  globalChanges.push("Claude Code plugin updated");
@@ -155,12 +163,16 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
155
163
  }
156
164
  }
157
165
 
158
- const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
159
- if (existsSync(enableScript)) {
160
- const slArg = fullStatusline ? " --full" : "";
161
- const slResult = run(`bash "${enableScript}"${slArg}`);
162
- if (slResult !== null && slResult.includes("statusline")) {
163
- globalChanges.push(`Statusline${fullStatusline ? " (full)" : ""} enabled`);
166
+ // Statusline: enabled later in the Claude Code project-setup branch (prompts user).
167
+ // If --full flag passed non-interactively, install eagerly here so non-Claude-Code
168
+ // flows (e.g. setting up Codex on a machine with Claude Code) still get the upgrade.
169
+ if (fullStatusline) {
170
+ const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
171
+ if (existsSync(enableScript)) {
172
+ const slResult = run(`bash "${enableScript}" --full`);
173
+ if (slResult !== null && slResult.includes("statusline")) {
174
+ globalChanges.push("Statusline (full) enabled");
175
+ }
164
176
  }
165
177
  }
166
178
  }
@@ -218,10 +230,10 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
218
230
  cpSync(join(pluginRoot, "skills", "patchcord-wait", "SKILL.md"), join(geminiWaitDir, "SKILL.md"));
219
231
  geminiChanged = true;
220
232
  }
221
- if (!existsSync(join(geminiCmdDir, "patchcord.toml"))) {
233
+ if (!existsSync(join(geminiCmdDir, "inbox.toml"))) {
222
234
  mkdirSync(geminiCmdDir, { recursive: true });
223
- cpSync(join(pluginRoot, "commands", "patchcord.toml"), join(geminiCmdDir, "patchcord.toml"));
224
- cpSync(join(pluginRoot, "commands", "patchcord-wait.toml"), join(geminiCmdDir, "patchcord-wait.toml"));
235
+ cpSync(join(pluginRoot, "commands", "inbox.toml"), join(geminiCmdDir, "inbox.toml"));
236
+ cpSync(join(pluginRoot, "commands", "wait.toml"), join(geminiCmdDir, "wait.toml"));
225
237
  geminiChanged = true;
226
238
  }
227
239
  if (geminiChanged) globalChanges.push("Gemini CLI skills + commands installed");
@@ -993,14 +1005,71 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
993
1005
  }
994
1006
  console.log(`\n ${green}✓${r} Claude Code configured: ${dim}${mcpPath}${r}`);
995
1007
 
996
- // Enable patchcord statusline (agent identity + inbox count in status bar)
997
- try {
998
- const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
999
- if (existsSync(enableScript)) {
1000
- execSync(`bash "${enableScript}" --full`, { stdio: "ignore" });
1001
- console.log(` ${green}✓${r} Statusline enabled`);
1008
+ // Statusline: ask whether to enable full mode (unless --full flag passed or already configured).
1009
+ const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
1010
+ if (existsSync(enableScript)) {
1011
+ let currentStatusLine = "";
1012
+ const claudeSettingsPath = join(HOME, ".claude", "settings.json");
1013
+ if (existsSync(claudeSettingsPath)) {
1014
+ try {
1015
+ const s = JSON.parse(readFileSync(claudeSettingsPath, "utf-8"));
1016
+ currentStatusLine = s?.statusLine?.command || "";
1017
+ } catch {}
1002
1018
  }
1003
- } catch {}
1019
+ const hasPatchcordStatusLine = currentStatusLine.includes("patchcord");
1020
+
1021
+ let installFull = null; // true = install, null = skip
1022
+ if (fullStatusline) {
1023
+ installFull = true; // already handled in global setup, but re-run is idempotent
1024
+ } else if (!hasPatchcordStatusLine) {
1025
+ // Build preview with user's real values
1026
+ const dirName = basename(cwd) || "project";
1027
+ let branchPart = "";
1028
+ const gitBranch = run(`git -C "${cwd}" symbolic-ref --short HEAD 2>/dev/null`);
1029
+ if (gitBranch) {
1030
+ const dirty = run(`git -C "${cwd}" status --porcelain 2>/dev/null`);
1031
+ const star = dirty ? `${red}*${green}` : "";
1032
+ branchPart = ` ${green}(${gitBranch}${star})${r}`;
1033
+ }
1034
+ const idStr = identity || "your-agent@namespace";
1035
+ const idParts = idStr.split("@");
1036
+ const idPart = idParts.length === 2
1037
+ ? `${white}${idParts[0]}${r}${dim}@${idParts[1]}${r}`
1038
+ : `${white}${idStr}${r}`;
1039
+ const machinePart = hostname ? ` ${dim}(${hostname})${r}` : "";
1040
+ const sep = `${dim} │ ${r}`;
1041
+ const blue = "\x1b[38;2;0;153;255m";
1042
+ const example = `${blue}Claude Opus 4.7${r}${sep}${green}0%${r}${sep}${cyan}${dirName}${r}${branchPart}${sep}${idPart}${machinePart}`;
1043
+
1044
+ console.log(``);
1045
+ console.log(` Patchcord can show live agent identity + context in Claude Code's status bar.`);
1046
+ console.log(``);
1047
+ console.log(` Full mode: ${example}`);
1048
+ console.log(``);
1049
+
1050
+ const defaultYes = !wasPluginInstalled;
1051
+ const promptStr = defaultYes
1052
+ ? ` ${bold}Install full status bar?${r} ${dim}(Y/n):${r} `
1053
+ : ` ${bold}Install full status bar?${r} ${dim}(y/N):${r} `;
1054
+ const { createInterface: createRLS } = await import("readline");
1055
+ const rlS = createRLS({ input: process.stdin, output: process.stdout });
1056
+ const answer = await new Promise((resolve) => rlS.question(promptStr, resolve));
1057
+ rlS.close();
1058
+ const a = (answer || "").trim().toLowerCase();
1059
+ const yes = defaultYes
1060
+ ? !(a === "n" || a === "no")
1061
+ : (a === "y" || a === "yes");
1062
+ if (yes) installFull = true;
1063
+ }
1064
+ // else: existing patchcord statusline — leave it alone
1065
+
1066
+ if (installFull === true) {
1067
+ try {
1068
+ execSync(`bash "${enableScript}" --full`, { stdio: "ignore" });
1069
+ console.log(` ${green}✓${r} Statusline enabled`);
1070
+ } catch {}
1071
+ }
1072
+ }
1004
1073
  }
1005
1074
 
1006
1075
  // Warn about gitignore for per-project configs with tokens
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.3.99",
3
+ "version": "0.4.1",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",