@sideboard-ai/core 0.1.23 → 0.1.30
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/{chunk-JNMLRJ3D.js → chunk-RPSHBKLW.js} +308 -50
- package/dist/index.cjs +318 -53
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +14 -1
- package/dist/mcp/run-stdio.cjs +225 -12
- package/dist/mcp/run-stdio.js +1 -1
- package/package.json +1 -1
|
@@ -1776,6 +1776,9 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
1776
1776
|
parts.push("");
|
|
1777
1777
|
parts.push(`## Attachment: ${att.name}`);
|
|
1778
1778
|
parts.push(`Kind: ${att.kind}`);
|
|
1779
|
+
if (att.path) {
|
|
1780
|
+
parts.push(`Path in worktree: \`${att.path}\``);
|
|
1781
|
+
}
|
|
1779
1782
|
parts.push("");
|
|
1780
1783
|
parts.push(att.content);
|
|
1781
1784
|
}
|
|
@@ -1818,6 +1821,234 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
1818
1821
|
};
|
|
1819
1822
|
}
|
|
1820
1823
|
|
|
1824
|
+
// src/composer/stage-files.ts
|
|
1825
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
1826
|
+
import { basename as basename2, extname, join as join6 } from "path";
|
|
1827
|
+
import { randomUUID } from "crypto";
|
|
1828
|
+
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
1829
|
+
"png",
|
|
1830
|
+
"jpg",
|
|
1831
|
+
"jpeg",
|
|
1832
|
+
"gif",
|
|
1833
|
+
"webp",
|
|
1834
|
+
"svg",
|
|
1835
|
+
"bmp",
|
|
1836
|
+
"ico"
|
|
1837
|
+
]);
|
|
1838
|
+
var IMAGE_MIME_BY_EXT = {
|
|
1839
|
+
png: "image/png",
|
|
1840
|
+
jpg: "image/jpeg",
|
|
1841
|
+
jpeg: "image/jpeg",
|
|
1842
|
+
gif: "image/gif",
|
|
1843
|
+
webp: "image/webp",
|
|
1844
|
+
svg: "image/svg+xml",
|
|
1845
|
+
bmp: "image/bmp",
|
|
1846
|
+
ico: "image/x-icon"
|
|
1847
|
+
};
|
|
1848
|
+
var ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
1849
|
+
var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
|
|
1850
|
+
*
|
|
1851
|
+
!.gitignore
|
|
1852
|
+
`;
|
|
1853
|
+
var MAX_INLINE_BYTES = 4e5;
|
|
1854
|
+
var MAX_PREVIEW_BYTES = 5e6;
|
|
1855
|
+
function fileExtension(filePath) {
|
|
1856
|
+
const base = basename2(filePath).toLowerCase();
|
|
1857
|
+
return base.includes(".") ? base.split(".").pop() || "" : "";
|
|
1858
|
+
}
|
|
1859
|
+
function isImageFilePath(filePath) {
|
|
1860
|
+
return IMAGE_EXTENSIONS2.has(fileExtension(filePath));
|
|
1861
|
+
}
|
|
1862
|
+
function imageMimeType(filePath) {
|
|
1863
|
+
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
1864
|
+
}
|
|
1865
|
+
function ensureAttachmentsDir(worktreePath) {
|
|
1866
|
+
const dir = join6(worktreePath, ATTACHMENTS_DIR);
|
|
1867
|
+
mkdirSync3(dir, { recursive: true });
|
|
1868
|
+
const gi = join6(dir, ".gitignore");
|
|
1869
|
+
if (!existsSync6(gi)) {
|
|
1870
|
+
writeFileSync2(gi, ATTACHMENTS_GITIGNORE, "utf8");
|
|
1871
|
+
}
|
|
1872
|
+
return dir;
|
|
1873
|
+
}
|
|
1874
|
+
function uniqueAttachmentName(dir, originalName) {
|
|
1875
|
+
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
1876
|
+
if (!existsSync6(join6(dir, safe))) return safe;
|
|
1877
|
+
const ext = extname(safe);
|
|
1878
|
+
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
1879
|
+
for (let i = 1; i < 1e4; i++) {
|
|
1880
|
+
const candidate = `${stem}-${i}${ext}`;
|
|
1881
|
+
if (!existsSync6(join6(dir, candidate))) return candidate;
|
|
1882
|
+
}
|
|
1883
|
+
return `${stem}-${randomUUID()}${ext}`;
|
|
1884
|
+
}
|
|
1885
|
+
function previewDataUrlFromBuf(filePath, buf) {
|
|
1886
|
+
if (!isImageFilePath(filePath)) return void 0;
|
|
1887
|
+
if (buf.length > MAX_PREVIEW_BYTES) return void 0;
|
|
1888
|
+
return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
|
|
1889
|
+
}
|
|
1890
|
+
function attachmentFromBuffer(name, buf, opts) {
|
|
1891
|
+
const previewDataUrl = previewDataUrlFromBuf(name, buf);
|
|
1892
|
+
if (isImageFilePath(name)) {
|
|
1893
|
+
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
1894
|
+
return {
|
|
1895
|
+
id: randomUUID(),
|
|
1896
|
+
name,
|
|
1897
|
+
kind: "file",
|
|
1898
|
+
path: opts.path,
|
|
1899
|
+
previewDataUrl,
|
|
1900
|
+
content: [
|
|
1901
|
+
`Image attached: ${pathHint}`,
|
|
1902
|
+
opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
|
|
1903
|
+
].join("\n")
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
if (buf.length > MAX_INLINE_BYTES) {
|
|
1907
|
+
return {
|
|
1908
|
+
id: randomUUID(),
|
|
1909
|
+
name,
|
|
1910
|
+
kind: "file",
|
|
1911
|
+
path: opts.path,
|
|
1912
|
+
content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
if (buf.includes(0)) {
|
|
1916
|
+
return {
|
|
1917
|
+
id: randomUUID(),
|
|
1918
|
+
name,
|
|
1919
|
+
kind: "file",
|
|
1920
|
+
path: opts.path,
|
|
1921
|
+
content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
return {
|
|
1925
|
+
id: randomUUID(),
|
|
1926
|
+
name,
|
|
1927
|
+
kind: "file",
|
|
1928
|
+
path: opts.path,
|
|
1929
|
+
content: buf.toString("utf8")
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
function attachmentFromAbsolutePath(absolutePath) {
|
|
1933
|
+
const name = basename2(absolutePath);
|
|
1934
|
+
try {
|
|
1935
|
+
const st = statSync4(absolutePath);
|
|
1936
|
+
if (!st.isFile()) {
|
|
1937
|
+
return {
|
|
1938
|
+
id: randomUUID(),
|
|
1939
|
+
name,
|
|
1940
|
+
kind: "file",
|
|
1941
|
+
content: `(not a file: ${absolutePath})`
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
const buf = readFileSync6(absolutePath);
|
|
1945
|
+
return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
return {
|
|
1948
|
+
id: randomUUID(),
|
|
1949
|
+
name,
|
|
1950
|
+
kind: "file",
|
|
1951
|
+
content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
1956
|
+
if (absolutePaths.length === 0) return [];
|
|
1957
|
+
const dir = ensureAttachmentsDir(worktreePath);
|
|
1958
|
+
const out = [];
|
|
1959
|
+
for (const abs of absolutePaths) {
|
|
1960
|
+
const originalName = basename2(abs);
|
|
1961
|
+
try {
|
|
1962
|
+
const st = statSync4(abs);
|
|
1963
|
+
if (!st.isFile()) continue;
|
|
1964
|
+
const name = uniqueAttachmentName(dir, originalName);
|
|
1965
|
+
const destAbs = join6(dir, name);
|
|
1966
|
+
copyFileSync2(abs, destAbs);
|
|
1967
|
+
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
1968
|
+
const buf = readFileSync6(destAbs);
|
|
1969
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
1970
|
+
} catch (err) {
|
|
1971
|
+
out.push({
|
|
1972
|
+
id: randomUUID(),
|
|
1973
|
+
name: originalName,
|
|
1974
|
+
kind: "file",
|
|
1975
|
+
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
return out;
|
|
1980
|
+
}
|
|
1981
|
+
function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
1982
|
+
if (buffers.length === 0) return [];
|
|
1983
|
+
const dir = ensureAttachmentsDir(worktreePath);
|
|
1984
|
+
const out = [];
|
|
1985
|
+
for (const item of buffers) {
|
|
1986
|
+
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
1987
|
+
try {
|
|
1988
|
+
const buf = Buffer.from(item.dataBase64, "base64");
|
|
1989
|
+
const name = uniqueAttachmentName(dir, originalName);
|
|
1990
|
+
const destAbs = join6(dir, name);
|
|
1991
|
+
writeFileSync2(destAbs, buf);
|
|
1992
|
+
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
1993
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
1994
|
+
} catch (err) {
|
|
1995
|
+
out.push({
|
|
1996
|
+
id: randomUUID(),
|
|
1997
|
+
name: originalName,
|
|
1998
|
+
kind: "file",
|
|
1999
|
+
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return out;
|
|
2004
|
+
}
|
|
2005
|
+
function attachmentsFromBuffers(buffers) {
|
|
2006
|
+
return buffers.map((item) => {
|
|
2007
|
+
const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
2008
|
+
try {
|
|
2009
|
+
const buf = Buffer.from(item.dataBase64, "base64");
|
|
2010
|
+
return attachmentFromBuffer(name, buf, { sourceLabel: name });
|
|
2011
|
+
} catch (err) {
|
|
2012
|
+
return {
|
|
2013
|
+
id: randomUUID(),
|
|
2014
|
+
name,
|
|
2015
|
+
kind: "file",
|
|
2016
|
+
content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
2022
|
+
const out = [];
|
|
2023
|
+
for (const rel of relativePaths) {
|
|
2024
|
+
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
2025
|
+
out.push({
|
|
2026
|
+
id: randomUUID(),
|
|
2027
|
+
name: basename2(rel) || "file",
|
|
2028
|
+
kind: "file",
|
|
2029
|
+
content: `(invalid path: ${rel})`
|
|
2030
|
+
});
|
|
2031
|
+
continue;
|
|
2032
|
+
}
|
|
2033
|
+
const name = basename2(rel);
|
|
2034
|
+
try {
|
|
2035
|
+
const abs = join6(worktreePath, rel);
|
|
2036
|
+
const st = statSync4(abs);
|
|
2037
|
+
if (!st.isFile()) continue;
|
|
2038
|
+
const buf = readFileSync6(abs);
|
|
2039
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
2040
|
+
} catch (err) {
|
|
2041
|
+
out.push({
|
|
2042
|
+
id: randomUUID(),
|
|
2043
|
+
name,
|
|
2044
|
+
kind: "file",
|
|
2045
|
+
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
return out;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1821
2052
|
// src/composer/summarize.ts
|
|
1822
2053
|
var MAX_TRANSCRIPT_CHARS = 12e4;
|
|
1823
2054
|
var MAX_SUMMARY_CHARS = 6e3;
|
|
@@ -2249,11 +2480,11 @@ async function confirmLand(thread, opts) {
|
|
|
2249
2480
|
}
|
|
2250
2481
|
|
|
2251
2482
|
// src/threads/create.ts
|
|
2252
|
-
import { existsSync as
|
|
2483
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2253
2484
|
async function createThread(input, onSetupLine) {
|
|
2254
2485
|
await requireAgent(input.agent);
|
|
2255
2486
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
2256
|
-
if (!
|
|
2487
|
+
if (!existsSync7(repoPath)) {
|
|
2257
2488
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
2258
2489
|
}
|
|
2259
2490
|
let sourceRef = input.sourceRef;
|
|
@@ -2336,7 +2567,7 @@ async function listLinearIssues(agent, repoPath) {
|
|
|
2336
2567
|
}
|
|
2337
2568
|
|
|
2338
2569
|
// src/threads/chat-tabs.ts
|
|
2339
|
-
import { randomUUID } from "crypto";
|
|
2570
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2340
2571
|
function sameWorktreePath(a, b) {
|
|
2341
2572
|
return normalizeWorktreePath(a) === normalizeWorktreePath(b);
|
|
2342
2573
|
}
|
|
@@ -2387,7 +2618,7 @@ function forkMessageSlice(from, throughIndex) {
|
|
|
2387
2618
|
function buildForkTranscriptAttachment(baseTitle, messages) {
|
|
2388
2619
|
const title = baseTitle || "Chat";
|
|
2389
2620
|
return {
|
|
2390
|
-
id:
|
|
2621
|
+
id: randomUUID2(),
|
|
2391
2622
|
name: `Transcript of ${title}.md`,
|
|
2392
2623
|
kind: "transcript",
|
|
2393
2624
|
content: formatTranscriptMarkdown(title, messages)
|
|
@@ -2445,35 +2676,39 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
2445
2676
|
repoPath: from.repoPath,
|
|
2446
2677
|
agent: input.agent ?? from.agent,
|
|
2447
2678
|
autonomy: from.autonomy,
|
|
2679
|
+
model: from.model,
|
|
2680
|
+
fast: from.fast,
|
|
2681
|
+
planMode: from.planMode,
|
|
2448
2682
|
title: input.title?.trim() || void 0,
|
|
2449
|
-
parentThreadId: from.id
|
|
2683
|
+
parentThreadId: from.id,
|
|
2684
|
+
attachments: [attachment]
|
|
2450
2685
|
},
|
|
2451
2686
|
onSetupLine
|
|
2452
2687
|
);
|
|
2453
|
-
return
|
|
2688
|
+
return thread;
|
|
2454
2689
|
}
|
|
2455
2690
|
|
|
2456
2691
|
// src/threads/adopt.ts
|
|
2457
2692
|
import { execFileSync } from "child_process";
|
|
2458
2693
|
import {
|
|
2459
|
-
copyFileSync as
|
|
2460
|
-
existsSync as
|
|
2694
|
+
copyFileSync as copyFileSync3,
|
|
2695
|
+
existsSync as existsSync8,
|
|
2461
2696
|
mkdtempSync,
|
|
2462
2697
|
readdirSync as readdirSync3,
|
|
2463
|
-
readFileSync as
|
|
2698
|
+
readFileSync as readFileSync7,
|
|
2464
2699
|
rmSync
|
|
2465
2700
|
} from "fs";
|
|
2466
2701
|
import { tmpdir } from "os";
|
|
2467
|
-
import { join as
|
|
2702
|
+
import { join as join7 } from "path";
|
|
2468
2703
|
import Database from "better-sqlite3";
|
|
2469
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
2704
|
+
var CONDUCTOR_APP_SUPPORT = join7(
|
|
2470
2705
|
process.env.HOME ?? "",
|
|
2471
2706
|
"Library",
|
|
2472
2707
|
"Application Support",
|
|
2473
2708
|
"com.conductor.app"
|
|
2474
2709
|
);
|
|
2475
|
-
var CONDUCTOR_DB =
|
|
2476
|
-
var CURSOR_SDK_STORE =
|
|
2710
|
+
var CONDUCTOR_DB = join7(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
2711
|
+
var CURSOR_SDK_STORE = join7(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
2477
2712
|
function mapAgentType(raw) {
|
|
2478
2713
|
if (!raw) return null;
|
|
2479
2714
|
const v = raw.toLowerCase();
|
|
@@ -2485,7 +2720,7 @@ function mapAgentType(raw) {
|
|
|
2485
2720
|
return null;
|
|
2486
2721
|
}
|
|
2487
2722
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
2488
|
-
if (!workspacePath || !
|
|
2723
|
+
if (!workspacePath || !existsSync8(CURSOR_SDK_STORE)) return null;
|
|
2489
2724
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
2490
2725
|
let best = null;
|
|
2491
2726
|
let hashes;
|
|
@@ -2495,11 +2730,11 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2495
2730
|
return null;
|
|
2496
2731
|
}
|
|
2497
2732
|
for (const hash of hashes) {
|
|
2498
|
-
const agentsFile =
|
|
2499
|
-
if (!
|
|
2733
|
+
const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
2734
|
+
if (!existsSync8(agentsFile)) continue;
|
|
2500
2735
|
let text;
|
|
2501
2736
|
try {
|
|
2502
|
-
text =
|
|
2737
|
+
text = readFileSync7(agentsFile, "utf8");
|
|
2503
2738
|
} catch {
|
|
2504
2739
|
continue;
|
|
2505
2740
|
}
|
|
@@ -2523,7 +2758,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2523
2758
|
return best?.agentId ?? null;
|
|
2524
2759
|
}
|
|
2525
2760
|
async function adoptThread(input) {
|
|
2526
|
-
if (!
|
|
2761
|
+
if (!existsSync8(input.worktreePath)) {
|
|
2527
2762
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
2528
2763
|
}
|
|
2529
2764
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -2550,18 +2785,18 @@ function conductorDbPath() {
|
|
|
2550
2785
|
return CONDUCTOR_DB;
|
|
2551
2786
|
}
|
|
2552
2787
|
function listConductorWorkspaces() {
|
|
2553
|
-
if (!
|
|
2788
|
+
if (!existsSync8(CONDUCTOR_DB)) {
|
|
2554
2789
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2555
2790
|
}
|
|
2556
|
-
const tmp = mkdtempSync(
|
|
2557
|
-
const snapshot =
|
|
2791
|
+
const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
|
|
2792
|
+
const snapshot = join7(tmp, "conductor.db");
|
|
2558
2793
|
try {
|
|
2559
|
-
|
|
2794
|
+
copyFileSync3(CONDUCTOR_DB, snapshot);
|
|
2560
2795
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2561
2796
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2562
|
-
if (
|
|
2797
|
+
if (existsSync8(src)) {
|
|
2563
2798
|
try {
|
|
2564
|
-
|
|
2799
|
+
copyFileSync3(src, `${snapshot}${suffix}`);
|
|
2565
2800
|
} catch {
|
|
2566
2801
|
}
|
|
2567
2802
|
}
|
|
@@ -2641,18 +2876,18 @@ function listConductorWorkspaces() {
|
|
|
2641
2876
|
}
|
|
2642
2877
|
}
|
|
2643
2878
|
function importConductorWorkspace(workspaceId) {
|
|
2644
|
-
if (!
|
|
2879
|
+
if (!existsSync8(CONDUCTOR_DB)) {
|
|
2645
2880
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2646
2881
|
}
|
|
2647
|
-
const tmp = mkdtempSync(
|
|
2648
|
-
const snapshot =
|
|
2882
|
+
const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
|
|
2883
|
+
const snapshot = join7(tmp, "conductor.db");
|
|
2649
2884
|
try {
|
|
2650
|
-
|
|
2885
|
+
copyFileSync3(CONDUCTOR_DB, snapshot);
|
|
2651
2886
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2652
2887
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2653
|
-
if (
|
|
2888
|
+
if (existsSync8(src)) {
|
|
2654
2889
|
try {
|
|
2655
|
-
|
|
2890
|
+
copyFileSync3(src, `${snapshot}${suffix}`);
|
|
2656
2891
|
} catch {
|
|
2657
2892
|
}
|
|
2658
2893
|
}
|
|
@@ -2670,7 +2905,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2670
2905
|
).get(workspaceId);
|
|
2671
2906
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
2672
2907
|
const worktreePath = String(row.workspacePath);
|
|
2673
|
-
if (!
|
|
2908
|
+
if (!existsSync8(worktreePath)) {
|
|
2674
2909
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
2675
2910
|
}
|
|
2676
2911
|
let sessionId = null;
|
|
@@ -2741,8 +2976,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
2741
2976
|
}
|
|
2742
2977
|
|
|
2743
2978
|
// src/git/orphan-cleanup.ts
|
|
2744
|
-
import { existsSync as
|
|
2745
|
-
import { join as
|
|
2979
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
2980
|
+
import { join as join8 } from "path";
|
|
2746
2981
|
function isSideboardWorktreePath(path) {
|
|
2747
2982
|
return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
|
|
2748
2983
|
}
|
|
@@ -2753,7 +2988,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2753
2988
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
2754
2989
|
);
|
|
2755
2990
|
const homeRoot = sideboardWorkspacesDir();
|
|
2756
|
-
if (
|
|
2991
|
+
if (existsSync9(homeRoot)) {
|
|
2757
2992
|
try {
|
|
2758
2993
|
for (const entry of readdirSync4(homeRoot, { withFileTypes: true })) {
|
|
2759
2994
|
if (!entry.isDirectory()) continue;
|
|
@@ -2765,7 +3000,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2765
3000
|
const orphans = [];
|
|
2766
3001
|
const seen = /* @__PURE__ */ new Set();
|
|
2767
3002
|
for (const repoPath of repos) {
|
|
2768
|
-
if (!repoPath || !
|
|
3003
|
+
if (!repoPath || !existsSync9(repoPath)) continue;
|
|
2769
3004
|
try {
|
|
2770
3005
|
const wts = await listWorktrees(repoPath);
|
|
2771
3006
|
for (const wt of wts) {
|
|
@@ -2776,7 +3011,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2776
3011
|
seen.add(path);
|
|
2777
3012
|
let mtimeMs = 0;
|
|
2778
3013
|
try {
|
|
2779
|
-
mtimeMs =
|
|
3014
|
+
mtimeMs = statSync5(path).mtimeMs;
|
|
2780
3015
|
} catch {
|
|
2781
3016
|
mtimeMs = 0;
|
|
2782
3017
|
}
|
|
@@ -2786,16 +3021,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2786
3021
|
}
|
|
2787
3022
|
try {
|
|
2788
3023
|
const root = worktreesRoot(repoPath);
|
|
2789
|
-
if (
|
|
3024
|
+
if (existsSync9(root)) {
|
|
2790
3025
|
for (const entry of readdirSync4(root, { withFileTypes: true })) {
|
|
2791
3026
|
if (!entry.isDirectory()) continue;
|
|
2792
|
-
const path =
|
|
3027
|
+
const path = join8(root, entry.name).replace(/\/$/, "");
|
|
2793
3028
|
if (known.has(path) || seen.has(path)) continue;
|
|
2794
|
-
if (!
|
|
3029
|
+
if (!existsSync9(join8(path, ".git"))) continue;
|
|
2795
3030
|
seen.add(path);
|
|
2796
3031
|
let mtimeMs = 0;
|
|
2797
3032
|
try {
|
|
2798
|
-
mtimeMs =
|
|
3033
|
+
mtimeMs = statSync5(path).mtimeMs;
|
|
2799
3034
|
} catch {
|
|
2800
3035
|
mtimeMs = Date.now();
|
|
2801
3036
|
}
|
|
@@ -2940,20 +3175,20 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
2940
3175
|
}
|
|
2941
3176
|
|
|
2942
3177
|
// src/git/clone-repo.ts
|
|
2943
|
-
import { existsSync as
|
|
2944
|
-
import { basename as
|
|
3178
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3179
|
+
import { basename as basename3, join as join9 } from "path";
|
|
2945
3180
|
import { execa as execa5 } from "execa";
|
|
2946
3181
|
async function cloneRepoIntoSideboard(opts) {
|
|
2947
3182
|
const url = opts.url.trim();
|
|
2948
3183
|
if (!url) throw new Error("Clone URL is required");
|
|
2949
3184
|
let name = opts.name?.trim();
|
|
2950
3185
|
if (!name) {
|
|
2951
|
-
const leaf =
|
|
3186
|
+
const leaf = basename3(url.replace(/\/$/, "").replace(/\.git$/, ""));
|
|
2952
3187
|
name = leaf || "repo";
|
|
2953
3188
|
}
|
|
2954
3189
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
2955
|
-
const dest =
|
|
2956
|
-
if (
|
|
3190
|
+
const dest = join9(sideboardReposDir(), name);
|
|
3191
|
+
if (existsSync10(dest)) {
|
|
2957
3192
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
2958
3193
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
2959
3194
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -2971,7 +3206,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
2971
3206
|
|
|
2972
3207
|
// src/orchestrator/orchestrator.ts
|
|
2973
3208
|
import { EventEmitter } from "events";
|
|
2974
|
-
import { existsSync as
|
|
3209
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2975
3210
|
|
|
2976
3211
|
// src/threads/sync-branch.ts
|
|
2977
3212
|
async function syncThreadBranchFromGit(threadId) {
|
|
@@ -3058,7 +3293,7 @@ var Orchestrator = class {
|
|
|
3058
3293
|
}
|
|
3059
3294
|
continue;
|
|
3060
3295
|
}
|
|
3061
|
-
if (!
|
|
3296
|
+
if (!existsSync11(thread.worktreePath)) {
|
|
3062
3297
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
3063
3298
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
3064
3299
|
continue;
|
|
@@ -3870,6 +4105,23 @@ var Orchestrator = class {
|
|
|
3870
4105
|
setAttachments(threadRef, attachments) {
|
|
3871
4106
|
return updateThread(this.requireThread(threadRef).id, { attachments });
|
|
3872
4107
|
}
|
|
4108
|
+
/**
|
|
4109
|
+
* Stage OS / worktree files into composer attachments (copies external files
|
|
4110
|
+
* into `.sideboard/attachments/` so agents can Read images and binaries).
|
|
4111
|
+
*/
|
|
4112
|
+
attachComposerFiles(threadRef, opts) {
|
|
4113
|
+
const thread = this.requireThread(threadRef);
|
|
4114
|
+
const fromAbs = stageAbsolutePathsAsAttachments(
|
|
4115
|
+
thread.worktreePath,
|
|
4116
|
+
opts.absolutePaths ?? []
|
|
4117
|
+
);
|
|
4118
|
+
const fromRel = attachmentsFromWorktreePaths(
|
|
4119
|
+
thread.worktreePath,
|
|
4120
|
+
opts.relativePaths ?? []
|
|
4121
|
+
);
|
|
4122
|
+
const fromBuf = stageBuffersAsAttachments(thread.worktreePath, opts.buffers ?? []);
|
|
4123
|
+
return [...fromAbs, ...fromRel, ...fromBuf];
|
|
4124
|
+
}
|
|
3873
4125
|
listWorktreeChats(threadRef) {
|
|
3874
4126
|
const thread = this.requireThread(threadRef);
|
|
3875
4127
|
return threadsSharingWorktree(thread.worktreePath);
|
|
@@ -3929,7 +4181,7 @@ var Orchestrator = class {
|
|
|
3929
4181
|
updateThread(thread.id, { worktreePath: globalAgentCwd() });
|
|
3930
4182
|
return setStatus(thread.id, "idle");
|
|
3931
4183
|
}
|
|
3932
|
-
if (!
|
|
4184
|
+
if (!existsSync11(thread.worktreePath)) {
|
|
3933
4185
|
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-RYTBDHHP.js");
|
|
3934
4186
|
const { execa: execa6 } = await import("execa");
|
|
3935
4187
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -4028,7 +4280,7 @@ async function startOrchestration(opts) {
|
|
|
4028
4280
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4029
4281
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4030
4282
|
import { z } from "zod";
|
|
4031
|
-
import { basename as
|
|
4283
|
+
import { basename as basename4 } from "path";
|
|
4032
4284
|
|
|
4033
4285
|
// src/mcp/archive-guard.ts
|
|
4034
4286
|
function mcpArchiveBlockedReason(thread) {
|
|
@@ -4073,7 +4325,7 @@ async function startMcpServer() {
|
|
|
4073
4325
|
async () => {
|
|
4074
4326
|
const threads = orch.getThreads(true);
|
|
4075
4327
|
const lines = threads.map((t) => {
|
|
4076
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" :
|
|
4328
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : basename4(t.repoPath) || t.repoPath;
|
|
4077
4329
|
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}` : ""}`;
|
|
4078
4330
|
});
|
|
4079
4331
|
return {
|
|
@@ -4647,6 +4899,12 @@ export {
|
|
|
4647
4899
|
discoverSkills,
|
|
4648
4900
|
readSkillBody,
|
|
4649
4901
|
expandComposerPrompt,
|
|
4902
|
+
isImageFilePath,
|
|
4903
|
+
attachmentFromAbsolutePath,
|
|
4904
|
+
stageAbsolutePathsAsAttachments,
|
|
4905
|
+
stageBuffersAsAttachments,
|
|
4906
|
+
attachmentsFromBuffers,
|
|
4907
|
+
attachmentsFromWorktreePaths,
|
|
4650
4908
|
summarizeConversation,
|
|
4651
4909
|
extractiveSummary,
|
|
4652
4910
|
CONTEXT_COMPACT_CHARS,
|