@sideboard-ai/core 0.1.98 → 0.1.100
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/cursor-runner.cjs +69 -24
- package/dist/agents/cursor-runner.js +48 -3
- package/dist/{agents-ESJKIQQA.js → agents-CLP5BG4O.js} +3 -3
- package/dist/{agents-3MWWWSMF.js → agents-OXZDMPSE.js} +4 -4
- package/dist/{chunk-FYS2BULQ.js → chunk-EQPQTLW6.js} +209 -59
- package/dist/{chunk-FO67IJTY.js → chunk-GR4JKWR4.js} +1 -1
- package/dist/{chunk-GXSYI7FH.js → chunk-HUKCGRAT.js} +209 -59
- package/dist/{chunk-XUWDLRAE.js → chunk-HYKZEP5A.js} +2 -2
- package/dist/{chunk-XH2GS2LO.js → chunk-IFHKPZHA.js} +2 -2
- package/dist/{chunk-UYQYK2RY.js → chunk-MHJV4WS3.js} +1 -1
- package/dist/{chunk-OANJQTVG.js → chunk-MRBKGFTL.js} +1 -1
- package/dist/{chunk-MBP3XG57.js → chunk-OXC3MEFI.js} +3 -3
- package/dist/{chunk-HI2OTFFR.js → chunk-WQTTUC4N.js} +1 -1
- package/dist/{coordinator-prompt-AKEY4WSO.js → coordinator-prompt-HHRKQWCX.js} +1 -1
- package/dist/{coordinator-prompt-OQOOD5ET.js → coordinator-prompt-OOBSQCWQ.js} +1 -1
- package/dist/{global-workspace-RSQXRLT7.js → global-workspace-KYEBFWKB.js} +2 -2
- package/dist/{global-workspace-WMF3BJP5.js → global-workspace-YZWYHLQY.js} +2 -2
- package/dist/index.cjs +662 -440
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +194 -128
- package/dist/mcp/run-stdio.cjs +424 -227
- package/dist/mcp/run-stdio.js +141 -92
- package/dist/{workspaces-MZVQHRSJ.js → workspaces-ENVUDK6C.js} +3 -3
- package/dist/{workspaces-4ZY4QPWQ.js → workspaces-NNPD7QVV.js} +3 -3
- package/package.json +2 -2
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
fallbackTurnFailDetail,
|
|
7
7
|
formatTurnExitError,
|
|
8
8
|
getAdapter,
|
|
9
|
-
humanizeAgentFailDetail,
|
|
10
9
|
isSessionQuotaLimit,
|
|
11
10
|
listModelsForAgent,
|
|
12
11
|
looksLikeAgentFailureMessage,
|
|
@@ -15,16 +14,18 @@ import {
|
|
|
15
14
|
parseSessionQuotaResetAt,
|
|
16
15
|
pushTurnStderr,
|
|
17
16
|
resolveQuotaFallbackAgent,
|
|
17
|
+
shouldRetryFailedAgentTurn,
|
|
18
18
|
sideboardMcpProfile,
|
|
19
|
-
summarizeTurnStderr
|
|
20
|
-
|
|
19
|
+
summarizeTurnStderr,
|
|
20
|
+
turnFailChatText
|
|
21
|
+
} from "../chunk-EQPQTLW6.js";
|
|
21
22
|
import "../chunk-DKHGWYWR.js";
|
|
22
23
|
import {
|
|
23
24
|
addWorkspace,
|
|
24
25
|
ensureWorkspace,
|
|
25
26
|
removeWorkspace,
|
|
26
27
|
syncWorkspacesFromThreads
|
|
27
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-MRBKGFTL.js";
|
|
28
29
|
import {
|
|
29
30
|
extractPresentedPlan,
|
|
30
31
|
readPlanFile,
|
|
@@ -43,14 +44,14 @@ import {
|
|
|
43
44
|
isOrchestratorThread,
|
|
44
45
|
isSlackCoordinatorThread,
|
|
45
46
|
orchestratorSessionPoisonedByBuiltins
|
|
46
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-WQTTUC4N.js";
|
|
47
48
|
import {
|
|
48
49
|
SLACK_REPLY_FORMATTING,
|
|
49
50
|
coordinatorSystemPrompt,
|
|
50
51
|
coordinatorTurnReminder,
|
|
51
52
|
enrichWorkspacesWithGithub,
|
|
52
53
|
ensureGlobalCoordinatorCwd
|
|
53
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-IFHKPZHA.js";
|
|
54
55
|
import {
|
|
55
56
|
addPrStackLayer,
|
|
56
57
|
allocateTeamName,
|
|
@@ -944,9 +945,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
944
945
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
945
946
|
);
|
|
946
947
|
}
|
|
947
|
-
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-
|
|
948
|
+
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-YZWYHLQY.js");
|
|
948
949
|
if (isGlobalThread2(thread)) {
|
|
949
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-
|
|
950
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-OOBSQCWQ.js");
|
|
950
951
|
ensureGlobalCoordinatorCwd2(
|
|
951
952
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
952
953
|
);
|
|
@@ -1745,6 +1746,41 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
1745
1746
|
return { repoPath, workspace };
|
|
1746
1747
|
}
|
|
1747
1748
|
|
|
1749
|
+
// src/store/desktop-host.ts
|
|
1750
|
+
import { readFileSync as readFileSync4, unlinkSync, writeFileSync } from "fs";
|
|
1751
|
+
import { join as join7 } from "path";
|
|
1752
|
+
function desktopHostPidPath() {
|
|
1753
|
+
return join7(appDataDir(), "desktop-host.pid");
|
|
1754
|
+
}
|
|
1755
|
+
function readDesktopHostPid() {
|
|
1756
|
+
try {
|
|
1757
|
+
const pid = Number.parseInt(readFileSync4(desktopHostPidPath(), "utf8").trim(), 10);
|
|
1758
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
1759
|
+
return pid;
|
|
1760
|
+
} catch {
|
|
1761
|
+
return null;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
function pidAlive(pid) {
|
|
1765
|
+
try {
|
|
1766
|
+
process.kill(pid, 0);
|
|
1767
|
+
return true;
|
|
1768
|
+
} catch {
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
function isDesktopHostAlive() {
|
|
1773
|
+
const pid = readDesktopHostPid();
|
|
1774
|
+
return pid != null && pidAlive(pid);
|
|
1775
|
+
}
|
|
1776
|
+
function isThisProcessDesktopHost() {
|
|
1777
|
+
return readDesktopHostPid() === process.pid && pidAlive(process.pid);
|
|
1778
|
+
}
|
|
1779
|
+
function thisProcessShouldDrainAgentQueues() {
|
|
1780
|
+
if (isThisProcessDesktopHost()) return true;
|
|
1781
|
+
return !isDesktopHostAlive();
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1748
1784
|
// src/threads/create.ts
|
|
1749
1785
|
import { existsSync as existsSync6 } from "fs";
|
|
1750
1786
|
|
|
@@ -1852,7 +1888,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
1852
1888
|
return readThread(thread.id) ?? thread;
|
|
1853
1889
|
}
|
|
1854
1890
|
async function listLinearIssues(agent, repoPath) {
|
|
1855
|
-
const { getAdapter: getAdapter2 } = await import("../agents-
|
|
1891
|
+
const { getAdapter: getAdapter2 } = await import("../agents-CLP5BG4O.js");
|
|
1856
1892
|
await requireAgent(agent, { requireLinear: true });
|
|
1857
1893
|
const adapter = getAdapter2(agent);
|
|
1858
1894
|
if (!adapter.listLinearIssues) {
|
|
@@ -2251,8 +2287,8 @@ function forkChatTab(input) {
|
|
|
2251
2287
|
|
|
2252
2288
|
// src/review/request-review.ts
|
|
2253
2289
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2254
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as
|
|
2255
|
-
import { dirname as dirname2, join as
|
|
2290
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
2291
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
2256
2292
|
|
|
2257
2293
|
// src/review/review-request-template.ts
|
|
2258
2294
|
var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
|
|
@@ -2399,20 +2435,20 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
2399
2435
|
function readTextIfPresent(abs) {
|
|
2400
2436
|
if (!existsSync7(abs)) return null;
|
|
2401
2437
|
try {
|
|
2402
|
-
const content =
|
|
2438
|
+
const content = readFileSync5(abs, "utf8");
|
|
2403
2439
|
return content.trim() ? content : null;
|
|
2404
2440
|
} catch {
|
|
2405
2441
|
return null;
|
|
2406
2442
|
}
|
|
2407
2443
|
}
|
|
2408
2444
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
2409
|
-
const gitignoreAbs =
|
|
2445
|
+
const gitignoreAbs = join8(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
2410
2446
|
if (existsSync7(gitignoreAbs)) return;
|
|
2411
2447
|
mkdirSync2(dirname2(gitignoreAbs), { recursive: true });
|
|
2412
|
-
|
|
2448
|
+
writeFileSync2(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
2413
2449
|
}
|
|
2414
2450
|
function resolveReviewGuidelines(worktreePath) {
|
|
2415
|
-
const repoAbs =
|
|
2451
|
+
const repoAbs = join8(worktreePath, REPO_REVIEW_PATH);
|
|
2416
2452
|
const repoContent = readTextIfPresent(repoAbs);
|
|
2417
2453
|
if (repoContent) {
|
|
2418
2454
|
return {
|
|
@@ -2422,7 +2458,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
2422
2458
|
source: "repo"
|
|
2423
2459
|
};
|
|
2424
2460
|
}
|
|
2425
|
-
const localAbs =
|
|
2461
|
+
const localAbs = join8(worktreePath, REVIEW_REQUEST_PATH);
|
|
2426
2462
|
const localContent = readTextIfPresent(localAbs);
|
|
2427
2463
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
2428
2464
|
return {
|
|
@@ -2432,7 +2468,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
2432
2468
|
source: "local"
|
|
2433
2469
|
};
|
|
2434
2470
|
}
|
|
2435
|
-
const legacyAbs =
|
|
2471
|
+
const legacyAbs = join8(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
|
|
2436
2472
|
const legacyContent = readTextIfPresent(legacyAbs);
|
|
2437
2473
|
if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
|
|
2438
2474
|
return {
|
|
@@ -2444,7 +2480,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
2444
2480
|
}
|
|
2445
2481
|
ensureAttachmentsGitignore(worktreePath);
|
|
2446
2482
|
mkdirSync2(dirname2(localAbs), { recursive: true });
|
|
2447
|
-
|
|
2483
|
+
writeFileSync2(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
2448
2484
|
return {
|
|
2449
2485
|
path: REVIEW_REQUEST_PATH,
|
|
2450
2486
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -2643,20 +2679,20 @@ import {
|
|
|
2643
2679
|
existsSync as existsSync8,
|
|
2644
2680
|
mkdtempSync,
|
|
2645
2681
|
readdirSync as readdirSync3,
|
|
2646
|
-
readFileSync as
|
|
2682
|
+
readFileSync as readFileSync6,
|
|
2647
2683
|
rmSync
|
|
2648
2684
|
} from "fs";
|
|
2649
2685
|
import { tmpdir } from "os";
|
|
2650
|
-
import { join as
|
|
2686
|
+
import { join as join9 } from "path";
|
|
2651
2687
|
import { createRequire } from "module";
|
|
2652
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
2688
|
+
var CONDUCTOR_APP_SUPPORT = join9(
|
|
2653
2689
|
process.env.HOME ?? "",
|
|
2654
2690
|
"Library",
|
|
2655
2691
|
"Application Support",
|
|
2656
2692
|
"com.conductor.app"
|
|
2657
2693
|
);
|
|
2658
|
-
var CONDUCTOR_DB =
|
|
2659
|
-
var CURSOR_SDK_STORE =
|
|
2694
|
+
var CONDUCTOR_DB = join9(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
2695
|
+
var CURSOR_SDK_STORE = join9(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
2660
2696
|
function openReadonlySqlite(file) {
|
|
2661
2697
|
const req = createRequire(import.meta.url);
|
|
2662
2698
|
const Database = req("better-sqlite3");
|
|
@@ -2683,11 +2719,11 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2683
2719
|
return null;
|
|
2684
2720
|
}
|
|
2685
2721
|
for (const hash of hashes) {
|
|
2686
|
-
const agentsFile =
|
|
2722
|
+
const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
2687
2723
|
if (!existsSync8(agentsFile)) continue;
|
|
2688
2724
|
let text3;
|
|
2689
2725
|
try {
|
|
2690
|
-
text3 =
|
|
2726
|
+
text3 = readFileSync6(agentsFile, "utf8");
|
|
2691
2727
|
} catch {
|
|
2692
2728
|
continue;
|
|
2693
2729
|
}
|
|
@@ -2730,7 +2766,7 @@ async function adoptThread(input) {
|
|
|
2730
2766
|
messages: input.messages ?? []
|
|
2731
2767
|
});
|
|
2732
2768
|
writeThread(thread);
|
|
2733
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
2769
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-ENVUDK6C.js");
|
|
2734
2770
|
await ensureWorkspace2(repoPath);
|
|
2735
2771
|
return thread;
|
|
2736
2772
|
}
|
|
@@ -2738,8 +2774,8 @@ function listConductorWorkspaces() {
|
|
|
2738
2774
|
if (!existsSync8(CONDUCTOR_DB)) {
|
|
2739
2775
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2740
2776
|
}
|
|
2741
|
-
const tmp = mkdtempSync(
|
|
2742
|
-
const snapshot =
|
|
2777
|
+
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
2778
|
+
const snapshot = join9(tmp, "conductor.db");
|
|
2743
2779
|
try {
|
|
2744
2780
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2745
2781
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -2829,8 +2865,8 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2829
2865
|
if (!existsSync8(CONDUCTOR_DB)) {
|
|
2830
2866
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2831
2867
|
}
|
|
2832
|
-
const tmp = mkdtempSync(
|
|
2833
|
-
const snapshot =
|
|
2868
|
+
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
2869
|
+
const snapshot = join9(tmp, "conductor.db");
|
|
2834
2870
|
try {
|
|
2835
2871
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2836
2872
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -3210,8 +3246,8 @@ async function createPrStack(input, onSetupLine) {
|
|
|
3210
3246
|
}
|
|
3211
3247
|
|
|
3212
3248
|
// src/diff/diff.ts
|
|
3213
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as
|
|
3214
|
-
import { dirname as dirname3, join as
|
|
3249
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync7, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3250
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
3215
3251
|
async function inspectGitWorktree(worktreePath) {
|
|
3216
3252
|
if (!worktreePath || !existsSync10(worktreePath)) return "missing_worktree";
|
|
3217
3253
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
@@ -3357,11 +3393,11 @@ new file mode 100644
|
|
|
3357
3393
|
};
|
|
3358
3394
|
}
|
|
3359
3395
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
3360
|
-
const abs =
|
|
3396
|
+
const abs = join10(worktreePath, path);
|
|
3361
3397
|
try {
|
|
3362
3398
|
const st = statSync2(abs);
|
|
3363
3399
|
if (st.isFile() && st.size > maxHunk) {
|
|
3364
|
-
const buf =
|
|
3400
|
+
const buf = readFileSync7(abs).subarray(0, maxHunk);
|
|
3365
3401
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
3366
3402
|
}
|
|
3367
3403
|
} catch {
|
|
@@ -3862,7 +3898,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
3862
3898
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
3863
3899
|
assertSafeRelativePath(relativePath);
|
|
3864
3900
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
3865
|
-
const abs =
|
|
3901
|
+
const abs = join10(worktreePath, relativePath);
|
|
3866
3902
|
const st = statSync2(abs);
|
|
3867
3903
|
if (!st.isFile()) {
|
|
3868
3904
|
throw new Error(`Not a file: ${relativePath}`);
|
|
@@ -3872,7 +3908,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
3872
3908
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
3873
3909
|
);
|
|
3874
3910
|
}
|
|
3875
|
-
const buf =
|
|
3911
|
+
const buf = readFileSync7(abs);
|
|
3876
3912
|
return {
|
|
3877
3913
|
path: relativePath,
|
|
3878
3914
|
contentBase64: buf.toString("base64"),
|
|
@@ -3882,12 +3918,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
3882
3918
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
3883
3919
|
assertSafeRelativePath(relativePath);
|
|
3884
3920
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
3885
|
-
const abs =
|
|
3921
|
+
const abs = join10(worktreePath, relativePath);
|
|
3886
3922
|
const st = statSync2(abs);
|
|
3887
3923
|
if (!st.isFile()) {
|
|
3888
3924
|
throw new Error(`Not a file: ${relativePath}`);
|
|
3889
3925
|
}
|
|
3890
|
-
const buf =
|
|
3926
|
+
const buf = readFileSync7(abs);
|
|
3891
3927
|
if (isImageRelativePath(relativePath)) {
|
|
3892
3928
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
3893
3929
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -3930,9 +3966,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
3930
3966
|
}
|
|
3931
3967
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
3932
3968
|
assertSafeRelativePath(relativePath);
|
|
3933
|
-
const abs =
|
|
3969
|
+
const abs = join10(worktreePath, relativePath);
|
|
3934
3970
|
mkdirSync3(dirname3(abs), { recursive: true });
|
|
3935
|
-
|
|
3971
|
+
writeFileSync3(abs, content, "utf8");
|
|
3936
3972
|
return { path: relativePath };
|
|
3937
3973
|
}
|
|
3938
3974
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -4035,9 +4071,9 @@ async function confirmLand(thread, opts) {
|
|
|
4035
4071
|
}
|
|
4036
4072
|
|
|
4037
4073
|
// src/skills/discover.ts
|
|
4038
|
-
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as
|
|
4074
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
4039
4075
|
import { homedir } from "os";
|
|
4040
|
-
import { join as
|
|
4076
|
+
import { join as join11 } from "path";
|
|
4041
4077
|
function toCommand(name) {
|
|
4042
4078
|
return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4043
4079
|
}
|
|
@@ -4069,7 +4105,7 @@ function parseFrontmatter(content) {
|
|
|
4069
4105
|
}
|
|
4070
4106
|
function readSkill(skillMd, source) {
|
|
4071
4107
|
try {
|
|
4072
|
-
const content =
|
|
4108
|
+
const content = readFileSync8(skillMd, "utf8");
|
|
4073
4109
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
4074
4110
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
4075
4111
|
const name = fmName || dirName;
|
|
@@ -4097,7 +4133,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4097
4133
|
}
|
|
4098
4134
|
for (const entry of entries) {
|
|
4099
4135
|
if (entry.startsWith(".")) continue;
|
|
4100
|
-
const skillMd =
|
|
4136
|
+
const skillMd = join11(dir, entry, "SKILL.md");
|
|
4101
4137
|
if (!existsSync11(skillMd)) continue;
|
|
4102
4138
|
try {
|
|
4103
4139
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -4119,12 +4155,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
4119
4155
|
return;
|
|
4120
4156
|
}
|
|
4121
4157
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
4122
|
-
const skill = readSkill(
|
|
4158
|
+
const skill = readSkill(join11(dir, "SKILL.md"), "cli");
|
|
4123
4159
|
if (skill) out.push(skill);
|
|
4124
4160
|
}
|
|
4125
4161
|
for (const entry of entries) {
|
|
4126
4162
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
4127
|
-
const full =
|
|
4163
|
+
const full = join11(dir, entry);
|
|
4128
4164
|
try {
|
|
4129
4165
|
if (!statSync3(full).isDirectory()) continue;
|
|
4130
4166
|
} catch {
|
|
@@ -4144,17 +4180,17 @@ function discoverSkills(worktreePath) {
|
|
|
4144
4180
|
const home = homedir();
|
|
4145
4181
|
const collected = [];
|
|
4146
4182
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
4147
|
-
scanSkillsDir(
|
|
4183
|
+
scanSkillsDir(join11(worktreePath, rel), "workspace", collected);
|
|
4148
4184
|
}
|
|
4149
4185
|
for (const abs of [
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4186
|
+
join11(home, ".claude/skills"),
|
|
4187
|
+
join11(home, ".cursor/skills"),
|
|
4188
|
+
join11(home, ".sideboard/skills"),
|
|
4189
|
+
join11(home, ".brightsy/skills")
|
|
4154
4190
|
]) {
|
|
4155
4191
|
scanSkillsDir(abs, "user", collected);
|
|
4156
4192
|
}
|
|
4157
|
-
scanClaudePluginSkills(
|
|
4193
|
+
scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
|
|
4158
4194
|
const rank = { workspace: 0, user: 1, cli: 2 };
|
|
4159
4195
|
const byCommand = /* @__PURE__ */ new Map();
|
|
4160
4196
|
for (const skill of collected) {
|
|
@@ -4166,7 +4202,7 @@ function discoverSkills(worktreePath) {
|
|
|
4166
4202
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
4167
4203
|
}
|
|
4168
4204
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
4169
|
-
const raw =
|
|
4205
|
+
const raw = readFileSync8(skillPath, "utf8");
|
|
4170
4206
|
if (raw.startsWith("---")) {
|
|
4171
4207
|
const end = raw.indexOf("\n---", 3);
|
|
4172
4208
|
if (end >= 0) {
|
|
@@ -4259,8 +4295,8 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
4259
4295
|
}
|
|
4260
4296
|
|
|
4261
4297
|
// src/composer/stage-files.ts
|
|
4262
|
-
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as
|
|
4263
|
-
import { basename as basename3, extname, join as
|
|
4298
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
4299
|
+
import { basename as basename3, extname, join as join12 } from "path";
|
|
4264
4300
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4265
4301
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
4266
4302
|
"png",
|
|
@@ -4295,22 +4331,22 @@ function imageMimeType(filePath) {
|
|
|
4295
4331
|
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
4296
4332
|
}
|
|
4297
4333
|
function ensureAttachmentsDir(worktreePath) {
|
|
4298
|
-
const dir =
|
|
4334
|
+
const dir = join12(worktreePath, ATTACHMENTS_DIR);
|
|
4299
4335
|
mkdirSync4(dir, { recursive: true });
|
|
4300
|
-
const gi =
|
|
4336
|
+
const gi = join12(dir, ".gitignore");
|
|
4301
4337
|
if (!existsSync12(gi)) {
|
|
4302
|
-
|
|
4338
|
+
writeFileSync4(gi, attachmentsGitignoreBody(), "utf8");
|
|
4303
4339
|
}
|
|
4304
4340
|
return dir;
|
|
4305
4341
|
}
|
|
4306
4342
|
function uniqueAttachmentName(dir, originalName) {
|
|
4307
4343
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
4308
|
-
if (!existsSync12(
|
|
4344
|
+
if (!existsSync12(join12(dir, safe))) return safe;
|
|
4309
4345
|
const ext = extname(safe);
|
|
4310
4346
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
4311
4347
|
for (let i = 1; i < 1e4; i++) {
|
|
4312
4348
|
const candidate = `${stem}-${i}${ext}`;
|
|
4313
|
-
if (!existsSync12(
|
|
4349
|
+
if (!existsSync12(join12(dir, candidate))) return candidate;
|
|
4314
4350
|
}
|
|
4315
4351
|
return `${stem}-${randomUUID4()}${ext}`;
|
|
4316
4352
|
}
|
|
@@ -4371,10 +4407,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
4371
4407
|
const st = statSync4(abs);
|
|
4372
4408
|
if (!st.isFile()) continue;
|
|
4373
4409
|
const name = uniqueAttachmentName(dir, originalName);
|
|
4374
|
-
const destAbs =
|
|
4410
|
+
const destAbs = join12(dir, name);
|
|
4375
4411
|
copyFileSync3(abs, destAbs);
|
|
4376
4412
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4377
|
-
const buf =
|
|
4413
|
+
const buf = readFileSync9(destAbs);
|
|
4378
4414
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4379
4415
|
} catch (err) {
|
|
4380
4416
|
out.push({
|
|
@@ -4396,8 +4432,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
4396
4432
|
try {
|
|
4397
4433
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
4398
4434
|
const name = uniqueAttachmentName(dir, originalName);
|
|
4399
|
-
const destAbs =
|
|
4400
|
-
|
|
4435
|
+
const destAbs = join12(dir, name);
|
|
4436
|
+
writeFileSync4(destAbs, buf);
|
|
4401
4437
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4402
4438
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
4403
4439
|
} catch (err) {
|
|
@@ -4425,10 +4461,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4425
4461
|
}
|
|
4426
4462
|
const name = basename3(rel);
|
|
4427
4463
|
try {
|
|
4428
|
-
const abs =
|
|
4464
|
+
const abs = join12(worktreePath, rel);
|
|
4429
4465
|
const st = statSync4(abs);
|
|
4430
4466
|
if (!st.isFile()) continue;
|
|
4431
|
-
const buf =
|
|
4467
|
+
const buf = readFileSync9(abs);
|
|
4432
4468
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4433
4469
|
} catch (err) {
|
|
4434
4470
|
out.push({
|
|
@@ -4443,8 +4479,8 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4443
4479
|
}
|
|
4444
4480
|
|
|
4445
4481
|
// src/agents/instructions.ts
|
|
4446
|
-
import { existsSync as existsSync13, readFileSync as
|
|
4447
|
-
import { join as
|
|
4482
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
4483
|
+
import { join as join13 } from "path";
|
|
4448
4484
|
function normPath(p) {
|
|
4449
4485
|
return p.replace(/\/+$/, "");
|
|
4450
4486
|
}
|
|
@@ -4961,7 +4997,9 @@ var Orchestrator = class {
|
|
|
4961
4997
|
updateThread(thread.id, patch);
|
|
4962
4998
|
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
4963
4999
|
this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
|
|
4964
|
-
|
|
5000
|
+
if (thisProcessShouldDrainAgentQueues()) {
|
|
5001
|
+
void this.drainQueue(thread.id);
|
|
5002
|
+
}
|
|
4965
5003
|
return this.requireThread(thread.id);
|
|
4966
5004
|
});
|
|
4967
5005
|
}
|
|
@@ -5014,10 +5052,10 @@ var Orchestrator = class {
|
|
|
5014
5052
|
async sendQueuedMessageNow(threadRef, index) {
|
|
5015
5053
|
const thread = this.requireThread(threadRef);
|
|
5016
5054
|
const promoted = await withThreadLock(thread.id, async () => {
|
|
5017
|
-
const
|
|
5018
|
-
if (index < 0 || index >=
|
|
5019
|
-
const item =
|
|
5020
|
-
const rest =
|
|
5055
|
+
const current2 = this.requireThread(thread.id);
|
|
5056
|
+
if (index < 0 || index >= current2.queue.length) return false;
|
|
5057
|
+
const item = current2.queue[index];
|
|
5058
|
+
const rest = current2.queue.filter((_, i) => i !== index);
|
|
5021
5059
|
const queue = [item, ...rest];
|
|
5022
5060
|
this.haltDrain.delete(thread.id);
|
|
5023
5061
|
updateThread(thread.id, { queue });
|
|
@@ -5025,10 +5063,16 @@ var Orchestrator = class {
|
|
|
5025
5063
|
return true;
|
|
5026
5064
|
});
|
|
5027
5065
|
if (!promoted) return this.requireThread(thread.id);
|
|
5066
|
+
const current = this.requireThread(thread.id);
|
|
5028
5067
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
5068
|
+
const livePid = current.agentPid;
|
|
5069
|
+
const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
|
|
5029
5070
|
if (inFlight) {
|
|
5030
5071
|
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5031
5072
|
} else {
|
|
5073
|
+
if (foreignLive) {
|
|
5074
|
+
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5075
|
+
}
|
|
5032
5076
|
void this.drainQueue(thread.id);
|
|
5033
5077
|
}
|
|
5034
5078
|
return this.requireThread(thread.id);
|
|
@@ -5279,19 +5323,16 @@ var Orchestrator = class {
|
|
|
5279
5323
|
}
|
|
5280
5324
|
let lastStderr = summarizeTurnStderr(stderrTail);
|
|
5281
5325
|
let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
5282
|
-
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) &&
|
|
5326
|
+
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
|
|
5327
|
+
hasSession: Boolean(this.requireThread(threadId).sessionId)
|
|
5328
|
+
})) {
|
|
5283
5329
|
updateThread(threadId, { sessionId: null });
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
"Agent session missing \u2014 starting a fresh session"
|
|
5287
|
-
);
|
|
5330
|
+
const retryNote = looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
|
|
5331
|
+
pushTurnStderr(stderrTail, retryNote);
|
|
5288
5332
|
this.emit({
|
|
5289
5333
|
type: "turn_output",
|
|
5290
5334
|
threadId,
|
|
5291
|
-
event: {
|
|
5292
|
-
type: "stderr",
|
|
5293
|
-
data: "Agent session missing \u2014 starting a fresh session"
|
|
5294
|
-
}
|
|
5335
|
+
event: { type: "stderr", data: retryNote }
|
|
5295
5336
|
});
|
|
5296
5337
|
const retryThread = this.requireThread(threadId);
|
|
5297
5338
|
const prior = retryThread.messages.slice(0, -1);
|
|
@@ -5340,10 +5381,7 @@ var Orchestrator = class {
|
|
|
5340
5381
|
lastStderr = summarizeTurnStderr(stderrTail);
|
|
5341
5382
|
detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
5342
5383
|
}
|
|
5343
|
-
let chatText = assistantText;
|
|
5344
|
-
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
5345
|
-
chatText = humanizeAgentFailDetail(detail);
|
|
5346
|
-
}
|
|
5384
|
+
let chatText = this.stoppedTurns.has(threadId) ? assistantText : turnFailChatText({ exitCode, assistantText, detail });
|
|
5347
5385
|
if (chatText || parts.length > 0) {
|
|
5348
5386
|
appendMessage(threadId, {
|
|
5349
5387
|
role: "agent",
|
|
@@ -5463,6 +5501,13 @@ var Orchestrator = class {
|
|
|
5463
5501
|
if (handle) handle.kill();
|
|
5464
5502
|
const proc = this.processes.get(`${thread.id}:agent`);
|
|
5465
5503
|
if (proc) proc.kill();
|
|
5504
|
+
const pid = thread.agentPid;
|
|
5505
|
+
if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
|
|
5506
|
+
try {
|
|
5507
|
+
process.kill(pid, "SIGTERM");
|
|
5508
|
+
} catch {
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5466
5511
|
const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
|
|
5467
5512
|
if (stopped.status === "stopped") {
|
|
5468
5513
|
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
@@ -5744,10 +5789,13 @@ var Orchestrator = class {
|
|
|
5744
5789
|
getTurnResult(threadRef) {
|
|
5745
5790
|
const thread = this.requireThread(threadRef);
|
|
5746
5791
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
5792
|
+
const lastError = thread.lastError ?? null;
|
|
5793
|
+
const text3 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
|
|
5747
5794
|
return {
|
|
5748
|
-
text:
|
|
5795
|
+
text: text3,
|
|
5749
5796
|
status: thread.status,
|
|
5750
|
-
sessionId: thread.sessionId
|
|
5797
|
+
sessionId: thread.sessionId,
|
|
5798
|
+
lastError
|
|
5751
5799
|
};
|
|
5752
5800
|
}
|
|
5753
5801
|
assertNotGlobal(thread, action) {
|
|
@@ -6201,7 +6249,7 @@ var Orchestrator = class {
|
|
|
6201
6249
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
6202
6250
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
6203
6251
|
try {
|
|
6204
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
6252
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-ENVUDK6C.js");
|
|
6205
6253
|
await ensureWorkspace2(thread.repoPath);
|
|
6206
6254
|
} catch {
|
|
6207
6255
|
}
|
|
@@ -7822,7 +7870,7 @@ async function startMcpServer() {
|
|
|
7822
7870
|
);
|
|
7823
7871
|
server.tool(
|
|
7824
7872
|
"wait_for_turn",
|
|
7825
|
-
"Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git
|
|
7873
|
+
"Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git. Returns status, text, and lastError. If status is error, lastError/text is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success.",
|
|
7826
7874
|
{
|
|
7827
7875
|
ref: z3.string(),
|
|
7828
7876
|
timeoutMs: z3.number().optional()
|
|
@@ -7837,7 +7885,8 @@ async function startMcpServer() {
|
|
|
7837
7885
|
text: JSON.stringify({
|
|
7838
7886
|
id: thread.id,
|
|
7839
7887
|
status: result.status,
|
|
7840
|
-
text: result.text
|
|
7888
|
+
text: result.text,
|
|
7889
|
+
lastError: result.lastError
|
|
7841
7890
|
})
|
|
7842
7891
|
}
|
|
7843
7892
|
]
|
|
@@ -7846,7 +7895,7 @@ async function startMcpServer() {
|
|
|
7846
7895
|
);
|
|
7847
7896
|
server.tool(
|
|
7848
7897
|
"get_turn_result",
|
|
7849
|
-
"Final assistant message
|
|
7898
|
+
"Final assistant message (and lastError when the turn failed). Not the full transcript.",
|
|
7850
7899
|
{ ref: z3.string() },
|
|
7851
7900
|
async ({ ref }) => {
|
|
7852
7901
|
const result = orch.getTurnResult(ref);
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
listWorkspaces,
|
|
7
7
|
removeWorkspace,
|
|
8
8
|
syncWorkspacesFromThreads
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-MRBKGFTL.js";
|
|
10
|
+
import "./chunk-WQTTUC4N.js";
|
|
11
|
+
import "./chunk-IFHKPZHA.js";
|
|
12
12
|
import "./chunk-R7BQBSDT.js";
|
|
13
13
|
import "./chunk-B3SJXYIJ.js";
|
|
14
14
|
import "./chunk-JOF3XIEM.js";
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
listWorkspaces,
|
|
5
5
|
removeWorkspace,
|
|
6
6
|
syncWorkspacesFromThreads
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-MHJV4WS3.js";
|
|
8
|
+
import "./chunk-GR4JKWR4.js";
|
|
9
|
+
import "./chunk-HYKZEP5A.js";
|
|
10
10
|
import "./chunk-CIRXAYWS.js";
|
|
11
11
|
import "./chunk-FKOIHGKV.js";
|
|
12
12
|
import "./chunk-FT2SQOL4.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sideboard-ai/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.100",
|
|
4
4
|
"description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"worktree"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@cursor/sdk": "^1.0.
|
|
40
|
+
"@cursor/sdk": "^1.0.28",
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
42
42
|
"better-sqlite3": "^11.9.1",
|
|
43
43
|
"chokidar": "^4.0.3",
|