opencode-swarm-plugin 0.12.16 → 0.12.18
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/index.js +114 -46
- package/dist/plugin.js +112 -46
- package/package.json +1 -1
- package/src/beads.ts +196 -66
- package/src/index.ts +6 -2
package/dist/index.js
CHANGED
|
@@ -21765,6 +21765,42 @@ var SwarmStatusSchema = exports_external.object({
|
|
|
21765
21765
|
last_update: exports_external.string().datetime({ offset: true })
|
|
21766
21766
|
});
|
|
21767
21767
|
// src/beads.ts
|
|
21768
|
+
var beadsWorkingDirectory = null;
|
|
21769
|
+
function setBeadsWorkingDirectory(directory) {
|
|
21770
|
+
beadsWorkingDirectory = directory;
|
|
21771
|
+
}
|
|
21772
|
+
function getBeadsWorkingDirectory() {
|
|
21773
|
+
return beadsWorkingDirectory || process.cwd();
|
|
21774
|
+
}
|
|
21775
|
+
async function runBdCommand(args) {
|
|
21776
|
+
const cwd = getBeadsWorkingDirectory();
|
|
21777
|
+
const proc = Bun.spawn(["bd", ...args], {
|
|
21778
|
+
cwd,
|
|
21779
|
+
stdout: "pipe",
|
|
21780
|
+
stderr: "pipe"
|
|
21781
|
+
});
|
|
21782
|
+
const [stdout, stderr] = await Promise.all([
|
|
21783
|
+
new Response(proc.stdout).text(),
|
|
21784
|
+
new Response(proc.stderr).text()
|
|
21785
|
+
]);
|
|
21786
|
+
const exitCode = await proc.exited;
|
|
21787
|
+
return { exitCode, stdout, stderr };
|
|
21788
|
+
}
|
|
21789
|
+
async function runGitCommand(args) {
|
|
21790
|
+
const cwd = getBeadsWorkingDirectory();
|
|
21791
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
21792
|
+
cwd,
|
|
21793
|
+
stdout: "pipe",
|
|
21794
|
+
stderr: "pipe"
|
|
21795
|
+
});
|
|
21796
|
+
const [stdout, stderr] = await Promise.all([
|
|
21797
|
+
new Response(proc.stdout).text(),
|
|
21798
|
+
new Response(proc.stderr).text()
|
|
21799
|
+
]);
|
|
21800
|
+
const exitCode = await proc.exited;
|
|
21801
|
+
return { exitCode, stdout, stderr };
|
|
21802
|
+
}
|
|
21803
|
+
|
|
21768
21804
|
class BeadError extends Error {
|
|
21769
21805
|
command;
|
|
21770
21806
|
exitCode;
|
|
@@ -21844,11 +21880,11 @@ var beads_create = tool({
|
|
|
21844
21880
|
async execute(args, ctx) {
|
|
21845
21881
|
const validated = BeadCreateArgsSchema.parse(args);
|
|
21846
21882
|
const cmdParts = buildCreateCommand(validated);
|
|
21847
|
-
const result = await
|
|
21883
|
+
const result = await runBdCommand(cmdParts.slice(1));
|
|
21848
21884
|
if (result.exitCode !== 0) {
|
|
21849
|
-
throw new BeadError(`Failed to create bead: ${result.stderr
|
|
21885
|
+
throw new BeadError(`Failed to create bead: ${result.stderr}`, cmdParts.join(" "), result.exitCode, result.stderr);
|
|
21850
21886
|
}
|
|
21851
|
-
const stdout = result.stdout.
|
|
21887
|
+
const stdout = result.stdout.trim();
|
|
21852
21888
|
if (!stdout) {
|
|
21853
21889
|
throw new BeadError("bd create returned empty output", cmdParts.join(" "), 0, "Empty stdout");
|
|
21854
21890
|
}
|
|
@@ -21880,11 +21916,11 @@ var beads_create_epic = tool({
|
|
|
21880
21916
|
priority: 1,
|
|
21881
21917
|
description: validated.epic_description
|
|
21882
21918
|
});
|
|
21883
|
-
const epicResult = await
|
|
21919
|
+
const epicResult = await runBdCommand(epicCmd.slice(1));
|
|
21884
21920
|
if (epicResult.exitCode !== 0) {
|
|
21885
|
-
throw new BeadError(`Failed to create epic: ${epicResult.stderr
|
|
21921
|
+
throw new BeadError(`Failed to create epic: ${epicResult.stderr}`, epicCmd.join(" "), epicResult.exitCode);
|
|
21886
21922
|
}
|
|
21887
|
-
const epic = parseBead(epicResult.stdout
|
|
21923
|
+
const epic = parseBead(epicResult.stdout);
|
|
21888
21924
|
created.push(epic);
|
|
21889
21925
|
for (const subtask of validated.subtasks) {
|
|
21890
21926
|
const subtaskCmd = buildCreateCommand({
|
|
@@ -21893,11 +21929,11 @@ var beads_create_epic = tool({
|
|
|
21893
21929
|
priority: subtask.priority ?? 2,
|
|
21894
21930
|
parent_id: epic.id
|
|
21895
21931
|
});
|
|
21896
|
-
const subtaskResult = await
|
|
21932
|
+
const subtaskResult = await runBdCommand(subtaskCmd.slice(1));
|
|
21897
21933
|
if (subtaskResult.exitCode !== 0) {
|
|
21898
|
-
throw new BeadError(`Failed to create subtask: ${subtaskResult.stderr
|
|
21934
|
+
throw new BeadError(`Failed to create subtask: ${subtaskResult.stderr}`, subtaskCmd.join(" "), subtaskResult.exitCode);
|
|
21899
21935
|
}
|
|
21900
|
-
const subtaskBead = parseBead(subtaskResult.stdout
|
|
21936
|
+
const subtaskBead = parseBead(subtaskResult.stdout);
|
|
21901
21937
|
created.push(subtaskBead);
|
|
21902
21938
|
}
|
|
21903
21939
|
const result = {
|
|
@@ -21911,19 +21947,18 @@ var beads_create_epic = tool({
|
|
|
21911
21947
|
const rollbackErrors = [];
|
|
21912
21948
|
for (const bead of created) {
|
|
21913
21949
|
try {
|
|
21914
|
-
const
|
|
21915
|
-
"bd",
|
|
21950
|
+
const closeArgs = [
|
|
21916
21951
|
"close",
|
|
21917
21952
|
bead.id,
|
|
21918
21953
|
"--reason",
|
|
21919
21954
|
"Rollback partial epic",
|
|
21920
21955
|
"--json"
|
|
21921
21956
|
];
|
|
21922
|
-
const rollbackResult = await
|
|
21957
|
+
const rollbackResult = await runBdCommand(closeArgs);
|
|
21923
21958
|
if (rollbackResult.exitCode === 0) {
|
|
21924
21959
|
rollbackCommands.push(`bd close ${bead.id} --reason "Rollback partial epic"`);
|
|
21925
21960
|
} else {
|
|
21926
|
-
rollbackErrors.push(`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.
|
|
21961
|
+
rollbackErrors.push(`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.trim()}`);
|
|
21927
21962
|
}
|
|
21928
21963
|
} catch (rollbackError) {
|
|
21929
21964
|
const errMsg = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
@@ -21978,11 +22013,11 @@ var beads_query = tool({
|
|
|
21978
22013
|
cmd.push("--type", validated.type);
|
|
21979
22014
|
}
|
|
21980
22015
|
}
|
|
21981
|
-
const result = await
|
|
22016
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
21982
22017
|
if (result.exitCode !== 0) {
|
|
21983
|
-
throw new BeadError(`Failed to query beads: ${result.stderr
|
|
22018
|
+
throw new BeadError(`Failed to query beads: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
21984
22019
|
}
|
|
21985
|
-
const beads = parseBeads(result.stdout
|
|
22020
|
+
const beads = parseBeads(result.stdout);
|
|
21986
22021
|
const limited = beads.slice(0, validated.limit);
|
|
21987
22022
|
return JSON.stringify(limited, null, 2);
|
|
21988
22023
|
}
|
|
@@ -22008,11 +22043,11 @@ var beads_update = tool({
|
|
|
22008
22043
|
cmd.push("-p", validated.priority.toString());
|
|
22009
22044
|
}
|
|
22010
22045
|
cmd.push("--json");
|
|
22011
|
-
const result = await
|
|
22046
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
22012
22047
|
if (result.exitCode !== 0) {
|
|
22013
|
-
throw new BeadError(`Failed to update bead: ${result.stderr
|
|
22048
|
+
throw new BeadError(`Failed to update bead: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
22014
22049
|
}
|
|
22015
|
-
const bead = parseBead(result.stdout
|
|
22050
|
+
const bead = parseBead(result.stdout);
|
|
22016
22051
|
return JSON.stringify(bead, null, 2);
|
|
22017
22052
|
}
|
|
22018
22053
|
});
|
|
@@ -22032,11 +22067,11 @@ var beads_close = tool({
|
|
|
22032
22067
|
validated.reason,
|
|
22033
22068
|
"--json"
|
|
22034
22069
|
];
|
|
22035
|
-
const result = await
|
|
22070
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
22036
22071
|
if (result.exitCode !== 0) {
|
|
22037
|
-
throw new BeadError(`Failed to close bead: ${result.stderr
|
|
22072
|
+
throw new BeadError(`Failed to close bead: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
22038
22073
|
}
|
|
22039
|
-
const bead = parseBead(result.stdout
|
|
22074
|
+
const bead = parseBead(result.stdout);
|
|
22040
22075
|
return `Closed ${bead.id}: ${validated.reason}`;
|
|
22041
22076
|
}
|
|
22042
22077
|
});
|
|
@@ -22046,12 +22081,17 @@ var beads_start = tool({
|
|
|
22046
22081
|
id: tool.schema.string().describe("Bead ID")
|
|
22047
22082
|
},
|
|
22048
22083
|
async execute(args, ctx) {
|
|
22049
|
-
const
|
|
22050
|
-
|
|
22084
|
+
const result = await runBdCommand([
|
|
22085
|
+
"update",
|
|
22086
|
+
args.id,
|
|
22087
|
+
"--status",
|
|
22088
|
+
"in_progress",
|
|
22089
|
+
"--json"
|
|
22090
|
+
]);
|
|
22051
22091
|
if (result.exitCode !== 0) {
|
|
22052
|
-
throw new BeadError(`Failed to start bead: ${result.stderr
|
|
22092
|
+
throw new BeadError(`Failed to start bead: ${result.stderr}`, `bd update ${args.id} --status in_progress --json`, result.exitCode);
|
|
22053
22093
|
}
|
|
22054
|
-
const bead = parseBead(result.stdout
|
|
22094
|
+
const bead = parseBead(result.stdout);
|
|
22055
22095
|
return `Started: ${bead.id}`;
|
|
22056
22096
|
}
|
|
22057
22097
|
});
|
|
@@ -22059,12 +22099,11 @@ var beads_ready = tool({
|
|
|
22059
22099
|
description: "Get the next ready bead (unblocked, highest priority)",
|
|
22060
22100
|
args: {},
|
|
22061
22101
|
async execute(args, ctx) {
|
|
22062
|
-
const
|
|
22063
|
-
const result = await Bun.$`${cmd}`.quiet().nothrow();
|
|
22102
|
+
const result = await runBdCommand(["ready", "--json"]);
|
|
22064
22103
|
if (result.exitCode !== 0) {
|
|
22065
|
-
throw new BeadError(`Failed to get ready beads: ${result.stderr
|
|
22104
|
+
throw new BeadError(`Failed to get ready beads: ${result.stderr}`, "bd ready --json", result.exitCode);
|
|
22066
22105
|
}
|
|
22067
|
-
const beads = parseBeads(result.stdout
|
|
22106
|
+
const beads = parseBeads(result.stdout);
|
|
22068
22107
|
if (beads.length === 0) {
|
|
22069
22108
|
return "No ready beads";
|
|
22070
22109
|
}
|
|
@@ -22093,22 +22132,42 @@ var beads_sync = tool({
|
|
|
22093
22132
|
}
|
|
22094
22133
|
}
|
|
22095
22134
|
};
|
|
22135
|
+
const flushResult = await withTimeout(runBdCommand(["sync", "--flush-only"]), TIMEOUT_MS, "bd sync --flush-only");
|
|
22136
|
+
if (flushResult.exitCode !== 0) {
|
|
22137
|
+
throw new BeadError(`Failed to flush beads: ${flushResult.stderr}`, "bd sync --flush-only", flushResult.exitCode);
|
|
22138
|
+
}
|
|
22139
|
+
const beadsStatusResult = await runGitCommand([
|
|
22140
|
+
"status",
|
|
22141
|
+
"--porcelain",
|
|
22142
|
+
".beads/"
|
|
22143
|
+
]);
|
|
22144
|
+
const hasChanges = beadsStatusResult.stdout.trim() !== "";
|
|
22145
|
+
if (hasChanges) {
|
|
22146
|
+
const addResult = await runGitCommand(["add", ".beads/"]);
|
|
22147
|
+
if (addResult.exitCode !== 0) {
|
|
22148
|
+
throw new BeadError(`Failed to stage beads: ${addResult.stderr}`, "git add .beads/", addResult.exitCode);
|
|
22149
|
+
}
|
|
22150
|
+
const commitResult = await withTimeout(runGitCommand(["commit", "-m", "chore: sync beads"]), TIMEOUT_MS, "git commit");
|
|
22151
|
+
if (commitResult.exitCode !== 0 && !commitResult.stdout.includes("nothing to commit")) {
|
|
22152
|
+
throw new BeadError(`Failed to commit beads: ${commitResult.stderr}`, "git commit", commitResult.exitCode);
|
|
22153
|
+
}
|
|
22154
|
+
}
|
|
22096
22155
|
if (autoPull) {
|
|
22097
|
-
const pullResult = await withTimeout(
|
|
22156
|
+
const pullResult = await withTimeout(runGitCommand(["pull", "--rebase"]), TIMEOUT_MS, "git pull --rebase");
|
|
22098
22157
|
if (pullResult.exitCode !== 0) {
|
|
22099
|
-
throw new BeadError(`Failed to pull: ${pullResult.stderr
|
|
22158
|
+
throw new BeadError(`Failed to pull: ${pullResult.stderr}`, "git pull --rebase", pullResult.exitCode);
|
|
22159
|
+
}
|
|
22160
|
+
const importResult = await withTimeout(runBdCommand(["sync", "--import-only"]), TIMEOUT_MS, "bd sync --import-only");
|
|
22161
|
+
if (importResult.exitCode !== 0) {
|
|
22162
|
+
console.warn(`[beads] Import warning: ${importResult.stderr}`);
|
|
22100
22163
|
}
|
|
22101
22164
|
}
|
|
22102
|
-
const
|
|
22103
|
-
if (syncResult.exitCode !== 0) {
|
|
22104
|
-
throw new BeadError(`Failed to sync beads: ${syncResult.stderr.toString()}`, "bd sync", syncResult.exitCode);
|
|
22105
|
-
}
|
|
22106
|
-
const pushResult = await withTimeout(Bun.$`git push`.quiet().nothrow(), TIMEOUT_MS, "git push");
|
|
22165
|
+
const pushResult = await withTimeout(runGitCommand(["push"]), TIMEOUT_MS, "git push");
|
|
22107
22166
|
if (pushResult.exitCode !== 0) {
|
|
22108
|
-
throw new BeadError(`Failed to push: ${pushResult.stderr
|
|
22167
|
+
throw new BeadError(`Failed to push: ${pushResult.stderr}`, "git push", pushResult.exitCode);
|
|
22109
22168
|
}
|
|
22110
|
-
const statusResult = await
|
|
22111
|
-
const status = statusResult.stdout.
|
|
22169
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
22170
|
+
const status = statusResult.stdout.trim();
|
|
22112
22171
|
if (status !== "") {
|
|
22113
22172
|
return `Beads synced and pushed, but working directory not clean:
|
|
22114
22173
|
${status}`;
|
|
@@ -22123,11 +22182,11 @@ var beads_link_thread = tool({
|
|
|
22123
22182
|
thread_id: tool.schema.string().describe("Agent Mail thread ID")
|
|
22124
22183
|
},
|
|
22125
22184
|
async execute(args, ctx) {
|
|
22126
|
-
const queryResult = await
|
|
22185
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
22127
22186
|
if (queryResult.exitCode !== 0) {
|
|
22128
|
-
throw new BeadError(`Failed to get bead: ${queryResult.stderr
|
|
22187
|
+
throw new BeadError(`Failed to get bead: ${queryResult.stderr}`, `bd show ${args.bead_id} --json`, queryResult.exitCode);
|
|
22129
22188
|
}
|
|
22130
|
-
const bead = parseBead(queryResult.stdout
|
|
22189
|
+
const bead = parseBead(queryResult.stdout);
|
|
22131
22190
|
const existingDesc = bead.description || "";
|
|
22132
22191
|
const threadMarker = `[thread:${args.thread_id}]`;
|
|
22133
22192
|
if (existingDesc.includes(threadMarker)) {
|
|
@@ -22136,9 +22195,15 @@ var beads_link_thread = tool({
|
|
|
22136
22195
|
const newDesc = existingDesc ? `${existingDesc}
|
|
22137
22196
|
|
|
22138
22197
|
${threadMarker}` : threadMarker;
|
|
22139
|
-
const updateResult = await
|
|
22198
|
+
const updateResult = await runBdCommand([
|
|
22199
|
+
"update",
|
|
22200
|
+
args.bead_id,
|
|
22201
|
+
"-d",
|
|
22202
|
+
newDesc,
|
|
22203
|
+
"--json"
|
|
22204
|
+
]);
|
|
22140
22205
|
if (updateResult.exitCode !== 0) {
|
|
22141
|
-
throw new BeadError(`Failed to update bead: ${updateResult.stderr
|
|
22206
|
+
throw new BeadError(`Failed to update bead: ${updateResult.stderr}`, `bd update ${args.bead_id} -d ...`, updateResult.exitCode);
|
|
22142
22207
|
}
|
|
22143
22208
|
return `Linked bead ${args.bead_id} to thread ${args.thread_id}`;
|
|
22144
22209
|
}
|
|
@@ -26496,7 +26561,8 @@ async function resetStorage() {
|
|
|
26496
26561
|
|
|
26497
26562
|
// src/index.ts
|
|
26498
26563
|
var SwarmPlugin = async (input) => {
|
|
26499
|
-
const {
|
|
26564
|
+
const { $, directory } = input;
|
|
26565
|
+
setBeadsWorkingDirectory(directory);
|
|
26500
26566
|
let activeAgentMailState = null;
|
|
26501
26567
|
async function releaseReservations() {
|
|
26502
26568
|
if (!activeAgentMailState || activeAgentMailState.reservations.length === 0) {
|
|
@@ -26587,6 +26653,7 @@ export {
|
|
|
26587
26653
|
swarmTools,
|
|
26588
26654
|
structuredTools,
|
|
26589
26655
|
setStorage,
|
|
26656
|
+
setBeadsWorkingDirectory,
|
|
26590
26657
|
selectStrategy,
|
|
26591
26658
|
resetToolCache,
|
|
26592
26659
|
resetStorage,
|
|
@@ -26598,6 +26665,7 @@ export {
|
|
|
26598
26665
|
getToolAvailability,
|
|
26599
26666
|
getStorage,
|
|
26600
26667
|
getSchemaByName,
|
|
26668
|
+
getBeadsWorkingDirectory,
|
|
26601
26669
|
formatZodErrors,
|
|
26602
26670
|
formatToolAvailability,
|
|
26603
26671
|
formatSubtaskPromptV2,
|
package/dist/plugin.js
CHANGED
|
@@ -21765,6 +21765,42 @@ var SwarmStatusSchema = exports_external.object({
|
|
|
21765
21765
|
last_update: exports_external.string().datetime({ offset: true })
|
|
21766
21766
|
});
|
|
21767
21767
|
// src/beads.ts
|
|
21768
|
+
var beadsWorkingDirectory = null;
|
|
21769
|
+
function setBeadsWorkingDirectory(directory) {
|
|
21770
|
+
beadsWorkingDirectory = directory;
|
|
21771
|
+
}
|
|
21772
|
+
function getBeadsWorkingDirectory() {
|
|
21773
|
+
return beadsWorkingDirectory || process.cwd();
|
|
21774
|
+
}
|
|
21775
|
+
async function runBdCommand(args) {
|
|
21776
|
+
const cwd = getBeadsWorkingDirectory();
|
|
21777
|
+
const proc = Bun.spawn(["bd", ...args], {
|
|
21778
|
+
cwd,
|
|
21779
|
+
stdout: "pipe",
|
|
21780
|
+
stderr: "pipe"
|
|
21781
|
+
});
|
|
21782
|
+
const [stdout, stderr] = await Promise.all([
|
|
21783
|
+
new Response(proc.stdout).text(),
|
|
21784
|
+
new Response(proc.stderr).text()
|
|
21785
|
+
]);
|
|
21786
|
+
const exitCode = await proc.exited;
|
|
21787
|
+
return { exitCode, stdout, stderr };
|
|
21788
|
+
}
|
|
21789
|
+
async function runGitCommand(args) {
|
|
21790
|
+
const cwd = getBeadsWorkingDirectory();
|
|
21791
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
21792
|
+
cwd,
|
|
21793
|
+
stdout: "pipe",
|
|
21794
|
+
stderr: "pipe"
|
|
21795
|
+
});
|
|
21796
|
+
const [stdout, stderr] = await Promise.all([
|
|
21797
|
+
new Response(proc.stdout).text(),
|
|
21798
|
+
new Response(proc.stderr).text()
|
|
21799
|
+
]);
|
|
21800
|
+
const exitCode = await proc.exited;
|
|
21801
|
+
return { exitCode, stdout, stderr };
|
|
21802
|
+
}
|
|
21803
|
+
|
|
21768
21804
|
class BeadError extends Error {
|
|
21769
21805
|
command;
|
|
21770
21806
|
exitCode;
|
|
@@ -21844,11 +21880,11 @@ var beads_create = tool({
|
|
|
21844
21880
|
async execute(args, ctx) {
|
|
21845
21881
|
const validated = BeadCreateArgsSchema.parse(args);
|
|
21846
21882
|
const cmdParts = buildCreateCommand(validated);
|
|
21847
|
-
const result = await
|
|
21883
|
+
const result = await runBdCommand(cmdParts.slice(1));
|
|
21848
21884
|
if (result.exitCode !== 0) {
|
|
21849
|
-
throw new BeadError(`Failed to create bead: ${result.stderr
|
|
21885
|
+
throw new BeadError(`Failed to create bead: ${result.stderr}`, cmdParts.join(" "), result.exitCode, result.stderr);
|
|
21850
21886
|
}
|
|
21851
|
-
const stdout = result.stdout.
|
|
21887
|
+
const stdout = result.stdout.trim();
|
|
21852
21888
|
if (!stdout) {
|
|
21853
21889
|
throw new BeadError("bd create returned empty output", cmdParts.join(" "), 0, "Empty stdout");
|
|
21854
21890
|
}
|
|
@@ -21880,11 +21916,11 @@ var beads_create_epic = tool({
|
|
|
21880
21916
|
priority: 1,
|
|
21881
21917
|
description: validated.epic_description
|
|
21882
21918
|
});
|
|
21883
|
-
const epicResult = await
|
|
21919
|
+
const epicResult = await runBdCommand(epicCmd.slice(1));
|
|
21884
21920
|
if (epicResult.exitCode !== 0) {
|
|
21885
|
-
throw new BeadError(`Failed to create epic: ${epicResult.stderr
|
|
21921
|
+
throw new BeadError(`Failed to create epic: ${epicResult.stderr}`, epicCmd.join(" "), epicResult.exitCode);
|
|
21886
21922
|
}
|
|
21887
|
-
const epic = parseBead(epicResult.stdout
|
|
21923
|
+
const epic = parseBead(epicResult.stdout);
|
|
21888
21924
|
created.push(epic);
|
|
21889
21925
|
for (const subtask of validated.subtasks) {
|
|
21890
21926
|
const subtaskCmd = buildCreateCommand({
|
|
@@ -21893,11 +21929,11 @@ var beads_create_epic = tool({
|
|
|
21893
21929
|
priority: subtask.priority ?? 2,
|
|
21894
21930
|
parent_id: epic.id
|
|
21895
21931
|
});
|
|
21896
|
-
const subtaskResult = await
|
|
21932
|
+
const subtaskResult = await runBdCommand(subtaskCmd.slice(1));
|
|
21897
21933
|
if (subtaskResult.exitCode !== 0) {
|
|
21898
|
-
throw new BeadError(`Failed to create subtask: ${subtaskResult.stderr
|
|
21934
|
+
throw new BeadError(`Failed to create subtask: ${subtaskResult.stderr}`, subtaskCmd.join(" "), subtaskResult.exitCode);
|
|
21899
21935
|
}
|
|
21900
|
-
const subtaskBead = parseBead(subtaskResult.stdout
|
|
21936
|
+
const subtaskBead = parseBead(subtaskResult.stdout);
|
|
21901
21937
|
created.push(subtaskBead);
|
|
21902
21938
|
}
|
|
21903
21939
|
const result = {
|
|
@@ -21911,19 +21947,18 @@ var beads_create_epic = tool({
|
|
|
21911
21947
|
const rollbackErrors = [];
|
|
21912
21948
|
for (const bead of created) {
|
|
21913
21949
|
try {
|
|
21914
|
-
const
|
|
21915
|
-
"bd",
|
|
21950
|
+
const closeArgs = [
|
|
21916
21951
|
"close",
|
|
21917
21952
|
bead.id,
|
|
21918
21953
|
"--reason",
|
|
21919
21954
|
"Rollback partial epic",
|
|
21920
21955
|
"--json"
|
|
21921
21956
|
];
|
|
21922
|
-
const rollbackResult = await
|
|
21957
|
+
const rollbackResult = await runBdCommand(closeArgs);
|
|
21923
21958
|
if (rollbackResult.exitCode === 0) {
|
|
21924
21959
|
rollbackCommands.push(`bd close ${bead.id} --reason "Rollback partial epic"`);
|
|
21925
21960
|
} else {
|
|
21926
|
-
rollbackErrors.push(`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.
|
|
21961
|
+
rollbackErrors.push(`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.trim()}`);
|
|
21927
21962
|
}
|
|
21928
21963
|
} catch (rollbackError) {
|
|
21929
21964
|
const errMsg = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
@@ -21978,11 +22013,11 @@ var beads_query = tool({
|
|
|
21978
22013
|
cmd.push("--type", validated.type);
|
|
21979
22014
|
}
|
|
21980
22015
|
}
|
|
21981
|
-
const result = await
|
|
22016
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
21982
22017
|
if (result.exitCode !== 0) {
|
|
21983
|
-
throw new BeadError(`Failed to query beads: ${result.stderr
|
|
22018
|
+
throw new BeadError(`Failed to query beads: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
21984
22019
|
}
|
|
21985
|
-
const beads = parseBeads(result.stdout
|
|
22020
|
+
const beads = parseBeads(result.stdout);
|
|
21986
22021
|
const limited = beads.slice(0, validated.limit);
|
|
21987
22022
|
return JSON.stringify(limited, null, 2);
|
|
21988
22023
|
}
|
|
@@ -22008,11 +22043,11 @@ var beads_update = tool({
|
|
|
22008
22043
|
cmd.push("-p", validated.priority.toString());
|
|
22009
22044
|
}
|
|
22010
22045
|
cmd.push("--json");
|
|
22011
|
-
const result = await
|
|
22046
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
22012
22047
|
if (result.exitCode !== 0) {
|
|
22013
|
-
throw new BeadError(`Failed to update bead: ${result.stderr
|
|
22048
|
+
throw new BeadError(`Failed to update bead: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
22014
22049
|
}
|
|
22015
|
-
const bead = parseBead(result.stdout
|
|
22050
|
+
const bead = parseBead(result.stdout);
|
|
22016
22051
|
return JSON.stringify(bead, null, 2);
|
|
22017
22052
|
}
|
|
22018
22053
|
});
|
|
@@ -22032,11 +22067,11 @@ var beads_close = tool({
|
|
|
22032
22067
|
validated.reason,
|
|
22033
22068
|
"--json"
|
|
22034
22069
|
];
|
|
22035
|
-
const result = await
|
|
22070
|
+
const result = await runBdCommand(cmd.slice(1));
|
|
22036
22071
|
if (result.exitCode !== 0) {
|
|
22037
|
-
throw new BeadError(`Failed to close bead: ${result.stderr
|
|
22072
|
+
throw new BeadError(`Failed to close bead: ${result.stderr}`, cmd.join(" "), result.exitCode);
|
|
22038
22073
|
}
|
|
22039
|
-
const bead = parseBead(result.stdout
|
|
22074
|
+
const bead = parseBead(result.stdout);
|
|
22040
22075
|
return `Closed ${bead.id}: ${validated.reason}`;
|
|
22041
22076
|
}
|
|
22042
22077
|
});
|
|
@@ -22046,12 +22081,17 @@ var beads_start = tool({
|
|
|
22046
22081
|
id: tool.schema.string().describe("Bead ID")
|
|
22047
22082
|
},
|
|
22048
22083
|
async execute(args, ctx) {
|
|
22049
|
-
const
|
|
22050
|
-
|
|
22084
|
+
const result = await runBdCommand([
|
|
22085
|
+
"update",
|
|
22086
|
+
args.id,
|
|
22087
|
+
"--status",
|
|
22088
|
+
"in_progress",
|
|
22089
|
+
"--json"
|
|
22090
|
+
]);
|
|
22051
22091
|
if (result.exitCode !== 0) {
|
|
22052
|
-
throw new BeadError(`Failed to start bead: ${result.stderr
|
|
22092
|
+
throw new BeadError(`Failed to start bead: ${result.stderr}`, `bd update ${args.id} --status in_progress --json`, result.exitCode);
|
|
22053
22093
|
}
|
|
22054
|
-
const bead = parseBead(result.stdout
|
|
22094
|
+
const bead = parseBead(result.stdout);
|
|
22055
22095
|
return `Started: ${bead.id}`;
|
|
22056
22096
|
}
|
|
22057
22097
|
});
|
|
@@ -22059,12 +22099,11 @@ var beads_ready = tool({
|
|
|
22059
22099
|
description: "Get the next ready bead (unblocked, highest priority)",
|
|
22060
22100
|
args: {},
|
|
22061
22101
|
async execute(args, ctx) {
|
|
22062
|
-
const
|
|
22063
|
-
const result = await Bun.$`${cmd}`.quiet().nothrow();
|
|
22102
|
+
const result = await runBdCommand(["ready", "--json"]);
|
|
22064
22103
|
if (result.exitCode !== 0) {
|
|
22065
|
-
throw new BeadError(`Failed to get ready beads: ${result.stderr
|
|
22104
|
+
throw new BeadError(`Failed to get ready beads: ${result.stderr}`, "bd ready --json", result.exitCode);
|
|
22066
22105
|
}
|
|
22067
|
-
const beads = parseBeads(result.stdout
|
|
22106
|
+
const beads = parseBeads(result.stdout);
|
|
22068
22107
|
if (beads.length === 0) {
|
|
22069
22108
|
return "No ready beads";
|
|
22070
22109
|
}
|
|
@@ -22093,22 +22132,42 @@ var beads_sync = tool({
|
|
|
22093
22132
|
}
|
|
22094
22133
|
}
|
|
22095
22134
|
};
|
|
22135
|
+
const flushResult = await withTimeout(runBdCommand(["sync", "--flush-only"]), TIMEOUT_MS, "bd sync --flush-only");
|
|
22136
|
+
if (flushResult.exitCode !== 0) {
|
|
22137
|
+
throw new BeadError(`Failed to flush beads: ${flushResult.stderr}`, "bd sync --flush-only", flushResult.exitCode);
|
|
22138
|
+
}
|
|
22139
|
+
const beadsStatusResult = await runGitCommand([
|
|
22140
|
+
"status",
|
|
22141
|
+
"--porcelain",
|
|
22142
|
+
".beads/"
|
|
22143
|
+
]);
|
|
22144
|
+
const hasChanges = beadsStatusResult.stdout.trim() !== "";
|
|
22145
|
+
if (hasChanges) {
|
|
22146
|
+
const addResult = await runGitCommand(["add", ".beads/"]);
|
|
22147
|
+
if (addResult.exitCode !== 0) {
|
|
22148
|
+
throw new BeadError(`Failed to stage beads: ${addResult.stderr}`, "git add .beads/", addResult.exitCode);
|
|
22149
|
+
}
|
|
22150
|
+
const commitResult = await withTimeout(runGitCommand(["commit", "-m", "chore: sync beads"]), TIMEOUT_MS, "git commit");
|
|
22151
|
+
if (commitResult.exitCode !== 0 && !commitResult.stdout.includes("nothing to commit")) {
|
|
22152
|
+
throw new BeadError(`Failed to commit beads: ${commitResult.stderr}`, "git commit", commitResult.exitCode);
|
|
22153
|
+
}
|
|
22154
|
+
}
|
|
22096
22155
|
if (autoPull) {
|
|
22097
|
-
const pullResult = await withTimeout(
|
|
22156
|
+
const pullResult = await withTimeout(runGitCommand(["pull", "--rebase"]), TIMEOUT_MS, "git pull --rebase");
|
|
22098
22157
|
if (pullResult.exitCode !== 0) {
|
|
22099
|
-
throw new BeadError(`Failed to pull: ${pullResult.stderr
|
|
22158
|
+
throw new BeadError(`Failed to pull: ${pullResult.stderr}`, "git pull --rebase", pullResult.exitCode);
|
|
22159
|
+
}
|
|
22160
|
+
const importResult = await withTimeout(runBdCommand(["sync", "--import-only"]), TIMEOUT_MS, "bd sync --import-only");
|
|
22161
|
+
if (importResult.exitCode !== 0) {
|
|
22162
|
+
console.warn(`[beads] Import warning: ${importResult.stderr}`);
|
|
22100
22163
|
}
|
|
22101
22164
|
}
|
|
22102
|
-
const
|
|
22103
|
-
if (syncResult.exitCode !== 0) {
|
|
22104
|
-
throw new BeadError(`Failed to sync beads: ${syncResult.stderr.toString()}`, "bd sync", syncResult.exitCode);
|
|
22105
|
-
}
|
|
22106
|
-
const pushResult = await withTimeout(Bun.$`git push`.quiet().nothrow(), TIMEOUT_MS, "git push");
|
|
22165
|
+
const pushResult = await withTimeout(runGitCommand(["push"]), TIMEOUT_MS, "git push");
|
|
22107
22166
|
if (pushResult.exitCode !== 0) {
|
|
22108
|
-
throw new BeadError(`Failed to push: ${pushResult.stderr
|
|
22167
|
+
throw new BeadError(`Failed to push: ${pushResult.stderr}`, "git push", pushResult.exitCode);
|
|
22109
22168
|
}
|
|
22110
|
-
const statusResult = await
|
|
22111
|
-
const status = statusResult.stdout.
|
|
22169
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
22170
|
+
const status = statusResult.stdout.trim();
|
|
22112
22171
|
if (status !== "") {
|
|
22113
22172
|
return `Beads synced and pushed, but working directory not clean:
|
|
22114
22173
|
${status}`;
|
|
@@ -22123,11 +22182,11 @@ var beads_link_thread = tool({
|
|
|
22123
22182
|
thread_id: tool.schema.string().describe("Agent Mail thread ID")
|
|
22124
22183
|
},
|
|
22125
22184
|
async execute(args, ctx) {
|
|
22126
|
-
const queryResult = await
|
|
22185
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
22127
22186
|
if (queryResult.exitCode !== 0) {
|
|
22128
|
-
throw new BeadError(`Failed to get bead: ${queryResult.stderr
|
|
22187
|
+
throw new BeadError(`Failed to get bead: ${queryResult.stderr}`, `bd show ${args.bead_id} --json`, queryResult.exitCode);
|
|
22129
22188
|
}
|
|
22130
|
-
const bead = parseBead(queryResult.stdout
|
|
22189
|
+
const bead = parseBead(queryResult.stdout);
|
|
22131
22190
|
const existingDesc = bead.description || "";
|
|
22132
22191
|
const threadMarker = `[thread:${args.thread_id}]`;
|
|
22133
22192
|
if (existingDesc.includes(threadMarker)) {
|
|
@@ -22136,9 +22195,15 @@ var beads_link_thread = tool({
|
|
|
22136
22195
|
const newDesc = existingDesc ? `${existingDesc}
|
|
22137
22196
|
|
|
22138
22197
|
${threadMarker}` : threadMarker;
|
|
22139
|
-
const updateResult = await
|
|
22198
|
+
const updateResult = await runBdCommand([
|
|
22199
|
+
"update",
|
|
22200
|
+
args.bead_id,
|
|
22201
|
+
"-d",
|
|
22202
|
+
newDesc,
|
|
22203
|
+
"--json"
|
|
22204
|
+
]);
|
|
22140
22205
|
if (updateResult.exitCode !== 0) {
|
|
22141
|
-
throw new BeadError(`Failed to update bead: ${updateResult.stderr
|
|
22206
|
+
throw new BeadError(`Failed to update bead: ${updateResult.stderr}`, `bd update ${args.bead_id} -d ...`, updateResult.exitCode);
|
|
22142
22207
|
}
|
|
22143
22208
|
return `Linked bead ${args.bead_id} to thread ${args.thread_id}`;
|
|
22144
22209
|
}
|
|
@@ -26111,7 +26176,8 @@ class InMemoryMaturityStorage {
|
|
|
26111
26176
|
|
|
26112
26177
|
// src/index.ts
|
|
26113
26178
|
var SwarmPlugin = async (input) => {
|
|
26114
|
-
const {
|
|
26179
|
+
const { $, directory } = input;
|
|
26180
|
+
setBeadsWorkingDirectory(directory);
|
|
26115
26181
|
let activeAgentMailState = null;
|
|
26116
26182
|
async function releaseReservations() {
|
|
26117
26183
|
if (!activeAgentMailState || activeAgentMailState.reservations.length === 0) {
|
package/package.json
CHANGED
package/src/beads.ts
CHANGED
|
@@ -9,9 +9,89 @@
|
|
|
9
9
|
* - Validate all output with Zod schemas
|
|
10
10
|
* - Throw typed errors on failure
|
|
11
11
|
* - Support atomic epic creation with rollback hints
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT: Call setBeadsWorkingDirectory() before using tools to ensure
|
|
14
|
+
* bd commands run in the correct project directory.
|
|
12
15
|
*/
|
|
13
16
|
import { tool } from "@opencode-ai/plugin";
|
|
14
17
|
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Working Directory Configuration
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Module-level working directory for bd commands.
|
|
25
|
+
* Set this via setBeadsWorkingDirectory() before using tools.
|
|
26
|
+
* If not set, commands run in process.cwd() which may be wrong for plugins.
|
|
27
|
+
*/
|
|
28
|
+
let beadsWorkingDirectory: string | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set the working directory for all beads commands.
|
|
32
|
+
* Call this from the plugin initialization with the project directory.
|
|
33
|
+
*
|
|
34
|
+
* @param directory - Absolute path to the project directory
|
|
35
|
+
*/
|
|
36
|
+
export function setBeadsWorkingDirectory(directory: string): void {
|
|
37
|
+
beadsWorkingDirectory = directory;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the current working directory for beads commands.
|
|
42
|
+
* Returns the configured directory or process.cwd() as fallback.
|
|
43
|
+
*/
|
|
44
|
+
export function getBeadsWorkingDirectory(): string {
|
|
45
|
+
return beadsWorkingDirectory || process.cwd();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run a bd command in the correct working directory.
|
|
50
|
+
* Uses Bun.spawn with cwd option to ensure commands run in project directory.
|
|
51
|
+
*/
|
|
52
|
+
async function runBdCommand(
|
|
53
|
+
args: string[],
|
|
54
|
+
): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
|
55
|
+
const cwd = getBeadsWorkingDirectory();
|
|
56
|
+
const proc = Bun.spawn(["bd", ...args], {
|
|
57
|
+
cwd,
|
|
58
|
+
stdout: "pipe",
|
|
59
|
+
stderr: "pipe",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const [stdout, stderr] = await Promise.all([
|
|
63
|
+
new Response(proc.stdout).text(),
|
|
64
|
+
new Response(proc.stderr).text(),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const exitCode = await proc.exited;
|
|
68
|
+
|
|
69
|
+
return { exitCode, stdout, stderr };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Run a git command in the correct working directory.
|
|
74
|
+
*/
|
|
75
|
+
async function runGitCommand(
|
|
76
|
+
args: string[],
|
|
77
|
+
): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
|
78
|
+
const cwd = getBeadsWorkingDirectory();
|
|
79
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
80
|
+
cwd,
|
|
81
|
+
stdout: "pipe",
|
|
82
|
+
stderr: "pipe",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const [stdout, stderr] = await Promise.all([
|
|
86
|
+
new Response(proc.stdout).text(),
|
|
87
|
+
new Response(proc.stderr).text(),
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
const exitCode = await proc.exited;
|
|
91
|
+
|
|
92
|
+
return { exitCode, stdout, stderr };
|
|
93
|
+
}
|
|
94
|
+
|
|
15
95
|
import {
|
|
16
96
|
BeadSchema,
|
|
17
97
|
BeadCreateArgsSchema,
|
|
@@ -159,20 +239,20 @@ export const beads_create = tool({
|
|
|
159
239
|
const validated = BeadCreateArgsSchema.parse(args);
|
|
160
240
|
const cmdParts = buildCreateCommand(validated);
|
|
161
241
|
|
|
162
|
-
// Execute command
|
|
163
|
-
const result = await
|
|
242
|
+
// Execute command in the correct working directory
|
|
243
|
+
const result = await runBdCommand(cmdParts.slice(1)); // Remove 'bd' prefix
|
|
164
244
|
|
|
165
245
|
if (result.exitCode !== 0) {
|
|
166
246
|
throw new BeadError(
|
|
167
|
-
`Failed to create bead: ${result.stderr
|
|
247
|
+
`Failed to create bead: ${result.stderr}`,
|
|
168
248
|
cmdParts.join(" "),
|
|
169
249
|
result.exitCode,
|
|
170
|
-
result.stderr
|
|
250
|
+
result.stderr,
|
|
171
251
|
);
|
|
172
252
|
}
|
|
173
253
|
|
|
174
254
|
// Validate output before parsing
|
|
175
|
-
const stdout = result.stdout.
|
|
255
|
+
const stdout = result.stdout.trim();
|
|
176
256
|
if (!stdout) {
|
|
177
257
|
throw new BeadError(
|
|
178
258
|
"bd create returned empty output",
|
|
@@ -231,17 +311,17 @@ export const beads_create_epic = tool({
|
|
|
231
311
|
description: validated.epic_description,
|
|
232
312
|
});
|
|
233
313
|
|
|
234
|
-
const epicResult = await
|
|
314
|
+
const epicResult = await runBdCommand(epicCmd.slice(1)); // Remove 'bd' prefix
|
|
235
315
|
|
|
236
316
|
if (epicResult.exitCode !== 0) {
|
|
237
317
|
throw new BeadError(
|
|
238
|
-
`Failed to create epic: ${epicResult.stderr
|
|
318
|
+
`Failed to create epic: ${epicResult.stderr}`,
|
|
239
319
|
epicCmd.join(" "),
|
|
240
320
|
epicResult.exitCode,
|
|
241
321
|
);
|
|
242
322
|
}
|
|
243
323
|
|
|
244
|
-
const epic = parseBead(epicResult.stdout
|
|
324
|
+
const epic = parseBead(epicResult.stdout);
|
|
245
325
|
created.push(epic);
|
|
246
326
|
|
|
247
327
|
// 2. Create subtasks
|
|
@@ -253,17 +333,17 @@ export const beads_create_epic = tool({
|
|
|
253
333
|
parent_id: epic.id,
|
|
254
334
|
});
|
|
255
335
|
|
|
256
|
-
const subtaskResult = await
|
|
336
|
+
const subtaskResult = await runBdCommand(subtaskCmd.slice(1)); // Remove 'bd' prefix
|
|
257
337
|
|
|
258
338
|
if (subtaskResult.exitCode !== 0) {
|
|
259
339
|
throw new BeadError(
|
|
260
|
-
`Failed to create subtask: ${subtaskResult.stderr
|
|
340
|
+
`Failed to create subtask: ${subtaskResult.stderr}`,
|
|
261
341
|
subtaskCmd.join(" "),
|
|
262
342
|
subtaskResult.exitCode,
|
|
263
343
|
);
|
|
264
344
|
}
|
|
265
345
|
|
|
266
|
-
const subtaskBead = parseBead(subtaskResult.stdout
|
|
346
|
+
const subtaskBead = parseBead(subtaskResult.stdout);
|
|
267
347
|
created.push(subtaskBead);
|
|
268
348
|
}
|
|
269
349
|
|
|
@@ -281,22 +361,21 @@ export const beads_create_epic = tool({
|
|
|
281
361
|
|
|
282
362
|
for (const bead of created) {
|
|
283
363
|
try {
|
|
284
|
-
const
|
|
285
|
-
"bd",
|
|
364
|
+
const closeArgs = [
|
|
286
365
|
"close",
|
|
287
366
|
bead.id,
|
|
288
367
|
"--reason",
|
|
289
368
|
"Rollback partial epic",
|
|
290
369
|
"--json",
|
|
291
370
|
];
|
|
292
|
-
const rollbackResult = await
|
|
371
|
+
const rollbackResult = await runBdCommand(closeArgs);
|
|
293
372
|
if (rollbackResult.exitCode === 0) {
|
|
294
373
|
rollbackCommands.push(
|
|
295
374
|
`bd close ${bead.id} --reason "Rollback partial epic"`,
|
|
296
375
|
);
|
|
297
376
|
} else {
|
|
298
377
|
rollbackErrors.push(
|
|
299
|
-
`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.
|
|
378
|
+
`${bead.id}: exit ${rollbackResult.exitCode} - ${rollbackResult.stderr.trim()}`,
|
|
300
379
|
);
|
|
301
380
|
}
|
|
302
381
|
} catch (rollbackError) {
|
|
@@ -375,17 +454,17 @@ export const beads_query = tool({
|
|
|
375
454
|
}
|
|
376
455
|
}
|
|
377
456
|
|
|
378
|
-
const result = await
|
|
457
|
+
const result = await runBdCommand(cmd.slice(1)); // Remove 'bd' prefix
|
|
379
458
|
|
|
380
459
|
if (result.exitCode !== 0) {
|
|
381
460
|
throw new BeadError(
|
|
382
|
-
`Failed to query beads: ${result.stderr
|
|
461
|
+
`Failed to query beads: ${result.stderr}`,
|
|
383
462
|
cmd.join(" "),
|
|
384
463
|
result.exitCode,
|
|
385
464
|
);
|
|
386
465
|
}
|
|
387
466
|
|
|
388
|
-
const beads = parseBeads(result.stdout
|
|
467
|
+
const beads = parseBeads(result.stdout);
|
|
389
468
|
const limited = beads.slice(0, validated.limit);
|
|
390
469
|
|
|
391
470
|
return JSON.stringify(limited, null, 2);
|
|
@@ -427,17 +506,17 @@ export const beads_update = tool({
|
|
|
427
506
|
}
|
|
428
507
|
cmd.push("--json");
|
|
429
508
|
|
|
430
|
-
const result = await
|
|
509
|
+
const result = await runBdCommand(cmd.slice(1)); // Remove 'bd' prefix
|
|
431
510
|
|
|
432
511
|
if (result.exitCode !== 0) {
|
|
433
512
|
throw new BeadError(
|
|
434
|
-
`Failed to update bead: ${result.stderr
|
|
513
|
+
`Failed to update bead: ${result.stderr}`,
|
|
435
514
|
cmd.join(" "),
|
|
436
515
|
result.exitCode,
|
|
437
516
|
);
|
|
438
517
|
}
|
|
439
518
|
|
|
440
|
-
const bead = parseBead(result.stdout
|
|
519
|
+
const bead = parseBead(result.stdout);
|
|
441
520
|
return JSON.stringify(bead, null, 2);
|
|
442
521
|
},
|
|
443
522
|
});
|
|
@@ -463,17 +542,17 @@ export const beads_close = tool({
|
|
|
463
542
|
"--json",
|
|
464
543
|
];
|
|
465
544
|
|
|
466
|
-
const result = await
|
|
545
|
+
const result = await runBdCommand(cmd.slice(1)); // Remove 'bd' prefix
|
|
467
546
|
|
|
468
547
|
if (result.exitCode !== 0) {
|
|
469
548
|
throw new BeadError(
|
|
470
|
-
`Failed to close bead: ${result.stderr
|
|
549
|
+
`Failed to close bead: ${result.stderr}`,
|
|
471
550
|
cmd.join(" "),
|
|
472
551
|
result.exitCode,
|
|
473
552
|
);
|
|
474
553
|
}
|
|
475
554
|
|
|
476
|
-
const bead = parseBead(result.stdout
|
|
555
|
+
const bead = parseBead(result.stdout);
|
|
477
556
|
return `Closed ${bead.id}: ${validated.reason}`;
|
|
478
557
|
},
|
|
479
558
|
});
|
|
@@ -488,19 +567,23 @@ export const beads_start = tool({
|
|
|
488
567
|
id: tool.schema.string().describe("Bead ID"),
|
|
489
568
|
},
|
|
490
569
|
async execute(args, ctx) {
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
570
|
+
const result = await runBdCommand([
|
|
571
|
+
"update",
|
|
572
|
+
args.id,
|
|
573
|
+
"--status",
|
|
574
|
+
"in_progress",
|
|
575
|
+
"--json",
|
|
576
|
+
]);
|
|
494
577
|
|
|
495
578
|
if (result.exitCode !== 0) {
|
|
496
579
|
throw new BeadError(
|
|
497
|
-
`Failed to start bead: ${result.stderr
|
|
498
|
-
|
|
580
|
+
`Failed to start bead: ${result.stderr}`,
|
|
581
|
+
`bd update ${args.id} --status in_progress --json`,
|
|
499
582
|
result.exitCode,
|
|
500
583
|
);
|
|
501
584
|
}
|
|
502
585
|
|
|
503
|
-
const bead = parseBead(result.stdout
|
|
586
|
+
const bead = parseBead(result.stdout);
|
|
504
587
|
return `Started: ${bead.id}`;
|
|
505
588
|
},
|
|
506
589
|
});
|
|
@@ -512,19 +595,17 @@ export const beads_ready = tool({
|
|
|
512
595
|
description: "Get the next ready bead (unblocked, highest priority)",
|
|
513
596
|
args: {},
|
|
514
597
|
async execute(args, ctx) {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
const result = await Bun.$`${cmd}`.quiet().nothrow();
|
|
598
|
+
const result = await runBdCommand(["ready", "--json"]);
|
|
518
599
|
|
|
519
600
|
if (result.exitCode !== 0) {
|
|
520
601
|
throw new BeadError(
|
|
521
|
-
`Failed to get ready beads: ${result.stderr
|
|
522
|
-
|
|
602
|
+
`Failed to get ready beads: ${result.stderr}`,
|
|
603
|
+
"bd ready --json",
|
|
523
604
|
result.exitCode,
|
|
524
605
|
);
|
|
525
606
|
}
|
|
526
607
|
|
|
527
|
-
const beads = parseBeads(result.stdout
|
|
608
|
+
const beads = parseBeads(result.stdout);
|
|
528
609
|
|
|
529
610
|
if (beads.length === 0) {
|
|
530
611
|
return "No ready beads";
|
|
@@ -583,53 +664,101 @@ export const beads_sync = tool({
|
|
|
583
664
|
}
|
|
584
665
|
};
|
|
585
666
|
|
|
586
|
-
// 1.
|
|
667
|
+
// 1. Flush beads to JSONL (doesn't use worktrees)
|
|
668
|
+
const flushResult = await withTimeout(
|
|
669
|
+
runBdCommand(["sync", "--flush-only"]),
|
|
670
|
+
TIMEOUT_MS,
|
|
671
|
+
"bd sync --flush-only",
|
|
672
|
+
);
|
|
673
|
+
if (flushResult.exitCode !== 0) {
|
|
674
|
+
throw new BeadError(
|
|
675
|
+
`Failed to flush beads: ${flushResult.stderr}`,
|
|
676
|
+
"bd sync --flush-only",
|
|
677
|
+
flushResult.exitCode,
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 2. Check if there are changes to commit
|
|
682
|
+
const beadsStatusResult = await runGitCommand([
|
|
683
|
+
"status",
|
|
684
|
+
"--porcelain",
|
|
685
|
+
".beads/",
|
|
686
|
+
]);
|
|
687
|
+
const hasChanges = beadsStatusResult.stdout.trim() !== "";
|
|
688
|
+
|
|
689
|
+
if (hasChanges) {
|
|
690
|
+
// 3. Stage .beads changes
|
|
691
|
+
const addResult = await runGitCommand(["add", ".beads/"]);
|
|
692
|
+
if (addResult.exitCode !== 0) {
|
|
693
|
+
throw new BeadError(
|
|
694
|
+
`Failed to stage beads: ${addResult.stderr}`,
|
|
695
|
+
"git add .beads/",
|
|
696
|
+
addResult.exitCode,
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// 4. Commit
|
|
701
|
+
const commitResult = await withTimeout(
|
|
702
|
+
runGitCommand(["commit", "-m", "chore: sync beads"]),
|
|
703
|
+
TIMEOUT_MS,
|
|
704
|
+
"git commit",
|
|
705
|
+
);
|
|
706
|
+
if (
|
|
707
|
+
commitResult.exitCode !== 0 &&
|
|
708
|
+
!commitResult.stdout.includes("nothing to commit")
|
|
709
|
+
) {
|
|
710
|
+
throw new BeadError(
|
|
711
|
+
`Failed to commit beads: ${commitResult.stderr}`,
|
|
712
|
+
"git commit",
|
|
713
|
+
commitResult.exitCode,
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// 5. Pull if requested (with rebase to avoid merge commits)
|
|
587
719
|
if (autoPull) {
|
|
588
720
|
const pullResult = await withTimeout(
|
|
589
|
-
|
|
721
|
+
runGitCommand(["pull", "--rebase"]),
|
|
590
722
|
TIMEOUT_MS,
|
|
591
723
|
"git pull --rebase",
|
|
592
724
|
);
|
|
593
725
|
if (pullResult.exitCode !== 0) {
|
|
594
726
|
throw new BeadError(
|
|
595
|
-
`Failed to pull: ${pullResult.stderr
|
|
727
|
+
`Failed to pull: ${pullResult.stderr}`,
|
|
596
728
|
"git pull --rebase",
|
|
597
729
|
pullResult.exitCode,
|
|
598
730
|
);
|
|
599
731
|
}
|
|
600
|
-
}
|
|
601
732
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
);
|
|
608
|
-
if (syncResult.exitCode !== 0) {
|
|
609
|
-
throw new BeadError(
|
|
610
|
-
`Failed to sync beads: ${syncResult.stderr.toString()}`,
|
|
611
|
-
"bd sync",
|
|
612
|
-
syncResult.exitCode,
|
|
733
|
+
// 6. Import any changes from remote
|
|
734
|
+
const importResult = await withTimeout(
|
|
735
|
+
runBdCommand(["sync", "--import-only"]),
|
|
736
|
+
TIMEOUT_MS,
|
|
737
|
+
"bd sync --import-only",
|
|
613
738
|
);
|
|
739
|
+
if (importResult.exitCode !== 0) {
|
|
740
|
+
// Non-fatal - just log warning
|
|
741
|
+
console.warn(`[beads] Import warning: ${importResult.stderr}`);
|
|
742
|
+
}
|
|
614
743
|
}
|
|
615
744
|
|
|
616
|
-
//
|
|
745
|
+
// 7. Push
|
|
617
746
|
const pushResult = await withTimeout(
|
|
618
|
-
|
|
747
|
+
runGitCommand(["push"]),
|
|
619
748
|
TIMEOUT_MS,
|
|
620
749
|
"git push",
|
|
621
750
|
);
|
|
622
751
|
if (pushResult.exitCode !== 0) {
|
|
623
752
|
throw new BeadError(
|
|
624
|
-
`Failed to push: ${pushResult.stderr
|
|
753
|
+
`Failed to push: ${pushResult.stderr}`,
|
|
625
754
|
"git push",
|
|
626
755
|
pushResult.exitCode,
|
|
627
756
|
);
|
|
628
757
|
}
|
|
629
758
|
|
|
630
759
|
// 4. Verify clean state
|
|
631
|
-
const statusResult = await
|
|
632
|
-
const status = statusResult.stdout.
|
|
760
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
761
|
+
const status = statusResult.stdout.trim();
|
|
633
762
|
|
|
634
763
|
if (status !== "") {
|
|
635
764
|
return `Beads synced and pushed, but working directory not clean:\n${status}`;
|
|
@@ -651,19 +780,17 @@ export const beads_link_thread = tool({
|
|
|
651
780
|
async execute(args, ctx) {
|
|
652
781
|
// Update bead description to include thread link
|
|
653
782
|
// This is a workaround since bd doesn't have native metadata support
|
|
654
|
-
const queryResult = await
|
|
655
|
-
.quiet()
|
|
656
|
-
.nothrow();
|
|
783
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
657
784
|
|
|
658
785
|
if (queryResult.exitCode !== 0) {
|
|
659
786
|
throw new BeadError(
|
|
660
|
-
`Failed to get bead: ${queryResult.stderr
|
|
787
|
+
`Failed to get bead: ${queryResult.stderr}`,
|
|
661
788
|
`bd show ${args.bead_id} --json`,
|
|
662
789
|
queryResult.exitCode,
|
|
663
790
|
);
|
|
664
791
|
}
|
|
665
792
|
|
|
666
|
-
const bead = parseBead(queryResult.stdout
|
|
793
|
+
const bead = parseBead(queryResult.stdout);
|
|
667
794
|
const existingDesc = bead.description || "";
|
|
668
795
|
|
|
669
796
|
// Add thread link if not already present
|
|
@@ -676,14 +803,17 @@ export const beads_link_thread = tool({
|
|
|
676
803
|
? `${existingDesc}\n\n${threadMarker}`
|
|
677
804
|
: threadMarker;
|
|
678
805
|
|
|
679
|
-
const updateResult =
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
806
|
+
const updateResult = await runBdCommand([
|
|
807
|
+
"update",
|
|
808
|
+
args.bead_id,
|
|
809
|
+
"-d",
|
|
810
|
+
newDesc,
|
|
811
|
+
"--json",
|
|
812
|
+
]);
|
|
683
813
|
|
|
684
814
|
if (updateResult.exitCode !== 0) {
|
|
685
815
|
throw new BeadError(
|
|
686
|
-
`Failed to update bead: ${updateResult.stderr
|
|
816
|
+
`Failed to update bead: ${updateResult.stderr}`,
|
|
687
817
|
`bd update ${args.bead_id} -d ...`,
|
|
688
818
|
updateResult.exitCode,
|
|
689
819
|
);
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { Plugin, PluginInput, Hooks } from "@opencode-ai/plugin";
|
|
24
24
|
|
|
25
|
-
import { beadsTools } from "./beads";
|
|
25
|
+
import { beadsTools, setBeadsWorkingDirectory } from "./beads";
|
|
26
26
|
import {
|
|
27
27
|
agentMailTools,
|
|
28
28
|
type AgentMailState,
|
|
@@ -48,7 +48,11 @@ import { repoCrawlTools } from "./repo-crawl";
|
|
|
48
48
|
export const SwarmPlugin: Plugin = async (
|
|
49
49
|
input: PluginInput,
|
|
50
50
|
): Promise<Hooks> => {
|
|
51
|
-
const {
|
|
51
|
+
const { $, directory } = input;
|
|
52
|
+
|
|
53
|
+
// Set the working directory for beads commands
|
|
54
|
+
// This ensures bd runs in the project directory, not ~/.config/opencode
|
|
55
|
+
setBeadsWorkingDirectory(directory);
|
|
52
56
|
|
|
53
57
|
/** Track active sessions for cleanup */
|
|
54
58
|
let activeAgentMailState: AgentMailState | null = null;
|