@dev.fast/whiteboard 0.2.5-preview.20261015.118 → 0.2.5-preview.20261015.120
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/{account-alias-C1ixP6bt.js → account-alias-pAq80fex.js} +372 -85
- package/dist/agent-cli-Bs2PzjQx.js +2 -0
- package/dist/{agent-cli-CPSnWc9V.js → agent-cli-CACGYhRh.js} +7 -7
- package/dist/{agent-client-Dogh8I83.js → agent-client-D6xrEdTj.js} +5 -6
- package/dist/{authoring-tools-CtsslOEf.js → authoring-tools-8UhtCkjL.js} +3 -3
- package/dist/{background-server-DJT0CnYH.js → background-server-D7maQkJV.js} +4 -4
- package/dist/{background-server-BKR_lpYb.js → background-server-KKSjffhS.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/{cli-Df6p-C19.js → cli-CGWs67Pz.js} +1 -1
- package/dist/{cli-runner-ZktCwlLk.js → cli-runner-D3XvcVCa.js} +51 -187
- package/dist/cli.js +2 -2
- package/dist/{client-_ltX1mah.js → client-tIkQ_x_x.js} +4 -4
- package/dist/fuzzy-match-CufFp9IR.js +335 -0
- package/dist/{headless-host-ZNZ_6ZRo.js → headless-host-CDh2qQbQ.js} +52 -344
- package/dist/{headless-host-Pj2xfGAo.js → headless-host-COLvXm9A.js} +1 -1
- package/dist/{local-data-D_9S--yI.js → local-data-Bhs4VnzC.js} +55 -414
- package/dist/{mcp-Blb7rzGw.js → mcp-DqmNumCl.js} +3 -3
- package/dist/{package-paths-BsuI4WUf.js → package-paths-jMg6JxDP.js} +1 -1
- package/dist/{profile-CKSB6UDi.js → profile-CbAW3qDm.js} +2 -2
- package/dist/profile-DY_QhMQ9.js +2 -0
- package/dist/{remote-attach-CiOwonJ3.js → remote-attach-DK4FaWbL.js} +4 -4
- package/dist/{remote-extensions-D_4DSOhO.js → remote-extensions-BCAbQZTv.js} +2 -2
- package/dist/{remote-extensions-ByiuSl1D.js → remote-extensions-Cf3L-ZGG.js} +1 -1
- package/dist/{remote-language-server-BQjBk81F.js → remote-language-server-BIP6hw3K.js} +4 -4
- package/dist/{remote-uninstall-pzHb-Hfd.js → remote-uninstall-CLf9aKCN.js} +5 -5
- package/dist/{request-origin-C-V9f3Fr.js → request-origin-2d7P4ugC.js} +2 -7
- package/dist/{review-app-launcher-S2FZBY4W.js → review-app-launcher-C7PUhYox.js} +2 -2
- package/dist/{review-home-paths-zwevIq4I.js → review-home-paths-Lc02WFuX.js} +1 -1
- package/dist/{review-telemetry-9ceqh4wz.js → review-telemetry-kTLRwBj_.js} +4 -4
- package/dist/server/desktop-host.js +95 -444
- package/dist/{server-discovery-CqRxbsER.js → server-discovery-IdX3Wc8g.js} +2 -2
- package/dist/sharing/index.d.ts +10 -9
- package/dist/sharing/index.js +1 -1
- package/dist/{src-HnIPjtjx.js → src-C_iUYopM.js} +3 -1
- package/dist/{stored-document-migration-DenDEYdI.js → stored-document-migration-DVJJPBGd.js} +1 -1
- package/dist/thread-state-CS-v-hc0.js +400 -0
- package/dist/{tool-failure-aSYcHDxP.js → tool-failure-A9dVocGs.js} +1 -1
- package/package.json +5 -5
- package/src/agent-connect.ts +29 -106
- package/src/cli-install.ts +13 -11
- package/src/cli-runner.ts +51 -24
- package/src/connect-prompts.ts +135 -71
- package/src/review-api/README.md +1 -1
- package/src/review-api/azure-devops-provider.ts +4 -0
- package/src/review-api/comparison-coverage.ts +0 -13
- package/src/review-api/github-provider.ts +21 -3
- package/src/review-api/http.ts +22 -41
- package/src/review-api/local-data.ts +22 -27
- package/src/review-api/pull-request.ts +2 -0
- package/src/review-api/review-progress.ts +6 -8
- package/src/review-api/store.ts +22 -20
- package/src/review-stack.ts +3 -2
- package/src/server/review-gateway-list.ts +3 -10
- package/src/server/review-gateway-memory.ts +6 -19
- package/src/server/review-gateway-streams.ts +32 -46
- package/src/sharing/import.ts +2 -2
- package/src/ui-telemetry-events.ts +1 -2
- package/dist/agent-cli-CGapivA1.js +0 -2
- package/dist/fs-utils-BMPLt0cr.js +0 -22
- package/dist/fuzzy-match-BoAmcyak.js +0 -30
- package/dist/profile-BUoTujrK.js +0 -2
- package/src/connect-agents.ts +0 -285
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as isJsonObject } from "./src-X9phtB2j.js";
|
|
2
|
+
import { w as ReviewCliInstallStampSchema } from "./src-C_iUYopM.js";
|
|
3
|
+
import { C as disableTraceMachine, E as traceMachineStatus, G as listTraceRepositoryRoots, K as traceRepositoryStatus, Q as removeAgentTraceHook, S as configureTraceMachine, T as traceMachineEnabled, U as disableAllTraceRepositories, W as enableTraceRepository, X as describeTraceHookOwners, Y as AGENT_TRACE_HOOK_AGENTS, Z as installHarnessHooks, dt as StoreClient, n as reviewDesktopStateDir, nt as traceScope, ot as readStoreAuth } from "./review-home-paths-Lc02WFuX.js";
|
|
3
4
|
import { A as withFileLock, F as writeFileAtomicAsync, I as writePrivateJsonAtomic, M as devReviewHome } from "./s3-config-DDWWMh5x.js";
|
|
4
|
-
import { i as readReviewPackageVersion } from "./package-paths-
|
|
5
|
-
import {
|
|
5
|
+
import { i as readReviewPackageVersion } from "./package-paths-jMg6JxDP.js";
|
|
6
|
+
import { f as isDirectory, p as isFile } from "./thread-state-CS-v-hc0.js";
|
|
7
|
+
import { s as findExecutable } from "./fuzzy-match-CufFp9IR.js";
|
|
6
8
|
import { createHash } from "node:crypto";
|
|
7
9
|
import { constants } from "node:fs";
|
|
8
|
-
import { access, chmod, lstat, readFile, rm, stat } from "node:fs/promises";
|
|
10
|
+
import { access, chmod, lstat, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
9
11
|
import path from "node:path";
|
|
10
12
|
import os from "node:os";
|
|
11
13
|
import { execFile } from "node:child_process";
|
|
@@ -71,105 +73,131 @@ function fffSteps(input, target) {
|
|
|
71
73
|
const root = shellQuote(input.fffCorpusRoot);
|
|
72
74
|
return [`Unless \`${target} mcp get fff\` finds an existing server, run:\n\n\`\`\`sh\n[ -x ${binary} ] || curl -fsSL ${FFF_INSTALL_URL} | bash\n${target === "claude" ? "claude mcp add -s user" : "codex mcp add"} fff -- ${binary} ${root}\n\`\`\``];
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"claude",
|
|
76
|
+
const WHITEBOARD_MARKETPLACE = "devdotfast/whiteboard";
|
|
77
|
+
const WHITEBOARD_PLUGIN = "whiteboard@devfast";
|
|
78
|
+
const ALREADY = /already/i;
|
|
79
|
+
/** Each CLI agent's connect commands: Connect runs them, prompts print them. */
|
|
80
|
+
function connectSteps(target, launch, platform) {
|
|
81
|
+
const marketplace = {
|
|
82
|
+
args: [
|
|
82
83
|
"plugin",
|
|
83
84
|
"marketplace",
|
|
84
85
|
"add",
|
|
85
|
-
|
|
86
|
-
]
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
WHITEBOARD_MARKETPLACE
|
|
87
|
+
],
|
|
88
|
+
optional: true
|
|
89
|
+
};
|
|
90
|
+
const cleanup = (args, note) => ({
|
|
91
|
+
args,
|
|
92
|
+
optional: true,
|
|
93
|
+
note
|
|
94
|
+
});
|
|
95
|
+
const windows = (scope) => [
|
|
96
|
+
cleanup([
|
|
89
97
|
"plugin",
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
"uninstall",
|
|
99
|
+
WHITEBOARD_PLUGIN
|
|
100
|
+
], "its launch cannot start on Windows, if installed"),
|
|
101
|
+
cleanup([
|
|
102
|
+
"mcp",
|
|
103
|
+
"remove",
|
|
104
|
+
...scope,
|
|
105
|
+
"whiteboard"
|
|
106
|
+
], "old registration, if any"),
|
|
95
107
|
{
|
|
96
|
-
|
|
97
|
-
"claude",
|
|
108
|
+
args: [
|
|
98
109
|
"mcp",
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
"
|
|
110
|
+
"add",
|
|
111
|
+
...scope,
|
|
112
|
+
"whiteboard",
|
|
113
|
+
"--",
|
|
114
|
+
launch.command,
|
|
115
|
+
...launch.args
|
|
103
116
|
],
|
|
104
|
-
|
|
117
|
+
done: ALREADY
|
|
105
118
|
}
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
];
|
|
120
|
+
const plugin = (install, scope) => [
|
|
121
|
+
marketplace,
|
|
122
|
+
{
|
|
123
|
+
args: install,
|
|
124
|
+
done: ALREADY
|
|
125
|
+
},
|
|
126
|
+
cleanup([
|
|
127
|
+
"mcp",
|
|
128
|
+
"remove",
|
|
129
|
+
...scope,
|
|
130
|
+
"whiteboard"
|
|
131
|
+
], "old manual registration, if any")
|
|
132
|
+
];
|
|
133
|
+
switch (target) {
|
|
134
|
+
case "claude": return platform === "win32" ? windows(["-s", "user"]) : plugin([
|
|
110
135
|
"plugin",
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
"install",
|
|
137
|
+
WHITEBOARD_PLUGIN,
|
|
138
|
+
"--scope",
|
|
139
|
+
"user"
|
|
140
|
+
], ["-s", "user"]);
|
|
141
|
+
case "codex": return plugin([
|
|
117
142
|
"plugin",
|
|
118
143
|
"add",
|
|
119
|
-
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
144
|
+
WHITEBOARD_PLUGIN
|
|
145
|
+
], []);
|
|
146
|
+
case "copilot": return platform === "win32" ? windows([]) : plugin([
|
|
147
|
+
"plugin",
|
|
148
|
+
"install",
|
|
149
|
+
WHITEBOARD_PLUGIN
|
|
150
|
+
], []);
|
|
151
|
+
case "opencode": return [{
|
|
152
|
+
args: [
|
|
124
153
|
"mcp",
|
|
125
|
-
"
|
|
126
|
-
"
|
|
154
|
+
"add",
|
|
155
|
+
"--global",
|
|
156
|
+
"whiteboard",
|
|
157
|
+
"--",
|
|
158
|
+
launch.command,
|
|
159
|
+
...launch.args
|
|
127
160
|
],
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return `Run:\n\n\`\`\`sh\n${commands.map(({ argv, ifAny }) => `${argv.join(" ")}${ifAny ? " # old manual registration, if any" : ""}`).join("\n")}\n\`\`\``;
|
|
161
|
+
done: ALREADY
|
|
162
|
+
}];
|
|
163
|
+
case "pi": return [{
|
|
164
|
+
args: [
|
|
165
|
+
"mcp",
|
|
166
|
+
"add",
|
|
167
|
+
"whiteboard",
|
|
168
|
+
"--",
|
|
169
|
+
launch.command,
|
|
170
|
+
...launch.args
|
|
171
|
+
],
|
|
172
|
+
done: ALREADY
|
|
173
|
+
}, cleanup(["remove", PI_WHITEBOARD_PACKAGE], "if installed")];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function connectLines(target, launch, platform, cleanups = true) {
|
|
177
|
+
return connectSteps(target, launch, platform).filter((step) => cleanups || !step.note).map(({ args, note }) => `${shellWords([target, ...args])}${note ? ` # ${note}` : ""}`).join("\n");
|
|
178
|
+
}
|
|
179
|
+
function runBlock(lines) {
|
|
180
|
+
return `\`\`\`sh\n${lines}\n\`\`\``;
|
|
149
181
|
}
|
|
150
182
|
function pluginSteps(target, platform) {
|
|
183
|
+
const launch = reviewMcpLaunch(true, platform);
|
|
151
184
|
switch (target) {
|
|
152
185
|
case "claude":
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
case "codex": return [runBlock(CONNECT_COMMANDS.codex)];
|
|
186
|
+
case "codex":
|
|
187
|
+
case "copilot": return [`Run:\n\n${runBlock(connectLines(target, launch, platform))}`];
|
|
156
188
|
case "opencode": {
|
|
157
|
-
const launch = reviewMcpLaunch(true, platform);
|
|
158
189
|
const entry = JSON.stringify({ whiteboard: {
|
|
159
190
|
type: "local",
|
|
160
191
|
command: [launch.command, ...launch.args]
|
|
161
192
|
} }, null, 2);
|
|
162
|
-
return [`On OpenCode 2.0 or later, run:\n\n
|
|
193
|
+
return [`On OpenCode 2.0 or later, run:\n\n${runBlock(connectLines(target, launch, platform))}\n\nOn older OpenCode, add this entry to "mcp" in ~/.config/opencode/opencode.json, keeping the rest of the file:\n\n\`\`\`json\n${entry}\n\`\`\``, `Remove "@dev.fast/opencode-whiteboard" from "plugin" in ~/.config/opencode/opencode.json if it is there.`];
|
|
163
194
|
}
|
|
164
|
-
case "
|
|
165
|
-
if (platform === "win32") return [`Run:\n\n\`\`\`sh\ncopilot plugin uninstall whiteboard@devfast # its launch cannot start on Windows, if installed\ncopilot mcp remove whiteboard # old registration, if any\n${COPILOT_WINDOWS_MCP_ADD}\n\`\`\``];
|
|
166
|
-
return ["Run:\n\n```sh\ncopilot plugin marketplace add devdotfast/whiteboard\ncopilot plugin install whiteboard@devfast\ncopilot mcp remove whiteboard # old manual registration, if any\n```"];
|
|
167
|
-
case "pi": return [`On Pi 0.99.0 or later, run:\n\n\`\`\`sh\npi mcp add whiteboard -- ${launchCommand(reviewMcpLaunch(true, platform))}\npi remove ${PI_WHITEBOARD_PACKAGE} # if installed\n\`\`\`\n\nOn older Pi, run \`pi install ${PI_WHITEBOARD_PACKAGE}\`.`];
|
|
195
|
+
case "pi": return [`On Pi 0.99.0 or later, run:\n\n${runBlock(connectLines(target, launch, platform))}\n\nOn older Pi, run \`pi install ${PI_WHITEBOARD_PACKAGE}\`.`];
|
|
168
196
|
case "omp": return [`Add this entry to mcpServers in ~/.omp/agent/mcp.json, keeping the rest of the file:\n\n\`\`\`json\n${JSON.stringify({ whiteboard: reviewMcpLaunch(true, platform) }, null, 2)}\n\`\`\``, `Run \`omp plugin uninstall @dev.fast/pi-whiteboard\` if installed.`];
|
|
169
197
|
}
|
|
170
198
|
}
|
|
171
|
-
function
|
|
172
|
-
return
|
|
199
|
+
function shellWords(words) {
|
|
200
|
+
return words.map((word) => /^[\w./@:-]+$/.test(word) ? word : shellQuote(word)).join(" ");
|
|
173
201
|
}
|
|
174
202
|
function cursorSteps() {
|
|
175
203
|
const launch = reviewMcpLaunch(true);
|
|
@@ -231,6 +259,263 @@ function isInstallTarget(value) {
|
|
|
231
259
|
return ALL_INSTALL_TARGETS.some((target) => target === value);
|
|
232
260
|
}
|
|
233
261
|
//#endregion
|
|
262
|
+
//#region src/agent-connect.ts
|
|
263
|
+
const STEP_TIMEOUT_MS = 12e4;
|
|
264
|
+
const HARNESSES = {
|
|
265
|
+
claude: {
|
|
266
|
+
name: "Claude Code",
|
|
267
|
+
commands: ["claude"],
|
|
268
|
+
installDirs: [".claude/local", ".local/bin"]
|
|
269
|
+
},
|
|
270
|
+
codex: {
|
|
271
|
+
name: "Codex",
|
|
272
|
+
commands: ["codex"]
|
|
273
|
+
},
|
|
274
|
+
cursor: {
|
|
275
|
+
name: "Cursor",
|
|
276
|
+
commands: ["cursor"],
|
|
277
|
+
homeDir: ".cursor",
|
|
278
|
+
apps: ["/Applications/Cursor.app"]
|
|
279
|
+
},
|
|
280
|
+
opencode: {
|
|
281
|
+
name: "OpenCode",
|
|
282
|
+
commands: ["opencode"],
|
|
283
|
+
installDirs: [".opencode/bin"]
|
|
284
|
+
},
|
|
285
|
+
pi: {
|
|
286
|
+
name: "Pi",
|
|
287
|
+
commands: ["pi"]
|
|
288
|
+
},
|
|
289
|
+
omp: {
|
|
290
|
+
name: "oh-my-pi",
|
|
291
|
+
commands: ["omp"],
|
|
292
|
+
homeDir: ".omp"
|
|
293
|
+
},
|
|
294
|
+
copilot: {
|
|
295
|
+
name: "Copilot CLI",
|
|
296
|
+
commands: ["copilot"]
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const runProcess = (file, args) => new Promise((resolve) => {
|
|
300
|
+
execFile(file, args, {
|
|
301
|
+
timeout: STEP_TIMEOUT_MS,
|
|
302
|
+
maxBuffer: 4194304,
|
|
303
|
+
env: {
|
|
304
|
+
...process.env,
|
|
305
|
+
CI: "1",
|
|
306
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
307
|
+
},
|
|
308
|
+
windowsHide: true
|
|
309
|
+
}, (error, stdout, stderr) => {
|
|
310
|
+
const output = `${stdout}${stderr}`.trim();
|
|
311
|
+
resolve(error ? {
|
|
312
|
+
code: 1,
|
|
313
|
+
output: output || error.message
|
|
314
|
+
} : {
|
|
315
|
+
code: 0,
|
|
316
|
+
output
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
async function exists(target) {
|
|
321
|
+
try {
|
|
322
|
+
await stat(target);
|
|
323
|
+
return true;
|
|
324
|
+
} catch {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function findHarness(harness, homeDir, env) {
|
|
329
|
+
const installed = (harness.installDirs ?? []).map((directory) => path.join(homeDir, directory));
|
|
330
|
+
for (const command of harness.commands) {
|
|
331
|
+
const found = await findExecutable(command, env) ?? await findExecutable(command, { PATH: installed.join(path.delimiter) });
|
|
332
|
+
if (found) return found;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async function harnessFound(harness, homeDir, env, platform) {
|
|
336
|
+
if (await findHarness(harness, homeDir, env)) return true;
|
|
337
|
+
if (harness.homeDir && await exists(path.join(homeDir, harness.homeDir))) return true;
|
|
338
|
+
if (platform !== "darwin") return false;
|
|
339
|
+
for (const app of harness.apps ?? []) if (await exists(app)) return true;
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
/** OpenCode's config is JSONC: drop comments and trailing commas, keep strings. */
|
|
343
|
+
const JSONC_EXTRAS = /("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\/|,(?=\s*[}\]])/g;
|
|
344
|
+
async function readJson(file) {
|
|
345
|
+
try {
|
|
346
|
+
return JSON.parse((await readFile(file, "utf8")).replace(JSONC_EXTRAS, "$1"));
|
|
347
|
+
} catch {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/** Follows `keys` down nested JSON objects. */
|
|
352
|
+
function lookup(value, ...keys) {
|
|
353
|
+
let current = value;
|
|
354
|
+
for (const key of keys) {
|
|
355
|
+
if (!isJsonObject(current)) return void 0;
|
|
356
|
+
current = current[key];
|
|
357
|
+
}
|
|
358
|
+
return current;
|
|
359
|
+
}
|
|
360
|
+
/** Whether a `[plugins."…"]` table names the plugin, and does not turn it
|
|
361
|
+
* off. */
|
|
362
|
+
function codexHasPlugin(toml) {
|
|
363
|
+
const lines = toml.split("\n");
|
|
364
|
+
const start = lines.findIndex((line) => line.trim() === `[plugins."${WHITEBOARD_PLUGIN}"]`);
|
|
365
|
+
if (start === -1) return false;
|
|
366
|
+
for (const line of lines.slice(start + 1)) {
|
|
367
|
+
const trimmed = line.trim();
|
|
368
|
+
if (trimmed.startsWith("[")) break;
|
|
369
|
+
if (/^enabled\s*=\s*false\b/.test(trimmed)) return false;
|
|
370
|
+
}
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
/** Whether a harness already has Whiteboard, as its own config records it:
|
|
374
|
+
* the plugin it installed, or the MCP server added to it. Copilot keeps no
|
|
375
|
+
* record Whiteboard reads, so it always installs again, which is harmless. */
|
|
376
|
+
async function harnessConnected(target, homeDir, env, platform) {
|
|
377
|
+
const claudeDir = env.CLAUDE_CONFIG_DIR ?? path.join(homeDir, ".claude");
|
|
378
|
+
const hasServer = async (file, ...keys) => lookup(await readJson(file), ...keys, "whiteboard") !== void 0;
|
|
379
|
+
switch (target) {
|
|
380
|
+
case "claude": {
|
|
381
|
+
if (platform === "win32") return hasServer(path.join(homeDir, ".claude.json"), "mcpServers");
|
|
382
|
+
const [installed, settings] = await Promise.all([readJson(path.join(claudeDir, "plugins", "installed_plugins.json")), readJson(path.join(claudeDir, "settings.json"))]);
|
|
383
|
+
const installs = lookup(installed, "plugins", WHITEBOARD_PLUGIN);
|
|
384
|
+
return Array.isArray(installs) && installs.length > 0 && lookup(settings, "enabledPlugins", "whiteboard@devfast") !== false;
|
|
385
|
+
}
|
|
386
|
+
case "codex": try {
|
|
387
|
+
return codexHasPlugin(await readFile(path.join(env.CODEX_HOME ?? path.join(homeDir, ".codex"), "config.toml"), "utf8"));
|
|
388
|
+
} catch {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
case "opencode": {
|
|
392
|
+
const configDir = path.join(env.XDG_CONFIG_HOME || path.join(homeDir, ".config"), "opencode");
|
|
393
|
+
for (const name of [
|
|
394
|
+
"opencode.json",
|
|
395
|
+
"opencode.jsonc",
|
|
396
|
+
"config.json"
|
|
397
|
+
]) {
|
|
398
|
+
const config = await readJson(path.join(configDir, name));
|
|
399
|
+
if (lookup(config, "mcp", "whiteboard") !== void 0 || lookup(config, "mcp", "servers", "whiteboard") !== void 0) return true;
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
case "pi": return hasServer(path.join(homeDir, ".pi", "agent", "mcp.json"), "mcpServers");
|
|
404
|
+
case "omp": return hasServer(path.join(homeDir, ".omp", "agent", "mcp.json"), "mcpServers");
|
|
405
|
+
case "cursor": return cursorHasServer(homeDir);
|
|
406
|
+
case "copilot": return false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/** Every harness Whiteboard connects to, whether this machine has it, and
|
|
410
|
+
* whether it already has Whiteboard. */
|
|
411
|
+
async function detectConnectAgents(input = {}) {
|
|
412
|
+
const homeDir = input.homeDir ?? os.homedir();
|
|
413
|
+
const env = input.env ?? process.env;
|
|
414
|
+
const platform = input.platform ?? process.platform;
|
|
415
|
+
return Promise.all(ALL_INSTALL_TARGETS.map(async (target) => {
|
|
416
|
+
const found = await harnessFound(HARNESSES[target], homeDir, env, platform);
|
|
417
|
+
return {
|
|
418
|
+
target,
|
|
419
|
+
name: HARNESSES[target].name,
|
|
420
|
+
found,
|
|
421
|
+
connected: found && await harnessConnected(target, homeDir, env, platform)
|
|
422
|
+
};
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
/** oh-my-pi reads its MCP servers from one JSON file; merge ours in. */
|
|
426
|
+
async function connectOmp(homeDir, launch) {
|
|
427
|
+
const file = path.join(homeDir, ".omp", "agent", "mcp.json");
|
|
428
|
+
try {
|
|
429
|
+
const config = await exists(file) ? JSON.parse(await readFile(file, "utf8")) : {};
|
|
430
|
+
if (!isJsonObject(config)) throw new Error(`${file} is not a JSON object.`);
|
|
431
|
+
const servers = isJsonObject(config.mcpServers) ? { ...config.mcpServers } : {};
|
|
432
|
+
servers.whiteboard = {
|
|
433
|
+
command: launch.command,
|
|
434
|
+
args: [...launch.args]
|
|
435
|
+
};
|
|
436
|
+
const next = {
|
|
437
|
+
...config,
|
|
438
|
+
mcpServers: servers
|
|
439
|
+
};
|
|
440
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
441
|
+
await writeFile(file, `${JSON.stringify(next, null, 2)}\n`);
|
|
442
|
+
return {
|
|
443
|
+
target: "omp",
|
|
444
|
+
state: "connected",
|
|
445
|
+
output: `Updated ${file}`
|
|
446
|
+
};
|
|
447
|
+
} catch (error) {
|
|
448
|
+
return {
|
|
449
|
+
target: "omp",
|
|
450
|
+
state: "failed",
|
|
451
|
+
output: error instanceof Error ? error.message : String(error)
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/** Whether Cursor's global MCP config already names Whiteboard's server. */
|
|
456
|
+
async function cursorHasServer(homeDir) {
|
|
457
|
+
try {
|
|
458
|
+
const config = JSON.parse(await readFile(path.join(homeDir, ".cursor", "mcp.json"), "utf8"));
|
|
459
|
+
return isJsonObject(config) && isJsonObject(config.mcpServers) && config.mcpServers.whiteboard !== void 0;
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/** Cursor only installs an MCP server the user confirms, so open its link,
|
|
465
|
+
* unless it already has one: confirming again would ask for nothing new. */
|
|
466
|
+
async function connectCursor(homeDir, launch, platform, run) {
|
|
467
|
+
if (await cursorHasServer(homeDir)) return {
|
|
468
|
+
target: "cursor",
|
|
469
|
+
state: "connected",
|
|
470
|
+
output: "Cursor already has the whiteboard MCP server."
|
|
471
|
+
};
|
|
472
|
+
const url = cursorInstallDeeplink(launch);
|
|
473
|
+
const [file, args] = platform === "darwin" ? ["open", ["-g", url]] : platform === "win32" ? ["cmd", [
|
|
474
|
+
"/d",
|
|
475
|
+
"/c",
|
|
476
|
+
"start",
|
|
477
|
+
"\"\"",
|
|
478
|
+
url
|
|
479
|
+
]] : ["xdg-open", [url]];
|
|
480
|
+
const result = await run(file, args);
|
|
481
|
+
return {
|
|
482
|
+
target: "cursor",
|
|
483
|
+
state: result.code === 0 ? "approve" : "failed",
|
|
484
|
+
output: result.output
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/** Installs Whiteboard's plugin, or its MCP server, into one harness. */
|
|
488
|
+
async function connectAgent(target, input) {
|
|
489
|
+
const homeDir = input.homeDir ?? os.homedir();
|
|
490
|
+
const env = input.env ?? process.env;
|
|
491
|
+
const platform = input.platform ?? process.platform;
|
|
492
|
+
const run = input.run ?? runProcess;
|
|
493
|
+
const launch = reviewMcpLaunch(input.hasShim, platform);
|
|
494
|
+
if (target === "omp") return connectOmp(homeDir, launch);
|
|
495
|
+
if (target === "cursor") return connectCursor(homeDir, launch, platform, run);
|
|
496
|
+
const executable = await findHarness(HARNESSES[target], homeDir, env);
|
|
497
|
+
if (!executable) return {
|
|
498
|
+
target,
|
|
499
|
+
state: "failed",
|
|
500
|
+
output: `${HARNESSES[target].name} is not installed.`
|
|
501
|
+
};
|
|
502
|
+
const outputs = [];
|
|
503
|
+
for (const step of connectSteps(target, launch, platform)) {
|
|
504
|
+
const result = await run(executable, step.args);
|
|
505
|
+
outputs.push(result.output);
|
|
506
|
+
if (result.code !== 0 && !step.optional && !step.done?.test(result.output)) return {
|
|
507
|
+
target,
|
|
508
|
+
state: "failed",
|
|
509
|
+
output: outputs.join("\n").trim()
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
target,
|
|
514
|
+
state: "connected",
|
|
515
|
+
output: outputs.join("\n").trim()
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
//#endregion
|
|
234
519
|
//#region src/legacy-skills.ts
|
|
235
520
|
const LEGACY_SKILL_NAMES = [
|
|
236
521
|
"whiteboard",
|
|
@@ -638,17 +923,19 @@ function withShimPath(result, shimPath) {
|
|
|
638
923
|
}
|
|
639
924
|
/** The published plugin per harness; Cursor's link needs the shim it launches. */
|
|
640
925
|
function connectPlugins(hasShim) {
|
|
926
|
+
const launch = reviewMcpLaunch(hasShim);
|
|
927
|
+
const command = (target) => connectLines(target, launch, process.platform, false);
|
|
641
928
|
return {
|
|
642
929
|
claude: process.platform === "win32" ? {
|
|
643
930
|
label: "Add the Claude Code MCP server",
|
|
644
|
-
command:
|
|
931
|
+
command: command("claude")
|
|
645
932
|
} : {
|
|
646
933
|
label: "Install the Claude Code plugin",
|
|
647
934
|
command: "/plugin marketplace add devdotfast/whiteboard\n/plugin install whiteboard@devfast"
|
|
648
935
|
},
|
|
649
936
|
codex: {
|
|
650
937
|
label: "Install the Codex plugin",
|
|
651
|
-
command: "codex
|
|
938
|
+
command: command("codex")
|
|
652
939
|
},
|
|
653
940
|
cursor: hasShim ? {
|
|
654
941
|
label: "Install in Cursor",
|
|
@@ -656,11 +943,11 @@ function connectPlugins(hasShim) {
|
|
|
656
943
|
} : { label: "Install in Cursor" },
|
|
657
944
|
opencode: {
|
|
658
945
|
label: "Add the OpenCode MCP server (OpenCode 2+)",
|
|
659
|
-
command:
|
|
946
|
+
command: command("opencode")
|
|
660
947
|
},
|
|
661
948
|
pi: {
|
|
662
949
|
label: "Add the Pi MCP server (Pi 0.99+)",
|
|
663
|
-
command:
|
|
950
|
+
command: command("pi")
|
|
664
951
|
},
|
|
665
952
|
omp: {
|
|
666
953
|
label: "Add the oh-my-pi MCP server",
|
|
@@ -668,10 +955,10 @@ function connectPlugins(hasShim) {
|
|
|
668
955
|
},
|
|
669
956
|
copilot: process.platform === "win32" ? {
|
|
670
957
|
label: "Add the Copilot CLI MCP server",
|
|
671
|
-
command:
|
|
958
|
+
command: command("copilot")
|
|
672
959
|
} : {
|
|
673
960
|
label: "Install the Copilot CLI plugin",
|
|
674
|
-
command: "copilot
|
|
961
|
+
command: command("copilot")
|
|
675
962
|
}
|
|
676
963
|
};
|
|
677
964
|
}
|
|
@@ -1055,4 +1342,4 @@ async function aliasInstallationToAccount(telemetry, env = process.env, fetchImp
|
|
|
1055
1342
|
} catch {}
|
|
1056
1343
|
}
|
|
1057
1344
|
//#endregion
|
|
1058
|
-
export {
|
|
1345
|
+
export { isInstallTarget as _, isOwnedShim as a, removeLegacyReviewSkills as c, skipCliInstall as d, windowsInstallerCommand as f, ALL_INSTALL_TARGETS as g, detectConnectAgents as h, finishCliInstallUpdate as i, resetCliInstall as l, connectAgent as m, applyCliInstall as n, pathShimPath as o, scanLegacySkills as p, declineCliInstall as r, removeCliInstall as s, aliasInstallationToAccount as t, resolveCliInstallStatus as u, connectPrompts as v };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
2
|
-
import { T as traceMachineEnabled } from "./review-home-paths-
|
|
3
|
-
import { a as toolResultText, i as connectReviewInstance, s as ReviewApiError } from "./agent-client-
|
|
2
|
+
import { T as traceMachineEnabled } from "./review-home-paths-Lc02WFuX.js";
|
|
3
|
+
import { a as toolResultText, i as connectReviewInstance, s as ReviewApiError } from "./agent-client-D6xrEdTj.js";
|
|
4
4
|
import { M as devReviewHome } from "./s3-config-DDWWMh5x.js";
|
|
5
|
-
import { b as selectReviewInstance, u as REVIEW_INSTANCE_ENV } from "./server-discovery-
|
|
6
|
-
import { n as reviewSessionAgent } from "./review-telemetry-
|
|
7
|
-
import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-
|
|
8
|
-
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-
|
|
5
|
+
import { b as selectReviewInstance, u as REVIEW_INSTANCE_ENV } from "./server-discovery-IdX3Wc8g.js";
|
|
6
|
+
import { n as reviewSessionAgent } from "./review-telemetry-kTLRwBj_.js";
|
|
7
|
+
import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-2d7P4ugC.js";
|
|
8
|
+
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-A9dVocGs.js";
|
|
9
9
|
//#region src/review-api/agent-cli.ts
|
|
10
10
|
const reviewAgentCliHelp = "whiteboard api tools (one line per tool)\nwhiteboard api tools <tool-name> (its description and input schema)\nwhiteboard api <tool-name> '<json>'\nwhiteboard api <tool-name> - (read JSON from stdin)\nwhiteboard mcp (stdio MCP adapter; Whiteboard Desktop or whiteboard server start must be running)\nSelect headless state with DEV_REVIEW_SERVER_DIR or whiteboard --state-dir <path> api/mcp.\n";
|
|
11
11
|
async function runReviewAgentCli(input) {
|
|
@@ -20,7 +20,7 @@ async function runReviewAgentCli(input) {
|
|
|
20
20
|
if (extra.length || mode === "mcp" && name) throw new Error("Unexpected arguments. Use whiteboard api --help.");
|
|
21
21
|
const headers = Object.fromEntries([[REVIEW_VIA_HEADER, mode === "mcp" ? "mcp" : "api"], [REVIEW_AGENT_HEADER, reviewSessionAgent(env)]]);
|
|
22
22
|
if (mode === "mcp") {
|
|
23
|
-
const { serveReviewMcp } = await import("./mcp-
|
|
23
|
+
const { serveReviewMcp } = await import("./mcp-DqmNumCl.js");
|
|
24
24
|
await serveReviewMcp(async (key, agentKind) => {
|
|
25
25
|
const connected = await connectReviewInstance(key ? {
|
|
26
26
|
...env,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { d as isBooleanValue, f as isNumberValue, m as isStringValue, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
2
|
-
import "./src-
|
|
2
|
+
import "./src-C_iUYopM.js";
|
|
3
3
|
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
4
|
-
import { b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, s as reviewServerIsHealthy, y as reviewInstanceUnavailable } from "./server-discovery-
|
|
4
|
+
import { b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, s as reviewServerIsHealthy, y as reviewInstanceUnavailable } from "./server-discovery-IdX3Wc8g.js";
|
|
5
5
|
//#region ../review-protocol/src/review-api-client.ts
|
|
6
6
|
const defaultRequest = (url, init) => fetch(url, init);
|
|
7
7
|
/** A non-2xx reply; the status tells a caller whether retrying can help. */
|
|
@@ -68,7 +68,7 @@ var ReviewApiClient = class {
|
|
|
68
68
|
reader.releaseLock();
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
async follow(reviewId, signal, accept, disconnected
|
|
71
|
+
async follow(reviewId, signal, accept, disconnected) {
|
|
72
72
|
if (signal.aborted) return;
|
|
73
73
|
let connections = liveConnections.get(this.request);
|
|
74
74
|
if (!connections) liveConnections.set(this.request, connections = /* @__PURE__ */ new Map());
|
|
@@ -79,7 +79,6 @@ var ReviewApiClient = class {
|
|
|
79
79
|
connections.set(key, live);
|
|
80
80
|
}
|
|
81
81
|
const subscription = { reviewId };
|
|
82
|
-
if (mode) subscription.mode = mode;
|
|
83
82
|
return live.add(subscription, signal, (value) => accept(value), disconnected);
|
|
84
83
|
}
|
|
85
84
|
/** Reopen the shared stream now rather than after its backoff. */
|
|
@@ -104,7 +103,7 @@ function sleep(ms, signal) {
|
|
|
104
103
|
});
|
|
105
104
|
}
|
|
106
105
|
function concerns(line, subscription) {
|
|
107
|
-
return line.kind === "list" ? subscription.reviewId === null
|
|
106
|
+
return line.kind === "list" ? subscription.reviewId === null : subscription.reviewId === line.reviewId;
|
|
108
107
|
}
|
|
109
108
|
function report(listener, cause) {
|
|
110
109
|
if (listener.signal.aborted) return;
|
|
@@ -250,7 +249,7 @@ async function connectReviewInstance(env = process.env, headers = {}, options =
|
|
|
250
249
|
const selection = await selectReviewInstance({ env });
|
|
251
250
|
const discovery = healthyReviewInstance(selection);
|
|
252
251
|
if (!discovery) {
|
|
253
|
-
const { ensureServerWithoutDesktop } = await import("./background-server-
|
|
252
|
+
const { ensureServerWithoutDesktop } = await import("./background-server-KKSjffhS.js");
|
|
254
253
|
const server = await ensureServerWithoutDesktop({
|
|
255
254
|
selection,
|
|
256
255
|
env,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { S as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-
|
|
1
|
+
import { c as activityEndSchema, i as commandSchema, l as activityUpdateSchema, n as uploadSchema, s as activityBeginSchema } from "./local-data-Bhs4VnzC.js";
|
|
2
|
+
import { S as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-DVJJPBGd.js";
|
|
3
3
|
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
4
|
-
import { t as findReviewPackageRoot } from "./package-paths-
|
|
4
|
+
import { t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { readFile } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./review-home-paths-
|
|
1
|
+
import "./review-home-paths-Lc02WFuX.js";
|
|
2
2
|
import { D as liveLockOwner, O as processIsAlive } from "./s3-config-DDWWMh5x.js";
|
|
3
|
-
import { t as findReviewPackageRoot } from "./package-paths-
|
|
4
|
-
import { a as readReviewServerHealth, c as reviewServerStateDir, i as readReviewServerDiscovery, n as desktopOwnsHome, p as desktopUsesHome, r as headlessServerLockPath } from "./server-discovery-
|
|
5
|
-
import { t as desktopApplicationInstalled } from "./review-app-launcher-
|
|
3
|
+
import { t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
|
|
4
|
+
import { a as readReviewServerHealth, c as reviewServerStateDir, i as readReviewServerDiscovery, n as desktopOwnsHome, p as desktopUsesHome, r as headlessServerLockPath } from "./server-discovery-IdX3Wc8g.js";
|
|
5
|
+
import { t as desktopApplicationInstalled } from "./review-app-launcher-C7PUhYox.js";
|
|
6
6
|
import { mkdir, open, readFile, realpath, stat } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as headlessServerOwner, c as stopBackgroundServer, i as ensureServerWithoutDesktop, r as ensureBackgroundServer } from "./background-server-
|
|
1
|
+
import { a as headlessServerOwner, c as stopBackgroundServer, i as ensureServerWithoutDesktop, r as ensureBackgroundServer } from "./background-server-D7maQkJV.js";
|
|
2
2
|
export { ensureBackgroundServer, ensureServerWithoutDesktop, headlessServerOwner, stopBackgroundServer };
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.2.5-preview.20261015.
|
|
1
|
+
{"version":"0.2.5-preview.20261015.120","commit":"99b613b6ce358d31fd4cc2b59822a939f7f113cf","dirty":true,"builtAt":"2026-10-15T23:32:28.351Z"}
|