@sideboard-ai/core 0.1.57 → 0.1.60
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/dist/{agents-2YYWW723.js → agents-B2723EOF.js} +4 -4
- package/dist/{agents-AQFEFKBL.js → agents-EADRAAPG.js} +4 -4
- package/dist/{app-settings-2LQNRTBE.js → app-settings-TBHTJWLQ.js} +1 -1
- package/dist/{app-settings-73DI4B6T.js → app-settings-YBNZ4XSR.js} +1 -1
- package/dist/{chunk-5DMYULLC.js → chunk-2P2UK7E7.js} +2 -2
- package/dist/{chunk-7LNGIP3X.js → chunk-73AT5K4A.js} +15 -3
- package/dist/{chunk-DIOF73S2.js → chunk-75QR74KM.js} +7 -2
- package/dist/{chunk-EBWEKG52.js → chunk-CHGOMFTA.js} +3 -3
- package/dist/{chunk-C4BCC5X5.js → chunk-FGNBTFRD.js} +7 -2
- package/dist/{chunk-H6GGDLYS.js → chunk-JB2H7E7V.js} +1 -1
- package/dist/{chunk-BXJ76RHF.js → chunk-MBAL5FE6.js} +4 -4
- package/dist/{chunk-XOU6HNQJ.js → chunk-MFGGIRUA.js} +39 -13
- package/dist/{chunk-S42XDFKF.js → chunk-MLAST5BB.js} +3 -3
- package/dist/{chunk-C3J4GDW4.js → chunk-MX7UH5AT.js} +76 -60
- package/dist/{chunk-DQJ5D42H.js → chunk-NTYRBRE7.js} +44 -13
- package/dist/{chunk-DZFH2KLT.js → chunk-P7EQUFB6.js} +39 -13
- package/dist/{chunk-V2DUTUNC.js → chunk-QJBC4BRK.js} +2 -2
- package/dist/{chunk-ZNM4MAN4.js → chunk-RIWUU7FO.js} +15 -3
- package/dist/{chunk-UA5NDAHL.js → chunk-XWPKLRB3.js} +44 -13
- package/dist/{chunk-7F454EE2.js → chunk-ZXDOI6IH.js} +76 -53
- package/dist/{connected-teams-ONC4666A.js → connected-teams-MKIYKOYA.js} +2 -2
- package/dist/{connected-teams-2ANNZCP5.js → connected-teams-RSL4VB3X.js} +2 -2
- package/dist/{coordinator-prompt-RBKUNRPR.js → coordinator-prompt-4P23VVFC.js} +4 -4
- package/dist/{coordinator-prompt-ICF36NUQ.js → coordinator-prompt-LNWHZ64D.js} +4 -4
- package/dist/{global-workspace-667XLBW6.js → global-workspace-BHT4XUAA.js} +5 -5
- package/dist/{global-workspace-S7VLWPWG.js → global-workspace-TMMAVRVW.js} +5 -5
- package/dist/index.cjs +303 -115
- package/dist/index.d.cts +15 -6
- package/dist/index.d.ts +15 -6
- package/dist/index.js +157 -64
- package/dist/mcp/run-stdio.cjs +303 -126
- package/dist/mcp/run-stdio.js +156 -68
- package/dist/{run-ZJKYHXGN.js → run-GPSVV5OH.js} +1 -1
- package/dist/{run-H3IMNOYN.js → run-HZTBWGQR.js} +1 -1
- package/dist/{workspaces-FPJJXXAE.js → workspaces-3GO3EDQG.js} +6 -6
- package/dist/{workspaces-LYIDE2VR.js → workspaces-GFPGDGCS.js} +6 -6
- package/dist/{worktree-5KEQWSAF.js → worktree-3L2G6PCM.js} +2 -2
- package/dist/{worktree-RWGL7FUV.js → worktree-GBLHEFHR.js} +2 -2
- package/package.json +1 -1
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
resolveGithubRepoSlug
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MFGGIRUA.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveThreadDefaults
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FGNBTFRD.js";
|
|
9
9
|
import {
|
|
10
10
|
globalAgentCwd,
|
|
11
11
|
sideboardReposDir
|
|
12
12
|
} from "./chunk-7MV3RXSC.js";
|
|
13
13
|
|
|
14
14
|
// src/orchestrator/coordinator-prompt.ts
|
|
15
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
15
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
16
16
|
import { join } from "path";
|
|
17
17
|
function formatWorkspaceInventory(workspaces) {
|
|
18
18
|
if (workspaces.length === 0) return "(no registered workspaces)";
|
|
@@ -53,7 +53,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
53
53
|
"Workspaces:",
|
|
54
54
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
55
55
|
"Worktree threads (chats):",
|
|
56
|
-
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort)",
|
|
56
|
+
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort). If you are Codex, do not set agent=codex (nested Codex deadlocks)",
|
|
57
57
|
"- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
|
|
58
58
|
"- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
|
|
59
59
|
"- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
|
|
@@ -65,7 +65,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
65
65
|
"- list_run_scripts / run_dev_script / stop_dev_script \u2014 start/stop named run scripts",
|
|
66
66
|
"Inspect / review / PRs:",
|
|
67
67
|
"- get_diff \u2014 compact diff summary",
|
|
68
|
-
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review."); then wait_for_turn / get_turn_result on the returned id',
|
|
68
|
+
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review changes in this workspace."); then wait_for_turn / get_turn_result on the returned id',
|
|
69
69
|
"- Ask the worktree agent via send_to_thread to open a draft PR with `gh pr create --draft -R <origin-owner/name>` (workspace `github:` slug / that worktree's origin \u2014 never upstream). Do not open PRs from the orchestrator yourself.",
|
|
70
70
|
"Human-only (do not attempt): merge, ready-for-review land, purge_thread.",
|
|
71
71
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
@@ -83,7 +83,9 @@ function coordinatorTurnReminder(opts) {
|
|
|
83
83
|
"- You oversee Sideboard worktree agents. You are not yourself checked out in a project worktree.",
|
|
84
84
|
"- This cwd is a synthetic empty home (not a git repo). Emptiness here is expected \u2014 it is not a problem to fix.",
|
|
85
85
|
"- Registered workspaces / child threads are the fleet you manage via Sideboard MCP.",
|
|
86
|
-
`-
|
|
86
|
+
`- YOUR orchestration thread id is ${opts.parentId}`,
|
|
87
|
+
`- Always pass parentThreadId="${opts.parentId}" on create_thread (never invent or reuse another uuid).`,
|
|
88
|
+
`- Or omit parentThreadId \u2014 Sideboard MCP already binds children to ${opts.parentId}.`,
|
|
87
89
|
goal ? `- Goal / title: ${goal}` : null,
|
|
88
90
|
accountDefaultsPlaybookLine(),
|
|
89
91
|
`- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
|
|
@@ -92,10 +94,33 @@ function coordinatorTurnReminder(opts) {
|
|
|
92
94
|
"- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`."
|
|
93
95
|
].filter(Boolean).join("\n");
|
|
94
96
|
}
|
|
95
|
-
function ensureGlobalCoordinatorCwd() {
|
|
97
|
+
function ensureGlobalCoordinatorCwd(opts) {
|
|
96
98
|
const dir = globalAgentCwd();
|
|
97
|
-
|
|
99
|
+
try {
|
|
100
|
+
mkdirSync(dir, { recursive: true });
|
|
101
|
+
} catch {
|
|
102
|
+
return dir;
|
|
103
|
+
}
|
|
98
104
|
const reposDir = sideboardReposDir();
|
|
105
|
+
let orchId = opts?.orchestratorThreadId?.trim() || "";
|
|
106
|
+
if (!orchId) {
|
|
107
|
+
try {
|
|
108
|
+
const existing = readFileSync(join(dir, "AGENTS.md"), "utf8");
|
|
109
|
+
const m = existing.match(
|
|
110
|
+
/YOUR orchestration thread id is `([0-9a-f-]{36})`/i
|
|
111
|
+
);
|
|
112
|
+
if (m?.[1]) orchId = m[1];
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const parentBlock = orchId ? [
|
|
117
|
+
"",
|
|
118
|
+
`## This chat's id`,
|
|
119
|
+
"",
|
|
120
|
+
`YOUR orchestration thread id is \`${orchId}\`.`,
|
|
121
|
+
`On every create_thread, pass parentThreadId="${orchId}" \u2014 or omit parentThreadId (Sideboard binds it automatically).`,
|
|
122
|
+
"Never invent a uuid and never reuse an id from an earlier conversation."
|
|
123
|
+
] : [];
|
|
99
124
|
const body = [
|
|
100
125
|
"# Sideboard Orchestration",
|
|
101
126
|
"",
|
|
@@ -105,6 +130,7 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
105
130
|
"Repos from `list_workspaces` and threads from `list_threads` are the fleet you orchestrate.",
|
|
106
131
|
'For status questions ("what\'s going on?"), use `list_threads` / `list_workspaces` \u2014 never diagnose this synthetic home as a broken worktree.',
|
|
107
132
|
"Bash is fine for inspecting **child worktree** / registered-repo paths, and for greenfield repo setup under the Sideboard repos directory \u2014 not for treating this home as the project.",
|
|
133
|
+
...parentBlock,
|
|
108
134
|
"",
|
|
109
135
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
110
136
|
"",
|
|
@@ -112,16 +138,21 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
112
138
|
"",
|
|
113
139
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
114
140
|
"",
|
|
115
|
-
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered)
|
|
141
|
+
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered).",
|
|
142
|
+
orchId ? `Pass parentThreadId="${orchId}" (or omit it). Never invent another parentThreadId.` : "Pass `parentThreadId` for children (this chat's id from the turn reminder).",
|
|
116
143
|
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
144
|
+
"Never pass `agent=codex` when you yourself are Codex \u2014 nested Codex deadlocks on shared ~/.codex locks. Omit agent (Account default) or use cursor/claude.",
|
|
117
145
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
118
146
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
119
147
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
120
148
|
].join("\n");
|
|
121
|
-
|
|
149
|
+
try {
|
|
150
|
+
writeFileSync(join(dir, "CLAUDE.md"), `${body}
|
|
122
151
|
`, "utf8");
|
|
123
|
-
|
|
152
|
+
writeFileSync(join(dir, "AGENTS.md"), `${body}
|
|
124
153
|
`, "utf8");
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
125
156
|
return dir;
|
|
126
157
|
}
|
|
127
158
|
function coordinatorSystemPrompt(opts) {
|
|
@@ -141,12 +172,12 @@ function coordinatorSystemPrompt(opts) {
|
|
|
141
172
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
142
173
|
accountDefaultsPlaybookLine(),
|
|
143
174
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
144
|
-
"When creating threads, pass the correct repoPath for the target workspace
|
|
175
|
+
"When creating threads, pass the correct repoPath for the target workspace.",
|
|
176
|
+
`YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit parentThreadId (Sideboard binds it). Never invent a uuid.`,
|
|
145
177
|
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
146
178
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Never target upstream. Never open PRs from the orchestrator.",
|
|
147
179
|
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
148
180
|
`Goal: ${opts.goal}`,
|
|
149
|
-
`Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
|
|
150
181
|
"Registered workspaces:",
|
|
151
182
|
formatWorkspaceInventory(opts.workspaces)
|
|
152
183
|
].join("\n");
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
gh,
|
|
12
12
|
git,
|
|
13
13
|
resolveGhAuthToken
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-73AT5K4A.js";
|
|
15
15
|
|
|
16
16
|
// src/git/worktree.ts
|
|
17
17
|
import { existsSync, readdirSync } from "fs";
|
|
@@ -1258,12 +1258,16 @@ async function ensureGhPreferOrigin(cwd) {
|
|
|
1258
1258
|
const hasAlt = Boolean(await slugFromGitRemote(cwd, "upstream")) || Boolean(await slugFromGitRemote(cwd, "github"));
|
|
1259
1259
|
if (!hasAlt) return;
|
|
1260
1260
|
const view = await gh(["repo", "set-default", "--view"], cwd, {
|
|
1261
|
-
reject: false
|
|
1261
|
+
reject: false,
|
|
1262
|
+
timeoutMs: 15e3
|
|
1262
1263
|
});
|
|
1263
1264
|
const current = `${view.stdout}
|
|
1264
1265
|
${view.stderr}`;
|
|
1265
1266
|
if (view.exitCode === 0 && current.includes(originSlug)) return;
|
|
1266
|
-
await gh(["repo", "set-default", "origin"], cwd, {
|
|
1267
|
+
await gh(["repo", "set-default", "origin"], cwd, {
|
|
1268
|
+
reject: false,
|
|
1269
|
+
timeoutMs: 15e3
|
|
1270
|
+
});
|
|
1267
1271
|
}
|
|
1268
1272
|
async function originGhRepoEnv(cwd) {
|
|
1269
1273
|
await ensureGhPreferOrigin(cwd);
|
|
@@ -1809,20 +1813,42 @@ async function createThreadWorktree(opts) {
|
|
|
1809
1813
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
1810
1814
|
}
|
|
1811
1815
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
await
|
|
1815
|
-
|
|
1816
|
+
let startPoint = null;
|
|
1817
|
+
try {
|
|
1818
|
+
startPoint = await resolveWorktreeStartPoint(opts.repoPath, opts.sourceRef);
|
|
1819
|
+
} catch {
|
|
1820
|
+
startPoint = null;
|
|
1821
|
+
}
|
|
1822
|
+
if (!isLocalPrFetchBranch(opts.sourceRef)) {
|
|
1823
|
+
const fetchTimeoutMs = startPoint ? 12e3 : 45e3;
|
|
1824
|
+
await git(["fetch", "origin", "--prune"], opts.repoPath, {
|
|
1825
|
+
reject: false,
|
|
1826
|
+
timeoutMs: fetchTimeoutMs
|
|
1816
1827
|
});
|
|
1828
|
+
if (!opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
|
|
1829
|
+
await git(["fetch", "origin", opts.sourceRef], opts.repoPath, {
|
|
1830
|
+
reject: false,
|
|
1831
|
+
timeoutMs: fetchTimeoutMs
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
try {
|
|
1835
|
+
startPoint = await resolveWorktreeStartPoint(
|
|
1836
|
+
opts.repoPath,
|
|
1837
|
+
opts.sourceRef
|
|
1838
|
+
);
|
|
1839
|
+
} catch (err) {
|
|
1840
|
+
if (!startPoint) throw err;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (!startPoint) {
|
|
1844
|
+
throw new Error(
|
|
1845
|
+
`Invalid git reference: ${opts.sourceRef} (and fetch did not resolve it)`
|
|
1846
|
+
);
|
|
1817
1847
|
}
|
|
1818
|
-
const startPoint = await resolveWorktreeStartPoint(
|
|
1819
|
-
opts.repoPath,
|
|
1820
|
-
opts.sourceRef
|
|
1821
|
-
);
|
|
1822
1848
|
const add = await git(
|
|
1823
1849
|
["worktree", "add", "-b", branchName, worktreePath, startPoint],
|
|
1824
1850
|
opts.repoPath,
|
|
1825
|
-
{ reject: false }
|
|
1851
|
+
{ reject: false, timeoutMs: 6e4 }
|
|
1826
1852
|
);
|
|
1827
1853
|
if (add.exitCode !== 0) {
|
|
1828
1854
|
const err = `${add.stderr}
|
|
@@ -1833,7 +1859,7 @@ ${add.stdout}`;
|
|
|
1833
1859
|
const retry = await git(
|
|
1834
1860
|
["worktree", "add", "-b", alt, worktreePath, startPoint],
|
|
1835
1861
|
opts.repoPath,
|
|
1836
|
-
{ reject: false }
|
|
1862
|
+
{ reject: false, timeoutMs: 6e4 }
|
|
1837
1863
|
);
|
|
1838
1864
|
if (retry.exitCode === 0) {
|
|
1839
1865
|
branchName = alt;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
isGlobalRepoPath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CHGOMFTA.js";
|
|
6
6
|
import {
|
|
7
7
|
ensureGhPreferOrigin,
|
|
8
8
|
resolveRepoRoot
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MFGGIRUA.js";
|
|
10
10
|
import {
|
|
11
11
|
appDataDir
|
|
12
12
|
} from "./chunk-7MV3RXSC.js";
|
|
@@ -43,7 +43,8 @@ async function run(file, args, opts) {
|
|
|
43
43
|
const result = await execa(file, args, {
|
|
44
44
|
cwd: opts?.cwd,
|
|
45
45
|
env: { ...process.env, ...opts?.env },
|
|
46
|
-
reject: opts?.reject ?? true
|
|
46
|
+
reject: opts?.reject ?? true,
|
|
47
|
+
...opts?.timeoutMs != null ? { timeout: opts.timeoutMs } : {}
|
|
47
48
|
});
|
|
48
49
|
return {
|
|
49
50
|
stdout: result.stdout ?? "",
|
|
@@ -73,11 +74,22 @@ async function git(args, cwd, opts) {
|
|
|
73
74
|
return run("git", [...prefix, ...args], {
|
|
74
75
|
cwd,
|
|
75
76
|
reject: opts?.reject,
|
|
76
|
-
|
|
77
|
+
timeoutMs: opts?.timeoutMs,
|
|
78
|
+
// Never block forever on a credential/SSH prompt inside MCP / Electron.
|
|
79
|
+
env: {
|
|
80
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
81
|
+
GIT_ASKPASS: process.env.GIT_ASKPASS || "echo",
|
|
82
|
+
GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND || "ssh -o BatchMode=yes -o ConnectTimeout=15",
|
|
83
|
+
...opts?.env
|
|
84
|
+
}
|
|
77
85
|
});
|
|
78
86
|
}
|
|
79
87
|
async function gh(args, cwd, opts) {
|
|
80
|
-
return run("gh", args, {
|
|
88
|
+
return run("gh", args, {
|
|
89
|
+
cwd,
|
|
90
|
+
reject: opts?.reject,
|
|
91
|
+
timeoutMs: opts?.timeoutMs
|
|
92
|
+
});
|
|
81
93
|
}
|
|
82
94
|
async function resolveGhAuthToken(cwd) {
|
|
83
95
|
const result = await gh(["auth", "token"], cwd, { reject: false });
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveThreadDefaults
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-75QR74KM.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveGithubRepoSlug
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-P7EQUFB6.js";
|
|
7
7
|
import {
|
|
8
8
|
globalAgentCwd,
|
|
9
9
|
sideboardReposDir
|
|
10
10
|
} from "./chunk-M37RITA6.js";
|
|
11
11
|
|
|
12
12
|
// src/orchestrator/coordinator-prompt.ts
|
|
13
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
13
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
function formatWorkspaceInventory(workspaces) {
|
|
16
16
|
if (workspaces.length === 0) return "(no registered workspaces)";
|
|
@@ -51,7 +51,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
51
51
|
"Workspaces:",
|
|
52
52
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
53
53
|
"Worktree threads (chats):",
|
|
54
|
-
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort)",
|
|
54
|
+
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort). If you are Codex, do not set agent=codex (nested Codex deadlocks)",
|
|
55
55
|
"- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
|
|
56
56
|
"- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
|
|
57
57
|
"- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
|
|
@@ -63,7 +63,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
63
63
|
"- list_run_scripts / run_dev_script / stop_dev_script \u2014 start/stop named run scripts",
|
|
64
64
|
"Inspect / review / PRs:",
|
|
65
65
|
"- get_diff \u2014 compact diff summary",
|
|
66
|
-
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review."); then wait_for_turn / get_turn_result on the returned id',
|
|
66
|
+
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review changes in this workspace."); then wait_for_turn / get_turn_result on the returned id',
|
|
67
67
|
"- Ask the worktree agent via send_to_thread to open a draft PR with `gh pr create --draft -R <origin-owner/name>` (workspace `github:` slug / that worktree's origin \u2014 never upstream). Do not open PRs from the orchestrator yourself.",
|
|
68
68
|
"Human-only (do not attempt): merge, ready-for-review land, purge_thread.",
|
|
69
69
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
@@ -81,7 +81,9 @@ function coordinatorTurnReminder(opts) {
|
|
|
81
81
|
"- You oversee Sideboard worktree agents. You are not yourself checked out in a project worktree.",
|
|
82
82
|
"- This cwd is a synthetic empty home (not a git repo). Emptiness here is expected \u2014 it is not a problem to fix.",
|
|
83
83
|
"- Registered workspaces / child threads are the fleet you manage via Sideboard MCP.",
|
|
84
|
-
`-
|
|
84
|
+
`- YOUR orchestration thread id is ${opts.parentId}`,
|
|
85
|
+
`- Always pass parentThreadId="${opts.parentId}" on create_thread (never invent or reuse another uuid).`,
|
|
86
|
+
`- Or omit parentThreadId \u2014 Sideboard MCP already binds children to ${opts.parentId}.`,
|
|
85
87
|
goal ? `- Goal / title: ${goal}` : null,
|
|
86
88
|
accountDefaultsPlaybookLine(),
|
|
87
89
|
`- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
|
|
@@ -90,10 +92,33 @@ function coordinatorTurnReminder(opts) {
|
|
|
90
92
|
"- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`."
|
|
91
93
|
].filter(Boolean).join("\n");
|
|
92
94
|
}
|
|
93
|
-
function ensureGlobalCoordinatorCwd() {
|
|
95
|
+
function ensureGlobalCoordinatorCwd(opts) {
|
|
94
96
|
const dir = globalAgentCwd();
|
|
95
|
-
|
|
97
|
+
try {
|
|
98
|
+
mkdirSync(dir, { recursive: true });
|
|
99
|
+
} catch {
|
|
100
|
+
return dir;
|
|
101
|
+
}
|
|
96
102
|
const reposDir = sideboardReposDir();
|
|
103
|
+
let orchId = opts?.orchestratorThreadId?.trim() || "";
|
|
104
|
+
if (!orchId) {
|
|
105
|
+
try {
|
|
106
|
+
const existing = readFileSync(join(dir, "AGENTS.md"), "utf8");
|
|
107
|
+
const m = existing.match(
|
|
108
|
+
/YOUR orchestration thread id is `([0-9a-f-]{36})`/i
|
|
109
|
+
);
|
|
110
|
+
if (m?.[1]) orchId = m[1];
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const parentBlock = orchId ? [
|
|
115
|
+
"",
|
|
116
|
+
`## This chat's id`,
|
|
117
|
+
"",
|
|
118
|
+
`YOUR orchestration thread id is \`${orchId}\`.`,
|
|
119
|
+
`On every create_thread, pass parentThreadId="${orchId}" \u2014 or omit parentThreadId (Sideboard binds it automatically).`,
|
|
120
|
+
"Never invent a uuid and never reuse an id from an earlier conversation."
|
|
121
|
+
] : [];
|
|
97
122
|
const body = [
|
|
98
123
|
"# Sideboard Orchestration",
|
|
99
124
|
"",
|
|
@@ -103,6 +128,7 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
103
128
|
"Repos from `list_workspaces` and threads from `list_threads` are the fleet you orchestrate.",
|
|
104
129
|
'For status questions ("what\'s going on?"), use `list_threads` / `list_workspaces` \u2014 never diagnose this synthetic home as a broken worktree.',
|
|
105
130
|
"Bash is fine for inspecting **child worktree** / registered-repo paths, and for greenfield repo setup under the Sideboard repos directory \u2014 not for treating this home as the project.",
|
|
131
|
+
...parentBlock,
|
|
106
132
|
"",
|
|
107
133
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
108
134
|
"",
|
|
@@ -110,16 +136,21 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
110
136
|
"",
|
|
111
137
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
112
138
|
"",
|
|
113
|
-
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered)
|
|
139
|
+
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered).",
|
|
140
|
+
orchId ? `Pass parentThreadId="${orchId}" (or omit it). Never invent another parentThreadId.` : "Pass `parentThreadId` for children (this chat's id from the turn reminder).",
|
|
114
141
|
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
142
|
+
"Never pass `agent=codex` when you yourself are Codex \u2014 nested Codex deadlocks on shared ~/.codex locks. Omit agent (Account default) or use cursor/claude.",
|
|
115
143
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
116
144
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
117
145
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
118
146
|
].join("\n");
|
|
119
|
-
|
|
147
|
+
try {
|
|
148
|
+
writeFileSync(join(dir, "CLAUDE.md"), `${body}
|
|
120
149
|
`, "utf8");
|
|
121
|
-
|
|
150
|
+
writeFileSync(join(dir, "AGENTS.md"), `${body}
|
|
122
151
|
`, "utf8");
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
123
154
|
return dir;
|
|
124
155
|
}
|
|
125
156
|
function coordinatorSystemPrompt(opts) {
|
|
@@ -139,12 +170,12 @@ function coordinatorSystemPrompt(opts) {
|
|
|
139
170
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
140
171
|
accountDefaultsPlaybookLine(),
|
|
141
172
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
142
|
-
"When creating threads, pass the correct repoPath for the target workspace
|
|
173
|
+
"When creating threads, pass the correct repoPath for the target workspace.",
|
|
174
|
+
`YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit parentThreadId (Sideboard binds it). Never invent a uuid.`,
|
|
143
175
|
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
144
176
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Never target upstream. Never open PRs from the orchestrator.",
|
|
145
177
|
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
146
178
|
`Goal: ${opts.goal}`,
|
|
147
|
-
`Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
|
|
148
179
|
"Registered workspaces:",
|
|
149
180
|
formatWorkspaceInventory(opts.workspaces)
|
|
150
181
|
].join("\n");
|