opencode-swarm-plugin 0.12.31 → 0.13.1
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/.beads/issues.jsonl +204 -10
- package/.opencode/skills/tdd/SKILL.md +182 -0
- package/README.md +165 -17
- package/bun.lock +23 -0
- package/dist/index.js +4082 -457
- package/dist/pglite.data +0 -0
- package/dist/pglite.wasm +0 -0
- package/dist/plugin.js +4070 -533
- package/examples/commands/swarm.md +100 -28
- package/examples/skills/beads-workflow/SKILL.md +75 -28
- package/examples/skills/swarm-coordination/SKILL.md +165 -21
- package/global-skills/swarm-coordination/SKILL.md +116 -58
- package/global-skills/testing-patterns/SKILL.md +430 -0
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +586 -0
- package/package.json +11 -5
- package/src/index.ts +44 -5
- package/src/streams/agent-mail.test.ts +777 -0
- package/src/streams/agent-mail.ts +535 -0
- package/src/streams/debug.test.ts +500 -0
- package/src/streams/debug.ts +629 -0
- package/src/streams/effect/ask.integration.test.ts +314 -0
- package/src/streams/effect/ask.ts +202 -0
- package/src/streams/effect/cursor.integration.test.ts +418 -0
- package/src/streams/effect/cursor.ts +288 -0
- package/src/streams/effect/deferred.test.ts +357 -0
- package/src/streams/effect/deferred.ts +445 -0
- package/src/streams/effect/index.ts +17 -0
- package/src/streams/effect/layers.ts +73 -0
- package/src/streams/effect/lock.test.ts +385 -0
- package/src/streams/effect/lock.ts +399 -0
- package/src/streams/effect/mailbox.test.ts +260 -0
- package/src/streams/effect/mailbox.ts +318 -0
- package/src/streams/events.test.ts +628 -0
- package/src/streams/events.ts +214 -0
- package/src/streams/index.test.ts +229 -0
- package/src/streams/index.ts +492 -0
- package/src/streams/migrations.test.ts +355 -0
- package/src/streams/migrations.ts +269 -0
- package/src/streams/projections.test.ts +611 -0
- package/src/streams/projections.ts +302 -0
- package/src/streams/store.integration.test.ts +548 -0
- package/src/streams/store.ts +546 -0
- package/src/streams/swarm-mail.ts +552 -0
- package/src/swarm-mail.integration.test.ts +970 -0
- package/src/swarm-mail.ts +739 -0
- package/src/swarm.integration.test.ts +16 -10
- package/src/swarm.ts +146 -78
- package/src/tool-availability.ts +35 -2
- package/global-skills/mcp-tool-authoring/SKILL.md +0 -695
|
@@ -1315,24 +1315,29 @@ describe("Swarm Prompt V2 (with Agent Mail/Beads)", () => {
|
|
|
1315
1315
|
describe("SUBTASK_PROMPT_V2", () => {
|
|
1316
1316
|
it("contains expected sections", () => {
|
|
1317
1317
|
// Check all main sections are present in the template
|
|
1318
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1318
|
+
expect(SUBTASK_PROMPT_V2).toContain("[TASK]");
|
|
1319
1319
|
expect(SUBTASK_PROMPT_V2).toContain("{subtask_title}");
|
|
1320
1320
|
expect(SUBTASK_PROMPT_V2).toContain("{subtask_description}");
|
|
1321
1321
|
|
|
1322
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1322
|
+
expect(SUBTASK_PROMPT_V2).toContain("[FILES]");
|
|
1323
1323
|
expect(SUBTASK_PROMPT_V2).toContain("{file_list}");
|
|
1324
1324
|
|
|
1325
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1325
|
+
expect(SUBTASK_PROMPT_V2).toContain("[CONTEXT]");
|
|
1326
1326
|
expect(SUBTASK_PROMPT_V2).toContain("{shared_context}");
|
|
1327
1327
|
|
|
1328
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1328
|
+
expect(SUBTASK_PROMPT_V2).toContain("[WORKFLOW]");
|
|
1329
1329
|
});
|
|
1330
1330
|
|
|
1331
|
-
it("DOES contain
|
|
1332
|
-
// V2 prompt tells agents to USE
|
|
1333
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1334
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1331
|
+
it("DOES contain Swarm Mail instructions (MANDATORY)", () => {
|
|
1332
|
+
// V2 prompt tells agents to USE Swarm Mail - this is non-negotiable
|
|
1333
|
+
expect(SUBTASK_PROMPT_V2).toContain("SWARM MAIL");
|
|
1334
|
+
expect(SUBTASK_PROMPT_V2).toContain("swarmmail_init");
|
|
1335
|
+
expect(SUBTASK_PROMPT_V2).toContain("swarmmail_send");
|
|
1336
|
+
expect(SUBTASK_PROMPT_V2).toContain("swarmmail_inbox");
|
|
1337
|
+
expect(SUBTASK_PROMPT_V2).toContain("swarmmail_reserve");
|
|
1338
|
+
expect(SUBTASK_PROMPT_V2).toContain("swarmmail_release");
|
|
1335
1339
|
expect(SUBTASK_PROMPT_V2).toContain("thread_id");
|
|
1340
|
+
expect(SUBTASK_PROMPT_V2).toContain("non-negotiable");
|
|
1336
1341
|
});
|
|
1337
1342
|
|
|
1338
1343
|
it("DOES contain beads instructions", () => {
|
|
@@ -1344,10 +1349,11 @@ describe("Swarm Prompt V2 (with Agent Mail/Beads)", () => {
|
|
|
1344
1349
|
expect(SUBTASK_PROMPT_V2).toContain("swarm_complete");
|
|
1345
1350
|
});
|
|
1346
1351
|
|
|
1347
|
-
it("instructs agents to communicate", () => {
|
|
1352
|
+
it("instructs agents to communicate via swarmmail", () => {
|
|
1348
1353
|
expect(SUBTASK_PROMPT_V2).toContain("Never work silently");
|
|
1349
|
-
expect(SUBTASK_PROMPT_V2).toContain("
|
|
1354
|
+
expect(SUBTASK_PROMPT_V2).toContain("progress");
|
|
1350
1355
|
expect(SUBTASK_PROMPT_V2).toContain("coordinator");
|
|
1356
|
+
expect(SUBTASK_PROMPT_V2).toContain("CRITICAL");
|
|
1351
1357
|
});
|
|
1352
1358
|
});
|
|
1353
1359
|
});
|
package/src/swarm.ts
CHANGED
|
@@ -25,7 +25,12 @@ import {
|
|
|
25
25
|
type SpawnedAgent,
|
|
26
26
|
type Bead,
|
|
27
27
|
} from "./schemas";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
sendSwarmMessage,
|
|
30
|
+
getSwarmInbox,
|
|
31
|
+
readSwarmMessage,
|
|
32
|
+
releaseSwarmFiles,
|
|
33
|
+
} from "./streams/swarm-mail";
|
|
29
34
|
import {
|
|
30
35
|
OutcomeSignalsSchema,
|
|
31
36
|
DecompositionStrategySchema,
|
|
@@ -704,21 +709,64 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
704
709
|
|
|
705
710
|
{error_context}
|
|
706
711
|
|
|
707
|
-
## [
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
+
## [MANDATORY: SWARM MAIL]
|
|
713
|
+
|
|
714
|
+
**YOU MUST USE SWARM MAIL FOR ALL COORDINATION.** This is non-negotiable.
|
|
715
|
+
|
|
716
|
+
### Initialize FIRST (before any work)
|
|
717
|
+
\`\`\`
|
|
718
|
+
swarmmail_init(project_path="$PWD", task_description="{subtask_title}")
|
|
719
|
+
\`\`\`
|
|
720
|
+
|
|
721
|
+
### Reserve Files (if not already reserved by coordinator)
|
|
722
|
+
\`\`\`
|
|
723
|
+
swarmmail_reserve(paths=[...files...], reason="{bead_id}: {subtask_title}")
|
|
724
|
+
\`\`\`
|
|
725
|
+
|
|
726
|
+
### Check Inbox Regularly
|
|
727
|
+
\`\`\`
|
|
728
|
+
swarmmail_inbox() # Check for coordinator messages
|
|
729
|
+
swarmmail_read_message(message_id=N) # Read specific message
|
|
730
|
+
\`\`\`
|
|
731
|
+
|
|
732
|
+
### Report Progress (REQUIRED - don't work silently)
|
|
733
|
+
\`\`\`
|
|
734
|
+
swarmmail_send(
|
|
735
|
+
to=["coordinator"],
|
|
736
|
+
subject="Progress: {bead_id}",
|
|
737
|
+
body="<what you did, blockers, questions>",
|
|
738
|
+
thread_id="{epic_id}"
|
|
739
|
+
)
|
|
740
|
+
\`\`\`
|
|
741
|
+
|
|
742
|
+
### When Blocked
|
|
743
|
+
\`\`\`
|
|
744
|
+
swarmmail_send(
|
|
745
|
+
to=["coordinator"],
|
|
746
|
+
subject="BLOCKED: {bead_id}",
|
|
747
|
+
body="<blocker description, what you need>",
|
|
748
|
+
importance="high",
|
|
749
|
+
thread_id="{epic_id}"
|
|
750
|
+
)
|
|
751
|
+
beads_update(id="{bead_id}", status="blocked")
|
|
752
|
+
\`\`\`
|
|
753
|
+
|
|
754
|
+
### Release Files When Done
|
|
755
|
+
\`\`\`
|
|
756
|
+
swarmmail_release() # Or let swarm_complete handle it
|
|
757
|
+
\`\`\`
|
|
712
758
|
|
|
713
|
-
|
|
714
|
-
|
|
759
|
+
## [OTHER TOOLS]
|
|
760
|
+
### Beads
|
|
761
|
+
- beads_update(id, status) - Mark blocked if stuck
|
|
762
|
+
- beads_create(title, type) - Log new bugs found
|
|
715
763
|
|
|
716
764
|
### Skills (if available)
|
|
717
|
-
- skills_list
|
|
718
|
-
- skills_use
|
|
765
|
+
- skills_list() - Discover available skills
|
|
766
|
+
- skills_use(name) - Activate skill for specialized guidance
|
|
719
767
|
|
|
720
|
-
### Completion
|
|
721
|
-
- swarm_complete
|
|
768
|
+
### Completion (REQUIRED)
|
|
769
|
+
- swarm_complete(project_key, agent_name, bead_id, summary, files_touched)
|
|
722
770
|
|
|
723
771
|
## [LEARNING]
|
|
724
772
|
As you work, note reusable patterns, best practices, or domain insights:
|
|
@@ -727,15 +775,15 @@ As you work, note reusable patterns, best practices, or domain insights:
|
|
|
727
775
|
- Good skills have clear "when to use" descriptions with actionable instructions
|
|
728
776
|
- Skills make swarms smarter over time
|
|
729
777
|
|
|
730
|
-
## [
|
|
731
|
-
1.
|
|
732
|
-
2.
|
|
733
|
-
3.
|
|
734
|
-
4.
|
|
778
|
+
## [WORKFLOW]
|
|
779
|
+
1. **swarmmail_init** - Initialize session FIRST
|
|
780
|
+
2. Read assigned files
|
|
781
|
+
3. Implement changes
|
|
782
|
+
4. **swarmmail_send** - Report progress to coordinator
|
|
783
|
+
5. Verify (typecheck)
|
|
784
|
+
6. **swarm_complete** - Mark done, release reservations
|
|
735
785
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
**Never work silently.** Communicate progress and blockers immediately.
|
|
786
|
+
**CRITICAL: Never work silently. Send progress updates via swarmmail_send every significant milestone.**
|
|
739
787
|
|
|
740
788
|
Begin now.`;
|
|
741
789
|
|
|
@@ -959,15 +1007,19 @@ async function querySwarmMessages(
|
|
|
959
1007
|
}
|
|
960
1008
|
|
|
961
1009
|
try {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
llm_mode: false, // Just need the count
|
|
1010
|
+
// Use embedded swarm-mail inbox to count messages in thread
|
|
1011
|
+
const inbox = await getSwarmInbox({
|
|
1012
|
+
projectPath: projectKey,
|
|
1013
|
+
agentName: "coordinator", // Dummy agent name for thread query
|
|
1014
|
+
limit: 5,
|
|
1015
|
+
includeBodies: false,
|
|
969
1016
|
});
|
|
970
|
-
|
|
1017
|
+
|
|
1018
|
+
// Count messages that match the thread ID
|
|
1019
|
+
const threadMessages = inbox.messages.filter(
|
|
1020
|
+
(m) => m.thread_id === threadId,
|
|
1021
|
+
);
|
|
1022
|
+
return threadMessages.length;
|
|
971
1023
|
} catch (error) {
|
|
972
1024
|
// Thread might not exist yet, or query failed
|
|
973
1025
|
console.warn(
|
|
@@ -1312,9 +1364,10 @@ export const swarm_plan_prompt = tool({
|
|
|
1312
1364
|
|
|
1313
1365
|
// Fetch skills context
|
|
1314
1366
|
let skillsContext = "";
|
|
1315
|
-
let skillsInfo: { included: boolean; count?: number; relevant?: string[] } =
|
|
1316
|
-
|
|
1317
|
-
|
|
1367
|
+
let skillsInfo: { included: boolean; count?: number; relevant?: string[] } =
|
|
1368
|
+
{
|
|
1369
|
+
included: false,
|
|
1370
|
+
};
|
|
1318
1371
|
|
|
1319
1372
|
if (args.include_skills !== false) {
|
|
1320
1373
|
const allSkills = await listSkills();
|
|
@@ -1769,15 +1822,14 @@ export const swarm_progress = tool({
|
|
|
1769
1822
|
? args.bead_id.split(".")[0]
|
|
1770
1823
|
: args.bead_id;
|
|
1771
1824
|
|
|
1772
|
-
// Send progress message to thread
|
|
1773
|
-
await
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
to: [], // Coordinator will pick it up from thread
|
|
1825
|
+
// Send progress message to thread using embedded swarm-mail
|
|
1826
|
+
await sendSwarmMessage({
|
|
1827
|
+
projectPath: args.project_key,
|
|
1828
|
+
fromAgent: args.agent_name,
|
|
1829
|
+
toAgents: [], // Coordinator will pick it up from thread
|
|
1778
1830
|
subject: `Progress: ${args.bead_id} - ${args.status}`,
|
|
1779
|
-
|
|
1780
|
-
|
|
1831
|
+
body: formatProgressMessage(validated),
|
|
1832
|
+
threadId: epicId,
|
|
1781
1833
|
importance: args.status === "blocked" ? "high" : "normal",
|
|
1782
1834
|
});
|
|
1783
1835
|
|
|
@@ -1895,6 +1947,12 @@ export const swarm_broadcast = tool({
|
|
|
1895
1947
|
description:
|
|
1896
1948
|
"Broadcast context update to all agents working on the same epic",
|
|
1897
1949
|
args: {
|
|
1950
|
+
project_path: tool.schema
|
|
1951
|
+
.string()
|
|
1952
|
+
.describe("Absolute path to project root"),
|
|
1953
|
+
agent_name: tool.schema
|
|
1954
|
+
.string()
|
|
1955
|
+
.describe("Name of the agent broadcasting the message"),
|
|
1898
1956
|
epic_id: tool.schema.string().describe("Epic ID (e.g., bd-abc123)"),
|
|
1899
1957
|
message: tool.schema
|
|
1900
1958
|
.string()
|
|
@@ -1909,18 +1967,14 @@ export const swarm_broadcast = tool({
|
|
|
1909
1967
|
.describe("Files this context relates to"),
|
|
1910
1968
|
},
|
|
1911
1969
|
async execute(args, ctx) {
|
|
1912
|
-
// Get agent state - requires prior initialization
|
|
1913
|
-
const state = requireState(ctx.sessionID);
|
|
1914
|
-
|
|
1915
1970
|
// Extract bead_id from context if available (for traceability)
|
|
1916
|
-
// In the swarm flow, ctx might have the current bead being worked on
|
|
1917
1971
|
const beadId = (ctx as { beadId?: string }).beadId || "unknown";
|
|
1918
1972
|
|
|
1919
1973
|
// Format the broadcast message
|
|
1920
1974
|
const body = [
|
|
1921
1975
|
`## Context Update`,
|
|
1922
1976
|
"",
|
|
1923
|
-
`**From**: ${
|
|
1977
|
+
`**From**: ${args.agent_name} (${beadId})`,
|
|
1924
1978
|
`**Priority**: ${args.importance.toUpperCase()}`,
|
|
1925
1979
|
"",
|
|
1926
1980
|
args.message,
|
|
@@ -1940,25 +1994,23 @@ export const swarm_broadcast = tool({
|
|
|
1940
1994
|
? "high"
|
|
1941
1995
|
: "normal";
|
|
1942
1996
|
|
|
1943
|
-
// Send as broadcast to thread
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
body_md: body,
|
|
1952
|
-
thread_id: args.epic_id,
|
|
1997
|
+
// Send as broadcast to thread using embedded swarm-mail
|
|
1998
|
+
await sendSwarmMessage({
|
|
1999
|
+
projectPath: args.project_path,
|
|
2000
|
+
fromAgent: args.agent_name,
|
|
2001
|
+
toAgents: [], // Broadcast to thread
|
|
2002
|
+
subject: `[${args.importance.toUpperCase()}] Context update from ${args.agent_name}`,
|
|
2003
|
+
body,
|
|
2004
|
+
threadId: args.epic_id,
|
|
1953
2005
|
importance: mailImportance,
|
|
1954
|
-
|
|
2006
|
+
ackRequired: args.importance === "blocker",
|
|
1955
2007
|
});
|
|
1956
2008
|
|
|
1957
2009
|
return JSON.stringify(
|
|
1958
2010
|
{
|
|
1959
2011
|
broadcast: true,
|
|
1960
2012
|
epic_id: args.epic_id,
|
|
1961
|
-
from:
|
|
2013
|
+
from: args.agent_name,
|
|
1962
2014
|
bead_id: beadId,
|
|
1963
2015
|
importance: args.importance,
|
|
1964
2016
|
recipients: "all agents in epic",
|
|
@@ -2070,16 +2122,15 @@ export const swarm_complete = tool({
|
|
|
2070
2122
|
);
|
|
2071
2123
|
}
|
|
2072
2124
|
|
|
2073
|
-
// Release file reservations for this agent
|
|
2074
|
-
// Uses auto-reinit wrapper to handle server restarts - this was the original
|
|
2075
|
-
// failure point that prompted the self-healing implementation
|
|
2125
|
+
// Release file reservations for this agent using embedded swarm-mail
|
|
2076
2126
|
try {
|
|
2077
|
-
await
|
|
2078
|
-
|
|
2079
|
-
|
|
2127
|
+
await releaseSwarmFiles({
|
|
2128
|
+
projectPath: args.project_key,
|
|
2129
|
+
agentName: args.agent_name,
|
|
2130
|
+
// Release all reservations for this agent
|
|
2080
2131
|
});
|
|
2081
2132
|
} catch (error) {
|
|
2082
|
-
//
|
|
2133
|
+
// Release might fail (e.g., no reservations existed)
|
|
2083
2134
|
// This is non-fatal - log and continue
|
|
2084
2135
|
console.warn(
|
|
2085
2136
|
`[swarm] Failed to release file reservations for ${args.agent_name}:`,
|
|
@@ -2092,7 +2143,7 @@ export const swarm_complete = tool({
|
|
|
2092
2143
|
? args.bead_id.split(".")[0]
|
|
2093
2144
|
: args.bead_id;
|
|
2094
2145
|
|
|
2095
|
-
// Send completion message
|
|
2146
|
+
// Send completion message using embedded swarm-mail
|
|
2096
2147
|
const completionBody = [
|
|
2097
2148
|
`## Subtask Complete: ${args.bead_id}`,
|
|
2098
2149
|
"",
|
|
@@ -2108,14 +2159,13 @@ export const swarm_complete = tool({
|
|
|
2108
2159
|
.filter(Boolean)
|
|
2109
2160
|
.join("\n");
|
|
2110
2161
|
|
|
2111
|
-
await
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
to: [], // Thread broadcast
|
|
2162
|
+
await sendSwarmMessage({
|
|
2163
|
+
projectPath: args.project_key,
|
|
2164
|
+
fromAgent: args.agent_name,
|
|
2165
|
+
toAgents: [], // Thread broadcast
|
|
2116
2166
|
subject: `Complete: ${args.bead_id}`,
|
|
2117
|
-
|
|
2118
|
-
|
|
2167
|
+
body: completionBody,
|
|
2168
|
+
threadId: epicId,
|
|
2119
2169
|
importance: "normal",
|
|
2120
2170
|
});
|
|
2121
2171
|
|
|
@@ -2650,7 +2700,14 @@ This tool helps you formalize learnings into a skill that future agents can disc
|
|
|
2650
2700
|
.string()
|
|
2651
2701
|
.describe("Brief summary of what was learned (1-2 sentences)"),
|
|
2652
2702
|
pattern_type: tool.schema
|
|
2653
|
-
.enum([
|
|
2703
|
+
.enum([
|
|
2704
|
+
"code-pattern",
|
|
2705
|
+
"best-practice",
|
|
2706
|
+
"gotcha",
|
|
2707
|
+
"tool-usage",
|
|
2708
|
+
"domain-knowledge",
|
|
2709
|
+
"workflow",
|
|
2710
|
+
])
|
|
2654
2711
|
.describe("Category of the learning"),
|
|
2655
2712
|
details: tool.schema
|
|
2656
2713
|
.string()
|
|
@@ -2669,7 +2726,9 @@ This tool helps you formalize learnings into a skill that future agents can disc
|
|
|
2669
2726
|
create_skill: tool.schema
|
|
2670
2727
|
.boolean()
|
|
2671
2728
|
.optional()
|
|
2672
|
-
.describe(
|
|
2729
|
+
.describe(
|
|
2730
|
+
"Create a skill from this learning (default: false, just document)",
|
|
2731
|
+
),
|
|
2673
2732
|
skill_name: tool.schema
|
|
2674
2733
|
.string()
|
|
2675
2734
|
.regex(/^[a-z0-9-]+$/)
|
|
@@ -2737,7 +2796,8 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
|
|
|
2737
2796
|
error: `Skill '${args.skill_name}' already exists`,
|
|
2738
2797
|
existing_path: existing.path,
|
|
2739
2798
|
learning: learning,
|
|
2740
|
-
suggestion:
|
|
2799
|
+
suggestion:
|
|
2800
|
+
"Use skills_update to add to existing skill, or choose a different name",
|
|
2741
2801
|
},
|
|
2742
2802
|
null,
|
|
2743
2803
|
2,
|
|
@@ -2745,7 +2805,12 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
|
|
|
2745
2805
|
}
|
|
2746
2806
|
|
|
2747
2807
|
// Create skill directory and file
|
|
2748
|
-
const skillDir = join(
|
|
2808
|
+
const skillDir = join(
|
|
2809
|
+
process.cwd(),
|
|
2810
|
+
".opencode",
|
|
2811
|
+
"skills",
|
|
2812
|
+
args.skill_name,
|
|
2813
|
+
);
|
|
2749
2814
|
const skillPath = join(skillDir, "SKILL.md");
|
|
2750
2815
|
|
|
2751
2816
|
const frontmatter = [
|
|
@@ -2798,8 +2863,10 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
|
|
|
2798
2863
|
success: true,
|
|
2799
2864
|
skill_created: false,
|
|
2800
2865
|
learning: learning,
|
|
2801
|
-
message:
|
|
2802
|
-
|
|
2866
|
+
message:
|
|
2867
|
+
"Learning documented. Use create_skill=true to persist as a skill for future agents.",
|
|
2868
|
+
suggested_skill_name:
|
|
2869
|
+
args.skill_name ||
|
|
2803
2870
|
args.summary
|
|
2804
2871
|
.toLowerCase()
|
|
2805
2872
|
.replace(/[^a-z0-9\s-]/g, "")
|
|
@@ -3029,7 +3096,8 @@ export const swarm_init = tool({
|
|
|
3029
3096
|
if (availableSkills.length > 0) {
|
|
3030
3097
|
skillsGuidance = `Found ${availableSkills.length} skill(s). Use skills_list to see details, skills_use to activate.`;
|
|
3031
3098
|
} else {
|
|
3032
|
-
skillsGuidance =
|
|
3099
|
+
skillsGuidance =
|
|
3100
|
+
"No skills found. Add skills to .opencode/skills/ or .claude/skills/ for specialized guidance.";
|
|
3033
3101
|
}
|
|
3034
3102
|
|
|
3035
3103
|
return JSON.stringify(
|
package/src/tool-availability.ts
CHANGED
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
* - cass: Cross-agent session search for historical context
|
|
10
10
|
* - ubs: Universal bug scanner for pre-commit checks
|
|
11
11
|
* - beads (bd): Git-backed issue tracking
|
|
12
|
-
* -
|
|
12
|
+
* - swarm-mail: Embedded multi-agent coordination (PGLite-based)
|
|
13
|
+
* - agent-mail: DEPRECATED - Legacy MCP server (use swarm-mail instead)
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
import { checkSwarmHealth } from "./streams/swarm-mail";
|
|
17
|
+
|
|
15
18
|
export type ToolName =
|
|
16
19
|
| "semantic-memory"
|
|
17
20
|
| "cass"
|
|
18
21
|
| "ubs"
|
|
19
22
|
| "beads"
|
|
23
|
+
| "swarm-mail"
|
|
20
24
|
| "agent-mail";
|
|
21
25
|
|
|
22
26
|
export interface ToolStatus {
|
|
@@ -199,6 +203,32 @@ const toolCheckers: Record<ToolName, () => Promise<ToolStatus>> = {
|
|
|
199
203
|
}
|
|
200
204
|
},
|
|
201
205
|
|
|
206
|
+
"swarm-mail": async () => {
|
|
207
|
+
try {
|
|
208
|
+
// Note: checkSwarmHealth() accepts optional projectPath parameter.
|
|
209
|
+
// For tool availability checking, we call it without args to check global health.
|
|
210
|
+
// This is intentional - we're verifying the embedded Swarm Mail system is functional,
|
|
211
|
+
// not checking health for a specific project.
|
|
212
|
+
const healthResult = await checkSwarmHealth();
|
|
213
|
+
return {
|
|
214
|
+
available: healthResult.healthy,
|
|
215
|
+
checkedAt: new Date().toISOString(),
|
|
216
|
+
error: healthResult.healthy
|
|
217
|
+
? undefined
|
|
218
|
+
: "Swarm Mail database not healthy",
|
|
219
|
+
version: "embedded",
|
|
220
|
+
};
|
|
221
|
+
} catch (e) {
|
|
222
|
+
return {
|
|
223
|
+
available: false,
|
|
224
|
+
checkedAt: new Date().toISOString(),
|
|
225
|
+
error: String(e),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
// DEPRECATED: Use swarm-mail instead
|
|
231
|
+
// Kept for backward compatibility only
|
|
202
232
|
"agent-mail": async () => {
|
|
203
233
|
const reachable = await urlReachable(
|
|
204
234
|
"http://127.0.0.1:8765/health/liveness",
|
|
@@ -220,8 +250,10 @@ const fallbackBehaviors: Record<ToolName, string> = {
|
|
|
220
250
|
cass: "Decomposition proceeds without historical context from past sessions",
|
|
221
251
|
ubs: "Subtask completion skips bug scanning - manual review recommended",
|
|
222
252
|
beads: "Swarm cannot track issues - task coordination will be less reliable",
|
|
223
|
-
"
|
|
253
|
+
"swarm-mail":
|
|
224
254
|
"Multi-agent coordination disabled - file conflicts possible if multiple agents active",
|
|
255
|
+
"agent-mail":
|
|
256
|
+
"DEPRECATED: Use swarm-mail instead. Legacy MCP server mode - file conflicts possible if multiple agents active",
|
|
225
257
|
};
|
|
226
258
|
|
|
227
259
|
/**
|
|
@@ -276,6 +308,7 @@ export async function checkAllTools(): Promise<
|
|
|
276
308
|
"cass",
|
|
277
309
|
"ubs",
|
|
278
310
|
"beads",
|
|
311
|
+
"swarm-mail",
|
|
279
312
|
"agent-mail",
|
|
280
313
|
];
|
|
281
314
|
|