claude-code-swarm 0.3.3 → 0.3.5
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +22 -1
- package/.claude-plugin/run-agent-inbox-mcp.sh +76 -0
- package/.claude-plugin/run-minimem-mcp.sh +98 -0
- package/.claude-plugin/run-opentasks-mcp.sh +65 -0
- package/CLAUDE.md +200 -36
- package/README.md +65 -0
- package/e2e/helpers/cleanup.mjs +17 -3
- package/e2e/helpers/map-mock-server.mjs +201 -25
- package/e2e/helpers/sidecar.mjs +222 -0
- package/e2e/helpers/workspace.mjs +2 -1
- package/e2e/tier5-sidecar-inbox.test.mjs +900 -0
- package/e2e/tier6-inbox-mcp.test.mjs +173 -0
- package/e2e/tier6-live-agent.test.mjs +759 -0
- package/e2e/vitest.config.e2e.mjs +1 -1
- package/hooks/hooks.json +15 -8
- package/package.json +13 -1
- package/references/agent-inbox/CLAUDE.md +151 -0
- package/references/agent-inbox/README.md +238 -0
- package/references/agent-inbox/docs/CLAUDE-CODE-SWARM-PROPOSAL.md +137 -0
- package/references/agent-inbox/docs/DESIGN.md +1156 -0
- package/references/agent-inbox/hooks/inbox-hook.mjs +119 -0
- package/references/agent-inbox/hooks/register-hook.mjs +69 -0
- package/references/agent-inbox/package-lock.json +3347 -0
- package/references/agent-inbox/package.json +58 -0
- package/references/agent-inbox/rules/agent-inbox.md +78 -0
- package/references/agent-inbox/src/federation/address.ts +61 -0
- package/references/agent-inbox/src/federation/connection-manager.ts +573 -0
- package/references/agent-inbox/src/federation/delivery-queue.ts +222 -0
- package/references/agent-inbox/src/federation/index.ts +6 -0
- package/references/agent-inbox/src/federation/routing-engine.ts +188 -0
- package/references/agent-inbox/src/federation/trust.ts +71 -0
- package/references/agent-inbox/src/index.ts +390 -0
- package/references/agent-inbox/src/ipc/ipc-server.ts +207 -0
- package/references/agent-inbox/src/jsonrpc/mail-server.ts +382 -0
- package/references/agent-inbox/src/map/map-client.ts +414 -0
- package/references/agent-inbox/src/mcp/mcp-server.ts +272 -0
- package/references/agent-inbox/src/mesh/delivery-bridge.ts +110 -0
- package/references/agent-inbox/src/mesh/mesh-connector.ts +41 -0
- package/references/agent-inbox/src/mesh/mesh-transport.ts +157 -0
- package/references/agent-inbox/src/mesh/type-mapper.ts +239 -0
- package/references/agent-inbox/src/push/notifier.ts +233 -0
- package/references/agent-inbox/src/registry/warm-registry.ts +255 -0
- package/references/agent-inbox/src/router/message-router.ts +175 -0
- package/references/agent-inbox/src/storage/interface.ts +48 -0
- package/references/agent-inbox/src/storage/memory.ts +145 -0
- package/references/agent-inbox/src/storage/sqlite.ts +671 -0
- package/references/agent-inbox/src/traceability/traceability.ts +183 -0
- package/references/agent-inbox/src/types.ts +303 -0
- package/references/agent-inbox/test/federation/address.test.ts +101 -0
- package/references/agent-inbox/test/federation/connection-manager.test.ts +546 -0
- package/references/agent-inbox/test/federation/delivery-queue.test.ts +159 -0
- package/references/agent-inbox/test/federation/integration.test.ts +857 -0
- package/references/agent-inbox/test/federation/routing-engine.test.ts +117 -0
- package/references/agent-inbox/test/federation/sdk-integration.test.ts +744 -0
- package/references/agent-inbox/test/federation/trust.test.ts +89 -0
- package/references/agent-inbox/test/ipc-jsonrpc.test.ts +113 -0
- package/references/agent-inbox/test/ipc-server.test.ts +197 -0
- package/references/agent-inbox/test/mail-server.test.ts +285 -0
- package/references/agent-inbox/test/map-client.test.ts +408 -0
- package/references/agent-inbox/test/mesh/delivery-bridge.test.ts +178 -0
- package/references/agent-inbox/test/mesh/e2e-mesh.test.ts +527 -0
- package/references/agent-inbox/test/mesh/e2e-real-meshpeer.test.ts +629 -0
- package/references/agent-inbox/test/mesh/federation-mesh.test.ts +269 -0
- package/references/agent-inbox/test/mesh/mesh-connector.test.ts +66 -0
- package/references/agent-inbox/test/mesh/mesh-transport.test.ts +191 -0
- package/references/agent-inbox/test/mesh/meshpeer-integration.test.ts +442 -0
- package/references/agent-inbox/test/mesh/mock-mesh.ts +125 -0
- package/references/agent-inbox/test/mesh/mock-meshpeer.ts +266 -0
- package/references/agent-inbox/test/mesh/type-mapper.test.ts +226 -0
- package/references/agent-inbox/test/message-router.test.ts +184 -0
- package/references/agent-inbox/test/push-notifier.test.ts +139 -0
- package/references/agent-inbox/test/registry/warm-registry.test.ts +171 -0
- package/references/agent-inbox/test/sqlite-prefix.test.ts +192 -0
- package/references/agent-inbox/test/sqlite-storage.test.ts +243 -0
- package/references/agent-inbox/test/storage.test.ts +196 -0
- package/references/agent-inbox/test/traceability.test.ts +123 -0
- package/references/agent-inbox/test/wake.test.ts +330 -0
- package/references/agent-inbox/tsconfig.json +20 -0
- package/references/agent-inbox/tsup.config.ts +10 -0
- package/references/agent-inbox/vitest.config.ts +8 -0
- package/references/minimem/.claude/settings.json +7 -0
- package/references/minimem/.sudocode/issues.jsonl +18 -0
- package/references/minimem/.sudocode/specs.jsonl +1 -0
- package/references/minimem/CLAUDE.md +329 -0
- package/references/minimem/README.md +565 -0
- package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
- package/references/minimem/claude-plugin/.mcp.json +7 -0
- package/references/minimem/claude-plugin/README.md +158 -0
- package/references/minimem/claude-plugin/commands/recall.md +47 -0
- package/references/minimem/claude-plugin/commands/remember.md +41 -0
- package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
- package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
- package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
- package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
- package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
- package/references/minimem/media/banner.png +0 -0
- package/references/minimem/package-lock.json +5373 -0
- package/references/minimem/package.json +76 -0
- package/references/minimem/scripts/postbuild.js +49 -0
- package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
- package/references/minimem/src/__tests__/errors.test.ts +265 -0
- package/references/minimem/src/__tests__/helpers.ts +199 -0
- package/references/minimem/src/__tests__/internal.test.ts +407 -0
- package/references/minimem/src/__tests__/knowledge-frontmatter.test.ts +148 -0
- package/references/minimem/src/__tests__/knowledge.test.ts +148 -0
- package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
- package/references/minimem/src/__tests__/session.test.ts +190 -0
- package/references/minimem/src/cli/__tests__/commands.test.ts +760 -0
- package/references/minimem/src/cli/__tests__/contained-layout.test.ts +286 -0
- package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
- package/references/minimem/src/cli/commands/append.ts +76 -0
- package/references/minimem/src/cli/commands/config.ts +262 -0
- package/references/minimem/src/cli/commands/conflicts.ts +415 -0
- package/references/minimem/src/cli/commands/daemon.ts +169 -0
- package/references/minimem/src/cli/commands/index.ts +12 -0
- package/references/minimem/src/cli/commands/init.ts +166 -0
- package/references/minimem/src/cli/commands/mcp.ts +221 -0
- package/references/minimem/src/cli/commands/push-pull.ts +213 -0
- package/references/minimem/src/cli/commands/search.ts +223 -0
- package/references/minimem/src/cli/commands/status.ts +84 -0
- package/references/minimem/src/cli/commands/store.ts +189 -0
- package/references/minimem/src/cli/commands/sync-init.ts +290 -0
- package/references/minimem/src/cli/commands/sync.ts +70 -0
- package/references/minimem/src/cli/commands/upsert.ts +197 -0
- package/references/minimem/src/cli/config.ts +611 -0
- package/references/minimem/src/cli/index.ts +299 -0
- package/references/minimem/src/cli/shared.ts +189 -0
- package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
- package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
- package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
- package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
- package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
- package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
- package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
- package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
- package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
- package/references/minimem/src/cli/sync/central.ts +292 -0
- package/references/minimem/src/cli/sync/conflicts.ts +205 -0
- package/references/minimem/src/cli/sync/daemon.ts +407 -0
- package/references/minimem/src/cli/sync/detection.ts +138 -0
- package/references/minimem/src/cli/sync/index.ts +107 -0
- package/references/minimem/src/cli/sync/operations.ts +373 -0
- package/references/minimem/src/cli/sync/registry.ts +279 -0
- package/references/minimem/src/cli/sync/state.ts +358 -0
- package/references/minimem/src/cli/sync/validation.ts +206 -0
- package/references/minimem/src/cli/sync/watcher.ts +237 -0
- package/references/minimem/src/cli/version.ts +34 -0
- package/references/minimem/src/core/index.ts +9 -0
- package/references/minimem/src/core/indexer.ts +628 -0
- package/references/minimem/src/core/searcher.ts +221 -0
- package/references/minimem/src/db/schema.ts +183 -0
- package/references/minimem/src/db/sqlite-vec.ts +24 -0
- package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
- package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
- package/references/minimem/src/embeddings/batch-openai.ts +409 -0
- package/references/minimem/src/embeddings/embeddings.ts +434 -0
- package/references/minimem/src/index.ts +132 -0
- package/references/minimem/src/internal.ts +299 -0
- package/references/minimem/src/minimem.ts +1291 -0
- package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
- package/references/minimem/src/search/graph.ts +234 -0
- package/references/minimem/src/search/hybrid.ts +151 -0
- package/references/minimem/src/search/search.ts +256 -0
- package/references/minimem/src/server/__tests__/mcp.test.ts +347 -0
- package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
- package/references/minimem/src/server/mcp.ts +326 -0
- package/references/minimem/src/server/tools.ts +720 -0
- package/references/minimem/src/session.ts +460 -0
- package/references/minimem/src/store/__tests__/manifest.test.ts +177 -0
- package/references/minimem/src/store/__tests__/materialize.test.ts +52 -0
- package/references/minimem/src/store/__tests__/store-graph.test.ts +228 -0
- package/references/minimem/src/store/index.ts +27 -0
- package/references/minimem/src/store/manifest.ts +203 -0
- package/references/minimem/src/store/materialize.ts +185 -0
- package/references/minimem/src/store/store-graph.ts +252 -0
- package/references/minimem/tsconfig.json +19 -0
- package/references/minimem/tsup.config.ts +26 -0
- package/references/minimem/vitest.config.ts +29 -0
- package/references/openteams/src/cli/generate.ts +23 -1
- package/references/openteams/src/generators/agent-prompt-generator.test.ts +94 -0
- package/references/openteams/src/generators/agent-prompt-generator.ts +42 -13
- package/references/openteams/src/generators/package-generator.ts +9 -1
- package/references/openteams/src/generators/skill-generator.test.ts +28 -0
- package/references/openteams/src/generators/skill-generator.ts +10 -4
- package/references/skill-tree/.claude/settings.json +6 -0
- package/references/skill-tree/.sudocode/issues.jsonl +19 -0
- package/references/skill-tree/.sudocode/specs.jsonl +3 -0
- package/references/skill-tree/CLAUDE.md +132 -0
- package/references/skill-tree/README.md +396 -0
- package/references/skill-tree/docs/GAPS_v1.md +221 -0
- package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
- package/references/skill-tree/docs/TODOS.md +91 -0
- package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
- package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
- package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
- package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
- package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
- package/references/skill-tree/docs/scraper/README.md +170 -0
- package/references/skill-tree/examples/basic-usage.ts +157 -0
- package/references/skill-tree/package-lock.json +1852 -0
- package/references/skill-tree/package.json +66 -0
- package/references/skill-tree/plan.md +78 -0
- package/references/skill-tree/scraper/README.md +123 -0
- package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
- package/references/skill-tree/scraper/docs/PLAN.md +336 -0
- package/references/skill-tree/scraper/drizzle.config.ts +10 -0
- package/references/skill-tree/scraper/package-lock.json +6329 -0
- package/references/skill-tree/scraper/package.json +68 -0
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
- package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
- package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
- package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
- package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
- package/references/skill-tree/scraper/tsup.config.ts +14 -0
- package/references/skill-tree/scraper/vitest.config.ts +17 -0
- package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
- package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
- package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
- package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
- package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
- package/references/skill-tree/test/run-all.ts +106 -0
- package/references/skill-tree/test/utils.ts +128 -0
- package/references/skill-tree/vitest.config.ts +16 -0
- package/references/swarmkit/src/commands/init/phases/configure.ts +0 -22
- package/references/swarmkit/src/commands/init/phases/global-setup.ts +5 -3
- package/references/swarmkit/src/commands/init/wizard.ts +2 -2
- package/references/swarmkit/src/packages/setup.test.ts +53 -7
- package/references/swarmkit/src/packages/setup.ts +37 -1
- package/scripts/bootstrap.mjs +26 -1
- package/scripts/generate-agents.mjs +5 -1
- package/scripts/map-hook.mjs +97 -64
- package/scripts/map-sidecar.mjs +179 -25
- package/scripts/team-loader.mjs +12 -41
- package/skills/swarm/SKILL.md +89 -25
- package/src/__tests__/agent-generator.test.mjs +6 -13
- package/src/__tests__/bootstrap.test.mjs +124 -1
- package/src/__tests__/config.test.mjs +200 -27
- package/src/__tests__/e2e-live-map.test.mjs +536 -0
- package/src/__tests__/e2e-mesh-sidecar.test.mjs +570 -0
- package/src/__tests__/e2e-native-task-hooks.test.mjs +376 -0
- package/src/__tests__/e2e-sidecar-bridge.test.mjs +477 -0
- package/src/__tests__/helpers.mjs +13 -0
- package/src/__tests__/inbox.test.mjs +22 -89
- package/src/__tests__/index.test.mjs +35 -9
- package/src/__tests__/integration.test.mjs +513 -0
- package/src/__tests__/map-events.test.mjs +514 -150
- package/src/__tests__/mesh-connection.test.mjs +308 -0
- package/src/__tests__/opentasks-client.test.mjs +517 -0
- package/src/__tests__/paths.test.mjs +185 -41
- package/src/__tests__/sidecar-client.test.mjs +35 -0
- package/src/__tests__/sidecar-server.test.mjs +124 -0
- package/src/__tests__/skilltree-client.test.mjs +80 -0
- package/src/agent-generator.mjs +104 -33
- package/src/bootstrap.mjs +150 -10
- package/src/config.mjs +81 -17
- package/src/context-output.mjs +58 -8
- package/src/inbox.mjs +9 -54
- package/src/index.mjs +39 -8
- package/src/map-connection.mjs +4 -3
- package/src/map-events.mjs +350 -80
- package/src/mesh-connection.mjs +148 -0
- package/src/opentasks-client.mjs +269 -0
- package/src/paths.mjs +182 -27
- package/src/sessionlog.mjs +14 -9
- package/src/sidecar-client.mjs +81 -27
- package/src/sidecar-server.mjs +175 -16
- package/src/skilltree-client.mjs +173 -0
- package/src/template.mjs +68 -4
- package/vitest.config.mjs +1 -0
|
@@ -1,70 +1,214 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import os from "os";
|
|
4
|
+
|
|
5
|
+
// We need to test path resolution with different CWD states.
|
|
6
|
+
// Since paths.mjs computes values at module load time, we test the
|
|
7
|
+
// exported values AND mock fs.existsSync to test resolution logic.
|
|
8
|
+
|
|
9
|
+
describe("paths (current environment)", () => {
|
|
10
|
+
// These tests verify the exports work correctly in the test environment.
|
|
11
|
+
// The actual resolution depends on whether .swarm/claude-swarm/ exists in CWD.
|
|
12
|
+
|
|
13
|
+
let paths;
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
// Fresh import each time (vitest module cache may apply)
|
|
17
|
+
paths = await import("../paths.mjs");
|
|
18
|
+
});
|
|
8
19
|
|
|
9
|
-
describe("paths", () => {
|
|
10
20
|
it("exports all expected path constants", () => {
|
|
11
|
-
expect(typeof SWARM_DIR).toBe("string");
|
|
12
|
-
expect(typeof CONFIG_PATH).toBe("string");
|
|
13
|
-
expect(typeof TMP_DIR).toBe("string");
|
|
14
|
-
expect(typeof TEAMS_DIR).toBe("string");
|
|
15
|
-
expect(typeof MAP_DIR).toBe("string");
|
|
16
|
-
expect(typeof SOCKET_PATH).toBe("string");
|
|
17
|
-
expect(typeof
|
|
18
|
-
expect(typeof
|
|
19
|
-
expect(typeof
|
|
21
|
+
expect(typeof paths.SWARM_DIR).toBe("string");
|
|
22
|
+
expect(typeof paths.CONFIG_PATH).toBe("string");
|
|
23
|
+
expect(typeof paths.TMP_DIR).toBe("string");
|
|
24
|
+
expect(typeof paths.TEAMS_DIR).toBe("string");
|
|
25
|
+
expect(typeof paths.MAP_DIR).toBe("string");
|
|
26
|
+
expect(typeof paths.SOCKET_PATH).toBe("string");
|
|
27
|
+
expect(typeof paths.PID_PATH).toBe("string");
|
|
28
|
+
expect(typeof paths.ROLES_PATH).toBe("string");
|
|
29
|
+
expect(typeof paths.IS_GLOBAL_PATHS).toBe("boolean");
|
|
20
30
|
});
|
|
21
31
|
|
|
22
|
-
it("
|
|
23
|
-
expect(CONFIG_PATH).
|
|
24
|
-
expect(TMP_DIR).toMatch(/^\.swarm\/claude-swarm\//);
|
|
25
|
-
expect(TEAMS_DIR).toMatch(/^\.swarm\/claude-swarm\//);
|
|
26
|
-
expect(MAP_DIR).toMatch(/^\.swarm\/claude-swarm\//);
|
|
27
|
-
expect(SOCKET_PATH).toMatch(/^\.swarm\/claude-swarm\//);
|
|
28
|
-
expect(INBOX_PATH).toMatch(/^\.swarm\/claude-swarm\//);
|
|
29
|
-
expect(PID_PATH).toMatch(/^\.swarm\/claude-swarm\//);
|
|
30
|
-
expect(ROLES_PATH).toMatch(/^\.swarm\/claude-swarm\//);
|
|
32
|
+
it("CONFIG_PATH is always project-relative", () => {
|
|
33
|
+
expect(paths.CONFIG_PATH).toBe(".swarm/claude-swarm/config.json");
|
|
31
34
|
});
|
|
32
35
|
|
|
33
|
-
it("
|
|
34
|
-
expect(
|
|
36
|
+
it("SWARM_DIR is always project-relative", () => {
|
|
37
|
+
expect(paths.SWARM_DIR).toBe(".swarm/claude-swarm");
|
|
35
38
|
});
|
|
36
39
|
|
|
37
|
-
it("
|
|
38
|
-
expect(
|
|
40
|
+
it("GLOBAL_CONFIG_DIR is under ~/.claude-swarm/", () => {
|
|
41
|
+
expect(paths.GLOBAL_CONFIG_DIR).toBe(path.join(os.homedir(), ".claude-swarm"));
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
it("
|
|
42
|
-
expect(
|
|
44
|
+
it("GLOBAL_CONFIG_PATH is config.json under GLOBAL_CONFIG_DIR", () => {
|
|
45
|
+
expect(paths.GLOBAL_CONFIG_PATH).toBe(path.join(os.homedir(), ".claude-swarm", "config.json"));
|
|
43
46
|
});
|
|
44
47
|
|
|
45
|
-
it("
|
|
46
|
-
expect(
|
|
48
|
+
it("TEAMS_DIR is under TMP_DIR", () => {
|
|
49
|
+
expect(paths.TEAMS_DIR).toBe(path.join(paths.TMP_DIR, "teams"));
|
|
47
50
|
});
|
|
48
51
|
|
|
49
|
-
it("MAP runtime files are under
|
|
50
|
-
expect(SOCKET_PATH).
|
|
51
|
-
expect(
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
52
|
+
it("MAP runtime files are under MAP_DIR", () => {
|
|
53
|
+
expect(paths.SOCKET_PATH).toBe(path.join(paths.MAP_DIR, "sidecar.sock"));
|
|
54
|
+
expect(paths.PID_PATH).toBe(path.join(paths.MAP_DIR, "sidecar.pid"));
|
|
55
|
+
expect(paths.ROLES_PATH).toBe(path.join(paths.MAP_DIR, "roles.json"));
|
|
56
|
+
expect(paths.SESSIONLOG_STATE_PATH).toBe(path.join(paths.MAP_DIR, "sessionlog-state.json"));
|
|
57
|
+
expect(paths.SIDECAR_LOG_PATH).toBe(path.join(paths.MAP_DIR, "sidecar.log"));
|
|
54
58
|
});
|
|
55
59
|
|
|
56
60
|
describe("teamDir", () => {
|
|
57
61
|
it("returns per-template path under TEAMS_DIR", () => {
|
|
58
|
-
expect(teamDir("gsd")).toBe("
|
|
59
|
-
expect(teamDir("bmad-method")).toBe("
|
|
62
|
+
expect(paths.teamDir("gsd")).toBe(path.join(paths.TEAMS_DIR, "gsd"));
|
|
63
|
+
expect(paths.teamDir("bmad-method")).toBe(path.join(paths.TEAMS_DIR, "bmad-method"));
|
|
60
64
|
});
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
describe("pluginDir", () => {
|
|
64
68
|
it("resolves to the repository root (parent of src/)", () => {
|
|
65
|
-
const dir = pluginDir();
|
|
69
|
+
const dir = paths.pluginDir();
|
|
66
70
|
expect(dir).toContain("claude-code-swarm");
|
|
67
71
|
expect(dir).not.toContain("src");
|
|
68
72
|
});
|
|
69
73
|
});
|
|
70
74
|
});
|
|
75
|
+
|
|
76
|
+
describe("path resolution logic", () => {
|
|
77
|
+
// These tests verify the resolution rules by checking the relationship
|
|
78
|
+
// between IS_GLOBAL_PATHS and the computed paths.
|
|
79
|
+
|
|
80
|
+
let paths;
|
|
81
|
+
|
|
82
|
+
beforeEach(async () => {
|
|
83
|
+
paths = await import("../paths.mjs");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const globalBase = path.join(os.homedir(), ".claude", "claude-swarm");
|
|
87
|
+
|
|
88
|
+
it("when global: TMP_DIR is under ~/.claude/claude-swarm/tmp/", () => {
|
|
89
|
+
if (!paths.IS_GLOBAL_PATHS) return; // skip if project-level
|
|
90
|
+
expect(paths.TMP_DIR).toBe(path.join(globalBase, "tmp"));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("when global: MAP_DIR includes CWD hash for isolation", () => {
|
|
94
|
+
if (!paths.IS_GLOBAL_PATHS) return;
|
|
95
|
+
// MAP_DIR should be: ~/.claude/claude-swarm/tmp/map/<12-char-hash>
|
|
96
|
+
const mapRelative = path.relative(path.join(globalBase, "tmp", "map"), paths.MAP_DIR);
|
|
97
|
+
expect(mapRelative).toMatch(/^[a-f0-9]{12}$/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("when global: TEAMS_DIR is shared (no CWD hash)", () => {
|
|
101
|
+
if (!paths.IS_GLOBAL_PATHS) return;
|
|
102
|
+
expect(paths.TEAMS_DIR).toBe(path.join(globalBase, "tmp", "teams"));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("when project-level: TMP_DIR is under .swarm/claude-swarm/tmp", () => {
|
|
106
|
+
if (paths.IS_GLOBAL_PATHS) return; // skip if global
|
|
107
|
+
expect(paths.TMP_DIR).toBe(".swarm/claude-swarm/tmp");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("when project-level: MAP_DIR has no CWD hash", () => {
|
|
111
|
+
if (paths.IS_GLOBAL_PATHS) return;
|
|
112
|
+
expect(paths.MAP_DIR).toBe(".swarm/claude-swarm/tmp/map");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("when project-level: TEAMS_DIR is under project tmp", () => {
|
|
116
|
+
if (paths.IS_GLOBAL_PATHS) return;
|
|
117
|
+
expect(paths.TEAMS_DIR).toBe(".swarm/claude-swarm/tmp/teams");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("sessionPaths", () => {
|
|
122
|
+
let paths;
|
|
123
|
+
|
|
124
|
+
beforeEach(async () => {
|
|
125
|
+
paths = await import("../paths.mjs");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("returns legacy paths when sessionId is null", () => {
|
|
129
|
+
const sp = paths.sessionPaths(null);
|
|
130
|
+
expect(sp.socketPath).toBe(paths.SOCKET_PATH);
|
|
131
|
+
expect(sp.pidPath).toBe(paths.PID_PATH);
|
|
132
|
+
expect(sp.sidecarLogPath).toBe(paths.SIDECAR_LOG_PATH);
|
|
133
|
+
expect(sp.sessionDir).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("returns legacy paths when sessionId is undefined", () => {
|
|
137
|
+
const sp = paths.sessionPaths(undefined);
|
|
138
|
+
expect(sp.socketPath).toBe(paths.SOCKET_PATH);
|
|
139
|
+
expect(sp.sessionDir).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("returns legacy paths when sessionId is empty string", () => {
|
|
143
|
+
const sp = paths.sessionPaths("");
|
|
144
|
+
expect(sp.socketPath).toBe(paths.SOCKET_PATH);
|
|
145
|
+
expect(sp.sessionDir).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("returns session-scoped paths when sessionId is provided", () => {
|
|
149
|
+
const sp = paths.sessionPaths("abc123");
|
|
150
|
+
expect(sp.sessionDir).toBe(path.join(paths.MAP_DIR, "sessions", "abc123"));
|
|
151
|
+
expect(sp.socketPath).toBe(path.join(paths.MAP_DIR, "sessions", "abc123", "sidecar.sock"));
|
|
152
|
+
expect(sp.pidPath).toBe(path.join(paths.MAP_DIR, "sessions", "abc123", "sidecar.pid"));
|
|
153
|
+
expect(sp.sidecarLogPath).toBe(path.join(paths.MAP_DIR, "sessions", "abc123", "sidecar.log"));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("hashes long session IDs (>12 chars) to 12 hex chars", () => {
|
|
157
|
+
const longId = "this-is-a-very-long-session-id-that-exceeds-12-chars";
|
|
158
|
+
const sp = paths.sessionPaths(longId);
|
|
159
|
+
const sessionDirName = path.basename(sp.sessionDir);
|
|
160
|
+
expect(sessionDirName).toMatch(/^[a-f0-9]{12}$/);
|
|
161
|
+
expect(sessionDirName.length).toBe(12);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("uses short session IDs directly (<=12 chars)", () => {
|
|
165
|
+
const sp = paths.sessionPaths("short-id");
|
|
166
|
+
const sessionDirName = path.basename(sp.sessionDir);
|
|
167
|
+
expect(sessionDirName).toBe("short-id");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("produces consistent hashes for the same long ID", () => {
|
|
171
|
+
const longId = "a-very-long-session-identifier-here";
|
|
172
|
+
const sp1 = paths.sessionPaths(longId);
|
|
173
|
+
const sp2 = paths.sessionPaths(longId);
|
|
174
|
+
expect(sp1.sessionDir).toBe(sp2.sessionDir);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("ensureSessionDir", () => {
|
|
179
|
+
let paths;
|
|
180
|
+
let fs;
|
|
181
|
+
let tmpDir;
|
|
182
|
+
|
|
183
|
+
beforeEach(async () => {
|
|
184
|
+
paths = await import("../paths.mjs");
|
|
185
|
+
fs = await import("fs");
|
|
186
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "session-dir-test-"));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
afterEach(async () => {
|
|
190
|
+
const fs = await import("fs");
|
|
191
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("is exported as a function", () => {
|
|
195
|
+
expect(typeof paths.ensureSessionDir).toBe("function");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("listSessionDirs", () => {
|
|
200
|
+
let paths;
|
|
201
|
+
|
|
202
|
+
beforeEach(async () => {
|
|
203
|
+
paths = await import("../paths.mjs");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("returns empty array when no sessions directory exists", () => {
|
|
207
|
+
const result = paths.listSessionDirs();
|
|
208
|
+
expect(Array.isArray(result)).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("is exported as a function", () => {
|
|
212
|
+
expect(typeof paths.listSessionDirs).toBe("function");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -65,9 +65,44 @@ describe("sidecar-client", () => {
|
|
|
65
65
|
});
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
describe("isSidecarAlive with custom pidPath", () => {
|
|
69
|
+
let tmpDir;
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
tmpDir = makeTmpDir();
|
|
73
|
+
});
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
cleanupTmpDir(tmpDir);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("returns true when PID file contains current process PID", () => {
|
|
79
|
+
const pidPath = path.join(tmpDir, "test.pid");
|
|
80
|
+
fs.writeFileSync(pidPath, String(process.pid));
|
|
81
|
+
expect(isSidecarAlive(pidPath)).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("returns false when PID file contains non-existent PID", () => {
|
|
85
|
+
const pidPath = path.join(tmpDir, "test.pid");
|
|
86
|
+
fs.writeFileSync(pidPath, "999999999");
|
|
87
|
+
expect(isSidecarAlive(pidPath)).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("returns false when custom pidPath does not exist", () => {
|
|
91
|
+
expect(isSidecarAlive(path.join(tmpDir, "nope.pid"))).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
68
95
|
describe("killSidecar", () => {
|
|
69
96
|
it("does not throw when PID file is missing", () => {
|
|
70
97
|
expect(() => killSidecar()).not.toThrow();
|
|
71
98
|
});
|
|
99
|
+
|
|
100
|
+
it("does not throw when sessionId is null (legacy paths)", () => {
|
|
101
|
+
expect(() => killSidecar(null)).not.toThrow();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("does not throw when sessionId is provided but no PID file exists", () => {
|
|
105
|
+
expect(() => killSidecar("nonexistent-session")).not.toThrow();
|
|
106
|
+
});
|
|
72
107
|
});
|
|
73
108
|
});
|
|
@@ -247,6 +247,130 @@ describe("sidecar-server", () => {
|
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
+
// ── Task event bridge (opentasks → MAP) ─────────────────────────────
|
|
251
|
+
|
|
252
|
+
describe("bridge-task-created", () => {
|
|
253
|
+
it("sends task.created event via conn.send with broadcast", async () => {
|
|
254
|
+
await handler({
|
|
255
|
+
action: "bridge-task-created",
|
|
256
|
+
task: { id: "task-1", title: "Fix bug", status: "open", assignee: "worker-1" },
|
|
257
|
+
agentId: "worker-1",
|
|
258
|
+
}, mockClient);
|
|
259
|
+
expect(mockConnection.send).toHaveBeenCalledWith(
|
|
260
|
+
{ scope: "swarm:test" },
|
|
261
|
+
{ type: "task.created", task: { id: "task-1", title: "Fix bug", status: "open", assignee: "worker-1" }, _origin: "worker-1" },
|
|
262
|
+
{ relationship: "broadcast" }
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("responds {ok: true} even without connection", async () => {
|
|
267
|
+
const nullHandler = createCommandHandler(null, "swarm:test", registeredAgents);
|
|
268
|
+
await nullHandler({
|
|
269
|
+
action: "bridge-task-created",
|
|
270
|
+
task: { id: "t-1" },
|
|
271
|
+
}, mockClient);
|
|
272
|
+
const written = JSON.parse(mockClient.write.mock.calls[0][0]);
|
|
273
|
+
expect(written.ok).toBe(true);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("defaults _origin to 'opentasks' when agentId missing", async () => {
|
|
277
|
+
await handler({
|
|
278
|
+
action: "bridge-task-created",
|
|
279
|
+
task: { id: "t-1" },
|
|
280
|
+
}, mockClient);
|
|
281
|
+
const [, payload] = mockConnection.send.mock.calls[0];
|
|
282
|
+
expect(payload._origin).toBe("opentasks");
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("bridge-task-status", () => {
|
|
287
|
+
it("sends task.status event via conn.send", async () => {
|
|
288
|
+
await handler({
|
|
289
|
+
action: "bridge-task-status",
|
|
290
|
+
taskId: "task-1",
|
|
291
|
+
previous: "open",
|
|
292
|
+
current: "in_progress",
|
|
293
|
+
agentId: "worker-1",
|
|
294
|
+
}, mockClient);
|
|
295
|
+
expect(mockConnection.send).toHaveBeenCalledWith(
|
|
296
|
+
{ scope: "swarm:test" },
|
|
297
|
+
{ type: "task.status", taskId: "task-1", previous: "open", current: "in_progress", _origin: "worker-1" },
|
|
298
|
+
{ relationship: "broadcast" }
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("also sends task.completed for terminal state 'completed'", async () => {
|
|
303
|
+
await handler({
|
|
304
|
+
action: "bridge-task-status",
|
|
305
|
+
taskId: "task-1",
|
|
306
|
+
previous: "open",
|
|
307
|
+
current: "completed",
|
|
308
|
+
agentId: "worker-1",
|
|
309
|
+
}, mockClient);
|
|
310
|
+
expect(mockConnection.send).toHaveBeenCalledTimes(2);
|
|
311
|
+
const [, secondPayload] = mockConnection.send.mock.calls[1];
|
|
312
|
+
expect(secondPayload.type).toBe("task.completed");
|
|
313
|
+
expect(secondPayload.taskId).toBe("task-1");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("also sends task.completed for terminal state 'closed'", async () => {
|
|
317
|
+
await handler({
|
|
318
|
+
action: "bridge-task-status",
|
|
319
|
+
taskId: "task-1",
|
|
320
|
+
current: "closed",
|
|
321
|
+
}, mockClient);
|
|
322
|
+
expect(mockConnection.send).toHaveBeenCalledTimes(2);
|
|
323
|
+
const [, secondPayload] = mockConnection.send.mock.calls[1];
|
|
324
|
+
expect(secondPayload.type).toBe("task.completed");
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("does not send task.completed for non-terminal states", async () => {
|
|
328
|
+
await handler({
|
|
329
|
+
action: "bridge-task-status",
|
|
330
|
+
taskId: "task-1",
|
|
331
|
+
current: "in_progress",
|
|
332
|
+
}, mockClient);
|
|
333
|
+
expect(mockConnection.send).toHaveBeenCalledTimes(1);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("responds {ok: true}", async () => {
|
|
337
|
+
await handler({
|
|
338
|
+
action: "bridge-task-status",
|
|
339
|
+
taskId: "task-1",
|
|
340
|
+
current: "completed",
|
|
341
|
+
}, mockClient);
|
|
342
|
+
const written = JSON.parse(mockClient.write.mock.calls[0][0]);
|
|
343
|
+
expect(written.ok).toBe(true);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe("bridge-task-assigned", () => {
|
|
348
|
+
it("sends task.assigned event via conn.send", async () => {
|
|
349
|
+
await handler({
|
|
350
|
+
action: "bridge-task-assigned",
|
|
351
|
+
taskId: "task-1",
|
|
352
|
+
assignee: "gsd-executor",
|
|
353
|
+
agentId: "gsd-executor",
|
|
354
|
+
}, mockClient);
|
|
355
|
+
expect(mockConnection.send).toHaveBeenCalledWith(
|
|
356
|
+
{ scope: "swarm:test" },
|
|
357
|
+
{ type: "task.assigned", taskId: "task-1", agentId: "gsd-executor", _origin: "gsd-executor" },
|
|
358
|
+
{ relationship: "broadcast" }
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("responds {ok: true} without connection", async () => {
|
|
363
|
+
const nullHandler = createCommandHandler(null, "swarm:test", registeredAgents);
|
|
364
|
+
await nullHandler({
|
|
365
|
+
action: "bridge-task-assigned",
|
|
366
|
+
taskId: "t-1",
|
|
367
|
+
assignee: "worker",
|
|
368
|
+
}, mockClient);
|
|
369
|
+
const written = JSON.parse(mockClient.write.mock.calls[0][0]);
|
|
370
|
+
expect(written.ok).toBe(true);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
250
374
|
// ── State ─────────────────────────────────────────────────────────────
|
|
251
375
|
|
|
252
376
|
describe("state", () => {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseSkillTreeExtension } from "../skilltree-client.mjs";
|
|
3
|
+
|
|
4
|
+
describe("skilltree-client", () => {
|
|
5
|
+
describe("parseSkillTreeExtension", () => {
|
|
6
|
+
it("returns empty defaults and roles for manifest without skilltree", () => {
|
|
7
|
+
const result = parseSkillTreeExtension({ name: "test", roles: ["a"] });
|
|
8
|
+
expect(result).toEqual({ defaults: {}, roles: {} });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("returns empty defaults and roles for null manifest", () => {
|
|
12
|
+
const result = parseSkillTreeExtension(null);
|
|
13
|
+
expect(result).toEqual({ defaults: {}, roles: {} });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns empty defaults and roles for undefined manifest", () => {
|
|
17
|
+
const result = parseSkillTreeExtension(undefined);
|
|
18
|
+
expect(result).toEqual({ defaults: {}, roles: {} });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("extracts defaults from skilltree extension", () => {
|
|
22
|
+
const manifest = {
|
|
23
|
+
name: "test",
|
|
24
|
+
roles: ["a", "b"],
|
|
25
|
+
skilltree: {
|
|
26
|
+
defaults: { profile: "implementation", maxSkills: 6 },
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const result = parseSkillTreeExtension(manifest);
|
|
30
|
+
expect(result.defaults).toEqual({ profile: "implementation", maxSkills: 6 });
|
|
31
|
+
expect(result.roles).toEqual({});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("extracts per-role overrides from skilltree extension", () => {
|
|
35
|
+
const manifest = {
|
|
36
|
+
name: "test",
|
|
37
|
+
roles: ["orchestrator", "executor", "verifier"],
|
|
38
|
+
skilltree: {
|
|
39
|
+
defaults: { profile: "implementation" },
|
|
40
|
+
roles: {
|
|
41
|
+
orchestrator: { profile: "code-review" },
|
|
42
|
+
executor: { profile: "implementation", tags: ["development"] },
|
|
43
|
+
verifier: { profile: "testing" },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const result = parseSkillTreeExtension(manifest);
|
|
48
|
+
expect(result.defaults).toEqual({ profile: "implementation" });
|
|
49
|
+
expect(result.roles.orchestrator).toEqual({ profile: "code-review" });
|
|
50
|
+
expect(result.roles.executor).toEqual({ profile: "implementation", tags: ["development"] });
|
|
51
|
+
expect(result.roles.verifier).toEqual({ profile: "testing" });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("handles skilltree extension with only roles, no defaults", () => {
|
|
55
|
+
const manifest = {
|
|
56
|
+
name: "test",
|
|
57
|
+
roles: ["a"],
|
|
58
|
+
skilltree: {
|
|
59
|
+
roles: { a: { profile: "debugging" } },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const result = parseSkillTreeExtension(manifest);
|
|
63
|
+
expect(result.defaults).toEqual({});
|
|
64
|
+
expect(result.roles.a).toEqual({ profile: "debugging" });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("handles skilltree extension with only defaults, no roles", () => {
|
|
68
|
+
const manifest = {
|
|
69
|
+
name: "test",
|
|
70
|
+
roles: ["a"],
|
|
71
|
+
skilltree: {
|
|
72
|
+
defaults: { tags: ["typescript"] },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
const result = parseSkillTreeExtension(manifest);
|
|
76
|
+
expect(result.defaults).toEqual({ tags: ["typescript"] });
|
|
77
|
+
expect(result.roles).toEqual({});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|