swarmkit 0.0.2 → 0.0.4

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.
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ // Mock child_process.execFile
3
+ const mockExecFile = vi.fn();
4
+ vi.mock("node:child_process", () => ({
5
+ execFile: mockExecFile,
6
+ }));
7
+ vi.mock("node:util", async () => {
8
+ const actual = await vi.importActual("node:util");
9
+ return {
10
+ ...actual,
11
+ promisify: () => mockExecFile,
12
+ };
13
+ });
14
+ // Mock fs.existsSync for isInstalledPlugin
15
+ const mockExistsSync = vi.fn();
16
+ vi.mock("node:fs", async () => {
17
+ const actual = await vi.importActual("node:fs");
18
+ return {
19
+ ...actual,
20
+ existsSync: (...args) => mockExistsSync(...args),
21
+ };
22
+ });
23
+ const { isInstalledPlugin, registerPlugin } = await import("./plugin.js");
24
+ describe("plugin", () => {
25
+ beforeEach(() => {
26
+ mockExecFile.mockReset();
27
+ mockExistsSync.mockReset();
28
+ });
29
+ describe("isInstalledPlugin", () => {
30
+ it("returns true when .claude-plugin/plugin.json exists", async () => {
31
+ // getGlobalPackagePath
32
+ mockExecFile.mockResolvedValueOnce({
33
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/claude-code-swarm\n",
34
+ });
35
+ mockExistsSync.mockReturnValue(true);
36
+ const result = await isInstalledPlugin("claude-code-swarm");
37
+ expect(result).toBe(true);
38
+ expect(mockExistsSync).toHaveBeenCalledWith(expect.stringContaining(".claude-plugin/plugin.json"));
39
+ });
40
+ it("returns false when package is not installed", async () => {
41
+ mockExecFile.mockRejectedValueOnce(new Error("not found"));
42
+ const result = await isInstalledPlugin("not-installed");
43
+ expect(result).toBe(false);
44
+ });
45
+ it("returns false when package exists but has no .claude-plugin/", async () => {
46
+ mockExecFile.mockResolvedValueOnce({
47
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/minimem\n",
48
+ });
49
+ mockExistsSync.mockReturnValue(false);
50
+ const result = await isInstalledPlugin("minimem");
51
+ expect(result).toBe(false);
52
+ });
53
+ });
54
+ describe("registerPlugin", () => {
55
+ it("calls claude plugin add with user scope by default", async () => {
56
+ // getGlobalPackagePath
57
+ mockExecFile.mockResolvedValueOnce({
58
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/claude-code-swarm\n",
59
+ });
60
+ // claude plugin add
61
+ mockExecFile.mockResolvedValueOnce({ stdout: "Plugin added\n" });
62
+ await registerPlugin("claude-code-swarm");
63
+ expect(mockExecFile).toHaveBeenCalledWith("claude", ["plugin", "add", "/usr/local/lib/node_modules/claude-code-swarm", "--scope", "user"], { timeout: 30_000 });
64
+ });
65
+ it("passes project scope to claude plugin add", async () => {
66
+ // getGlobalPackagePath
67
+ mockExecFile.mockResolvedValueOnce({
68
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/claude-code-swarm\n",
69
+ });
70
+ // claude plugin add
71
+ mockExecFile.mockResolvedValueOnce({ stdout: "" });
72
+ await registerPlugin("claude-code-swarm", "project");
73
+ expect(mockExecFile).toHaveBeenCalledWith("claude", ["plugin", "add", "/usr/local/lib/node_modules/claude-code-swarm", "--scope", "project"], { timeout: 30_000 });
74
+ });
75
+ it("passes local scope to claude plugin add", async () => {
76
+ // getGlobalPackagePath
77
+ mockExecFile.mockResolvedValueOnce({
78
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/claude-code-swarm\n",
79
+ });
80
+ // claude plugin add
81
+ mockExecFile.mockResolvedValueOnce({ stdout: "" });
82
+ await registerPlugin("claude-code-swarm", "local");
83
+ expect(mockExecFile).toHaveBeenCalledWith("claude", ["plugin", "add", "/usr/local/lib/node_modules/claude-code-swarm", "--scope", "local"], { timeout: 30_000 });
84
+ });
85
+ it("throws when global path cannot be resolved", async () => {
86
+ mockExecFile.mockRejectedValueOnce(new Error("not found"));
87
+ await expect(registerPlugin("not-installed")).rejects.toThrow("Could not resolve global install path");
88
+ });
89
+ it("throws when claude plugin add fails", async () => {
90
+ // getGlobalPackagePath
91
+ mockExecFile.mockResolvedValueOnce({
92
+ stdout: "/usr/local/lib/node_modules\n/usr/local/lib/node_modules/claude-code-swarm\n",
93
+ });
94
+ // claude plugin add fails
95
+ mockExecFile.mockRejectedValueOnce(new Error("registration failed"));
96
+ await expect(registerPlugin("claude-code-swarm")).rejects.toThrow("registration failed");
97
+ });
98
+ });
99
+ });
@@ -6,7 +6,7 @@ export interface PackageDefinition {
6
6
  /** Short description */
7
7
  description: string;
8
8
  /** Layer in the stack */
9
- category: "orchestration" | "protocol" | "tasks" | "security" | "interface" | "learning" | "observability";
9
+ category: "orchestration" | "protocol" | "tasks" | "interface" | "learning" | "observability";
10
10
  /** No per-project config — global only */
11
11
  globalOnly?: boolean;
12
12
  }
@@ -1,9 +1,4 @@
1
1
  export const PACKAGES = {
2
- "macro-agent": {
3
- name: "macro-agent",
4
- description: "Multi-agent orchestration for hierarchical Claude Code agents",
5
- category: "orchestration",
6
- },
7
2
  "self-driving-repo": {
8
3
  name: "self-driving-repo",
9
4
  description: "Event-driven workflow engine for autonomous GitHub SDLC",
@@ -20,22 +15,6 @@ export const PACKAGES = {
20
15
  description: "Cross-system graph for tasks, specs, and relationships",
21
16
  category: "tasks",
22
17
  },
23
- "agent-iam": {
24
- name: "agent-iam",
25
- description: "Capability-based credential broker for AI agents",
26
- category: "security",
27
- globalOnly: true,
28
- },
29
- openswarm: {
30
- name: "openswarm",
31
- description: "Multi-agent terminal UI and MAP server host",
32
- category: "interface",
33
- },
34
- openhive: {
35
- name: "openhive",
36
- description: "Self-hostable social network and coordination hub for agents",
37
- category: "interface",
38
- },
39
18
  "cognitive-core": {
40
19
  name: "cognitive-core",
41
20
  description: "Learning system — trajectories to playbooks to guidance",
@@ -51,17 +30,25 @@ export const PACKAGES = {
51
30
  description: "Versioned skill extraction, evolution, and serving",
52
31
  category: "learning",
53
32
  },
33
+ openhive: {
34
+ name: "openhive",
35
+ description: "Self-hostable social network and coordination hub for agents",
36
+ category: "interface",
37
+ },
54
38
  openteams: {
55
39
  name: "openteams",
56
40
  description: "Team topology definitions and agent role generation",
57
41
  category: "orchestration",
58
- globalOnly: true,
59
42
  },
60
43
  sessionlog: {
61
44
  name: "sessionlog",
62
45
  description: "Git-integrated session capture with rewind and search",
63
46
  category: "observability",
64
- globalOnly: true,
47
+ },
48
+ "claude-code-swarm": {
49
+ name: "claude-code-swarm",
50
+ description: "Claude Code plugin for agent team orchestration with openteams templates",
51
+ category: "orchestration",
65
52
  },
66
53
  };
67
54
  export const BUNDLES = {
@@ -69,33 +56,29 @@ export const BUNDLES = {
69
56
  name: "solo",
70
57
  label: "Local agent development",
71
58
  description: "Single developer running local agents",
72
- packages: ["macro-agent", "opentasks", "minimem", "agent-iam"],
59
+ packages: ["opentasks", "minimem"],
73
60
  },
74
61
  team: {
75
62
  name: "team",
76
63
  label: "Multi-agent team with learning",
77
64
  description: "Agents that learn and share skills across a team",
78
65
  packages: [
79
- "macro-agent",
80
66
  "opentasks",
81
67
  "minimem",
82
- "agent-iam",
83
68
  "cognitive-core",
84
69
  "skill-tree",
85
70
  ],
86
71
  },
87
72
  platform: {
88
73
  name: "platform",
89
- label: "Full self-hosted platform",
90
- description: "Complete platform with UI, agent hub, and learning",
74
+ label: "Full platform with learning and workflows",
75
+ description: "Complete platform with learning, skills, and GitHub automation",
91
76
  packages: [
92
- "macro-agent",
93
77
  "opentasks",
94
78
  "minimem",
95
- "agent-iam",
96
79
  "cognitive-core",
97
80
  "skill-tree",
98
- "openswarm",
81
+ "self-driving-repo",
99
82
  "openhive",
100
83
  ],
101
84
  },
@@ -103,26 +86,10 @@ export const BUNDLES = {
103
86
  name: "github",
104
87
  label: "Autonomous GitHub workflows",
105
88
  description: "Event-driven agents for GitHub SDLC automation",
106
- packages: ["self-driving-repo", "opentasks", "agent-iam"],
89
+ packages: ["self-driving-repo", "opentasks"],
107
90
  },
108
91
  };
109
92
  export const INTEGRATIONS = [
110
- {
111
- packages: ["macro-agent", "opentasks"],
112
- description: "macro-agent uses opentasks as task backend",
113
- },
114
- {
115
- packages: ["macro-agent", "multi-agent-protocol"],
116
- description: "macro-agent implements MAP for agent observation",
117
- },
118
- {
119
- packages: ["openswarm", "macro-agent"],
120
- description: "openswarm's macro-agent plugin provides TUI management",
121
- },
122
- {
123
- packages: ["openhive", "openswarm"],
124
- description: "openhive hosts openswarm instances and acts as MAP Hub",
125
- },
126
93
  {
127
94
  packages: ["cognitive-core", "minimem"],
128
95
  description: "cognitive-core uses minimem as search layer",
@@ -132,8 +99,16 @@ export const INTEGRATIONS = [
132
99
  description: "skill-tree delegates to cognitive-core for batch learning",
133
100
  },
134
101
  {
135
- packages: ["agent-iam", "macro-agent"],
136
- description: "macro-agent uses agent-iam for agent credential management",
102
+ packages: ["claude-code-swarm", "openteams"],
103
+ description: "claude-code-swarm uses openteams for team topology definitions",
104
+ },
105
+ {
106
+ packages: ["claude-code-swarm", "multi-agent-protocol"],
107
+ description: "claude-code-swarm uses MAP for external agent observability",
108
+ },
109
+ {
110
+ packages: ["claude-code-swarm", "sessionlog"],
111
+ description: "claude-code-swarm bridges sessionlog data to MAP via trajectory protocol",
137
112
  },
138
113
  ];
139
114
  /** Get all package names in a bundle */
@@ -2,8 +2,8 @@ import { describe, it, expect } from "vitest";
2
2
  import { PACKAGES, BUNDLES, INTEGRATIONS, getBundlePackages, getActiveIntegrations, getNewIntegrations, getLostIntegrations, getNpmName, isKnownPackage, getAllPackageNames, } from "./registry.js";
3
3
  describe("registry", () => {
4
4
  describe("PACKAGES", () => {
5
- it("contains all 12 packages", () => {
6
- expect(Object.keys(PACKAGES)).toHaveLength(12);
5
+ it("contains all 10 packages", () => {
6
+ expect(Object.keys(PACKAGES)).toHaveLength(10);
7
7
  });
8
8
  it("every package has name, description, and category", () => {
9
9
  for (const [key, pkg] of Object.entries(PACKAGES)) {
@@ -12,23 +12,25 @@ describe("registry", () => {
12
12
  expect(pkg.category).toBeTruthy();
13
13
  }
14
14
  });
15
- it("agent-iam is marked globalOnly", () => {
16
- expect(PACKAGES["agent-iam"].globalOnly).toBe(true);
17
- });
18
- it("openteams and sessionlog are marked globalOnly", () => {
19
- expect(PACKAGES["openteams"].globalOnly).toBe(true);
20
- expect(PACKAGES["sessionlog"].globalOnly).toBe(true);
15
+ it("no packages are marked globalOnly", () => {
16
+ for (const pkg of Object.values(PACKAGES)) {
17
+ expect(pkg.globalOnly).toBeUndefined();
18
+ }
21
19
  });
22
20
  it("multi-agent-protocol has npmName override", () => {
23
21
  expect(PACKAGES["multi-agent-protocol"].npmName).toBe("@multi-agent-protocol/sdk");
24
22
  });
25
23
  it("most packages do not have npmName", () => {
26
- expect(PACKAGES["macro-agent"].npmName).toBeUndefined();
24
+ expect(PACKAGES["opentasks"].npmName).toBeUndefined();
27
25
  expect(PACKAGES["openteams"].npmName).toBeUndefined();
28
26
  });
29
- it("other packages are not globalOnly", () => {
30
- expect(PACKAGES["macro-agent"].globalOnly).toBeUndefined();
31
- expect(PACKAGES["opentasks"].globalOnly).toBeUndefined();
27
+ it("openteams and sessionlog have project-level config", () => {
28
+ expect(PACKAGES["openteams"].globalOnly).toBeUndefined();
29
+ expect(PACKAGES["sessionlog"].globalOnly).toBeUndefined();
30
+ });
31
+ it("claude-code-swarm is an orchestration package", () => {
32
+ expect(PACKAGES["claude-code-swarm"]).toBeDefined();
33
+ expect(PACKAGES["claude-code-swarm"].category).toBe("orchestration");
32
34
  });
33
35
  });
34
36
  describe("BUNDLES", () => {
@@ -38,10 +40,8 @@ describe("registry", () => {
38
40
  });
39
41
  it("solo bundle has the right packages", () => {
40
42
  expect(BUNDLES.solo.packages).toEqual([
41
- "macro-agent",
42
43
  "opentasks",
43
44
  "minimem",
44
- "agent-iam",
45
45
  ]);
46
46
  });
47
47
  it("team bundle is a superset of solo", () => {
@@ -55,13 +55,12 @@ describe("registry", () => {
55
55
  for (const pkg of BUNDLES.team.packages) {
56
56
  expect(BUNDLES.platform.packages).toContain(pkg);
57
57
  }
58
- expect(BUNDLES.platform.packages).toContain("openswarm");
58
+ expect(BUNDLES.platform.packages).toContain("self-driving-repo");
59
59
  expect(BUNDLES.platform.packages).toContain("openhive");
60
60
  });
61
61
  it("github bundle has self-driving-repo", () => {
62
62
  expect(BUNDLES.github.packages).toContain("self-driving-repo");
63
63
  expect(BUNDLES.github.packages).toContain("opentasks");
64
- expect(BUNDLES.github.packages).toContain("agent-iam");
65
64
  });
66
65
  it("all bundle packages reference known packages", () => {
67
66
  for (const bundle of Object.values(BUNDLES)) {
@@ -102,34 +101,24 @@ describe("registry", () => {
102
101
  expect(getActiveIntegrations([])).toEqual([]);
103
102
  });
104
103
  it("returns nothing for single package", () => {
105
- expect(getActiveIntegrations(["macro-agent"])).toEqual([]);
104
+ expect(getActiveIntegrations(["opentasks"])).toEqual([]);
106
105
  });
107
106
  it("returns integration when both packages are installed", () => {
108
- const result = getActiveIntegrations(["macro-agent", "opentasks"]);
107
+ const result = getActiveIntegrations(["cognitive-core", "minimem"]);
109
108
  expect(result).toHaveLength(1);
110
- expect(result[0].packages).toEqual(["macro-agent", "opentasks"]);
111
- });
112
- it("returns multiple integrations for the solo bundle", () => {
113
- const result = getActiveIntegrations(BUNDLES.solo.packages);
114
- expect(result.length).toBeGreaterThanOrEqual(2);
115
- // Should include macro-agent+opentasks and agent-iam+macro-agent
116
- const pairs = result.map((i) => i.packages.join("+"));
117
- expect(pairs).toContain("macro-agent+opentasks");
118
- expect(pairs).toContain("agent-iam+macro-agent");
109
+ expect(result[0].packages).toEqual(["cognitive-core", "minimem"]);
119
110
  });
120
- it("returns all integrations for the platform bundle", () => {
111
+ it("returns multiple integrations for the platform bundle", () => {
121
112
  const result = getActiveIntegrations(BUNDLES.platform.packages);
122
- // platform has all packages except self-driving-repo and multi-agent-protocol
123
- // so it should activate most integrations
124
- expect(result.length).toBeGreaterThanOrEqual(5);
113
+ expect(result.length).toBeGreaterThanOrEqual(2);
125
114
  });
126
115
  });
127
116
  describe("getNewIntegrations", () => {
128
117
  it("returns integrations that would activate with a new package", () => {
129
- const current = ["macro-agent"];
130
- const result = getNewIntegrations(current, "opentasks");
118
+ const current = ["cognitive-core"];
119
+ const result = getNewIntegrations(current, "minimem");
131
120
  expect(result).toHaveLength(1);
132
- expect(result[0].packages).toEqual(["macro-agent", "opentasks"]);
121
+ expect(result[0].packages).toEqual(["cognitive-core", "minimem"]);
133
122
  });
134
123
  it("returns nothing if no new integrations would activate", () => {
135
124
  const current = ["minimem"];
@@ -137,22 +126,22 @@ describe("registry", () => {
137
126
  expect(result).toEqual([]);
138
127
  });
139
128
  it("returns multiple integrations when package connects to several", () => {
140
- const current = ["opentasks", "multi-agent-protocol", "agent-iam"];
141
- const result = getNewIntegrations(current, "macro-agent");
142
- expect(result.length).toBeGreaterThanOrEqual(2);
129
+ const current = ["minimem", "skill-tree"];
130
+ const result = getNewIntegrations(current, "cognitive-core");
131
+ expect(result).toHaveLength(2);
143
132
  });
144
133
  it("works regardless of package order in integration definition", () => {
145
- // openswarm+macro-agent is defined as ["openswarm", "macro-agent"]
146
- const current = ["macro-agent"];
147
- const result = getNewIntegrations(current, "openswarm");
134
+ // cognitive-core+minimem is defined as ["cognitive-core", "minimem"]
135
+ const current = ["minimem"];
136
+ const result = getNewIntegrations(current, "cognitive-core");
148
137
  expect(result).toHaveLength(1);
149
138
  });
150
139
  });
151
140
  describe("getLostIntegrations", () => {
152
141
  it("returns integrations that would break when removing a package", () => {
153
- const current = ["macro-agent", "opentasks", "agent-iam"];
154
- const result = getLostIntegrations(current, "macro-agent");
155
- // Removing macro-agent breaks macro-agent+opentasks and agent-iam+macro-agent
142
+ const current = ["cognitive-core", "minimem", "skill-tree"];
143
+ const result = getLostIntegrations(current, "cognitive-core");
144
+ // Removing cognitive-core breaks cognitive-core+minimem and cognitive-core+skill-tree
156
145
  expect(result).toHaveLength(2);
157
146
  });
158
147
  it("returns nothing if removing a package with no active integrations", () => {
@@ -163,9 +152,9 @@ describe("registry", () => {
163
152
  });
164
153
  describe("isKnownPackage", () => {
165
154
  it("returns true for known packages", () => {
166
- expect(isKnownPackage("macro-agent")).toBe(true);
155
+ expect(isKnownPackage("opentasks")).toBe(true);
167
156
  expect(isKnownPackage("minimem")).toBe(true);
168
- expect(isKnownPackage("agent-iam")).toBe(true);
157
+ expect(isKnownPackage("openhive")).toBe(true);
169
158
  });
170
159
  it("returns false for unknown packages", () => {
171
160
  expect(isKnownPackage("not-a-package")).toBe(false);
@@ -177,7 +166,7 @@ describe("registry", () => {
177
166
  expect(getNpmName("multi-agent-protocol")).toBe("@multi-agent-protocol/sdk");
178
167
  });
179
168
  it("returns registry key when npmName is not set", () => {
180
- expect(getNpmName("macro-agent")).toBe("macro-agent");
169
+ expect(getNpmName("opentasks")).toBe("opentasks");
181
170
  expect(getNpmName("openteams")).toBe("openteams");
182
171
  expect(getNpmName("sessionlog")).toBe("sessionlog");
183
172
  });
@@ -186,14 +175,14 @@ describe("registry", () => {
186
175
  });
187
176
  });
188
177
  describe("getAllPackageNames", () => {
189
- it("returns all 12 package names", () => {
178
+ it("returns all 10 package names", () => {
190
179
  const names = getAllPackageNames();
191
- expect(names).toHaveLength(12);
192
- expect(names).toContain("macro-agent");
180
+ expect(names).toHaveLength(10);
193
181
  expect(names).toContain("minimem");
194
182
  expect(names).toContain("self-driving-repo");
195
183
  expect(names).toContain("openteams");
196
184
  expect(names).toContain("sessionlog");
185
+ expect(names).toContain("claude-code-swarm");
197
186
  });
198
187
  });
199
188
  });
@@ -7,6 +7,8 @@ export interface InitContext {
7
7
  embeddingProvider: "openai" | "gemini" | "local" | null;
8
8
  /** Stored API keys (provider → key) */
9
9
  apiKeys: Record<string, string>;
10
+ /** Whether to nest project configs under .swarm/ (default true) */
11
+ usePrefix: boolean;
10
12
  }
11
13
  export interface GlobalContext {
12
14
  /** All selected packages */
@@ -28,15 +30,20 @@ export interface SetupResult {
28
30
  success: boolean;
29
31
  message?: string;
30
32
  }
31
- /** Config directories each package creates at the project level */
33
+ /** Root directory for all swarmkit project-level config */
34
+ export declare const PROJECT_ROOT = ".swarm";
35
+ /** Config directories with .swarm/ prefix (default layout) */
32
36
  export declare const PROJECT_CONFIG_DIRS: Record<string, string>;
37
+ /** Config directories without prefix (flat layout, --no-prefix) */
38
+ export declare const FLAT_PROJECT_CONFIG_DIRS: Record<string, string>;
39
+ /** Get config dirs for the given mode */
40
+ export declare function projectConfigDirs(usePrefix: boolean): Record<string, string>;
33
41
  /**
34
42
  * Project-level packages in correct init order.
35
- * Order matters: minimem before cognitive-core (runtime detection),
36
- * opentasks before macro-agent (task backend wiring).
43
+ * Order matters: minimem before cognitive-core (runtime detection).
37
44
  */
38
45
  export declare const PROJECT_INIT_ORDER: string[];
39
- /** Check whether a package is already initialized in a project */
46
+ /** Check whether a package is already initialized in a project (checks both layouts) */
40
47
  export declare function isProjectInit(cwd: string, pkg: string): boolean;
41
48
  /** Initialize a single project-level package */
42
49
  export declare function initProjectPackage(pkg: string, ctx: InitContext): Promise<SetupResult>;