patchcord 0.3.99 → 0.4.0

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 +80 -17
  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,6 +80,7 @@ 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
+ let wasPluginInstalled = false;
83
84
  const { readFileSync, writeFileSync, unlinkSync } = await import("fs");
84
85
 
85
86
  function safeReadJson(filePath) {
@@ -120,6 +121,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
120
121
  // doesn't detect new versions from local sources (#37252).
121
122
  run(`claude plugin marketplace add "${pluginRoot}"`);
122
123
  const installed = run(`claude plugin list`)?.includes("patchcord");
124
+ wasPluginInstalled = !!installed;
123
125
  if (installed) {
124
126
  run(`claude plugin update patchcord@patchcord-marketplace`);
125
127
  globalChanges.push("Claude Code plugin updated");
@@ -155,12 +157,16 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
155
157
  }
156
158
  }
157
159
 
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`);
160
+ // Statusline: enabled later in the Claude Code project-setup branch (prompts user).
161
+ // If --full flag passed non-interactively, install eagerly here so non-Claude-Code
162
+ // flows (e.g. setting up Codex on a machine with Claude Code) still get the upgrade.
163
+ if (fullStatusline) {
164
+ const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
165
+ if (existsSync(enableScript)) {
166
+ const slResult = run(`bash "${enableScript}" --full`);
167
+ if (slResult !== null && slResult.includes("statusline")) {
168
+ globalChanges.push("Statusline (full) enabled");
169
+ }
164
170
  }
165
171
  }
166
172
  }
@@ -218,10 +224,10 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
218
224
  cpSync(join(pluginRoot, "skills", "patchcord-wait", "SKILL.md"), join(geminiWaitDir, "SKILL.md"));
219
225
  geminiChanged = true;
220
226
  }
221
- if (!existsSync(join(geminiCmdDir, "patchcord.toml"))) {
227
+ if (!existsSync(join(geminiCmdDir, "inbox.toml"))) {
222
228
  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"));
229
+ cpSync(join(pluginRoot, "commands", "inbox.toml"), join(geminiCmdDir, "inbox.toml"));
230
+ cpSync(join(pluginRoot, "commands", "wait.toml"), join(geminiCmdDir, "wait.toml"));
225
231
  geminiChanged = true;
226
232
  }
227
233
  if (geminiChanged) globalChanges.push("Gemini CLI skills + commands installed");
@@ -993,14 +999,71 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
993
999
  }
994
1000
  console.log(`\n ${green}✓${r} Claude Code configured: ${dim}${mcpPath}${r}`);
995
1001
 
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`);
1002
+ // Statusline: ask whether to enable full mode (unless --full flag passed or already configured).
1003
+ const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
1004
+ if (existsSync(enableScript)) {
1005
+ let currentStatusLine = "";
1006
+ const claudeSettingsPath = join(HOME, ".claude", "settings.json");
1007
+ if (existsSync(claudeSettingsPath)) {
1008
+ try {
1009
+ const s = JSON.parse(readFileSync(claudeSettingsPath, "utf-8"));
1010
+ currentStatusLine = s?.statusLine?.command || "";
1011
+ } catch {}
1002
1012
  }
1003
- } catch {}
1013
+ const hasPatchcordStatusLine = currentStatusLine.includes("patchcord");
1014
+
1015
+ let installFull = null; // true = install, null = skip
1016
+ if (fullStatusline) {
1017
+ installFull = true; // already handled in global setup, but re-run is idempotent
1018
+ } else if (!hasPatchcordStatusLine) {
1019
+ // Build preview with user's real values
1020
+ const dirName = basename(cwd) || "project";
1021
+ let branchPart = "";
1022
+ const gitBranch = run(`git -C "${cwd}" symbolic-ref --short HEAD 2>/dev/null`);
1023
+ if (gitBranch) {
1024
+ const dirty = run(`git -C "${cwd}" status --porcelain 2>/dev/null`);
1025
+ const star = dirty ? `${red}*${green}` : "";
1026
+ branchPart = ` ${green}(${gitBranch}${star})${r}`;
1027
+ }
1028
+ const idStr = identity || "your-agent@namespace";
1029
+ const idParts = idStr.split("@");
1030
+ const idPart = idParts.length === 2
1031
+ ? `${white}${idParts[0]}${r}${dim}@${idParts[1]}${r}`
1032
+ : `${white}${idStr}${r}`;
1033
+ const machinePart = hostname ? ` ${dim}(${hostname})${r}` : "";
1034
+ const sep = `${dim} │ ${r}`;
1035
+ const blue = "\x1b[38;2;0;153;255m";
1036
+ const example = `${blue}Claude Opus 4.7${r}${sep}${green}0%${r}${sep}${cyan}${dirName}${r}${branchPart}${sep}${idPart}${machinePart}`;
1037
+
1038
+ console.log(``);
1039
+ console.log(` Patchcord can show live agent identity + context in Claude Code's status bar.`);
1040
+ console.log(``);
1041
+ console.log(` Full mode: ${example}`);
1042
+ console.log(``);
1043
+
1044
+ const defaultYes = !wasPluginInstalled;
1045
+ const promptStr = defaultYes
1046
+ ? ` ${bold}Install full status bar?${r} ${dim}(Y/n):${r} `
1047
+ : ` ${bold}Install full status bar?${r} ${dim}(y/N):${r} `;
1048
+ const { createInterface: createRLS } = await import("readline");
1049
+ const rlS = createRLS({ input: process.stdin, output: process.stdout });
1050
+ const answer = await new Promise((resolve) => rlS.question(promptStr, resolve));
1051
+ rlS.close();
1052
+ const a = (answer || "").trim().toLowerCase();
1053
+ const yes = defaultYes
1054
+ ? !(a === "n" || a === "no")
1055
+ : (a === "y" || a === "yes");
1056
+ if (yes) installFull = true;
1057
+ }
1058
+ // else: existing patchcord statusline — leave it alone
1059
+
1060
+ if (installFull === true) {
1061
+ try {
1062
+ execSync(`bash "${enableScript}" --full`, { stdio: "ignore" });
1063
+ console.log(` ${green}✓${r} Statusline enabled`);
1064
+ } catch {}
1065
+ }
1066
+ }
1004
1067
  }
1005
1068
 
1006
1069
  // 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.0",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",