opencode-swarm-plugin 0.12.16 → 0.12.17
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 +92 -44
- package/dist/plugin.js +90 -44
- package/package.json +1 -1
- package/src/beads.ts +136 -54
- 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
|
}
|
|
@@ -22094,21 +22133,21 @@ var beads_sync = tool({
|
|
|
22094
22133
|
}
|
|
22095
22134
|
};
|
|
22096
22135
|
if (autoPull) {
|
|
22097
|
-
const pullResult = await withTimeout(
|
|
22136
|
+
const pullResult = await withTimeout(runGitCommand(["pull", "--rebase"]), TIMEOUT_MS, "git pull --rebase");
|
|
22098
22137
|
if (pullResult.exitCode !== 0) {
|
|
22099
|
-
throw new BeadError(`Failed to pull: ${pullResult.stderr
|
|
22138
|
+
throw new BeadError(`Failed to pull: ${pullResult.stderr}`, "git pull --rebase", pullResult.exitCode);
|
|
22100
22139
|
}
|
|
22101
22140
|
}
|
|
22102
|
-
const syncResult = await withTimeout(
|
|
22141
|
+
const syncResult = await withTimeout(runBdCommand(["sync"]), TIMEOUT_MS, "bd sync");
|
|
22103
22142
|
if (syncResult.exitCode !== 0) {
|
|
22104
|
-
throw new BeadError(`Failed to sync beads: ${syncResult.stderr
|
|
22143
|
+
throw new BeadError(`Failed to sync beads: ${syncResult.stderr}`, "bd sync", syncResult.exitCode);
|
|
22105
22144
|
}
|
|
22106
|
-
const pushResult = await withTimeout(
|
|
22145
|
+
const pushResult = await withTimeout(runGitCommand(["push"]), TIMEOUT_MS, "git push");
|
|
22107
22146
|
if (pushResult.exitCode !== 0) {
|
|
22108
|
-
throw new BeadError(`Failed to push: ${pushResult.stderr
|
|
22147
|
+
throw new BeadError(`Failed to push: ${pushResult.stderr}`, "git push", pushResult.exitCode);
|
|
22109
22148
|
}
|
|
22110
|
-
const statusResult = await
|
|
22111
|
-
const status = statusResult.stdout.
|
|
22149
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
22150
|
+
const status = statusResult.stdout.trim();
|
|
22112
22151
|
if (status !== "") {
|
|
22113
22152
|
return `Beads synced and pushed, but working directory not clean:
|
|
22114
22153
|
${status}`;
|
|
@@ -22123,11 +22162,11 @@ var beads_link_thread = tool({
|
|
|
22123
22162
|
thread_id: tool.schema.string().describe("Agent Mail thread ID")
|
|
22124
22163
|
},
|
|
22125
22164
|
async execute(args, ctx) {
|
|
22126
|
-
const queryResult = await
|
|
22165
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
22127
22166
|
if (queryResult.exitCode !== 0) {
|
|
22128
|
-
throw new BeadError(`Failed to get bead: ${queryResult.stderr
|
|
22167
|
+
throw new BeadError(`Failed to get bead: ${queryResult.stderr}`, `bd show ${args.bead_id} --json`, queryResult.exitCode);
|
|
22129
22168
|
}
|
|
22130
|
-
const bead = parseBead(queryResult.stdout
|
|
22169
|
+
const bead = parseBead(queryResult.stdout);
|
|
22131
22170
|
const existingDesc = bead.description || "";
|
|
22132
22171
|
const threadMarker = `[thread:${args.thread_id}]`;
|
|
22133
22172
|
if (existingDesc.includes(threadMarker)) {
|
|
@@ -22136,9 +22175,15 @@ var beads_link_thread = tool({
|
|
|
22136
22175
|
const newDesc = existingDesc ? `${existingDesc}
|
|
22137
22176
|
|
|
22138
22177
|
${threadMarker}` : threadMarker;
|
|
22139
|
-
const updateResult = await
|
|
22178
|
+
const updateResult = await runBdCommand([
|
|
22179
|
+
"update",
|
|
22180
|
+
args.bead_id,
|
|
22181
|
+
"-d",
|
|
22182
|
+
newDesc,
|
|
22183
|
+
"--json"
|
|
22184
|
+
]);
|
|
22140
22185
|
if (updateResult.exitCode !== 0) {
|
|
22141
|
-
throw new BeadError(`Failed to update bead: ${updateResult.stderr
|
|
22186
|
+
throw new BeadError(`Failed to update bead: ${updateResult.stderr}`, `bd update ${args.bead_id} -d ...`, updateResult.exitCode);
|
|
22142
22187
|
}
|
|
22143
22188
|
return `Linked bead ${args.bead_id} to thread ${args.thread_id}`;
|
|
22144
22189
|
}
|
|
@@ -26496,7 +26541,8 @@ async function resetStorage() {
|
|
|
26496
26541
|
|
|
26497
26542
|
// src/index.ts
|
|
26498
26543
|
var SwarmPlugin = async (input) => {
|
|
26499
|
-
const {
|
|
26544
|
+
const { $, directory } = input;
|
|
26545
|
+
setBeadsWorkingDirectory(directory);
|
|
26500
26546
|
let activeAgentMailState = null;
|
|
26501
26547
|
async function releaseReservations() {
|
|
26502
26548
|
if (!activeAgentMailState || activeAgentMailState.reservations.length === 0) {
|
|
@@ -26587,6 +26633,7 @@ export {
|
|
|
26587
26633
|
swarmTools,
|
|
26588
26634
|
structuredTools,
|
|
26589
26635
|
setStorage,
|
|
26636
|
+
setBeadsWorkingDirectory,
|
|
26590
26637
|
selectStrategy,
|
|
26591
26638
|
resetToolCache,
|
|
26592
26639
|
resetStorage,
|
|
@@ -26598,6 +26645,7 @@ export {
|
|
|
26598
26645
|
getToolAvailability,
|
|
26599
26646
|
getStorage,
|
|
26600
26647
|
getSchemaByName,
|
|
26648
|
+
getBeadsWorkingDirectory,
|
|
26601
26649
|
formatZodErrors,
|
|
26602
26650
|
formatToolAvailability,
|
|
26603
26651
|
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
|
}
|
|
@@ -22094,21 +22133,21 @@ var beads_sync = tool({
|
|
|
22094
22133
|
}
|
|
22095
22134
|
};
|
|
22096
22135
|
if (autoPull) {
|
|
22097
|
-
const pullResult = await withTimeout(
|
|
22136
|
+
const pullResult = await withTimeout(runGitCommand(["pull", "--rebase"]), TIMEOUT_MS, "git pull --rebase");
|
|
22098
22137
|
if (pullResult.exitCode !== 0) {
|
|
22099
|
-
throw new BeadError(`Failed to pull: ${pullResult.stderr
|
|
22138
|
+
throw new BeadError(`Failed to pull: ${pullResult.stderr}`, "git pull --rebase", pullResult.exitCode);
|
|
22100
22139
|
}
|
|
22101
22140
|
}
|
|
22102
|
-
const syncResult = await withTimeout(
|
|
22141
|
+
const syncResult = await withTimeout(runBdCommand(["sync"]), TIMEOUT_MS, "bd sync");
|
|
22103
22142
|
if (syncResult.exitCode !== 0) {
|
|
22104
|
-
throw new BeadError(`Failed to sync beads: ${syncResult.stderr
|
|
22143
|
+
throw new BeadError(`Failed to sync beads: ${syncResult.stderr}`, "bd sync", syncResult.exitCode);
|
|
22105
22144
|
}
|
|
22106
|
-
const pushResult = await withTimeout(
|
|
22145
|
+
const pushResult = await withTimeout(runGitCommand(["push"]), TIMEOUT_MS, "git push");
|
|
22107
22146
|
if (pushResult.exitCode !== 0) {
|
|
22108
|
-
throw new BeadError(`Failed to push: ${pushResult.stderr
|
|
22147
|
+
throw new BeadError(`Failed to push: ${pushResult.stderr}`, "git push", pushResult.exitCode);
|
|
22109
22148
|
}
|
|
22110
|
-
const statusResult = await
|
|
22111
|
-
const status = statusResult.stdout.
|
|
22149
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
22150
|
+
const status = statusResult.stdout.trim();
|
|
22112
22151
|
if (status !== "") {
|
|
22113
22152
|
return `Beads synced and pushed, but working directory not clean:
|
|
22114
22153
|
${status}`;
|
|
@@ -22123,11 +22162,11 @@ var beads_link_thread = tool({
|
|
|
22123
22162
|
thread_id: tool.schema.string().describe("Agent Mail thread ID")
|
|
22124
22163
|
},
|
|
22125
22164
|
async execute(args, ctx) {
|
|
22126
|
-
const queryResult = await
|
|
22165
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
22127
22166
|
if (queryResult.exitCode !== 0) {
|
|
22128
|
-
throw new BeadError(`Failed to get bead: ${queryResult.stderr
|
|
22167
|
+
throw new BeadError(`Failed to get bead: ${queryResult.stderr}`, `bd show ${args.bead_id} --json`, queryResult.exitCode);
|
|
22129
22168
|
}
|
|
22130
|
-
const bead = parseBead(queryResult.stdout
|
|
22169
|
+
const bead = parseBead(queryResult.stdout);
|
|
22131
22170
|
const existingDesc = bead.description || "";
|
|
22132
22171
|
const threadMarker = `[thread:${args.thread_id}]`;
|
|
22133
22172
|
if (existingDesc.includes(threadMarker)) {
|
|
@@ -22136,9 +22175,15 @@ var beads_link_thread = tool({
|
|
|
22136
22175
|
const newDesc = existingDesc ? `${existingDesc}
|
|
22137
22176
|
|
|
22138
22177
|
${threadMarker}` : threadMarker;
|
|
22139
|
-
const updateResult = await
|
|
22178
|
+
const updateResult = await runBdCommand([
|
|
22179
|
+
"update",
|
|
22180
|
+
args.bead_id,
|
|
22181
|
+
"-d",
|
|
22182
|
+
newDesc,
|
|
22183
|
+
"--json"
|
|
22184
|
+
]);
|
|
22140
22185
|
if (updateResult.exitCode !== 0) {
|
|
22141
|
-
throw new BeadError(`Failed to update bead: ${updateResult.stderr
|
|
22186
|
+
throw new BeadError(`Failed to update bead: ${updateResult.stderr}`, `bd update ${args.bead_id} -d ...`, updateResult.exitCode);
|
|
22142
22187
|
}
|
|
22143
22188
|
return `Linked bead ${args.bead_id} to thread ${args.thread_id}`;
|
|
22144
22189
|
}
|
|
@@ -26111,7 +26156,8 @@ class InMemoryMaturityStorage {
|
|
|
26111
26156
|
|
|
26112
26157
|
// src/index.ts
|
|
26113
26158
|
var SwarmPlugin = async (input) => {
|
|
26114
|
-
const {
|
|
26159
|
+
const { $, directory } = input;
|
|
26160
|
+
setBeadsWorkingDirectory(directory);
|
|
26115
26161
|
let activeAgentMailState = null;
|
|
26116
26162
|
async function releaseReservations() {
|
|
26117
26163
|
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";
|
|
@@ -586,13 +667,13 @@ export const beads_sync = tool({
|
|
|
586
667
|
// 1. Pull if requested
|
|
587
668
|
if (autoPull) {
|
|
588
669
|
const pullResult = await withTimeout(
|
|
589
|
-
|
|
670
|
+
runGitCommand(["pull", "--rebase"]),
|
|
590
671
|
TIMEOUT_MS,
|
|
591
672
|
"git pull --rebase",
|
|
592
673
|
);
|
|
593
674
|
if (pullResult.exitCode !== 0) {
|
|
594
675
|
throw new BeadError(
|
|
595
|
-
`Failed to pull: ${pullResult.stderr
|
|
676
|
+
`Failed to pull: ${pullResult.stderr}`,
|
|
596
677
|
"git pull --rebase",
|
|
597
678
|
pullResult.exitCode,
|
|
598
679
|
);
|
|
@@ -601,13 +682,13 @@ export const beads_sync = tool({
|
|
|
601
682
|
|
|
602
683
|
// 2. Sync beads
|
|
603
684
|
const syncResult = await withTimeout(
|
|
604
|
-
|
|
685
|
+
runBdCommand(["sync"]),
|
|
605
686
|
TIMEOUT_MS,
|
|
606
687
|
"bd sync",
|
|
607
688
|
);
|
|
608
689
|
if (syncResult.exitCode !== 0) {
|
|
609
690
|
throw new BeadError(
|
|
610
|
-
`Failed to sync beads: ${syncResult.stderr
|
|
691
|
+
`Failed to sync beads: ${syncResult.stderr}`,
|
|
611
692
|
"bd sync",
|
|
612
693
|
syncResult.exitCode,
|
|
613
694
|
);
|
|
@@ -615,21 +696,21 @@ export const beads_sync = tool({
|
|
|
615
696
|
|
|
616
697
|
// 3. Push
|
|
617
698
|
const pushResult = await withTimeout(
|
|
618
|
-
|
|
699
|
+
runGitCommand(["push"]),
|
|
619
700
|
TIMEOUT_MS,
|
|
620
701
|
"git push",
|
|
621
702
|
);
|
|
622
703
|
if (pushResult.exitCode !== 0) {
|
|
623
704
|
throw new BeadError(
|
|
624
|
-
`Failed to push: ${pushResult.stderr
|
|
705
|
+
`Failed to push: ${pushResult.stderr}`,
|
|
625
706
|
"git push",
|
|
626
707
|
pushResult.exitCode,
|
|
627
708
|
);
|
|
628
709
|
}
|
|
629
710
|
|
|
630
711
|
// 4. Verify clean state
|
|
631
|
-
const statusResult = await
|
|
632
|
-
const status = statusResult.stdout.
|
|
712
|
+
const statusResult = await runGitCommand(["status", "--porcelain"]);
|
|
713
|
+
const status = statusResult.stdout.trim();
|
|
633
714
|
|
|
634
715
|
if (status !== "") {
|
|
635
716
|
return `Beads synced and pushed, but working directory not clean:\n${status}`;
|
|
@@ -651,19 +732,17 @@ export const beads_link_thread = tool({
|
|
|
651
732
|
async execute(args, ctx) {
|
|
652
733
|
// Update bead description to include thread link
|
|
653
734
|
// This is a workaround since bd doesn't have native metadata support
|
|
654
|
-
const queryResult = await
|
|
655
|
-
.quiet()
|
|
656
|
-
.nothrow();
|
|
735
|
+
const queryResult = await runBdCommand(["show", args.bead_id, "--json"]);
|
|
657
736
|
|
|
658
737
|
if (queryResult.exitCode !== 0) {
|
|
659
738
|
throw new BeadError(
|
|
660
|
-
`Failed to get bead: ${queryResult.stderr
|
|
739
|
+
`Failed to get bead: ${queryResult.stderr}`,
|
|
661
740
|
`bd show ${args.bead_id} --json`,
|
|
662
741
|
queryResult.exitCode,
|
|
663
742
|
);
|
|
664
743
|
}
|
|
665
744
|
|
|
666
|
-
const bead = parseBead(queryResult.stdout
|
|
745
|
+
const bead = parseBead(queryResult.stdout);
|
|
667
746
|
const existingDesc = bead.description || "";
|
|
668
747
|
|
|
669
748
|
// Add thread link if not already present
|
|
@@ -676,14 +755,17 @@ export const beads_link_thread = tool({
|
|
|
676
755
|
? `${existingDesc}\n\n${threadMarker}`
|
|
677
756
|
: threadMarker;
|
|
678
757
|
|
|
679
|
-
const updateResult =
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
758
|
+
const updateResult = await runBdCommand([
|
|
759
|
+
"update",
|
|
760
|
+
args.bead_id,
|
|
761
|
+
"-d",
|
|
762
|
+
newDesc,
|
|
763
|
+
"--json",
|
|
764
|
+
]);
|
|
683
765
|
|
|
684
766
|
if (updateResult.exitCode !== 0) {
|
|
685
767
|
throw new BeadError(
|
|
686
|
-
`Failed to update bead: ${updateResult.stderr
|
|
768
|
+
`Failed to update bead: ${updateResult.stderr}`,
|
|
687
769
|
`bd update ${args.bead_id} -d ...`,
|
|
688
770
|
updateResult.exitCode,
|
|
689
771
|
);
|
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;
|