swarmkit 0.0.1 → 0.0.3
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/LICENSE +21 -0
- package/README.md +130 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +33 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +98 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +100 -0
- package/dist/commands/hive.d.ts +2 -0
- package/dist/commands/hive.js +248 -0
- package/dist/commands/init/phases/configure.d.ts +2 -0
- package/dist/commands/init/phases/configure.js +85 -0
- package/dist/commands/init/phases/global-setup.d.ts +2 -0
- package/dist/commands/init/phases/global-setup.js +81 -0
- package/dist/commands/init/phases/packages.d.ts +2 -0
- package/dist/commands/init/phases/packages.js +30 -0
- package/dist/commands/init/phases/project.d.ts +2 -0
- package/dist/commands/init/phases/project.js +56 -0
- package/dist/commands/init/phases/use-case.d.ts +2 -0
- package/dist/commands/init/phases/use-case.js +41 -0
- package/dist/commands/init/state.d.ts +13 -0
- package/dist/commands/init/state.js +9 -0
- package/dist/commands/init/state.test.d.ts +1 -0
- package/dist/commands/init/state.test.js +21 -0
- package/dist/commands/init/wizard.d.ts +4 -0
- package/dist/commands/init/wizard.js +108 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +11 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +91 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +19 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.js +55 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +87 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +54 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +40 -0
- package/dist/config/global.d.ts +26 -0
- package/dist/config/global.js +71 -0
- package/dist/config/global.test.d.ts +1 -0
- package/dist/config/global.test.js +167 -0
- package/dist/config/keys.d.ts +10 -0
- package/dist/config/keys.js +47 -0
- package/dist/config/keys.test.d.ts +1 -0
- package/dist/config/keys.test.js +87 -0
- package/dist/doctor/checks.d.ts +31 -0
- package/dist/doctor/checks.js +226 -0
- package/dist/doctor/checks.test.d.ts +1 -0
- package/dist/doctor/checks.test.js +301 -0
- package/dist/doctor/types.d.ts +29 -0
- package/dist/doctor/types.js +1 -0
- package/dist/hub/auth-flow.d.ts +16 -0
- package/dist/hub/auth-flow.js +118 -0
- package/dist/hub/auth-flow.test.d.ts +1 -0
- package/dist/hub/auth-flow.test.js +98 -0
- package/dist/hub/client.d.ts +51 -0
- package/dist/hub/client.js +107 -0
- package/dist/hub/client.test.d.ts +1 -0
- package/dist/hub/client.test.js +177 -0
- package/dist/hub/credentials.d.ts +14 -0
- package/dist/hub/credentials.js +41 -0
- package/dist/hub/credentials.test.d.ts +1 -0
- package/dist/hub/credentials.test.js +102 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +10 -2
- package/dist/packages/installer.d.ts +42 -0
- package/dist/packages/installer.js +158 -0
- package/dist/packages/installer.test.d.ts +1 -0
- package/dist/packages/installer.test.js +283 -0
- package/dist/packages/plugin.d.ts +13 -0
- package/dist/packages/plugin.js +33 -0
- package/dist/packages/plugin.test.d.ts +1 -0
- package/dist/packages/plugin.test.js +99 -0
- package/dist/packages/registry.d.ts +37 -0
- package/dist/packages/registry.js +154 -0
- package/dist/packages/registry.test.d.ts +1 -0
- package/dist/packages/registry.test.js +188 -0
- package/dist/packages/setup.d.ts +55 -0
- package/dist/packages/setup.js +414 -0
- package/dist/packages/setup.test.d.ts +1 -0
- package/dist/packages/setup.test.js +808 -0
- package/dist/utils/ui.d.ts +10 -0
- package/dist/utils/ui.js +47 -0
- package/dist/utils/ui.test.d.ts +1 -0
- package/dist/utils/ui.test.js +102 -0
- package/package.json +29 -6
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface PackageDefinition {
|
|
2
|
+
/** npm package name */
|
|
3
|
+
name: string;
|
|
4
|
+
/** npm package name if different from `name` (e.g. scoped packages) */
|
|
5
|
+
npmName?: string;
|
|
6
|
+
/** Short description */
|
|
7
|
+
description: string;
|
|
8
|
+
/** Layer in the stack */
|
|
9
|
+
category: "orchestration" | "protocol" | "tasks" | "interface" | "learning" | "observability";
|
|
10
|
+
/** No per-project config — global only */
|
|
11
|
+
globalOnly?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface BundleDefinition {
|
|
14
|
+
name: string;
|
|
15
|
+
label: string;
|
|
16
|
+
description: string;
|
|
17
|
+
packages: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface Integration {
|
|
20
|
+
packages: [string, string];
|
|
21
|
+
description: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const PACKAGES: Record<string, PackageDefinition>;
|
|
24
|
+
export declare const BUNDLES: Record<string, BundleDefinition>;
|
|
25
|
+
export declare const INTEGRATIONS: Integration[];
|
|
26
|
+
/** Get all package names in a bundle */
|
|
27
|
+
export declare function getBundlePackages(bundleName: string): string[];
|
|
28
|
+
/** Get active integrations for a set of installed packages */
|
|
29
|
+
export declare function getActiveIntegrations(installedPackages: string[]): Integration[];
|
|
30
|
+
/** Get integrations that would activate if a new package were added */
|
|
31
|
+
export declare function getNewIntegrations(currentPackages: string[], newPackage: string): Integration[];
|
|
32
|
+
/** Get integrations that would break if a package were removed */
|
|
33
|
+
export declare function getLostIntegrations(currentPackages: string[], removedPackage: string): Integration[];
|
|
34
|
+
/** Get the npm package name for a registry key (resolves npmName override) */
|
|
35
|
+
export declare function getNpmName(registryKey: string): string;
|
|
36
|
+
export declare function isKnownPackage(name: string): boolean;
|
|
37
|
+
export declare function getAllPackageNames(): string[];
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const PACKAGES = {
|
|
2
|
+
"self-driving-repo": {
|
|
3
|
+
name: "self-driving-repo",
|
|
4
|
+
description: "Event-driven workflow engine for autonomous GitHub SDLC",
|
|
5
|
+
category: "orchestration",
|
|
6
|
+
},
|
|
7
|
+
"multi-agent-protocol": {
|
|
8
|
+
name: "multi-agent-protocol",
|
|
9
|
+
npmName: "@multi-agent-protocol/sdk",
|
|
10
|
+
description: "JSON-RPC protocol for observing and coordinating agent systems",
|
|
11
|
+
category: "protocol",
|
|
12
|
+
},
|
|
13
|
+
opentasks: {
|
|
14
|
+
name: "opentasks",
|
|
15
|
+
description: "Cross-system graph for tasks, specs, and relationships",
|
|
16
|
+
category: "tasks",
|
|
17
|
+
},
|
|
18
|
+
"cognitive-core": {
|
|
19
|
+
name: "cognitive-core",
|
|
20
|
+
description: "Learning system — trajectories to playbooks to guidance",
|
|
21
|
+
category: "learning",
|
|
22
|
+
},
|
|
23
|
+
minimem: {
|
|
24
|
+
name: "minimem",
|
|
25
|
+
description: "File-based memory with hybrid vector + full-text search",
|
|
26
|
+
category: "learning",
|
|
27
|
+
},
|
|
28
|
+
"skill-tree": {
|
|
29
|
+
name: "skill-tree",
|
|
30
|
+
description: "Versioned skill extraction, evolution, and serving",
|
|
31
|
+
category: "learning",
|
|
32
|
+
},
|
|
33
|
+
openhive: {
|
|
34
|
+
name: "openhive",
|
|
35
|
+
description: "Self-hostable social network and coordination hub for agents",
|
|
36
|
+
category: "interface",
|
|
37
|
+
},
|
|
38
|
+
openteams: {
|
|
39
|
+
name: "openteams",
|
|
40
|
+
description: "Team topology definitions and agent role generation",
|
|
41
|
+
category: "orchestration",
|
|
42
|
+
},
|
|
43
|
+
sessionlog: {
|
|
44
|
+
name: "sessionlog",
|
|
45
|
+
description: "Git-integrated session capture with rewind and search",
|
|
46
|
+
category: "observability",
|
|
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",
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export const BUNDLES = {
|
|
55
|
+
solo: {
|
|
56
|
+
name: "solo",
|
|
57
|
+
label: "Local agent development",
|
|
58
|
+
description: "Single developer running local agents",
|
|
59
|
+
packages: ["opentasks", "minimem"],
|
|
60
|
+
},
|
|
61
|
+
team: {
|
|
62
|
+
name: "team",
|
|
63
|
+
label: "Multi-agent team with learning",
|
|
64
|
+
description: "Agents that learn and share skills across a team",
|
|
65
|
+
packages: [
|
|
66
|
+
"opentasks",
|
|
67
|
+
"minimem",
|
|
68
|
+
"cognitive-core",
|
|
69
|
+
"skill-tree",
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
platform: {
|
|
73
|
+
name: "platform",
|
|
74
|
+
label: "Full platform with learning and workflows",
|
|
75
|
+
description: "Complete platform with learning, skills, and GitHub automation",
|
|
76
|
+
packages: [
|
|
77
|
+
"opentasks",
|
|
78
|
+
"minimem",
|
|
79
|
+
"cognitive-core",
|
|
80
|
+
"skill-tree",
|
|
81
|
+
"self-driving-repo",
|
|
82
|
+
"openhive",
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
github: {
|
|
86
|
+
name: "github",
|
|
87
|
+
label: "Autonomous GitHub workflows",
|
|
88
|
+
description: "Event-driven agents for GitHub SDLC automation",
|
|
89
|
+
packages: ["self-driving-repo", "opentasks"],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
export const INTEGRATIONS = [
|
|
93
|
+
{
|
|
94
|
+
packages: ["cognitive-core", "minimem"],
|
|
95
|
+
description: "cognitive-core uses minimem as search layer",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
packages: ["cognitive-core", "skill-tree"],
|
|
99
|
+
description: "skill-tree delegates to cognitive-core for batch learning",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
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",
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
/** Get all package names in a bundle */
|
|
115
|
+
export function getBundlePackages(bundleName) {
|
|
116
|
+
const bundle = BUNDLES[bundleName];
|
|
117
|
+
if (!bundle)
|
|
118
|
+
return [];
|
|
119
|
+
return [...bundle.packages];
|
|
120
|
+
}
|
|
121
|
+
/** Get active integrations for a set of installed packages */
|
|
122
|
+
export function getActiveIntegrations(installedPackages) {
|
|
123
|
+
const installed = new Set(installedPackages);
|
|
124
|
+
return INTEGRATIONS.filter((i) => installed.has(i.packages[0]) && installed.has(i.packages[1]));
|
|
125
|
+
}
|
|
126
|
+
/** Get integrations that would activate if a new package were added */
|
|
127
|
+
export function getNewIntegrations(currentPackages, newPackage) {
|
|
128
|
+
const current = new Set(currentPackages);
|
|
129
|
+
return INTEGRATIONS.filter((i) => {
|
|
130
|
+
const [a, b] = i.packages;
|
|
131
|
+
return ((a === newPackage && current.has(b)) ||
|
|
132
|
+
(b === newPackage && current.has(a)));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/** Get integrations that would break if a package were removed */
|
|
136
|
+
export function getLostIntegrations(currentPackages, removedPackage) {
|
|
137
|
+
const remaining = new Set(currentPackages.filter((p) => p !== removedPackage));
|
|
138
|
+
return INTEGRATIONS.filter((i) => {
|
|
139
|
+
const [a, b] = i.packages;
|
|
140
|
+
return ((a === removedPackage && remaining.has(b)) ||
|
|
141
|
+
(b === removedPackage && remaining.has(a)));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** Get the npm package name for a registry key (resolves npmName override) */
|
|
145
|
+
export function getNpmName(registryKey) {
|
|
146
|
+
const pkg = PACKAGES[registryKey];
|
|
147
|
+
return pkg?.npmName ?? registryKey;
|
|
148
|
+
}
|
|
149
|
+
export function isKnownPackage(name) {
|
|
150
|
+
return name in PACKAGES;
|
|
151
|
+
}
|
|
152
|
+
export function getAllPackageNames() {
|
|
153
|
+
return Object.keys(PACKAGES);
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { PACKAGES, BUNDLES, INTEGRATIONS, getBundlePackages, getActiveIntegrations, getNewIntegrations, getLostIntegrations, getNpmName, isKnownPackage, getAllPackageNames, } from "./registry.js";
|
|
3
|
+
describe("registry", () => {
|
|
4
|
+
describe("PACKAGES", () => {
|
|
5
|
+
it("contains all 10 packages", () => {
|
|
6
|
+
expect(Object.keys(PACKAGES)).toHaveLength(10);
|
|
7
|
+
});
|
|
8
|
+
it("every package has name, description, and category", () => {
|
|
9
|
+
for (const [key, pkg] of Object.entries(PACKAGES)) {
|
|
10
|
+
expect(pkg.name).toBe(key);
|
|
11
|
+
expect(pkg.description).toBeTruthy();
|
|
12
|
+
expect(pkg.category).toBeTruthy();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
it("no packages are marked globalOnly", () => {
|
|
16
|
+
for (const pkg of Object.values(PACKAGES)) {
|
|
17
|
+
expect(pkg.globalOnly).toBeUndefined();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
it("multi-agent-protocol has npmName override", () => {
|
|
21
|
+
expect(PACKAGES["multi-agent-protocol"].npmName).toBe("@multi-agent-protocol/sdk");
|
|
22
|
+
});
|
|
23
|
+
it("most packages do not have npmName", () => {
|
|
24
|
+
expect(PACKAGES["opentasks"].npmName).toBeUndefined();
|
|
25
|
+
expect(PACKAGES["openteams"].npmName).toBeUndefined();
|
|
26
|
+
});
|
|
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");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("BUNDLES", () => {
|
|
37
|
+
it("contains 4 bundles", () => {
|
|
38
|
+
expect(Object.keys(BUNDLES)).toHaveLength(4);
|
|
39
|
+
expect(Object.keys(BUNDLES)).toEqual(expect.arrayContaining(["solo", "team", "platform", "github"]));
|
|
40
|
+
});
|
|
41
|
+
it("solo bundle has the right packages", () => {
|
|
42
|
+
expect(BUNDLES.solo.packages).toEqual([
|
|
43
|
+
"opentasks",
|
|
44
|
+
"minimem",
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
it("team bundle is a superset of solo", () => {
|
|
48
|
+
for (const pkg of BUNDLES.solo.packages) {
|
|
49
|
+
expect(BUNDLES.team.packages).toContain(pkg);
|
|
50
|
+
}
|
|
51
|
+
expect(BUNDLES.team.packages).toContain("cognitive-core");
|
|
52
|
+
expect(BUNDLES.team.packages).toContain("skill-tree");
|
|
53
|
+
});
|
|
54
|
+
it("platform bundle is a superset of team", () => {
|
|
55
|
+
for (const pkg of BUNDLES.team.packages) {
|
|
56
|
+
expect(BUNDLES.platform.packages).toContain(pkg);
|
|
57
|
+
}
|
|
58
|
+
expect(BUNDLES.platform.packages).toContain("self-driving-repo");
|
|
59
|
+
expect(BUNDLES.platform.packages).toContain("openhive");
|
|
60
|
+
});
|
|
61
|
+
it("github bundle has self-driving-repo", () => {
|
|
62
|
+
expect(BUNDLES.github.packages).toContain("self-driving-repo");
|
|
63
|
+
expect(BUNDLES.github.packages).toContain("opentasks");
|
|
64
|
+
});
|
|
65
|
+
it("all bundle packages reference known packages", () => {
|
|
66
|
+
for (const bundle of Object.values(BUNDLES)) {
|
|
67
|
+
for (const pkg of bundle.packages) {
|
|
68
|
+
expect(PACKAGES[pkg]).toBeDefined();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("INTEGRATIONS", () => {
|
|
74
|
+
it("all integrations reference known packages", () => {
|
|
75
|
+
for (const integration of INTEGRATIONS) {
|
|
76
|
+
expect(PACKAGES[integration.packages[0]]).toBeDefined();
|
|
77
|
+
expect(PACKAGES[integration.packages[1]]).toBeDefined();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it("all integrations have descriptions", () => {
|
|
81
|
+
for (const integration of INTEGRATIONS) {
|
|
82
|
+
expect(integration.description).toBeTruthy();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe("getBundlePackages", () => {
|
|
87
|
+
it("returns packages for a known bundle", () => {
|
|
88
|
+
expect(getBundlePackages("solo")).toEqual(BUNDLES.solo.packages);
|
|
89
|
+
});
|
|
90
|
+
it("returns empty array for unknown bundle", () => {
|
|
91
|
+
expect(getBundlePackages("nonexistent")).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
it("returns a copy (not the original array)", () => {
|
|
94
|
+
const packages = getBundlePackages("solo");
|
|
95
|
+
packages.push("extra");
|
|
96
|
+
expect(getBundlePackages("solo")).not.toContain("extra");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("getActiveIntegrations", () => {
|
|
100
|
+
it("returns nothing for empty package list", () => {
|
|
101
|
+
expect(getActiveIntegrations([])).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
it("returns nothing for single package", () => {
|
|
104
|
+
expect(getActiveIntegrations(["opentasks"])).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
it("returns integration when both packages are installed", () => {
|
|
107
|
+
const result = getActiveIntegrations(["cognitive-core", "minimem"]);
|
|
108
|
+
expect(result).toHaveLength(1);
|
|
109
|
+
expect(result[0].packages).toEqual(["cognitive-core", "minimem"]);
|
|
110
|
+
});
|
|
111
|
+
it("returns multiple integrations for the platform bundle", () => {
|
|
112
|
+
const result = getActiveIntegrations(BUNDLES.platform.packages);
|
|
113
|
+
expect(result.length).toBeGreaterThanOrEqual(2);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("getNewIntegrations", () => {
|
|
117
|
+
it("returns integrations that would activate with a new package", () => {
|
|
118
|
+
const current = ["cognitive-core"];
|
|
119
|
+
const result = getNewIntegrations(current, "minimem");
|
|
120
|
+
expect(result).toHaveLength(1);
|
|
121
|
+
expect(result[0].packages).toEqual(["cognitive-core", "minimem"]);
|
|
122
|
+
});
|
|
123
|
+
it("returns nothing if no new integrations would activate", () => {
|
|
124
|
+
const current = ["minimem"];
|
|
125
|
+
const result = getNewIntegrations(current, "opentasks");
|
|
126
|
+
expect(result).toEqual([]);
|
|
127
|
+
});
|
|
128
|
+
it("returns multiple integrations when package connects to several", () => {
|
|
129
|
+
const current = ["minimem", "skill-tree"];
|
|
130
|
+
const result = getNewIntegrations(current, "cognitive-core");
|
|
131
|
+
expect(result).toHaveLength(2);
|
|
132
|
+
});
|
|
133
|
+
it("works regardless of package order in integration definition", () => {
|
|
134
|
+
// cognitive-core+minimem is defined as ["cognitive-core", "minimem"]
|
|
135
|
+
const current = ["minimem"];
|
|
136
|
+
const result = getNewIntegrations(current, "cognitive-core");
|
|
137
|
+
expect(result).toHaveLength(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe("getLostIntegrations", () => {
|
|
141
|
+
it("returns integrations that would break when removing a package", () => {
|
|
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
|
|
145
|
+
expect(result).toHaveLength(2);
|
|
146
|
+
});
|
|
147
|
+
it("returns nothing if removing a package with no active integrations", () => {
|
|
148
|
+
const current = ["minimem", "opentasks"];
|
|
149
|
+
const result = getLostIntegrations(current, "minimem");
|
|
150
|
+
expect(result).toEqual([]);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe("isKnownPackage", () => {
|
|
154
|
+
it("returns true for known packages", () => {
|
|
155
|
+
expect(isKnownPackage("opentasks")).toBe(true);
|
|
156
|
+
expect(isKnownPackage("minimem")).toBe(true);
|
|
157
|
+
expect(isKnownPackage("openhive")).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
it("returns false for unknown packages", () => {
|
|
160
|
+
expect(isKnownPackage("not-a-package")).toBe(false);
|
|
161
|
+
expect(isKnownPackage("")).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe("getNpmName", () => {
|
|
165
|
+
it("returns npmName when set", () => {
|
|
166
|
+
expect(getNpmName("multi-agent-protocol")).toBe("@multi-agent-protocol/sdk");
|
|
167
|
+
});
|
|
168
|
+
it("returns registry key when npmName is not set", () => {
|
|
169
|
+
expect(getNpmName("opentasks")).toBe("opentasks");
|
|
170
|
+
expect(getNpmName("openteams")).toBe("openteams");
|
|
171
|
+
expect(getNpmName("sessionlog")).toBe("sessionlog");
|
|
172
|
+
});
|
|
173
|
+
it("returns the input for unknown packages", () => {
|
|
174
|
+
expect(getNpmName("not-a-package")).toBe("not-a-package");
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("getAllPackageNames", () => {
|
|
178
|
+
it("returns all 10 package names", () => {
|
|
179
|
+
const names = getAllPackageNames();
|
|
180
|
+
expect(names).toHaveLength(10);
|
|
181
|
+
expect(names).toContain("minimem");
|
|
182
|
+
expect(names).toContain("self-driving-repo");
|
|
183
|
+
expect(names).toContain("openteams");
|
|
184
|
+
expect(names).toContain("sessionlog");
|
|
185
|
+
expect(names).toContain("claude-code-swarm");
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface InitContext {
|
|
2
|
+
/** Project root directory */
|
|
3
|
+
cwd: string;
|
|
4
|
+
/** All selected packages (used for cross-package wiring) */
|
|
5
|
+
packages: string[];
|
|
6
|
+
/** Embedding provider chosen during wizard */
|
|
7
|
+
embeddingProvider: "openai" | "gemini" | "local" | null;
|
|
8
|
+
/** Stored API keys (provider → key) */
|
|
9
|
+
apiKeys: Record<string, string>;
|
|
10
|
+
/** Whether to nest project configs under .swarm/ (default true) */
|
|
11
|
+
usePrefix: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface GlobalContext {
|
|
14
|
+
/** All selected packages */
|
|
15
|
+
packages: string[];
|
|
16
|
+
/** Embedding provider */
|
|
17
|
+
embeddingProvider: "openai" | "gemini" | "local" | null;
|
|
18
|
+
/** Stored API keys */
|
|
19
|
+
apiKeys: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
export interface OpenhiveOptions {
|
|
22
|
+
name: string;
|
|
23
|
+
port: number;
|
|
24
|
+
authMode: string;
|
|
25
|
+
verification: string;
|
|
26
|
+
dataDir?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface SetupResult {
|
|
29
|
+
package: string;
|
|
30
|
+
success: boolean;
|
|
31
|
+
message?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Root directory for all swarmkit project-level config */
|
|
34
|
+
export declare const PROJECT_ROOT = ".swarm";
|
|
35
|
+
/** Config directories with .swarm/ prefix (default layout) */
|
|
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>;
|
|
41
|
+
/**
|
|
42
|
+
* Project-level packages in correct init order.
|
|
43
|
+
* Order matters: minimem before cognitive-core (runtime detection).
|
|
44
|
+
*/
|
|
45
|
+
export declare const PROJECT_INIT_ORDER: string[];
|
|
46
|
+
/** Check whether a package is already initialized in a project (checks both layouts) */
|
|
47
|
+
export declare function isProjectInit(cwd: string, pkg: string): boolean;
|
|
48
|
+
/** Initialize a single project-level package */
|
|
49
|
+
export declare function initProjectPackage(pkg: string, ctx: InitContext): Promise<SetupResult>;
|
|
50
|
+
/** Config directories for global packages (relative to homedir) */
|
|
51
|
+
export declare const GLOBAL_CONFIG_DIRS: Record<string, string>;
|
|
52
|
+
/** Check whether a global package is already configured */
|
|
53
|
+
export declare function isGlobalInit(pkg: string): boolean;
|
|
54
|
+
/** Initialize a single global package */
|
|
55
|
+
export declare function initGlobalPackage(pkg: string, ctx: GlobalContext, openhiveOpts?: OpenhiveOptions): Promise<SetupResult>;
|