patchcord 0.5.115 → 0.5.117
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 +71 -26
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -89,14 +89,17 @@ THIS AGENT — identity + messaging
|
|
|
89
89
|
ACCOUNT — log in to the CLI (commands below log in on demand)
|
|
90
90
|
patchcord login Authenticate the CLI (your account)
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
patchcord
|
|
94
|
-
Provision the
|
|
92
|
+
ORCHESTRATOR — set up the orchestrator agent (the shepherd) in this folder
|
|
93
|
+
patchcord orchestrator [--namespace <ns>] [--tool <harness>]
|
|
94
|
+
Provision the orchestrator here; launch it
|
|
95
95
|
to adopt this project or build a new team
|
|
96
96
|
|
|
97
|
-
TEAM — the
|
|
97
|
+
TEAM — the orchestrator's tools (run from the project root)
|
|
98
98
|
patchcord provision <agent> --tool <X> --role <Y> --namespace <ns> [--dir <sub/>]
|
|
99
99
|
Create a worker agent (identity + config)
|
|
100
|
+
patchcord pull <agent> --tool <X> --namespace <ns> [--dir <sub/>]
|
|
101
|
+
Place an EXISTING agent into a folder
|
|
102
|
+
(same identity, new token; nothing overwritten)
|
|
100
103
|
patchcord provision revoke <agent> --namespace <ns>
|
|
101
104
|
patchcord team list [--namespace <ns>] Provisioned agents (server view, deduped)
|
|
102
105
|
patchcord team status Reconcile folder ↔ identity ↔ tmux ↔ token
|
|
@@ -824,12 +827,12 @@ if (cmd === "subscribe") {
|
|
|
824
827
|
// works the same as the space-form (--token foo). The internal flag parsing below
|
|
825
828
|
// supports both. Non-flag commands (channel, init, skill, help, plugin-path) have
|
|
826
829
|
// their own branches above and below.
|
|
827
|
-
// ── CLI account auth +
|
|
830
|
+
// ── CLI account auth + orchestrator/team/provisioning/schedule ─────────────
|
|
828
831
|
// These commands act on the user's ACCOUNT, so they require CLI login. The
|
|
829
832
|
// account token (user-level, tied to NO agent) lives at ~/.patchcord/auth.json
|
|
830
833
|
// (legacy: main.json / master.json) or $PATCHCORD_TOKEN (legacy: *_MAIN/MASTER).
|
|
831
834
|
// `patchcord login` authenticates; everything else logs in on demand.
|
|
832
|
-
if (cmd === "login" || cmd === "teamlead" || cmd === "provision" || cmd === "team" || cmd === "schedule") {
|
|
835
|
+
if (cmd === "login" || cmd === "orchestrator" || cmd === "teamlead" || cmd === "provision" || cmd === "pull" || cmd === "team" || cmd === "schedule") {
|
|
833
836
|
const M = { cyan: "\x1b[36m", green: "\x1b[32m", dim: "\x1b[2m", rst: "\x1b[0m" };
|
|
834
837
|
const AUTH_CONFIG = join(HOME, ".patchcord", "auth.json");
|
|
835
838
|
const LEGACY_CONFIGS = [join(HOME, ".patchcord", "main.json"), join(HOME, ".patchcord", "master.json")];
|
|
@@ -1010,27 +1013,67 @@ if (cmd === "login" || cmd === "teamlead" || cmd === "provision" || cmd === "tea
|
|
|
1010
1013
|
process.exit(0);
|
|
1011
1014
|
}
|
|
1012
1015
|
|
|
1013
|
-
if (cmd === "
|
|
1014
|
-
//
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
//
|
|
1016
|
+
if (cmd === "pull") {
|
|
1017
|
+
// Inverse of provision: place an EXISTING agent identity into a folder.
|
|
1018
|
+
// Mints another token bound to the same agent_id@namespace (its original
|
|
1019
|
+
// token is hashed server-side and unrecoverable, so we can't return the
|
|
1020
|
+
// literal one — but the identity is the same). Nothing is overwritten; the
|
|
1021
|
+
// agent can now run from this folder too.
|
|
1022
|
+
const arg = process.argv[3];
|
|
1023
|
+
if (!arg || arg.startsWith("-")) { console.error("Usage: patchcord pull <agent> --namespace ns --tool X [--dir sub/]"); process.exit(1); }
|
|
1024
|
+
const tool = flagVal("tool", "claude_code");
|
|
1025
|
+
const ns = flagVal("namespace");
|
|
1026
|
+
const subdir = flagVal("dir", arg);
|
|
1027
|
+
if (!ns) { console.error("--namespace <project-namespace> required"); process.exit(1); }
|
|
1028
|
+
const { status, json, m } = await accountCall("POST", "/api/provision", { namespace_id: ns, agent_id: arg, tool, require_existing: true, label: `pull:${tool}` });
|
|
1029
|
+
if (status === "404") { console.error(`no agent ${M.green}${ns}:${arg}${M.rst} to pull — create it with: patchcord provision ${arg} --tool ${tool} --namespace ${ns}`); process.exit(1); }
|
|
1030
|
+
if (status !== "200" || !json?.token) { console.error(`pull failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1031
|
+
const base = String(json.url || `${m.baseUrl}/mcp`).replace(/\/mcp$/, "");
|
|
1032
|
+
const dir = join(process.cwd(), subdir);
|
|
1033
|
+
const hostname = run("hostname -s") || run("hostname") || "unknown";
|
|
1034
|
+
writeWorkerConfig(tool, dir, base, json.token, hostname);
|
|
1035
|
+
// Record in the local team manifest so `team launch` / `team status` see it.
|
|
1036
|
+
try {
|
|
1037
|
+
const tj = join(process.cwd(), ".patchcord", "team.json");
|
|
1038
|
+
if (existsSync(tj)) {
|
|
1039
|
+
const man = JSON.parse(readFileSync(tj, "utf-8"));
|
|
1040
|
+
man.agents = (man.agents || []).filter((a) => a.agent !== arg);
|
|
1041
|
+
man.agents.push({ agent: arg, tool, dir: subdir, namespace: ns });
|
|
1042
|
+
writeFileSync(tj, JSON.stringify(man, null, 2) + "\n");
|
|
1043
|
+
}
|
|
1044
|
+
} catch {}
|
|
1045
|
+
console.log(`✓ pulled ${M.green}${ns}:${arg}${M.rst} [${tool}] → ${dir}`);
|
|
1046
|
+
process.exit(0);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
if (cmd === "orchestrator" || cmd === "teamlead") {
|
|
1050
|
+
// Set up the ORCHESTRATOR agent in THIS folder. The orchestrator is a
|
|
1051
|
+
// distinct kind of agent — it shepherds a team: provisions, connects, and
|
|
1052
|
+
// manages the other agents. It is NOT a normal agent: it gets its own
|
|
1053
|
+
// identity, its own onboarding instruction, and on launch either ADOPTS
|
|
1054
|
+
// this existing project or CREATES a new team by interviewing the user.
|
|
1055
|
+
// (`teamlead` is kept as a back-compat alias for the command name.)
|
|
1019
1056
|
const root = process.cwd();
|
|
1020
1057
|
const ns = (flagVal("namespace", basename(root)) || "team").replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
1021
1058
|
const tool = flagVal("tool", "claude_code");
|
|
1022
1059
|
const hostname = run("hostname -s") || run("hostname") || "unknown";
|
|
1023
|
-
const { status, json, m } = await accountCall("POST", "/api/provision", { namespace_id: ns, agent_id: "
|
|
1024
|
-
if (status !== "200" || !json?.token) { console.error(`could not set up
|
|
1060
|
+
const { status, json, m } = await accountCall("POST", "/api/provision", { namespace_id: ns, agent_id: "orchestrator", tool, role: "orchestrator", label: "orchestrator:self" });
|
|
1061
|
+
if (status !== "200" || !json?.token) { console.error(`could not set up orchestrator (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1025
1062
|
const base = String(json.url || `${m.baseUrl}/mcp`).replace(/\/mcp$/, "");
|
|
1026
1063
|
writeWorkerConfig(tool, root, base, json.token, hostname);
|
|
1027
1064
|
mkdirSync(join(root, ".patchcord"), { recursive: true });
|
|
1028
1065
|
const tj = join(root, ".patchcord", "team.json");
|
|
1029
|
-
let manifest = { project: basename(root), namespace: ns,
|
|
1030
|
-
try {
|
|
1066
|
+
let manifest = { project: basename(root), namespace: ns, orchestrator: { agent: "orchestrator", tool }, agents: [] };
|
|
1067
|
+
try {
|
|
1068
|
+
if (existsSync(tj)) {
|
|
1069
|
+
const prev = JSON.parse(readFileSync(tj, "utf-8"));
|
|
1070
|
+
delete prev.teamlead; delete prev.main; delete prev.master; // drop pre-rename keys
|
|
1071
|
+
manifest = { ...prev, namespace: ns, orchestrator: { agent: "orchestrator", tool } };
|
|
1072
|
+
}
|
|
1073
|
+
} catch {}
|
|
1031
1074
|
writeFileSync(tj, JSON.stringify(manifest, null, 2) + "\n");
|
|
1032
|
-
// The
|
|
1033
|
-
writeFileSync(join(root, "
|
|
1075
|
+
// The orchestrator's onboarding instruction (read on launch).
|
|
1076
|
+
writeFileSync(join(root, "ORCHESTRATOR.md"), `# You are the ORCHESTRATOR (${ns}:orchestrator)
|
|
1034
1077
|
|
|
1035
1078
|
You shepherd a team of patchcord agents in this folder. You are NOT a worker —
|
|
1036
1079
|
you design the team, provision its agents, launch them, and manage them.
|
|
@@ -1043,14 +1086,16 @@ you design the team, provision its agents, launch them, and manage them.
|
|
|
1043
1086
|
which roles/harnesses, how many — confirm the plan, then build it.
|
|
1044
1087
|
|
|
1045
1088
|
## Build / run
|
|
1046
|
-
- Provision each worker: \`patchcord provision <agent> --tool <claude_code|codex|opencode|kimi> --role <role> --namespace ${ns} --dir <agent>/\`
|
|
1089
|
+
- Provision each worker: \`patchcord provision <agent> --tool <claude_code|codex|opencode|kimi|agy> --role <role> --namespace ${ns} --dir <agent>/\`
|
|
1090
|
+
- Pull an existing agent into a folder: \`patchcord pull <agent> --tool <X> --namespace ${ns} --dir <agent>/\`
|
|
1047
1091
|
- Launch: \`patchcord team launch\` (mux), verify with \`patchcord team status\`.
|
|
1048
1092
|
- Coordinate over patchcord (inbox / send_message); manage at the meta level.
|
|
1049
|
-
- Your own identity here is ${ns}:
|
|
1093
|
+
- Your own identity here is ${ns}:orchestrator — \`patchcord whoami\` confirms it.
|
|
1050
1094
|
`);
|
|
1051
|
-
|
|
1095
|
+
try { if (existsSync(join(root, "TEAMLEAD.md"))) unlinkSync(join(root, "TEAMLEAD.md")); } catch {} // remove pre-rename onboarding file
|
|
1096
|
+
console.log(`\n ${M.green}✓${M.rst} Orchestrator ready: ${M.green}${ns}:orchestrator${M.rst} [${tool}] in ${root}`);
|
|
1052
1097
|
console.log(` ${M.dim}Launch it here — it adopts this project or creates a new team, asking you:${M.rst}`);
|
|
1053
|
-
console.log(` mux new ${tool === "claude_code" ? "claude" : (tool === "kimi-code" ? "kimi" : tool)} --dir .`);
|
|
1098
|
+
console.log(` mux new ${tool === "claude_code" ? "claude" : (tool === "kimi-code" ? "kimi" : (tool === "antigravity" || tool === "agy") ? "agy" : tool)} --dir .`);
|
|
1054
1099
|
process.exit(0);
|
|
1055
1100
|
}
|
|
1056
1101
|
|
|
@@ -1124,10 +1169,10 @@ you design the team, provision its agents, launch them, and manage them.
|
|
|
1124
1169
|
panes.push({ session, window, path: real(path), cmd, claimed: false });
|
|
1125
1170
|
}
|
|
1126
1171
|
|
|
1127
|
-
// Build the roster:
|
|
1172
|
+
// Build the roster: orchestrator first, then workers.
|
|
1128
1173
|
const roster = [];
|
|
1129
|
-
const leadEntry = manifest.teamlead || manifest.main || manifest.master; // pre-rename names
|
|
1130
|
-
if (leadEntry) roster.push({ ...leadEntry, role: "
|
|
1174
|
+
const leadEntry = manifest.orchestrator || manifest.teamlead || manifest.main || manifest.master; // pre-rename names
|
|
1175
|
+
if (leadEntry) roster.push({ ...leadEntry, role: "orchestrator", dir: ".", _lead: true });
|
|
1131
1176
|
for (const a of (manifest.agents || [])) roster.push(a);
|
|
1132
1177
|
|
|
1133
1178
|
const rows = [];
|
|
@@ -2977,5 +3022,5 @@ if (cmd === "skill") {
|
|
|
2977
3022
|
process.exit(0);
|
|
2978
3023
|
}
|
|
2979
3024
|
|
|
2980
|
-
console.error(`Unknown command: ${cmd}. Available: install, whoami, agents, upload, subscribe, update, login, provision,
|
|
3025
|
+
console.error(`Unknown command: ${cmd}. Available: install, whoami, agents, upload, subscribe, update, login, provision, pull, orchestrator, team, schedule, --rename, --token, --agent-type, --version, --help`);
|
|
2981
3026
|
process.exit(1);
|