@sideboard-ai/core 0.1.43 → 0.1.44
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-T3EA7GZV.js → agents-PP3URTSF.js} +1 -1
- package/dist/{chunk-X6P2QVRJ.js → chunk-I3PKMLFW.js} +1 -1
- package/dist/{chunk-Z2BQMXVM.js → chunk-JM2TVGNW.js} +2 -1
- package/dist/{chunk-FUCEOJFO.js → chunk-TXFJEXFB.js} +1 -1
- package/dist/{chunk-AGU52GTV.js → chunk-V3S4NF5F.js} +104 -11
- package/dist/{chunk-VC7NORFX.js → chunk-XX26NCB6.js} +1 -1
- package/dist/{coordinator-prompt-BHXBQRPM.js → coordinator-prompt-I5YNS27B.js} +1 -1
- package/dist/{global-workspace-SNN45OCN.js → global-workspace-FU6UIPDY.js} +2 -2
- package/dist/index.cjs +116 -12
- package/dist/index.d.cts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +17 -5
- package/dist/mcp/run-stdio.cjs +200 -110
- package/dist/mcp/run-stdio.js +5 -5
- package/dist/{workspaces-AUYIJ64Z.js → workspaces-RSEDTBGJ.js} +3 -3
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -3235,8 +3235,9 @@ var init_coordinator_prompt = __esm({
|
|
|
3235
3235
|
"Setup / run:",
|
|
3236
3236
|
"- run_setup \u2014 re-run worktree setup",
|
|
3237
3237
|
"- list_run_scripts / run_dev_script / stop_dev_script \u2014 start/stop named run scripts",
|
|
3238
|
-
"Inspect / PRs:",
|
|
3238
|
+
"Inspect / review / PRs:",
|
|
3239
3239
|
"- get_diff \u2014 compact diff summary",
|
|
3240
|
+
"- request_review \u2014 open a Review chat tab on a worktree thread (merge-readiness recommendation); then wait_for_turn / get_turn_result on the returned id",
|
|
3240
3241
|
"- 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.",
|
|
3241
3242
|
"Human-only (do not attempt): merge, ready-for-review land, purge_thread.",
|
|
3242
3243
|
"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.",
|
|
@@ -5690,10 +5691,10 @@ __export(cursor_recover_exports, {
|
|
|
5690
5691
|
function recoverFinishedCursorRun(opts) {
|
|
5691
5692
|
const agentId = opts.agentId.trim();
|
|
5692
5693
|
if (!agentId) return null;
|
|
5693
|
-
const runsPath = (0,
|
|
5694
|
-
if (!(0,
|
|
5694
|
+
const runsPath = (0, import_node_path24.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
5695
|
+
if (!(0, import_node_fs25.existsSync)(runsPath)) return null;
|
|
5695
5696
|
try {
|
|
5696
|
-
const lines = (0,
|
|
5697
|
+
const lines = (0, import_node_fs25.readFileSync)(runsPath, "utf8").split("\n");
|
|
5697
5698
|
let best = null;
|
|
5698
5699
|
for (const line of lines) {
|
|
5699
5700
|
const trimmed = line.trim();
|
|
@@ -5719,12 +5720,12 @@ function recoverFinishedCursorRun(opts) {
|
|
|
5719
5720
|
return null;
|
|
5720
5721
|
}
|
|
5721
5722
|
}
|
|
5722
|
-
var
|
|
5723
|
+
var import_node_fs25, import_node_path24;
|
|
5723
5724
|
var init_cursor_recover = __esm({
|
|
5724
5725
|
"src/agents/cursor-recover.ts"() {
|
|
5725
5726
|
"use strict";
|
|
5726
|
-
|
|
5727
|
-
|
|
5727
|
+
import_node_fs25 = require("fs");
|
|
5728
|
+
import_node_path24 = require("path");
|
|
5728
5729
|
init_paths();
|
|
5729
5730
|
}
|
|
5730
5731
|
});
|
|
@@ -5733,11 +5734,11 @@ var init_cursor_recover = __esm({
|
|
|
5733
5734
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5734
5735
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
5735
5736
|
var import_zod = require("zod");
|
|
5736
|
-
var
|
|
5737
|
+
var import_node_path25 = require("path");
|
|
5737
5738
|
|
|
5738
5739
|
// src/orchestrator/orchestrator.ts
|
|
5739
5740
|
var import_node_events = require("events");
|
|
5740
|
-
var
|
|
5741
|
+
var import_node_fs26 = require("fs");
|
|
5741
5742
|
init_error_detail();
|
|
5742
5743
|
|
|
5743
5744
|
// src/agents/spawn.ts
|
|
@@ -7182,6 +7183,58 @@ function forkChatTab(input) {
|
|
|
7182
7183
|
});
|
|
7183
7184
|
}
|
|
7184
7185
|
|
|
7186
|
+
// src/review/request-review.ts
|
|
7187
|
+
var import_node_crypto3 = require("crypto");
|
|
7188
|
+
var import_node_fs19 = require("fs");
|
|
7189
|
+
var import_node_path18 = require("path");
|
|
7190
|
+
init_global_workspace();
|
|
7191
|
+
init_thread_store();
|
|
7192
|
+
var REVIEW_REQUEST_PATH = ".sideboard/attachments/Review request.md";
|
|
7193
|
+
var REVIEW_REQUEST_NAME = "Review request.md";
|
|
7194
|
+
var REVIEW_REQUEST_PREFILL = `Please review the changes in this workspace and recommend whether they are ready to merge.
|
|
7195
|
+
|
|
7196
|
+
Start with a **Recommendation**: Approve, Approve with nits, Request changes, or Needs more information \u2014 and say why in 1\u20133 sentences. Then list blocking findings vs nits (findings may be empty).`;
|
|
7197
|
+
function buildReviewRequestAttachment(content) {
|
|
7198
|
+
return {
|
|
7199
|
+
id: (0, import_node_crypto3.randomUUID)(),
|
|
7200
|
+
name: REVIEW_REQUEST_NAME,
|
|
7201
|
+
kind: "file",
|
|
7202
|
+
path: REVIEW_REQUEST_PATH,
|
|
7203
|
+
content
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
function readExistingReviewRequestFile(worktreePath) {
|
|
7207
|
+
const abs = (0, import_node_path18.join)(worktreePath, REVIEW_REQUEST_PATH);
|
|
7208
|
+
if (!(0, import_node_fs19.existsSync)(abs)) return null;
|
|
7209
|
+
try {
|
|
7210
|
+
const content = (0, import_node_fs19.readFileSync)(abs, "utf8");
|
|
7211
|
+
return content.trim() ? content : null;
|
|
7212
|
+
} catch {
|
|
7213
|
+
return null;
|
|
7214
|
+
}
|
|
7215
|
+
}
|
|
7216
|
+
async function requestReview(threadRef, send) {
|
|
7217
|
+
const from = findThreadByRef(threadRef);
|
|
7218
|
+
if (!from) throw new Error(`Thread not found: ${threadRef}`);
|
|
7219
|
+
if (isOrchestratorThread(from)) {
|
|
7220
|
+
throw new Error(
|
|
7221
|
+
"request_review targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
|
|
7222
|
+
);
|
|
7223
|
+
}
|
|
7224
|
+
if (from.status === "archived") {
|
|
7225
|
+
throw new Error(`Thread is archived: ${from.id}`);
|
|
7226
|
+
}
|
|
7227
|
+
const existing = readExistingReviewRequestFile(from.worktreePath);
|
|
7228
|
+
const attachments = existing ? [buildReviewRequestAttachment(existing)] : [];
|
|
7229
|
+
const tab = createChatTab({
|
|
7230
|
+
fromThreadId: from.id,
|
|
7231
|
+
title: "Review",
|
|
7232
|
+
attachments
|
|
7233
|
+
});
|
|
7234
|
+
const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
|
|
7235
|
+
return { tab: started, from };
|
|
7236
|
+
}
|
|
7237
|
+
|
|
7185
7238
|
// src/threads/fork-worktree.ts
|
|
7186
7239
|
init_thread_store();
|
|
7187
7240
|
function requireThread2(idOrRef) {
|
|
@@ -7215,20 +7268,20 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
7215
7268
|
|
|
7216
7269
|
// src/threads/adopt.ts
|
|
7217
7270
|
var import_node_child_process = require("child_process");
|
|
7218
|
-
var
|
|
7271
|
+
var import_node_fs20 = require("fs");
|
|
7219
7272
|
var import_node_os8 = require("os");
|
|
7220
|
-
var
|
|
7273
|
+
var import_node_path19 = require("path");
|
|
7221
7274
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
7222
7275
|
init_worktree();
|
|
7223
7276
|
init_thread_store();
|
|
7224
|
-
var CONDUCTOR_APP_SUPPORT = (0,
|
|
7277
|
+
var CONDUCTOR_APP_SUPPORT = (0, import_node_path19.join)(
|
|
7225
7278
|
process.env.HOME ?? "",
|
|
7226
7279
|
"Library",
|
|
7227
7280
|
"Application Support",
|
|
7228
7281
|
"com.conductor.app"
|
|
7229
7282
|
);
|
|
7230
|
-
var CONDUCTOR_DB = (0,
|
|
7231
|
-
var CURSOR_SDK_STORE = (0,
|
|
7283
|
+
var CONDUCTOR_DB = (0, import_node_path19.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
7284
|
+
var CURSOR_SDK_STORE = (0, import_node_path19.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
7232
7285
|
function mapAgentType(raw) {
|
|
7233
7286
|
if (!raw) return null;
|
|
7234
7287
|
const v = raw.toLowerCase();
|
|
@@ -7240,21 +7293,21 @@ function mapAgentType(raw) {
|
|
|
7240
7293
|
return null;
|
|
7241
7294
|
}
|
|
7242
7295
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
7243
|
-
if (!workspacePath || !(0,
|
|
7296
|
+
if (!workspacePath || !(0, import_node_fs20.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
7244
7297
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
7245
7298
|
let best = null;
|
|
7246
7299
|
let hashes;
|
|
7247
7300
|
try {
|
|
7248
|
-
hashes = (0,
|
|
7301
|
+
hashes = (0, import_node_fs20.readdirSync)(CURSOR_SDK_STORE);
|
|
7249
7302
|
} catch {
|
|
7250
7303
|
return null;
|
|
7251
7304
|
}
|
|
7252
7305
|
for (const hash of hashes) {
|
|
7253
|
-
const agentsFile = (0,
|
|
7254
|
-
if (!(0,
|
|
7306
|
+
const agentsFile = (0, import_node_path19.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
7307
|
+
if (!(0, import_node_fs20.existsSync)(agentsFile)) continue;
|
|
7255
7308
|
let text;
|
|
7256
7309
|
try {
|
|
7257
|
-
text = (0,
|
|
7310
|
+
text = (0, import_node_fs20.readFileSync)(agentsFile, "utf8");
|
|
7258
7311
|
} catch {
|
|
7259
7312
|
continue;
|
|
7260
7313
|
}
|
|
@@ -7278,7 +7331,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
7278
7331
|
return best?.agentId ?? null;
|
|
7279
7332
|
}
|
|
7280
7333
|
async function adoptThread(input) {
|
|
7281
|
-
if (!(0,
|
|
7334
|
+
if (!(0, import_node_fs20.existsSync)(input.worktreePath)) {
|
|
7282
7335
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
7283
7336
|
}
|
|
7284
7337
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -7302,18 +7355,18 @@ async function adoptThread(input) {
|
|
|
7302
7355
|
return thread;
|
|
7303
7356
|
}
|
|
7304
7357
|
function listConductorWorkspaces() {
|
|
7305
|
-
if (!(0,
|
|
7358
|
+
if (!(0, import_node_fs20.existsSync)(CONDUCTOR_DB)) {
|
|
7306
7359
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
7307
7360
|
}
|
|
7308
|
-
const tmp = (0,
|
|
7309
|
-
const snapshot = (0,
|
|
7361
|
+
const tmp = (0, import_node_fs20.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
7362
|
+
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
7310
7363
|
try {
|
|
7311
|
-
(0,
|
|
7364
|
+
(0, import_node_fs20.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
7312
7365
|
for (const suffix of ["-wal", "-shm"]) {
|
|
7313
7366
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
7314
|
-
if ((0,
|
|
7367
|
+
if ((0, import_node_fs20.existsSync)(src)) {
|
|
7315
7368
|
try {
|
|
7316
|
-
(0,
|
|
7369
|
+
(0, import_node_fs20.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
7317
7370
|
} catch {
|
|
7318
7371
|
}
|
|
7319
7372
|
}
|
|
@@ -7389,22 +7442,22 @@ function listConductorWorkspaces() {
|
|
|
7389
7442
|
db.close();
|
|
7390
7443
|
}
|
|
7391
7444
|
} finally {
|
|
7392
|
-
(0,
|
|
7445
|
+
(0, import_node_fs20.rmSync)(tmp, { recursive: true, force: true });
|
|
7393
7446
|
}
|
|
7394
7447
|
}
|
|
7395
7448
|
function importConductorWorkspace(workspaceId) {
|
|
7396
|
-
if (!(0,
|
|
7449
|
+
if (!(0, import_node_fs20.existsSync)(CONDUCTOR_DB)) {
|
|
7397
7450
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
7398
7451
|
}
|
|
7399
|
-
const tmp = (0,
|
|
7400
|
-
const snapshot = (0,
|
|
7452
|
+
const tmp = (0, import_node_fs20.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
7453
|
+
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
7401
7454
|
try {
|
|
7402
|
-
(0,
|
|
7455
|
+
(0, import_node_fs20.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
7403
7456
|
for (const suffix of ["-wal", "-shm"]) {
|
|
7404
7457
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
7405
|
-
if ((0,
|
|
7458
|
+
if ((0, import_node_fs20.existsSync)(src)) {
|
|
7406
7459
|
try {
|
|
7407
|
-
(0,
|
|
7460
|
+
(0, import_node_fs20.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
7408
7461
|
} catch {
|
|
7409
7462
|
}
|
|
7410
7463
|
}
|
|
@@ -7422,7 +7475,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
7422
7475
|
).get(workspaceId);
|
|
7423
7476
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
7424
7477
|
const worktreePath = String(row.workspacePath);
|
|
7425
|
-
if (!(0,
|
|
7478
|
+
if (!(0, import_node_fs20.existsSync)(worktreePath)) {
|
|
7426
7479
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
7427
7480
|
}
|
|
7428
7481
|
let sessionId = null;
|
|
@@ -7485,7 +7538,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
7485
7538
|
db.close();
|
|
7486
7539
|
}
|
|
7487
7540
|
} finally {
|
|
7488
|
-
(0,
|
|
7541
|
+
(0, import_node_fs20.rmSync)(tmp, { recursive: true, force: true });
|
|
7489
7542
|
}
|
|
7490
7543
|
}
|
|
7491
7544
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -7496,12 +7549,12 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
7496
7549
|
init_worktree();
|
|
7497
7550
|
|
|
7498
7551
|
// src/diff/diff.ts
|
|
7499
|
-
var
|
|
7500
|
-
var
|
|
7552
|
+
var import_node_fs21 = require("fs");
|
|
7553
|
+
var import_node_path20 = require("path");
|
|
7501
7554
|
init_run();
|
|
7502
7555
|
init_worktree();
|
|
7503
7556
|
async function inspectGitWorktree(worktreePath) {
|
|
7504
|
-
if (!worktreePath || !(0,
|
|
7557
|
+
if (!worktreePath || !(0, import_node_fs21.existsSync)(worktreePath)) return "missing_worktree";
|
|
7505
7558
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
7506
7559
|
reject: false
|
|
7507
7560
|
});
|
|
@@ -7509,7 +7562,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
7509
7562
|
return "ok";
|
|
7510
7563
|
}
|
|
7511
7564
|
async function initializeGitRepository(worktreePath) {
|
|
7512
|
-
if (!worktreePath || !(0,
|
|
7565
|
+
if (!worktreePath || !(0, import_node_fs21.existsSync)(worktreePath)) {
|
|
7513
7566
|
throw new Error("Worktree not found");
|
|
7514
7567
|
}
|
|
7515
7568
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -7941,8 +7994,8 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
7941
7994
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
7942
7995
|
assertSafeRelativePath(relativePath);
|
|
7943
7996
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
7944
|
-
const abs = (0,
|
|
7945
|
-
const st = (0,
|
|
7997
|
+
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
7998
|
+
const st = (0, import_node_fs21.statSync)(abs);
|
|
7946
7999
|
if (!st.isFile()) {
|
|
7947
8000
|
throw new Error(`Not a file: ${relativePath}`);
|
|
7948
8001
|
}
|
|
@@ -7951,7 +8004,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
7951
8004
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
7952
8005
|
);
|
|
7953
8006
|
}
|
|
7954
|
-
const buf = (0,
|
|
8007
|
+
const buf = (0, import_node_fs21.readFileSync)(abs);
|
|
7955
8008
|
return {
|
|
7956
8009
|
path: relativePath,
|
|
7957
8010
|
contentBase64: buf.toString("base64"),
|
|
@@ -7961,12 +8014,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
7961
8014
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
7962
8015
|
assertSafeRelativePath(relativePath);
|
|
7963
8016
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
7964
|
-
const abs = (0,
|
|
7965
|
-
const st = (0,
|
|
8017
|
+
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
8018
|
+
const st = (0, import_node_fs21.statSync)(abs);
|
|
7966
8019
|
if (!st.isFile()) {
|
|
7967
8020
|
throw new Error(`Not a file: ${relativePath}`);
|
|
7968
8021
|
}
|
|
7969
|
-
const buf = (0,
|
|
8022
|
+
const buf = (0, import_node_fs21.readFileSync)(abs);
|
|
7970
8023
|
if (isImageRelativePath(relativePath)) {
|
|
7971
8024
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
7972
8025
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -8009,9 +8062,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
8009
8062
|
}
|
|
8010
8063
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
8011
8064
|
assertSafeRelativePath(relativePath);
|
|
8012
|
-
const abs = (0,
|
|
8013
|
-
(0,
|
|
8014
|
-
(0,
|
|
8065
|
+
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
8066
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path20.dirname)(abs), { recursive: true });
|
|
8067
|
+
(0, import_node_fs21.writeFileSync)(abs, content, "utf8");
|
|
8015
8068
|
return { path: relativePath };
|
|
8016
8069
|
}
|
|
8017
8070
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -8190,9 +8243,9 @@ async function confirmLand(thread, opts) {
|
|
|
8190
8243
|
}
|
|
8191
8244
|
|
|
8192
8245
|
// src/skills/discover.ts
|
|
8193
|
-
var
|
|
8246
|
+
var import_node_fs22 = require("fs");
|
|
8194
8247
|
var import_node_os9 = require("os");
|
|
8195
|
-
var
|
|
8248
|
+
var import_node_path21 = require("path");
|
|
8196
8249
|
function toCommand(name) {
|
|
8197
8250
|
return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8198
8251
|
}
|
|
@@ -8224,7 +8277,7 @@ function parseFrontmatter(content) {
|
|
|
8224
8277
|
}
|
|
8225
8278
|
function readSkill(skillMd, source) {
|
|
8226
8279
|
try {
|
|
8227
|
-
const content = (0,
|
|
8280
|
+
const content = (0, import_node_fs22.readFileSync)(skillMd, "utf8");
|
|
8228
8281
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
8229
8282
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
8230
8283
|
const name = fmName || dirName;
|
|
@@ -8243,19 +8296,19 @@ function readSkill(skillMd, source) {
|
|
|
8243
8296
|
}
|
|
8244
8297
|
}
|
|
8245
8298
|
function scanSkillsDir(dir, source, out) {
|
|
8246
|
-
if (!(0,
|
|
8299
|
+
if (!(0, import_node_fs22.existsSync)(dir)) return;
|
|
8247
8300
|
let entries;
|
|
8248
8301
|
try {
|
|
8249
|
-
entries = (0,
|
|
8302
|
+
entries = (0, import_node_fs22.readdirSync)(dir);
|
|
8250
8303
|
} catch {
|
|
8251
8304
|
return;
|
|
8252
8305
|
}
|
|
8253
8306
|
for (const entry of entries) {
|
|
8254
8307
|
if (entry.startsWith(".")) continue;
|
|
8255
|
-
const skillMd = (0,
|
|
8256
|
-
if (!(0,
|
|
8308
|
+
const skillMd = (0, import_node_path21.join)(dir, entry, "SKILL.md");
|
|
8309
|
+
if (!(0, import_node_fs22.existsSync)(skillMd)) continue;
|
|
8257
8310
|
try {
|
|
8258
|
-
if (!(0,
|
|
8311
|
+
if (!(0, import_node_fs22.statSync)(skillMd).isFile()) continue;
|
|
8259
8312
|
} catch {
|
|
8260
8313
|
continue;
|
|
8261
8314
|
}
|
|
@@ -8264,24 +8317,24 @@ function scanSkillsDir(dir, source, out) {
|
|
|
8264
8317
|
}
|
|
8265
8318
|
}
|
|
8266
8319
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
8267
|
-
if (!(0,
|
|
8320
|
+
if (!(0, import_node_fs22.existsSync)(pluginsRoot)) return;
|
|
8268
8321
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
8269
8322
|
if (depth > 7) return;
|
|
8270
8323
|
let entries;
|
|
8271
8324
|
try {
|
|
8272
|
-
entries = (0,
|
|
8325
|
+
entries = (0, import_node_fs22.readdirSync)(dir);
|
|
8273
8326
|
} catch {
|
|
8274
8327
|
return;
|
|
8275
8328
|
}
|
|
8276
8329
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
8277
|
-
const skill = readSkill((0,
|
|
8330
|
+
const skill = readSkill((0, import_node_path21.join)(dir, "SKILL.md"), "cli");
|
|
8278
8331
|
if (skill) out.push(skill);
|
|
8279
8332
|
}
|
|
8280
8333
|
for (const entry of entries) {
|
|
8281
8334
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
8282
|
-
const full = (0,
|
|
8335
|
+
const full = (0, import_node_path21.join)(dir, entry);
|
|
8283
8336
|
try {
|
|
8284
|
-
if (!(0,
|
|
8337
|
+
if (!(0, import_node_fs22.statSync)(full).isDirectory()) continue;
|
|
8285
8338
|
} catch {
|
|
8286
8339
|
continue;
|
|
8287
8340
|
}
|
|
@@ -8299,17 +8352,17 @@ function discoverSkills(worktreePath) {
|
|
|
8299
8352
|
const home = (0, import_node_os9.homedir)();
|
|
8300
8353
|
const collected = [];
|
|
8301
8354
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
8302
|
-
scanSkillsDir((0,
|
|
8355
|
+
scanSkillsDir((0, import_node_path21.join)(worktreePath, rel), "workspace", collected);
|
|
8303
8356
|
}
|
|
8304
8357
|
for (const abs of [
|
|
8305
|
-
(0,
|
|
8306
|
-
(0,
|
|
8307
|
-
(0,
|
|
8308
|
-
(0,
|
|
8358
|
+
(0, import_node_path21.join)(home, ".claude/skills"),
|
|
8359
|
+
(0, import_node_path21.join)(home, ".cursor/skills"),
|
|
8360
|
+
(0, import_node_path21.join)(home, ".sideboard/skills"),
|
|
8361
|
+
(0, import_node_path21.join)(home, ".brightsy/skills")
|
|
8309
8362
|
]) {
|
|
8310
8363
|
scanSkillsDir(abs, "user", collected);
|
|
8311
8364
|
}
|
|
8312
|
-
scanClaudePluginSkills((0,
|
|
8365
|
+
scanClaudePluginSkills((0, import_node_path21.join)(home, ".claude/plugins"), collected);
|
|
8313
8366
|
const rank = { workspace: 0, user: 1, cli: 2 };
|
|
8314
8367
|
const byCommand = /* @__PURE__ */ new Map();
|
|
8315
8368
|
for (const skill of collected) {
|
|
@@ -8321,7 +8374,7 @@ function discoverSkills(worktreePath) {
|
|
|
8321
8374
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
8322
8375
|
}
|
|
8323
8376
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
8324
|
-
const raw = (0,
|
|
8377
|
+
const raw = (0, import_node_fs22.readFileSync)(skillPath, "utf8");
|
|
8325
8378
|
if (raw.startsWith("---")) {
|
|
8326
8379
|
const end = raw.indexOf("\n---", 3);
|
|
8327
8380
|
if (end >= 0) {
|
|
@@ -8414,9 +8467,9 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
8414
8467
|
}
|
|
8415
8468
|
|
|
8416
8469
|
// src/composer/stage-files.ts
|
|
8417
|
-
var
|
|
8418
|
-
var
|
|
8419
|
-
var
|
|
8470
|
+
var import_node_fs23 = require("fs");
|
|
8471
|
+
var import_node_path22 = require("path");
|
|
8472
|
+
var import_node_crypto4 = require("crypto");
|
|
8420
8473
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
8421
8474
|
"png",
|
|
8422
8475
|
"jpg",
|
|
@@ -8445,7 +8498,7 @@ var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local on
|
|
|
8445
8498
|
var MAX_INLINE_BYTES = 4e5;
|
|
8446
8499
|
var MAX_PREVIEW_BYTES = 5e6;
|
|
8447
8500
|
function fileExtension(filePath) {
|
|
8448
|
-
const base = (0,
|
|
8501
|
+
const base = (0, import_node_path22.basename)(filePath).toLowerCase();
|
|
8449
8502
|
return base.includes(".") ? base.split(".").pop() || "" : "";
|
|
8450
8503
|
}
|
|
8451
8504
|
function isImageFilePath(filePath) {
|
|
@@ -8455,24 +8508,24 @@ function imageMimeType(filePath) {
|
|
|
8455
8508
|
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
8456
8509
|
}
|
|
8457
8510
|
function ensureAttachmentsDir(worktreePath) {
|
|
8458
|
-
const dir = (0,
|
|
8459
|
-
(0,
|
|
8460
|
-
const gi = (0,
|
|
8461
|
-
if (!(0,
|
|
8462
|
-
(0,
|
|
8511
|
+
const dir = (0, import_node_path22.join)(worktreePath, ATTACHMENTS_DIR);
|
|
8512
|
+
(0, import_node_fs23.mkdirSync)(dir, { recursive: true });
|
|
8513
|
+
const gi = (0, import_node_path22.join)(dir, ".gitignore");
|
|
8514
|
+
if (!(0, import_node_fs23.existsSync)(gi)) {
|
|
8515
|
+
(0, import_node_fs23.writeFileSync)(gi, ATTACHMENTS_GITIGNORE, "utf8");
|
|
8463
8516
|
}
|
|
8464
8517
|
return dir;
|
|
8465
8518
|
}
|
|
8466
8519
|
function uniqueAttachmentName(dir, originalName) {
|
|
8467
8520
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
8468
|
-
if (!(0,
|
|
8469
|
-
const ext = (0,
|
|
8521
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path22.join)(dir, safe))) return safe;
|
|
8522
|
+
const ext = (0, import_node_path22.extname)(safe);
|
|
8470
8523
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
8471
8524
|
for (let i = 1; i < 1e4; i++) {
|
|
8472
8525
|
const candidate = `${stem}-${i}${ext}`;
|
|
8473
|
-
if (!(0,
|
|
8526
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
|
|
8474
8527
|
}
|
|
8475
|
-
return `${stem}-${(0,
|
|
8528
|
+
return `${stem}-${(0, import_node_crypto4.randomUUID)()}${ext}`;
|
|
8476
8529
|
}
|
|
8477
8530
|
function previewDataUrlFromBuf(filePath, buf) {
|
|
8478
8531
|
if (!isImageFilePath(filePath)) return void 0;
|
|
@@ -8484,7 +8537,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
8484
8537
|
if (isImageFilePath(name)) {
|
|
8485
8538
|
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
8486
8539
|
return {
|
|
8487
|
-
id: (0,
|
|
8540
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8488
8541
|
name,
|
|
8489
8542
|
kind: "file",
|
|
8490
8543
|
path: opts.path,
|
|
@@ -8497,7 +8550,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
8497
8550
|
}
|
|
8498
8551
|
if (buf.length > MAX_INLINE_BYTES) {
|
|
8499
8552
|
return {
|
|
8500
|
-
id: (0,
|
|
8553
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8501
8554
|
name,
|
|
8502
8555
|
kind: "file",
|
|
8503
8556
|
path: opts.path,
|
|
@@ -8506,7 +8559,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
8506
8559
|
}
|
|
8507
8560
|
if (buf.includes(0)) {
|
|
8508
8561
|
return {
|
|
8509
|
-
id: (0,
|
|
8562
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8510
8563
|
name,
|
|
8511
8564
|
kind: "file",
|
|
8512
8565
|
path: opts.path,
|
|
@@ -8514,7 +8567,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
8514
8567
|
};
|
|
8515
8568
|
}
|
|
8516
8569
|
return {
|
|
8517
|
-
id: (0,
|
|
8570
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8518
8571
|
name,
|
|
8519
8572
|
kind: "file",
|
|
8520
8573
|
path: opts.path,
|
|
@@ -8526,19 +8579,19 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
8526
8579
|
const dir = ensureAttachmentsDir(worktreePath);
|
|
8527
8580
|
const out = [];
|
|
8528
8581
|
for (const abs of absolutePaths) {
|
|
8529
|
-
const originalName = (0,
|
|
8582
|
+
const originalName = (0, import_node_path22.basename)(abs);
|
|
8530
8583
|
try {
|
|
8531
|
-
const st = (0,
|
|
8584
|
+
const st = (0, import_node_fs23.statSync)(abs);
|
|
8532
8585
|
if (!st.isFile()) continue;
|
|
8533
8586
|
const name = uniqueAttachmentName(dir, originalName);
|
|
8534
|
-
const destAbs = (0,
|
|
8535
|
-
(0,
|
|
8587
|
+
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
8588
|
+
(0, import_node_fs23.copyFileSync)(abs, destAbs);
|
|
8536
8589
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
8537
|
-
const buf = (0,
|
|
8590
|
+
const buf = (0, import_node_fs23.readFileSync)(destAbs);
|
|
8538
8591
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
8539
8592
|
} catch (err) {
|
|
8540
8593
|
out.push({
|
|
8541
|
-
id: (0,
|
|
8594
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8542
8595
|
name: originalName,
|
|
8543
8596
|
kind: "file",
|
|
8544
8597
|
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -8556,13 +8609,13 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
8556
8609
|
try {
|
|
8557
8610
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
8558
8611
|
const name = uniqueAttachmentName(dir, originalName);
|
|
8559
|
-
const destAbs = (0,
|
|
8560
|
-
(0,
|
|
8612
|
+
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
8613
|
+
(0, import_node_fs23.writeFileSync)(destAbs, buf);
|
|
8561
8614
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
8562
8615
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
8563
8616
|
} catch (err) {
|
|
8564
8617
|
out.push({
|
|
8565
|
-
id: (0,
|
|
8618
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8566
8619
|
name: originalName,
|
|
8567
8620
|
kind: "file",
|
|
8568
8621
|
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -8576,23 +8629,23 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
8576
8629
|
for (const rel of relativePaths) {
|
|
8577
8630
|
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
8578
8631
|
out.push({
|
|
8579
|
-
id: (0,
|
|
8580
|
-
name: (0,
|
|
8632
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8633
|
+
name: (0, import_node_path22.basename)(rel) || "file",
|
|
8581
8634
|
kind: "file",
|
|
8582
8635
|
content: `(invalid path: ${rel})`
|
|
8583
8636
|
});
|
|
8584
8637
|
continue;
|
|
8585
8638
|
}
|
|
8586
|
-
const name = (0,
|
|
8639
|
+
const name = (0, import_node_path22.basename)(rel);
|
|
8587
8640
|
try {
|
|
8588
|
-
const abs = (0,
|
|
8589
|
-
const st = (0,
|
|
8641
|
+
const abs = (0, import_node_path22.join)(worktreePath, rel);
|
|
8642
|
+
const st = (0, import_node_fs23.statSync)(abs);
|
|
8590
8643
|
if (!st.isFile()) continue;
|
|
8591
|
-
const buf = (0,
|
|
8644
|
+
const buf = (0, import_node_fs23.readFileSync)(abs);
|
|
8592
8645
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
8593
8646
|
} catch (err) {
|
|
8594
8647
|
out.push({
|
|
8595
|
-
id: (0,
|
|
8648
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
8596
8649
|
name,
|
|
8597
8650
|
kind: "file",
|
|
8598
8651
|
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -8603,8 +8656,8 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
8603
8656
|
}
|
|
8604
8657
|
|
|
8605
8658
|
// src/agents/instructions.ts
|
|
8606
|
-
var
|
|
8607
|
-
var
|
|
8659
|
+
var import_node_fs24 = require("fs");
|
|
8660
|
+
var import_node_path23 = require("path");
|
|
8608
8661
|
init_worktree_labels();
|
|
8609
8662
|
function normPath2(p) {
|
|
8610
8663
|
return p.replace(/\/+$/, "");
|
|
@@ -8743,11 +8796,11 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
8743
8796
|
const out = [];
|
|
8744
8797
|
for (const rel of candidates) {
|
|
8745
8798
|
if (seen.has(rel)) continue;
|
|
8746
|
-
const abs = (0,
|
|
8747
|
-
if (!(0,
|
|
8799
|
+
const abs = (0, import_node_path23.join)(worktreePath, rel);
|
|
8800
|
+
if (!(0, import_node_fs24.existsSync)(abs)) continue;
|
|
8748
8801
|
try {
|
|
8749
|
-
if (!(0,
|
|
8750
|
-
let content = (0,
|
|
8802
|
+
if (!(0, import_node_fs24.statSync)(abs).isFile()) continue;
|
|
8803
|
+
let content = (0, import_node_fs24.readFileSync)(abs, "utf8");
|
|
8751
8804
|
if (!content.trim()) continue;
|
|
8752
8805
|
if (content.length > MAX_CHARS_PER_FILE) {
|
|
8753
8806
|
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
@@ -8897,7 +8950,7 @@ var Orchestrator = class {
|
|
|
8897
8950
|
}
|
|
8898
8951
|
continue;
|
|
8899
8952
|
}
|
|
8900
|
-
if (!(0,
|
|
8953
|
+
if (!(0, import_node_fs26.existsSync)(thread.worktreePath)) {
|
|
8901
8954
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
8902
8955
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
8903
8956
|
continue;
|
|
@@ -9795,6 +9848,15 @@ var Orchestrator = class {
|
|
|
9795
9848
|
setAutonomy(threadRef, autonomy) {
|
|
9796
9849
|
return this.setThreadOptions(threadRef, { autonomy });
|
|
9797
9850
|
}
|
|
9851
|
+
/**
|
|
9852
|
+
* Open a Review chat tab on a worktree thread (same as the desktop Review button)
|
|
9853
|
+
* and send the merge-readiness prefill.
|
|
9854
|
+
*/
|
|
9855
|
+
async requestReview(threadRef) {
|
|
9856
|
+
const { tab } = await requestReview(threadRef, (ref, prompt) => this.send(ref, prompt));
|
|
9857
|
+
this.emit({ type: "status_changed", threadId: tab.id, status: tab.status });
|
|
9858
|
+
return tab;
|
|
9859
|
+
}
|
|
9798
9860
|
setThreadOptions(threadRef, patch) {
|
|
9799
9861
|
const thread = this.requireThread(threadRef);
|
|
9800
9862
|
const next = {};
|
|
@@ -9919,7 +9981,7 @@ var Orchestrator = class {
|
|
|
9919
9981
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
9920
9982
|
return setStatus(thread.id, "idle");
|
|
9921
9983
|
}
|
|
9922
|
-
if (!(0,
|
|
9984
|
+
if (!(0, import_node_fs26.existsSync)(thread.worktreePath)) {
|
|
9923
9985
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
9924
9986
|
const { execa: execa7 } = await import("execa");
|
|
9925
9987
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -10137,7 +10199,7 @@ async function startMcpServer() {
|
|
|
10137
10199
|
async () => {
|
|
10138
10200
|
const threads = orch.getThreads(true);
|
|
10139
10201
|
const lines = threads.map((t) => {
|
|
10140
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
10202
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path25.basename)(t.repoPath) || t.repoPath;
|
|
10141
10203
|
return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}`;
|
|
10142
10204
|
});
|
|
10143
10205
|
return {
|
|
@@ -10480,6 +10542,34 @@ async function startMcpServer() {
|
|
|
10480
10542
|
};
|
|
10481
10543
|
}
|
|
10482
10544
|
);
|
|
10545
|
+
server.tool(
|
|
10546
|
+
"request_review",
|
|
10547
|
+
"Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, optionally attaches .sideboard/attachments/Review request.md when present, and asks for Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.",
|
|
10548
|
+
{ ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
|
|
10549
|
+
async ({ ref }) => {
|
|
10550
|
+
try {
|
|
10551
|
+
const tab = await orch.requestReview(ref);
|
|
10552
|
+
const from = orch.getThread(ref);
|
|
10553
|
+
return {
|
|
10554
|
+
content: [
|
|
10555
|
+
{
|
|
10556
|
+
type: "text",
|
|
10557
|
+
text: JSON.stringify({
|
|
10558
|
+
id: tab.id,
|
|
10559
|
+
title: tab.title,
|
|
10560
|
+
status: tab.status,
|
|
10561
|
+
fromThreadId: from?.id ?? ref,
|
|
10562
|
+
link: `sideboard://thread/${tab.id}`
|
|
10563
|
+
})
|
|
10564
|
+
}
|
|
10565
|
+
]
|
|
10566
|
+
};
|
|
10567
|
+
} catch (err) {
|
|
10568
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10569
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
10570
|
+
}
|
|
10571
|
+
}
|
|
10572
|
+
);
|
|
10483
10573
|
server.tool(
|
|
10484
10574
|
"run_dev_script",
|
|
10485
10575
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|