patchcord 0.3.57 → 0.3.59
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 +48 -224
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -77,8 +77,8 @@ if (cmd === "plugin-path") {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// ── main flow: global setup + project setup (or just install/agent for back-compat) ──
|
|
80
|
-
if (!cmd || cmd === "install" || cmd === "agent") {
|
|
81
|
-
const flags = process.argv.slice(3);
|
|
80
|
+
if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd === "--no-browser") {
|
|
81
|
+
const flags = cmd?.startsWith("--") ? process.argv.slice(2) : process.argv.slice(3);
|
|
82
82
|
const fullStatusline = flags.includes("--full");
|
|
83
83
|
const { readFileSync, writeFileSync } = await import("fs");
|
|
84
84
|
|
|
@@ -252,231 +252,16 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
252
252
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
253
253
|
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
console.log(` ${cyan}2.${r} Codex CLI`);
|
|
258
|
-
console.log(` ${cyan}3.${r} Cursor`);
|
|
259
|
-
console.log(` ${cyan}4.${r} Windsurf`);
|
|
260
|
-
console.log(` ${cyan}5.${r} Gemini CLI`);
|
|
261
|
-
console.log(` ${cyan}6.${r} VS Code (Copilot)`);
|
|
262
|
-
console.log(` ${cyan}7.${r} Zed`);
|
|
263
|
-
console.log(` ${cyan}8.${r} OpenCode\n`);
|
|
264
|
-
|
|
265
|
-
const choice = (await ask(`${dim}Choose (1-8):${r} `)).trim();
|
|
266
|
-
const isCodex = choice === "2";
|
|
267
|
-
const isCursor = choice === "3";
|
|
268
|
-
const isWindsurf = choice === "4";
|
|
269
|
-
const isGemini = choice === "5";
|
|
270
|
-
const isVSCode = choice === "6";
|
|
271
|
-
const isZed = choice === "7";
|
|
272
|
-
const isOpenCode = choice === "8";
|
|
255
|
+
// Tool picker only shown for --token bypass. Browser flow gets tool from web.
|
|
256
|
+
let choice = "";
|
|
273
257
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (isWindsurf || isGemini || isZed) {
|
|
281
|
-
const toolLabel = isZed ? "Zed" : isWindsurf ? "Windsurf" : "Gemini CLI";
|
|
282
|
-
console.log(`\n ${yellow}Note: ${toolLabel} uses global config — applies to all projects.${r}`);
|
|
283
|
-
} else {
|
|
284
|
-
const folderType = detectFolder(cwd);
|
|
285
|
-
const folderName = cwd.split("/").pop() || cwd.split("\\").pop() || cwd;
|
|
286
|
-
|
|
287
|
-
if (folderType === "HOME") {
|
|
288
|
-
console.log(`\n ${red}✗ You're in your home folder.${r}`);
|
|
289
|
-
console.log(` ${yellow}Patchcord must be installed inside a project folder —${r}`);
|
|
290
|
-
console.log(` ${yellow}the folder where your agent actually runs.${r}`);
|
|
291
|
-
console.log(` ${dim}cd into your project first, then run this again.${r}`);
|
|
292
|
-
rl.close();
|
|
293
|
-
process.exit(0);
|
|
294
|
-
} else if (folderType === "CONTAINER") {
|
|
295
|
-
console.log(`\n ${yellow}⚠ This looks like a projects container, not a project.${r}`);
|
|
296
|
-
console.log(` ${dim}${cwd}${r}`);
|
|
297
|
-
console.log(` ${dim}Patchcord should be installed inside a project, not the folder above it.${r}`);
|
|
298
|
-
const proceed = (await ask(` ${dim}Set up here anyway? (y/N):${r} `)).trim().toLowerCase();
|
|
299
|
-
if (proceed !== "y" && proceed !== "yes") {
|
|
300
|
-
console.log(` ${dim}cd into your project and run this again.${r}`);
|
|
301
|
-
rl.close();
|
|
302
|
-
process.exit(0);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
258
|
+
const CLIENT_TYPE_MAP = {
|
|
259
|
+
"claude_code": "1", "codex": "2", "cursor": "3", "windsurf": "4",
|
|
260
|
+
"gemini": "5", "vscode": "6", "zed": "7", "opencode": "8",
|
|
261
|
+
};
|
|
305
262
|
|
|
306
|
-
console.log(`\n ${dim}Agent identity:${r} ${bold}${folderName}${r}`);
|
|
307
|
-
console.log(` ${dim}Folder:${r} ${cwd}`);
|
|
308
|
-
}
|
|
309
263
|
|
|
310
264
|
|
|
311
|
-
// Check if already configured
|
|
312
|
-
if (choice === "1") {
|
|
313
|
-
const mcpPath = join(cwd, ".mcp.json");
|
|
314
|
-
if (existsSync(mcpPath)) {
|
|
315
|
-
try {
|
|
316
|
-
const existing = JSON.parse(readFileSync(mcpPath, "utf-8"));
|
|
317
|
-
if (existing.mcpServers?.patchcord) {
|
|
318
|
-
console.log(`\n ${yellow}⚠ Claude Code already configured in this project${r}`);
|
|
319
|
-
console.log(` ${dim}${mcpPath}${r}`);
|
|
320
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
321
|
-
if (replace !== "y" && replace !== "yes") {
|
|
322
|
-
console.log("Keeping existing config.");
|
|
323
|
-
rl.close();
|
|
324
|
-
process.exit(0);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
} catch {}
|
|
328
|
-
}
|
|
329
|
-
} else if (isCursor) {
|
|
330
|
-
const cursorPath = join(cwd, ".cursor", "mcp.json");
|
|
331
|
-
if (existsSync(cursorPath)) {
|
|
332
|
-
try {
|
|
333
|
-
const existing = JSON.parse(readFileSync(cursorPath, "utf-8"));
|
|
334
|
-
if (existing.mcpServers?.patchcord) {
|
|
335
|
-
console.log(`\n ${yellow}⚠ Cursor already configured in this project${r}`);
|
|
336
|
-
console.log(` ${dim}${cursorPath}${r}`);
|
|
337
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
338
|
-
if (replace !== "y" && replace !== "yes") {
|
|
339
|
-
console.log("Keeping existing config.");
|
|
340
|
-
rl.close();
|
|
341
|
-
process.exit(0);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
} catch {}
|
|
345
|
-
}
|
|
346
|
-
// Warn about global config conflict
|
|
347
|
-
const globalCursor = join(HOME, ".cursor", "mcp.json");
|
|
348
|
-
if (existsSync(globalCursor)) {
|
|
349
|
-
try {
|
|
350
|
-
const global = JSON.parse(readFileSync(globalCursor, "utf-8"));
|
|
351
|
-
if (global.mcpServers?.patchcord) {
|
|
352
|
-
console.log(`\n ${yellow}⚠ Patchcord is also configured globally in Cursor${r}`);
|
|
353
|
-
console.log(` ${dim}${globalCursor}${r}`);
|
|
354
|
-
console.log(` ${yellow}Having both global AND per-project will cause duplicate tool calls.${r}`);
|
|
355
|
-
console.log(` ${dim}Remove patchcord from global config: Cursor Settings → MCP → remove patchcord${r}`);
|
|
356
|
-
}
|
|
357
|
-
} catch {}
|
|
358
|
-
}
|
|
359
|
-
} else if (isWindsurf) {
|
|
360
|
-
const wsPath = join(HOME, ".codeium", "windsurf", "mcp_config.json");
|
|
361
|
-
if (existsSync(wsPath)) {
|
|
362
|
-
try {
|
|
363
|
-
const content = readFileSync(wsPath, "utf-8").trim();
|
|
364
|
-
const existing = content ? JSON.parse(content) : {};
|
|
365
|
-
if (existing.mcpServers?.patchcord) {
|
|
366
|
-
console.log(`\n ${yellow}⚠ Windsurf already configured${r}`);
|
|
367
|
-
console.log(` ${dim}${wsPath}${r}`);
|
|
368
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
369
|
-
if (replace !== "y" && replace !== "yes") {
|
|
370
|
-
console.log("Keeping existing config.");
|
|
371
|
-
rl.close();
|
|
372
|
-
process.exit(0);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
} catch {}
|
|
376
|
-
}
|
|
377
|
-
} else if (isGemini) {
|
|
378
|
-
const geminiPath = join(HOME, ".gemini", "settings.json");
|
|
379
|
-
if (existsSync(geminiPath)) {
|
|
380
|
-
try {
|
|
381
|
-
const existing = safeReadJson(geminiPath) || {};
|
|
382
|
-
if (existing.mcpServers?.patchcord) {
|
|
383
|
-
console.log(`\n ${yellow}⚠ Gemini CLI already configured${r}`);
|
|
384
|
-
console.log(` ${dim}${geminiPath}${r}`);
|
|
385
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
386
|
-
if (replace !== "y" && replace !== "yes") {
|
|
387
|
-
console.log("Keeping existing config.");
|
|
388
|
-
rl.close();
|
|
389
|
-
process.exit(0);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
} catch {}
|
|
393
|
-
}
|
|
394
|
-
} else if (isVSCode) {
|
|
395
|
-
const vscodePath = join(cwd, ".vscode", "mcp.json");
|
|
396
|
-
if (existsSync(vscodePath)) {
|
|
397
|
-
try {
|
|
398
|
-
const existing = JSON.parse(readFileSync(vscodePath, "utf-8"));
|
|
399
|
-
if (existing.servers?.patchcord) {
|
|
400
|
-
console.log(`\n ${yellow}⚠ VS Code already configured in this project${r}`);
|
|
401
|
-
console.log(` ${dim}${vscodePath}${r}`);
|
|
402
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
403
|
-
if (replace !== "y" && replace !== "yes") {
|
|
404
|
-
console.log("Keeping existing config.");
|
|
405
|
-
rl.close();
|
|
406
|
-
process.exit(0);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
} catch {}
|
|
410
|
-
}
|
|
411
|
-
} else if (isZed) {
|
|
412
|
-
const zedPath = process.platform === "darwin"
|
|
413
|
-
? join(HOME, "Library", "Application Support", "Zed", "settings.json")
|
|
414
|
-
: join(HOME, ".config", "zed", "settings.json");
|
|
415
|
-
if (existsSync(zedPath)) {
|
|
416
|
-
try {
|
|
417
|
-
const existing = safeReadJson(zedPath) || {};
|
|
418
|
-
if (existing.context_servers?.patchcord) {
|
|
419
|
-
console.log(`\n ${yellow}⚠ Zed already configured${r}`);
|
|
420
|
-
console.log(` ${dim}${zedPath}${r}`);
|
|
421
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
422
|
-
if (replace !== "y" && replace !== "yes") {
|
|
423
|
-
console.log("Keeping existing config.");
|
|
424
|
-
rl.close();
|
|
425
|
-
process.exit(0);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
} catch {}
|
|
429
|
-
}
|
|
430
|
-
} else if (isOpenCode) {
|
|
431
|
-
const ocPath = join(cwd, "opencode.json");
|
|
432
|
-
if (existsSync(ocPath)) {
|
|
433
|
-
try {
|
|
434
|
-
const existing = JSON.parse(readFileSync(ocPath, "utf-8"));
|
|
435
|
-
if (existing.mcp?.patchcord) {
|
|
436
|
-
console.log(`\n ${yellow}⚠ OpenCode already configured in this project${r}`);
|
|
437
|
-
console.log(` ${dim}${ocPath}${r}`);
|
|
438
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
439
|
-
if (replace !== "y" && replace !== "yes") {
|
|
440
|
-
console.log("Keeping existing config.");
|
|
441
|
-
rl.close();
|
|
442
|
-
process.exit(0);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
} catch {}
|
|
446
|
-
}
|
|
447
|
-
} else if (isCodex) {
|
|
448
|
-
// Check global config for stale patchcord MCP — user may have run installer in ~ by mistake
|
|
449
|
-
const globalCodexConfig = join(HOME, ".codex", "config.toml");
|
|
450
|
-
if (existsSync(globalCodexConfig)) {
|
|
451
|
-
const globalContent = readFileSync(globalCodexConfig, "utf-8");
|
|
452
|
-
if (globalContent.includes("[mcp_servers.patchcord-codex]")) {
|
|
453
|
-
console.log(`\n ${red}⚠ Patchcord is in your GLOBAL Codex config!${r}`);
|
|
454
|
-
console.log(` ${dim}${globalCodexConfig}${r}`);
|
|
455
|
-
console.log(` ${yellow}This overrides per-project config and causes conflicts.${r}`);
|
|
456
|
-
const cleanGlobal = (await ask(` ${dim}Remove patchcord from global config? (Y/n):${r} `)).trim().toLowerCase();
|
|
457
|
-
if (cleanGlobal !== "n" && cleanGlobal !== "no") {
|
|
458
|
-
const cleaned = globalContent.replace(/\[mcp_servers\.patchcord\]\n(?:(?!\[)[^\n]*\n?)*/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
459
|
-
writeFileSync(globalCodexConfig, cleaned + "\n");
|
|
460
|
-
console.log(` ${green}✓${r} Removed from global config`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const configPath = join(cwd, ".codex", "config.toml");
|
|
465
|
-
if (existsSync(configPath)) {
|
|
466
|
-
const content = readFileSync(configPath, "utf-8");
|
|
467
|
-
if (content.includes("[mcp_servers.patchcord-codex]")) {
|
|
468
|
-
console.log(`\n ${yellow}⚠ Codex CLI already configured in this project${r}`);
|
|
469
|
-
console.log(` ${dim}${configPath}${r}`);
|
|
470
|
-
const replace = (await ask(` ${dim}Replace? (y/N):${r} `)).trim().toLowerCase();
|
|
471
|
-
if (replace !== "y" && replace !== "yes") {
|
|
472
|
-
console.log("Keeping existing config.");
|
|
473
|
-
rl.close();
|
|
474
|
-
process.exit(0);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
265
|
let token = "";
|
|
481
266
|
let identity = "";
|
|
482
267
|
let serverUrl = "https://mcp.patchcord.dev";
|
|
@@ -488,6 +273,18 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
488
273
|
|| (flags.includes("--token") ? flags[flags.indexOf("--token") + 1] : "");
|
|
489
274
|
|
|
490
275
|
if (tokenFlag) {
|
|
276
|
+
// --token bypass: need tool picker in terminal
|
|
277
|
+
console.log(`\n${bold}Which tool are you setting up?${r}\n`);
|
|
278
|
+
console.log(` ${cyan}1.${r} Claude Code ${cyan}5.${r} Gemini CLI`);
|
|
279
|
+
console.log(` ${cyan}2.${r} Codex CLI ${cyan}6.${r} VS Code`);
|
|
280
|
+
console.log(` ${cyan}3.${r} Cursor ${cyan}7.${r} Zed`);
|
|
281
|
+
console.log(` ${cyan}4.${r} Windsurf ${cyan}8.${r} OpenCode\n`);
|
|
282
|
+
choice = (await ask(`${dim}Choose (1-8):${r} `)).trim();
|
|
283
|
+
if (!["1","2","3","4","5","6","7","8"].includes(choice)) {
|
|
284
|
+
console.error("Invalid choice.");
|
|
285
|
+
rl.close();
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
491
288
|
token = tokenFlag.trim();
|
|
492
289
|
if (!isSafeToken(token)) {
|
|
493
290
|
console.error("Invalid token format.");
|
|
@@ -642,11 +439,38 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
642
439
|
|
|
643
440
|
token = sseResult.token;
|
|
644
441
|
identity = `${sseResult.agent_id}@${sseResult.namespace_id}`;
|
|
645
|
-
clientType = sseResult.client_type || "";
|
|
442
|
+
clientType = sseResult.client_type || sseResult.tool || "";
|
|
443
|
+
choice = CLIENT_TYPE_MAP[clientType] || "";
|
|
646
444
|
console.log(` ${green}✓${r} ${bold}${identity}${r} connected.`);
|
|
445
|
+
|
|
446
|
+
if (!choice) {
|
|
447
|
+
// Backend didn't send tool type — ask in terminal
|
|
448
|
+
const { createInterface: createRL3 } = await import("readline");
|
|
449
|
+
const rl3 = createRL3({ input: process.stdin, output: process.stdout });
|
|
450
|
+
const ask3 = (q) => new Promise((resolve) => rl3.question(q, resolve));
|
|
451
|
+
console.log(`\n${bold}Which tool are you setting up?${r}\n`);
|
|
452
|
+
console.log(` ${cyan}1.${r} Claude Code ${cyan}5.${r} Gemini CLI`);
|
|
453
|
+
console.log(` ${cyan}2.${r} Codex CLI ${cyan}6.${r} VS Code`);
|
|
454
|
+
console.log(` ${cyan}3.${r} Cursor ${cyan}7.${r} Zed`);
|
|
455
|
+
console.log(` ${cyan}4.${r} Windsurf ${cyan}8.${r} OpenCode\n`);
|
|
456
|
+
choice = (await ask3(`${dim}Choose (1-8):${r} `)).trim();
|
|
457
|
+
rl3.close();
|
|
458
|
+
if (!["1","2","3","4","5","6","7","8"].includes(choice)) {
|
|
459
|
+
console.error("Invalid choice.");
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
647
463
|
}
|
|
648
464
|
}
|
|
649
465
|
|
|
466
|
+
const isCodex = choice === "2";
|
|
467
|
+
const isCursor = choice === "3";
|
|
468
|
+
const isWindsurf = choice === "4";
|
|
469
|
+
const isGemini = choice === "5";
|
|
470
|
+
const isVSCode = choice === "6";
|
|
471
|
+
const isZed = choice === "7";
|
|
472
|
+
const isOpenCode = choice === "8";
|
|
473
|
+
|
|
650
474
|
const hostname = run("hostname -s") || run("hostname") || "unknown";
|
|
651
475
|
|
|
652
476
|
if (isCursor) {
|