opencode-swarm-plugin 0.29.0 → 0.30.0
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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +47 -0
- package/bin/swarm.test.ts +163 -0
- package/bin/swarm.ts +154 -51
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/plugin.js +0 -3
- package/examples/plugin-wrapper-template.ts +157 -28
- package/package.json +1 -1
- package/src/index.ts +3 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
$ bun build ./src/index.ts --outdir ./dist --target node --external @electric-sql/pglite --external swarm-mail && bun build ./src/plugin.ts --outfile ./dist/plugin.js --target node --external @electric-sql/pglite --external swarm-mail && tsc
|
|
2
|
-
Bundled 200 modules in
|
|
2
|
+
Bundled 200 modules in 34ms
|
|
3
3
|
|
|
4
4
|
index.js 1.20 MB (entry point)
|
|
5
5
|
|
|
6
|
-
Bundled 201 modules in
|
|
6
|
+
Bundled 201 modules in 44ms
|
|
7
7
|
|
|
8
8
|
plugin.js 1.16 MB (entry point)
|
|
9
9
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# opencode-swarm-plugin
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`f3917ad`](https://github.com/joelhooks/swarm-tools/commit/f3917ad911d3c716a2470a01c66bce3500f644f4) Thanks [@joelhooks](https://github.com/joelhooks)! - ## 🐝 The Great bd CLI Purge
|
|
8
|
+
|
|
9
|
+
The `bd` CLI is officially dead. Long live HiveAdapter!
|
|
10
|
+
|
|
11
|
+
**What changed:**
|
|
12
|
+
|
|
13
|
+
### `swarm init` Command Rewritten
|
|
14
|
+
|
|
15
|
+
- No longer shells out to `bd init` or `bd create`
|
|
16
|
+
- Uses `ensureHiveDirectory()` and `getHiveAdapter()` directly
|
|
17
|
+
- Supports `.beads` → `.hive` migration with user prompts
|
|
18
|
+
- Creates cells via HiveAdapter, not CLI
|
|
19
|
+
|
|
20
|
+
### Auto-sync Removed from `index.ts`
|
|
21
|
+
|
|
22
|
+
- Removed `void $\`bd sync\`.quiet().nothrow()`after`hive_close`
|
|
23
|
+
- Users should call `hive_sync` explicitly at session end
|
|
24
|
+
- This was a fire-and-forget that could race with other operations
|
|
25
|
+
|
|
26
|
+
### Plugin Template Updated
|
|
27
|
+
|
|
28
|
+
- `detectSwarm()` now has confidence levels (HIGH/MEDIUM/LOW/NONE)
|
|
29
|
+
- Added `SWARM_DETECTION_FALLBACK` for uncertain cases
|
|
30
|
+
- Compaction hook injects context based on confidence:
|
|
31
|
+
- HIGH/MEDIUM → Full swarm context
|
|
32
|
+
- LOW → Fallback detection prompt
|
|
33
|
+
- NONE → No injection
|
|
34
|
+
|
|
35
|
+
### Error Handling Fixed
|
|
36
|
+
|
|
37
|
+
- `execTool()` now handles both string and object error formats
|
|
38
|
+
- Fixes "Tool execution failed" generic error from `swarm_complete`
|
|
39
|
+
- Actual error messages now propagate to the agent
|
|
40
|
+
|
|
41
|
+
**Why it matters:**
|
|
42
|
+
|
|
43
|
+
- No external CLI dependency for core functionality
|
|
44
|
+
- HiveAdapter is type-safe and testable
|
|
45
|
+
- Plugin works in environments without `bd` installed
|
|
46
|
+
- Better error messages for debugging
|
|
47
|
+
|
|
48
|
+
**Migration:** Run `swarm setup` to update your deployed plugin.
|
|
49
|
+
|
|
3
50
|
## 0.29.0
|
|
4
51
|
|
|
5
52
|
### Minor Changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Tests for swarm CLI file operation helpers
|
|
4
|
+
*
|
|
5
|
+
* These tests verify the verbose output helpers used in `swarm setup`:
|
|
6
|
+
* - writeFileWithStatus: logs created/updated/unchanged status
|
|
7
|
+
* - mkdirWithStatus: logs directory creation
|
|
8
|
+
* - rmWithStatus: logs file removal
|
|
9
|
+
*/
|
|
10
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
11
|
+
import { mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { tmpdir } from "os";
|
|
14
|
+
|
|
15
|
+
type FileStatus = "created" | "updated" | "unchanged";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Mock logger for testing (matches @clack/prompts API)
|
|
19
|
+
*/
|
|
20
|
+
class MockLogger {
|
|
21
|
+
logs: Array<{ type: string; message: string }> = [];
|
|
22
|
+
|
|
23
|
+
success(msg: string) {
|
|
24
|
+
this.logs.push({ type: "success", message: msg });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
message(msg: string) {
|
|
28
|
+
this.logs.push({ type: "message", message: msg });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
reset() {
|
|
32
|
+
this.logs = [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("File operation helpers", () => {
|
|
37
|
+
let testDir: string;
|
|
38
|
+
let logger: MockLogger;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
testDir = join(tmpdir(), `swarm-test-${Date.now()}`);
|
|
42
|
+
mkdirSync(testDir, { recursive: true });
|
|
43
|
+
logger = new MockLogger();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
if (existsSync(testDir)) {
|
|
48
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("writeFileWithStatus", () => {
|
|
53
|
+
// Helper that mimics the implementation
|
|
54
|
+
function writeFileWithStatus(path: string, content: string, label: string): FileStatus {
|
|
55
|
+
const exists = existsSync(path);
|
|
56
|
+
|
|
57
|
+
if (exists) {
|
|
58
|
+
const current = readFileSync(path, "utf-8");
|
|
59
|
+
if (current === content) {
|
|
60
|
+
logger.message(` ${label}: ${path} (unchanged)`);
|
|
61
|
+
return "unchanged";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
writeFileSync(path, content);
|
|
66
|
+
const status: FileStatus = exists ? "updated" : "created";
|
|
67
|
+
logger.success(`${label}: ${path} (${status})`);
|
|
68
|
+
return status;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
test("returns 'created' for new file", () => {
|
|
72
|
+
const filePath = join(testDir, "new.txt");
|
|
73
|
+
const result = writeFileWithStatus(filePath, "content", "Test");
|
|
74
|
+
|
|
75
|
+
expect(result).toBe("created");
|
|
76
|
+
expect(logger.logs[0].type).toBe("success");
|
|
77
|
+
expect(logger.logs[0].message).toContain("(created)");
|
|
78
|
+
expect(existsSync(filePath)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("returns 'unchanged' if content is same", () => {
|
|
82
|
+
const filePath = join(testDir, "existing.txt");
|
|
83
|
+
writeFileSync(filePath, "same content");
|
|
84
|
+
|
|
85
|
+
const result = writeFileWithStatus(filePath, "same content", "Test");
|
|
86
|
+
|
|
87
|
+
expect(result).toBe("unchanged");
|
|
88
|
+
expect(logger.logs[0].type).toBe("message");
|
|
89
|
+
expect(logger.logs[0].message).toContain("(unchanged)");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("returns 'updated' if content differs", () => {
|
|
93
|
+
const filePath = join(testDir, "existing.txt");
|
|
94
|
+
writeFileSync(filePath, "old content");
|
|
95
|
+
|
|
96
|
+
const result = writeFileWithStatus(filePath, "new content", "Test");
|
|
97
|
+
|
|
98
|
+
expect(result).toBe("updated");
|
|
99
|
+
expect(logger.logs[0].type).toBe("success");
|
|
100
|
+
expect(logger.logs[0].message).toContain("(updated)");
|
|
101
|
+
expect(readFileSync(filePath, "utf-8")).toBe("new content");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("mkdirWithStatus", () => {
|
|
106
|
+
function mkdirWithStatus(path: string): boolean {
|
|
107
|
+
if (!existsSync(path)) {
|
|
108
|
+
mkdirSync(path, { recursive: true });
|
|
109
|
+
logger.message(` Created directory: ${path}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
test("creates directory and logs when it doesn't exist", () => {
|
|
116
|
+
const dirPath = join(testDir, "newdir");
|
|
117
|
+
const result = mkdirWithStatus(dirPath);
|
|
118
|
+
|
|
119
|
+
expect(result).toBe(true);
|
|
120
|
+
expect(existsSync(dirPath)).toBe(true);
|
|
121
|
+
expect(logger.logs[0].type).toBe("message");
|
|
122
|
+
expect(logger.logs[0].message).toContain("Created directory");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("returns false when directory already exists", () => {
|
|
126
|
+
const dirPath = join(testDir, "existing");
|
|
127
|
+
mkdirSync(dirPath);
|
|
128
|
+
|
|
129
|
+
const result = mkdirWithStatus(dirPath);
|
|
130
|
+
|
|
131
|
+
expect(result).toBe(false);
|
|
132
|
+
expect(logger.logs.length).toBe(0);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("rmWithStatus", () => {
|
|
137
|
+
function rmWithStatus(path: string, label: string): void {
|
|
138
|
+
if (existsSync(path)) {
|
|
139
|
+
rmSync(path);
|
|
140
|
+
logger.message(` Removed ${label}: ${path}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
test("removes file and logs when it exists", () => {
|
|
145
|
+
const filePath = join(testDir, "todelete.txt");
|
|
146
|
+
writeFileSync(filePath, "content");
|
|
147
|
+
|
|
148
|
+
rmWithStatus(filePath, "test file");
|
|
149
|
+
|
|
150
|
+
expect(existsSync(filePath)).toBe(false);
|
|
151
|
+
expect(logger.logs[0].type).toBe("message");
|
|
152
|
+
expect(logger.logs[0].message).toContain("Removed test file");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("does nothing when file doesn't exist", () => {
|
|
156
|
+
const filePath = join(testDir, "nonexistent.txt");
|
|
157
|
+
|
|
158
|
+
rmWithStatus(filePath, "test file");
|
|
159
|
+
|
|
160
|
+
expect(logger.logs.length).toBe(0);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
package/bin/swarm.ts
CHANGED
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
migrateBeadsToHive,
|
|
33
33
|
mergeHistoricBeads,
|
|
34
34
|
importJsonlToPGLite,
|
|
35
|
+
ensureHiveDirectory,
|
|
36
|
+
getHiveAdapter,
|
|
35
37
|
} from "../src/hive";
|
|
36
38
|
|
|
37
39
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -71,6 +73,68 @@ const magenta = (s: string) => `\x1b[35m${s}\x1b[0m`;
|
|
|
71
73
|
|
|
72
74
|
const PACKAGE_NAME = "opencode-swarm-plugin";
|
|
73
75
|
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// File Operation Helpers
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
type FileStatus = "created" | "updated" | "unchanged";
|
|
81
|
+
|
|
82
|
+
interface FileStats {
|
|
83
|
+
created: number;
|
|
84
|
+
updated: number;
|
|
85
|
+
unchanged: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Write a file with status logging (created/updated/unchanged)
|
|
90
|
+
* @param path - File path to write
|
|
91
|
+
* @param content - Content to write
|
|
92
|
+
* @param label - Label for logging (e.g., "Plugin", "Command")
|
|
93
|
+
* @returns Status of the operation
|
|
94
|
+
*/
|
|
95
|
+
function writeFileWithStatus(path: string, content: string, label: string): FileStatus {
|
|
96
|
+
const exists = existsSync(path);
|
|
97
|
+
|
|
98
|
+
if (exists) {
|
|
99
|
+
const current = readFileSync(path, "utf-8");
|
|
100
|
+
if (current === content) {
|
|
101
|
+
p.log.message(dim(` ${label}: ${path} (unchanged)`));
|
|
102
|
+
return "unchanged";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
writeFileSync(path, content);
|
|
107
|
+
const status: FileStatus = exists ? "updated" : "created";
|
|
108
|
+
p.log.success(`${label}: ${path} (${status})`);
|
|
109
|
+
return status;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create a directory with logging
|
|
114
|
+
* @param path - Directory path to create
|
|
115
|
+
* @returns true if created, false if already exists
|
|
116
|
+
*/
|
|
117
|
+
function mkdirWithStatus(path: string): boolean {
|
|
118
|
+
if (!existsSync(path)) {
|
|
119
|
+
mkdirSync(path, { recursive: true });
|
|
120
|
+
p.log.message(dim(` Created directory: ${path}`));
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove a file with logging
|
|
128
|
+
* @param path - File path to remove
|
|
129
|
+
* @param label - Label for logging
|
|
130
|
+
*/
|
|
131
|
+
function rmWithStatus(path: string, label: string): void {
|
|
132
|
+
if (existsSync(path)) {
|
|
133
|
+
rmSync(path);
|
|
134
|
+
p.log.message(dim(` Removed ${label}: ${path}`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
74
138
|
// ============================================================================
|
|
75
139
|
// Seasonal Messages (inspired by Astro's Houston)
|
|
76
140
|
// ============================================================================
|
|
@@ -1707,39 +1771,29 @@ async function setup() {
|
|
|
1707
1771
|
|
|
1708
1772
|
p.log.step("Setting up OpenCode integration...");
|
|
1709
1773
|
|
|
1774
|
+
// Track file operation statistics
|
|
1775
|
+
const stats: FileStats = { created: 0, updated: 0, unchanged: 0 };
|
|
1776
|
+
|
|
1710
1777
|
// Create directories if needed
|
|
1711
1778
|
const skillsDir = join(configDir, "skills");
|
|
1712
1779
|
for (const dir of [pluginDir, commandDir, agentDir, swarmAgentDir, skillsDir]) {
|
|
1713
|
-
|
|
1714
|
-
mkdirSync(dir, { recursive: true });
|
|
1715
|
-
}
|
|
1780
|
+
mkdirWithStatus(dir);
|
|
1716
1781
|
}
|
|
1717
1782
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
writeFileSync(commandPath, SWARM_COMMAND);
|
|
1722
|
-
p.log.success("Command: " + commandPath);
|
|
1783
|
+
// Write plugin and command files
|
|
1784
|
+
stats[writeFileWithStatus(pluginPath, getPluginWrapper(), "Plugin")]++;
|
|
1785
|
+
stats[writeFileWithStatus(commandPath, SWARM_COMMAND, "Command")]++;
|
|
1723
1786
|
|
|
1724
1787
|
// Write nested agent files (swarm/planner.md, swarm/worker.md)
|
|
1725
1788
|
// This is the format used by Task(subagent_type="swarm/worker")
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
writeFileSync(workerAgentPath, getWorkerAgent(workerModel as string));
|
|
1730
|
-
p.log.success("Worker agent: " + workerAgentPath);
|
|
1789
|
+
stats[writeFileWithStatus(plannerAgentPath, getPlannerAgent(coordinatorModel as string), "Planner agent")]++;
|
|
1790
|
+
stats[writeFileWithStatus(workerAgentPath, getWorkerAgent(workerModel as string), "Worker agent")]++;
|
|
1731
1791
|
|
|
1732
1792
|
// Clean up legacy flat agent files if they exist
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
p.log.message(dim(" Removed legacy: " + legacyPlannerPath));
|
|
1736
|
-
}
|
|
1737
|
-
if (existsSync(legacyWorkerPath)) {
|
|
1738
|
-
rmSync(legacyWorkerPath);
|
|
1739
|
-
p.log.message(dim(" Removed legacy: " + legacyWorkerPath));
|
|
1740
|
-
}
|
|
1793
|
+
rmWithStatus(legacyPlannerPath, "legacy planner");
|
|
1794
|
+
rmWithStatus(legacyWorkerPath, "legacy worker");
|
|
1741
1795
|
|
|
1742
|
-
p.log.
|
|
1796
|
+
p.log.message(dim(` Skills directory: ${skillsDir}`));
|
|
1743
1797
|
|
|
1744
1798
|
// Show bundled skills info (and optionally sync to global skills dir)
|
|
1745
1799
|
const bundledSkillsPath = join(__dirname, "..", "global-skills");
|
|
@@ -1850,17 +1904,29 @@ async function setup() {
|
|
|
1850
1904
|
}
|
|
1851
1905
|
}
|
|
1852
1906
|
|
|
1907
|
+
// Show setup summary
|
|
1908
|
+
const totalFiles = stats.created + stats.updated + stats.unchanged;
|
|
1909
|
+
const summaryParts: string[] = [];
|
|
1910
|
+
if (stats.created > 0) summaryParts.push(`${stats.created} created`);
|
|
1911
|
+
if (stats.updated > 0) summaryParts.push(`${stats.updated} updated`);
|
|
1912
|
+
if (stats.unchanged > 0) summaryParts.push(`${stats.unchanged} unchanged`);
|
|
1913
|
+
|
|
1914
|
+
p.log.message("");
|
|
1915
|
+
p.log.success(`Setup complete: ${totalFiles} files (${summaryParts.join(", ")})`);
|
|
1916
|
+
|
|
1853
1917
|
p.note(
|
|
1854
|
-
'cd your-project\
|
|
1918
|
+
'cd your-project\nswarm init\nopencode\n/swarm "your task"\n\nSkills: Use skills_list to see available skills',
|
|
1855
1919
|
"Next steps",
|
|
1856
1920
|
);
|
|
1857
1921
|
|
|
1858
|
-
p.outro("
|
|
1922
|
+
p.outro("Run 'swarm doctor' to verify installation.");
|
|
1859
1923
|
}
|
|
1860
1924
|
|
|
1861
1925
|
async function init() {
|
|
1862
1926
|
p.intro("swarm init v" + VERSION);
|
|
1863
1927
|
|
|
1928
|
+
const projectPath = process.cwd();
|
|
1929
|
+
|
|
1864
1930
|
const gitDir = existsSync(".git");
|
|
1865
1931
|
if (!gitDir) {
|
|
1866
1932
|
p.log.error("Not in a git repository");
|
|
@@ -1869,12 +1935,15 @@ async function init() {
|
|
|
1869
1935
|
process.exit(1);
|
|
1870
1936
|
}
|
|
1871
1937
|
|
|
1938
|
+
// Check for existing .hive or .beads directories
|
|
1939
|
+
const hiveDir = existsSync(".hive");
|
|
1872
1940
|
const beadsDir = existsSync(".beads");
|
|
1873
|
-
|
|
1874
|
-
|
|
1941
|
+
|
|
1942
|
+
if (hiveDir) {
|
|
1943
|
+
p.log.warn("Hive already initialized in this project (.hive/ exists)");
|
|
1875
1944
|
|
|
1876
1945
|
const reinit = await p.confirm({
|
|
1877
|
-
message: "
|
|
1946
|
+
message: "Continue anyway?",
|
|
1878
1947
|
initialValue: false,
|
|
1879
1948
|
});
|
|
1880
1949
|
|
|
@@ -1882,25 +1951,54 @@ async function init() {
|
|
|
1882
1951
|
p.outro("Aborted");
|
|
1883
1952
|
process.exit(0);
|
|
1884
1953
|
}
|
|
1954
|
+
} else if (beadsDir) {
|
|
1955
|
+
// Offer migration from .beads to .hive
|
|
1956
|
+
p.log.warn("Found legacy .beads/ directory");
|
|
1957
|
+
|
|
1958
|
+
const migrate = await p.confirm({
|
|
1959
|
+
message: "Migrate .beads/ to .hive/?",
|
|
1960
|
+
initialValue: true,
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
if (!p.isCancel(migrate) && migrate) {
|
|
1964
|
+
const s = p.spinner();
|
|
1965
|
+
s.start("Migrating .beads/ to .hive/...");
|
|
1966
|
+
|
|
1967
|
+
const result = await migrateBeadsToHive(projectPath);
|
|
1968
|
+
|
|
1969
|
+
if (result.migrated) {
|
|
1970
|
+
s.stop("Migration complete");
|
|
1971
|
+
p.log.success("Renamed .beads/ to .hive/");
|
|
1972
|
+
|
|
1973
|
+
// Merge historic beads if beads.base.jsonl exists
|
|
1974
|
+
const mergeResult = await mergeHistoricBeads(projectPath);
|
|
1975
|
+
if (mergeResult.merged > 0) {
|
|
1976
|
+
p.log.success(`Merged ${mergeResult.merged} historic cells`);
|
|
1977
|
+
}
|
|
1978
|
+
} else {
|
|
1979
|
+
s.stop("Migration skipped: " + result.reason);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1885
1982
|
}
|
|
1886
1983
|
|
|
1887
1984
|
const s = p.spinner();
|
|
1888
|
-
s.start("Initializing
|
|
1889
|
-
|
|
1890
|
-
const success = await runInstall("bd init");
|
|
1985
|
+
s.start("Initializing hive...");
|
|
1891
1986
|
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1987
|
+
try {
|
|
1988
|
+
// Create .hive directory using our function (no bd CLI needed)
|
|
1989
|
+
ensureHiveDirectory(projectPath);
|
|
1990
|
+
|
|
1991
|
+
s.stop("Hive initialized");
|
|
1992
|
+
p.log.success("Created .hive/ directory");
|
|
1895
1993
|
|
|
1896
|
-
const
|
|
1897
|
-
message: "Create your first
|
|
1994
|
+
const createCell = await p.confirm({
|
|
1995
|
+
message: "Create your first cell?",
|
|
1898
1996
|
initialValue: true,
|
|
1899
1997
|
});
|
|
1900
1998
|
|
|
1901
|
-
if (!p.isCancel(
|
|
1999
|
+
if (!p.isCancel(createCell) && createCell) {
|
|
1902
2000
|
const title = await p.text({
|
|
1903
|
-
message: "
|
|
2001
|
+
message: "Cell title:",
|
|
1904
2002
|
placeholder: "Implement user authentication",
|
|
1905
2003
|
validate: (v) => (v.length === 0 ? "Title required" : undefined),
|
|
1906
2004
|
});
|
|
@@ -1917,17 +2015,22 @@ async function init() {
|
|
|
1917
2015
|
});
|
|
1918
2016
|
|
|
1919
2017
|
if (!p.isCancel(typeResult)) {
|
|
1920
|
-
const
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2018
|
+
const cellSpinner = p.spinner();
|
|
2019
|
+
cellSpinner.start("Creating cell...");
|
|
2020
|
+
|
|
2021
|
+
try {
|
|
2022
|
+
// Use HiveAdapter to create the cell (no bd CLI needed)
|
|
2023
|
+
const adapter = await getHiveAdapter(projectPath);
|
|
2024
|
+
const cell = await adapter.createCell(projectPath, {
|
|
2025
|
+
title: title as string,
|
|
2026
|
+
type: typeResult as "feature" | "bug" | "task" | "chore",
|
|
2027
|
+
priority: 2,
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
cellSpinner.stop("Cell created: " + cell.id);
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
cellSpinner.stop("Failed to create cell");
|
|
2033
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1931
2034
|
}
|
|
1932
2035
|
}
|
|
1933
2036
|
}
|
|
@@ -1953,9 +2056,9 @@ async function init() {
|
|
|
1953
2056
|
}
|
|
1954
2057
|
|
|
1955
2058
|
p.outro("Project initialized! Use '/swarm' in OpenCode to get started.");
|
|
1956
|
-
}
|
|
1957
|
-
s.stop("Failed to initialize
|
|
1958
|
-
p.log.error(
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
s.stop("Failed to initialize hive");
|
|
2061
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1959
2062
|
p.outro("Aborted");
|
|
1960
2063
|
process.exit(1);
|
|
1961
2064
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,qBAAqB,CAAC;AAoCtE;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -36161,9 +36161,6 @@ var SwarmPlugin = async (input) => {
|
|
|
36161
36161
|
if (toolName === "swarm_complete" && activeAgentMailState) {
|
|
36162
36162
|
await releaseReservations();
|
|
36163
36163
|
}
|
|
36164
|
-
if (toolName === "hive_close" || toolName === "hive_close") {
|
|
36165
|
-
$`bd sync`.quiet().nothrow();
|
|
36166
|
-
}
|
|
36167
36164
|
}
|
|
36168
36165
|
};
|
|
36169
36166
|
};
|
package/dist/plugin.js
CHANGED
|
@@ -35197,9 +35197,6 @@ var SwarmPlugin = async (input) => {
|
|
|
35197
35197
|
if (toolName === "swarm_complete" && activeAgentMailState) {
|
|
35198
35198
|
await releaseReservations();
|
|
35199
35199
|
}
|
|
35200
|
-
if (toolName === "hive_close" || toolName === "hive_close") {
|
|
35201
|
-
$`bd sync`.quiet().nothrow();
|
|
35202
|
-
}
|
|
35203
35200
|
}
|
|
35204
35201
|
};
|
|
35205
35202
|
};
|
|
@@ -73,7 +73,11 @@ async function execTool(
|
|
|
73
73
|
);
|
|
74
74
|
} else if (!result.success && result.error) {
|
|
75
75
|
// Tool returned an error in JSON format
|
|
76
|
-
|
|
76
|
+
// Handle both string errors and object errors with .message
|
|
77
|
+
const errorMsg = typeof result.error === "string"
|
|
78
|
+
? result.error
|
|
79
|
+
: (result.error.message || "Tool execution failed");
|
|
80
|
+
reject(new Error(errorMsg));
|
|
77
81
|
} else {
|
|
78
82
|
resolve(stdout);
|
|
79
83
|
}
|
|
@@ -89,11 +93,11 @@ async function execTool(
|
|
|
89
93
|
try {
|
|
90
94
|
const result = JSON.parse(stdout);
|
|
91
95
|
if (!result.success && result.error) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
96
|
-
);
|
|
96
|
+
// Handle both string errors and object errors with .message
|
|
97
|
+
const errorMsg = typeof result.error === "string"
|
|
98
|
+
? result.error
|
|
99
|
+
: (result.error.message || `Tool failed with code ${code}`);
|
|
100
|
+
reject(new Error(errorMsg));
|
|
97
101
|
} else {
|
|
98
102
|
reject(
|
|
99
103
|
new Error(stderr || stdout || `Tool failed with code ${code}`),
|
|
@@ -883,15 +887,33 @@ const skills_execute = tool({
|
|
|
883
887
|
// Compaction Hook - Swarm Recovery Context
|
|
884
888
|
// =============================================================================
|
|
885
889
|
|
|
890
|
+
/**
|
|
891
|
+
* Detection result with confidence level
|
|
892
|
+
*/
|
|
893
|
+
interface SwarmDetection {
|
|
894
|
+
detected: boolean;
|
|
895
|
+
confidence: "high" | "medium" | "low" | "none";
|
|
896
|
+
reasons: string[];
|
|
897
|
+
}
|
|
898
|
+
|
|
886
899
|
/**
|
|
887
900
|
* Check for swarm sign - evidence a swarm passed through
|
|
888
901
|
*
|
|
889
|
-
*
|
|
890
|
-
* -
|
|
891
|
-
* - Open
|
|
892
|
-
* -
|
|
902
|
+
* Uses multiple signals with different confidence levels:
|
|
903
|
+
* - HIGH: in_progress cells (active work)
|
|
904
|
+
* - MEDIUM: Open subtasks, unclosed epics, recently updated cells
|
|
905
|
+
* - LOW: Any cells exist
|
|
906
|
+
*
|
|
907
|
+
* Philosophy: Err on the side of continuation.
|
|
908
|
+
* False positive = extra context (low cost)
|
|
909
|
+
* False negative = lost swarm (high cost)
|
|
893
910
|
*/
|
|
894
|
-
async function
|
|
911
|
+
async function detectSwarm(): Promise<SwarmDetection> {
|
|
912
|
+
const reasons: string[] = [];
|
|
913
|
+
let highConfidence = false;
|
|
914
|
+
let mediumConfidence = false;
|
|
915
|
+
let lowConfidence = false;
|
|
916
|
+
|
|
895
917
|
try {
|
|
896
918
|
const result = await new Promise<{ exitCode: number; stdout: string }>(
|
|
897
919
|
(resolve) => {
|
|
@@ -909,24 +931,82 @@ async function hasSwarmSign(): Promise<boolean> {
|
|
|
909
931
|
},
|
|
910
932
|
);
|
|
911
933
|
|
|
912
|
-
if (result.exitCode !== 0)
|
|
934
|
+
if (result.exitCode !== 0) {
|
|
935
|
+
return { detected: false, confidence: "none", reasons: ["hive_query failed"] };
|
|
936
|
+
}
|
|
913
937
|
|
|
914
|
-
const
|
|
915
|
-
if (!Array.isArray(
|
|
938
|
+
const cells = JSON.parse(result.stdout);
|
|
939
|
+
if (!Array.isArray(cells) || cells.length === 0) {
|
|
940
|
+
return { detected: false, confidence: "none", reasons: ["no cells found"] };
|
|
941
|
+
}
|
|
916
942
|
|
|
917
|
-
//
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
943
|
+
// HIGH: Any in_progress cells
|
|
944
|
+
const inProgress = cells.filter(
|
|
945
|
+
(c: { status: string }) => c.status === "in_progress"
|
|
946
|
+
);
|
|
947
|
+
if (inProgress.length > 0) {
|
|
948
|
+
highConfidence = true;
|
|
949
|
+
reasons.push(`${inProgress.length} cells in_progress`);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// MEDIUM: Open subtasks (cells with parent_id)
|
|
953
|
+
const subtasks = cells.filter(
|
|
954
|
+
(c: { status: string; parent_id?: string }) =>
|
|
955
|
+
c.status === "open" && c.parent_id
|
|
956
|
+
);
|
|
957
|
+
if (subtasks.length > 0) {
|
|
958
|
+
mediumConfidence = true;
|
|
959
|
+
reasons.push(`${subtasks.length} open subtasks`);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// MEDIUM: Unclosed epics
|
|
963
|
+
const openEpics = cells.filter(
|
|
964
|
+
(c: { status: string; type?: string }) =>
|
|
965
|
+
c.type === "epic" && c.status !== "closed"
|
|
966
|
+
);
|
|
967
|
+
if (openEpics.length > 0) {
|
|
968
|
+
mediumConfidence = true;
|
|
969
|
+
reasons.push(`${openEpics.length} unclosed epics`);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// MEDIUM: Recently updated cells (last hour)
|
|
973
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
974
|
+
const recentCells = cells.filter(
|
|
975
|
+
(c: { updated_at?: number }) => c.updated_at && c.updated_at > oneHourAgo
|
|
926
976
|
);
|
|
977
|
+
if (recentCells.length > 0) {
|
|
978
|
+
mediumConfidence = true;
|
|
979
|
+
reasons.push(`${recentCells.length} cells updated in last hour`);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// LOW: Any cells exist at all
|
|
983
|
+
if (cells.length > 0) {
|
|
984
|
+
lowConfidence = true;
|
|
985
|
+
reasons.push(`${cells.length} total cells in hive`);
|
|
986
|
+
}
|
|
927
987
|
} catch {
|
|
928
|
-
|
|
988
|
+
// Detection failed, use fallback
|
|
989
|
+
lowConfidence = true;
|
|
990
|
+
reasons.push("Detection error, using fallback");
|
|
929
991
|
}
|
|
992
|
+
|
|
993
|
+
// Determine overall confidence
|
|
994
|
+
let confidence: "high" | "medium" | "low" | "none";
|
|
995
|
+
if (highConfidence) {
|
|
996
|
+
confidence = "high";
|
|
997
|
+
} else if (mediumConfidence) {
|
|
998
|
+
confidence = "medium";
|
|
999
|
+
} else if (lowConfidence) {
|
|
1000
|
+
confidence = "low";
|
|
1001
|
+
} else {
|
|
1002
|
+
confidence = "none";
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return {
|
|
1006
|
+
detected: confidence !== "none",
|
|
1007
|
+
confidence,
|
|
1008
|
+
reasons,
|
|
1009
|
+
};
|
|
930
1010
|
}
|
|
931
1011
|
|
|
932
1012
|
/**
|
|
@@ -992,6 +1072,47 @@ Extract from session context:
|
|
|
992
1072
|
**You are not waiting for instructions. You are the coordinator. Coordinate.**
|
|
993
1073
|
`;
|
|
994
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* Fallback detection prompt - tells the compactor what to look for
|
|
1077
|
+
*
|
|
1078
|
+
* Used when we can't definitively detect a swarm but want to be safe.
|
|
1079
|
+
* The compactor can check the conversation context for these patterns.
|
|
1080
|
+
*/
|
|
1081
|
+
const SWARM_DETECTION_FALLBACK = `## 🐝 Swarm Detection - Check Your Context
|
|
1082
|
+
|
|
1083
|
+
**IMPORTANT:** Before summarizing, check if this session involves an active swarm.
|
|
1084
|
+
|
|
1085
|
+
Look for ANY of these patterns in the conversation:
|
|
1086
|
+
|
|
1087
|
+
### Tool Calls (definite swarm sign)
|
|
1088
|
+
- \`swarm_decompose\`, \`swarm_spawn_subtask\`, \`swarm_status\`, \`swarm_complete\`
|
|
1089
|
+
- \`swarmmail_init\`, \`swarmmail_reserve\`, \`swarmmail_send\`
|
|
1090
|
+
- \`hive_create_epic\`, \`hive_start\`, \`hive_close\`
|
|
1091
|
+
|
|
1092
|
+
### IDs and Names
|
|
1093
|
+
- Cell IDs: \`bd-xxx\`, \`bd-xxx.N\` (subtask format)
|
|
1094
|
+
- Agent names: BlueLake, RedMountain, GreenValley, etc.
|
|
1095
|
+
- Epic references: "epic", "subtask", "parent"
|
|
1096
|
+
|
|
1097
|
+
### Coordination Language
|
|
1098
|
+
- "spawn", "worker", "coordinator"
|
|
1099
|
+
- "reserve", "reservation", "files"
|
|
1100
|
+
- "blocked", "unblock", "dependency"
|
|
1101
|
+
- "progress", "complete", "in_progress"
|
|
1102
|
+
|
|
1103
|
+
### If You Find Swarm Evidence
|
|
1104
|
+
|
|
1105
|
+
Include this in your summary:
|
|
1106
|
+
1. Epic ID and title
|
|
1107
|
+
2. Project path
|
|
1108
|
+
3. Subtask status (running/blocked/done/pending)
|
|
1109
|
+
4. Any blockers or issues
|
|
1110
|
+
5. What should happen next
|
|
1111
|
+
|
|
1112
|
+
**Then tell the resumed session:**
|
|
1113
|
+
"This is an active swarm. Check swarm_status and swarmmail_inbox immediately."
|
|
1114
|
+
`;
|
|
1115
|
+
|
|
995
1116
|
// Extended hooks type to include experimental compaction hook
|
|
996
1117
|
type ExtendedHooks = Hooks & {
|
|
997
1118
|
"experimental.session.compacting"?: (
|
|
@@ -1065,15 +1186,23 @@ export const SwarmPlugin: Plugin = async (
|
|
|
1065
1186
|
skills_execute,
|
|
1066
1187
|
},
|
|
1067
1188
|
|
|
1068
|
-
// Swarm-aware compaction hook -
|
|
1189
|
+
// Swarm-aware compaction hook - injects context based on detection confidence
|
|
1069
1190
|
"experimental.session.compacting": async (
|
|
1070
1191
|
_input: { sessionID: string },
|
|
1071
1192
|
output: { context: string[] },
|
|
1072
1193
|
) => {
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1075
|
-
|
|
1194
|
+
const detection = await detectSwarm();
|
|
1195
|
+
|
|
1196
|
+
if (detection.confidence === "high" || detection.confidence === "medium") {
|
|
1197
|
+
// Definite or probable swarm - inject full context
|
|
1198
|
+
const header = `[Swarm detected: ${detection.reasons.join(", ")}]\n\n`;
|
|
1199
|
+
output.context.push(header + SWARM_COMPACTION_CONTEXT);
|
|
1200
|
+
} else if (detection.confidence === "low") {
|
|
1201
|
+
// Possible swarm - inject fallback detection prompt
|
|
1202
|
+
const header = `[Possible swarm: ${detection.reasons.join(", ")}]\n\n`;
|
|
1203
|
+
output.context.push(header + SWARM_DETECTION_FALLBACK);
|
|
1076
1204
|
}
|
|
1205
|
+
// confidence === "none" - no injection, probably not a swarm
|
|
1077
1206
|
},
|
|
1078
1207
|
};
|
|
1079
1208
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -249,11 +249,9 @@ export const SwarmPlugin: Plugin = async (
|
|
|
249
249
|
await releaseReservations();
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
void $`bd sync`.quiet().nothrow();
|
|
256
|
-
}
|
|
252
|
+
// Note: hive_sync should be called explicitly at session end
|
|
253
|
+
// Auto-sync was removed because bd CLI is deprecated
|
|
254
|
+
// The hive_sync tool handles flushing to JSONL and git commit/push
|
|
257
255
|
},
|
|
258
256
|
};
|
|
259
257
|
};
|