worktrust 0.6.1 → 0.6.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/package.json +1 -1
  2. package/setup-mcp.mjs +26 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worktrust",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Couple this computer to WorkTrust: approve a code in your browser, and every AI app here reports what you built. Metadata only, no dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
package/setup-mcp.mjs CHANGED
@@ -43,6 +43,23 @@ function fromClaude() {
43
43
  } catch { return null; }
44
44
  }
45
45
  const discovered = fromClaude();
46
+ /**
47
+ * EVERY DIRECT WORKTRUST ENTRY CLAUDE CODE HOLDS, BY NAME (2026-10-04). A coupling made by hand may
48
+ * carry any name ("worktrust-imac-home") and a key in its header; `npx worktrust` once read it as
49
+ * "already coupled" and left Claude Code on that unbound key. With the bridge, each such entry is
50
+ * taken out and the bridge put in; on removal, they all go. Only entries whose address is a
51
+ * WorkTrust door are touched, user scope here and local scope in the folder that holds it.
52
+ */
53
+ function directClaudeEntries() {
54
+ try {
55
+ const config = JSON.parse(readFileSync(join(homedir(), ".claude.json"), "utf8"));
56
+ const door = (server) => typeof server?.url === "string" && /\/api\/mcp\b/.test(server.url) && /worktrust/i.test(new URL(server.url).host);
57
+ const entries = Object.entries(config.mcpServers ?? {}).filter(([, server]) => door(server)).map(([name]) => ({ name, scope: "user", dir: null }));
58
+ for (const [dir, project] of Object.entries(config.projects ?? {})) for (const [name, server] of Object.entries(project?.mcpServers ?? {})) if (door(server)) entries.push({ name, scope: "local", dir });
59
+ const bridged = Object.values(config.mcpServers ?? {}).some((server) => Array.isArray(server?.args) && server.args.includes(BRIDGE));
60
+ return { entries, bridged };
61
+ } catch { return { entries: [], bridged: false }; }
62
+ }
46
63
  const URL_ = value("url") ?? process.env.WORKTRUST_MCP_URL ?? discovered?.url;
47
64
  const TOKEN = value("token") ?? process.env.WORKTRUST_MCP_TOKEN ?? discovered?.token;
48
65
  if (!URL_ || (!TOKEN && !BRIDGE)) { console.error("no coupling: pass --url and --token (from Sources → Claude (MCP) → Add coupling), or couple Claude Code first"); process.exit(1); }
@@ -75,7 +92,15 @@ for (const client of CLIENTS) {
75
92
  // it when the command is not. User scope, so the coupling holds in every folder on this machine.
76
93
  const claudeArgs = REMOVE ? ["mcp", "remove", "-s", "user", NAME] : BRIDGE ? ["mcp", "add", "-s", "user", NAME, "--", NODE, BRIDGE, "mcp"] : ["mcp", "add", "--transport", "http", "-s", "user", NAME, URL_, "-H", `Authorization: Bearer ${TOKEN}`];
77
94
  const shown = REMOVE ? `claude mcp remove -s user ${NAME}` : BRIDGE ? `claude mcp add -s user ${NAME} -- "${NODE}" "${BRIDGE}" mcp` : `claude mcp add --transport http -s user ${NAME} "${URL_}" -H "Authorization: Bearer <token>"`;
78
- if (!REMOVE && discovered) { console.log(` ✓ ${client.label}: already coupled`); standing += 1; continue; }
95
+ const direct = directClaudeEntries();
96
+ if (!REMOVE && (BRIDGE ? direct.bridged && direct.entries.length === 0 : discovered)) { console.log(` ✓ ${client.label}: already coupled`); standing += 1; continue; }
97
+ // A direct entry under any name is replaced by the bridge, and every one goes on removal.
98
+ if (BRIDGE || REMOVE) for (const entry of direct.entries.filter((one) => !(REMOVE && one.name === NAME && one.scope === "user"))) {
99
+ const verb = `claude mcp remove -s ${entry.scope} ${entry.name}`;
100
+ if (!WRITE) { console.log(` ↻ ${client.label}: the direct entry "${entry.name}" (${entry.scope}) is replaced`); continue; }
101
+ const out = claudeOnPath() ? spawnSync("claude", ["mcp", "remove", "-s", entry.scope, entry.name], { encoding: "utf8", shell: platform() === "win32", ...(entry.dir ? { cwd: entry.dir } : {}) }) : null;
102
+ console.log(out?.status === 0 ? ` – ${client.label}: removed the direct entry "${entry.name}" (${entry.scope})` : ` ! ${client.label}: could not remove "${entry.name}"; run it yourself${entry.dir ? ` in ${entry.dir}` : ""}: ${verb}`);
103
+ }
79
104
  if (WRITE && claudeOnPath()) {
80
105
  const run = spawnSync("claude", claudeArgs, { encoding: "utf8", shell: platform() === "win32" });
81
106
  if (run.status === 0) { console.log(` + ${client.label}: ${REMOVE ? "removed" : "coupled"} through \`claude mcp ${REMOVE ? "remove" : "add"}\` (user scope)`); touched += 1; continue; }