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
package/README.md
CHANGED
|
@@ -58,6 +58,71 @@ npm install -g openteams
|
|
|
58
58
|
openteams editor
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
### Config resolution
|
|
64
|
+
|
|
65
|
+
Configuration is resolved with tiered fallthrough — each level overrides the one below:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
SWARM_* env vars > project config > global config > defaults
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Project config
|
|
72
|
+
|
|
73
|
+
Lives in your project directory at `.swarm/claude-swarm/config.json` (or `.claude-swarm/config.json` with `--no-prefix`):
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"template": "get-shit-done",
|
|
78
|
+
"map": {
|
|
79
|
+
"scope": "my-project",
|
|
80
|
+
"systemId": "my-project-swarm"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Project config is typically committed to the repo or gitignored per preference.
|
|
86
|
+
|
|
87
|
+
### Global config
|
|
88
|
+
|
|
89
|
+
Lives at `~/.claude-swarm/config.json`. Created by `swarmkit init` or manually. Sets user-wide defaults that apply to all projects unless overridden:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"map": {
|
|
94
|
+
"server": "ws://my-map-server:8080",
|
|
95
|
+
"sidecar": "session",
|
|
96
|
+
"auth": {
|
|
97
|
+
"token": "my-token"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"sessionlog": {
|
|
101
|
+
"enabled": true,
|
|
102
|
+
"sync": "metrics"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This is useful for settings you don't want to repeat in every project — MAP server address, auth tokens, sidecar mode, sessionlog preferences. Fields like `template` and `map.scope` are typically project-specific and belong in the project config.
|
|
108
|
+
|
|
109
|
+
### Environment variable overrides
|
|
110
|
+
|
|
111
|
+
All config values can be overridden via `SWARM_*` environment variables:
|
|
112
|
+
|
|
113
|
+
| Config field | Environment variable |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `template` | `SWARM_TEMPLATE` |
|
|
116
|
+
| `map.server` | `SWARM_MAP_SERVER` |
|
|
117
|
+
| `map.enabled` | `SWARM_MAP_ENABLED` |
|
|
118
|
+
| `map.scope` | `SWARM_MAP_SCOPE` |
|
|
119
|
+
| `map.systemId` | `SWARM_MAP_SYSTEM_ID` |
|
|
120
|
+
| `map.sidecar` | `SWARM_MAP_SIDECAR` |
|
|
121
|
+
| `map.auth.token` | `SWARM_MAP_AUTH_TOKEN` |
|
|
122
|
+
| `map.auth.param` | `SWARM_MAP_AUTH_PARAM` |
|
|
123
|
+
| `sessionlog.enabled` | `SWARM_SESSIONLOG_ENABLED` |
|
|
124
|
+
| `sessionlog.sync` | `SWARM_SESSIONLOG_SYNC` |
|
|
125
|
+
|
|
61
126
|
## How it works
|
|
62
127
|
|
|
63
128
|
1. **SessionStart hook** ensures `openteams` is installed and reads your team configuration
|
package/e2e/helpers/cleanup.mjs
CHANGED
|
@@ -19,11 +19,25 @@ export function killByPidFile(pidPath) {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Clean up generated artifacts and sidecar processes in a workspace.
|
|
22
|
+
* Kills both default and per-session sidecar processes.
|
|
22
23
|
*/
|
|
23
24
|
export function cleanupWorkspace(dir) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const mapDir = path.join(dir, ".swarm", "claude-swarm", "tmp", "map");
|
|
26
|
+
|
|
27
|
+
// Kill default MAP sidecar if running
|
|
28
|
+
killByPidFile(path.join(mapDir, "sidecar.pid"));
|
|
29
|
+
|
|
30
|
+
// Kill all per-session sidecars
|
|
31
|
+
const sessionsDir = path.join(mapDir, "sessions");
|
|
32
|
+
try {
|
|
33
|
+
if (fs.existsSync(sessionsDir)) {
|
|
34
|
+
for (const entry of fs.readdirSync(sessionsDir)) {
|
|
35
|
+
killByPidFile(path.join(sessionsDir, entry, "sidecar.pid"));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// ignore
|
|
40
|
+
}
|
|
27
41
|
|
|
28
42
|
// Remove all generated/tmp artifacts
|
|
29
43
|
const tmpDir = path.join(dir, ".swarm", "claude-swarm", "tmp");
|
|
@@ -1,23 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* map-mock-server.mjs —
|
|
2
|
+
* map-mock-server.mjs — Protocol-compliant mock MAP WebSocket server for e2e tests
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Speaks enough of the MAP JSON-RPC protocol for the SDK's AgentConnection.connect()
|
|
5
|
+
* to succeed: handles map/connect, map/agents/register, map/agents/spawn,
|
|
6
|
+
* map/agents/unregister, map/agents/update, map/send, and trajectory/checkpoint.
|
|
7
|
+
*
|
|
8
|
+
* Also supports:
|
|
9
|
+
* - Sending inbound messages to connected clients
|
|
10
|
+
* - Configurable response delays for resilience testing
|
|
11
|
+
* - Method-specific message tracking
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
import { WebSocketServer } from "ws";
|
|
9
15
|
|
|
16
|
+
let _counter = 0;
|
|
17
|
+
function nextId() {
|
|
18
|
+
return `mock-${++_counter}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
export class MockMapServer {
|
|
11
22
|
constructor() {
|
|
12
23
|
this.wss = null;
|
|
13
24
|
this.port = 0;
|
|
14
|
-
this.receivedMessages = [];
|
|
15
25
|
this.connections = [];
|
|
26
|
+
this.receivedMessages = [];
|
|
27
|
+
this.responseDelayMs = 0;
|
|
28
|
+
this.trajectorySupported = false;
|
|
29
|
+
|
|
30
|
+
// Method-specific tracking
|
|
31
|
+
this.spawnedAgents = [];
|
|
32
|
+
this.sentMessages = [];
|
|
33
|
+
this.callExtensions = [];
|
|
34
|
+
this.stateUpdates = [];
|
|
16
35
|
}
|
|
17
36
|
|
|
18
37
|
/**
|
|
19
38
|
* Start the mock server on a random available port.
|
|
20
|
-
* Returns a promise that resolves with the assigned port.
|
|
21
39
|
*/
|
|
22
40
|
start() {
|
|
23
41
|
return new Promise((resolve, reject) => {
|
|
@@ -38,7 +56,6 @@ export class MockMapServer {
|
|
|
38
56
|
});
|
|
39
57
|
this._handleMessage(ws, msg);
|
|
40
58
|
} catch {
|
|
41
|
-
// Invalid JSON, record raw
|
|
42
59
|
this.receivedMessages.push({
|
|
43
60
|
timestamp: Date.now(),
|
|
44
61
|
raw: data.toString(),
|
|
@@ -56,30 +73,168 @@ export class MockMapServer {
|
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
/**
|
|
59
|
-
* Handle incoming MAP protocol messages with
|
|
76
|
+
* Handle incoming MAP protocol messages with method-specific responses.
|
|
60
77
|
*/
|
|
61
|
-
_handleMessage(ws, msg) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
ws.send(
|
|
65
|
-
JSON.stringify({
|
|
66
|
-
jsonrpc: "2.0",
|
|
67
|
-
id: msg.id,
|
|
68
|
-
result: { ok: true, agentId: msg.params?.name || "mock-agent" },
|
|
69
|
-
})
|
|
70
|
-
);
|
|
71
|
-
return;
|
|
78
|
+
async _handleMessage(ws, msg) {
|
|
79
|
+
if (this.responseDelayMs > 0) {
|
|
80
|
+
await new Promise((r) => setTimeout(r, this.responseDelayMs));
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
const { id, method, params } = msg;
|
|
84
|
+
|
|
85
|
+
// Non-RPC messages (no id) — legacy compat
|
|
86
|
+
if (id === undefined) {
|
|
87
|
+
if (msg.type === "connect" || msg.method === "connect") {
|
|
88
|
+
ws.send(JSON.stringify({
|
|
78
89
|
type: "connected",
|
|
79
90
|
agentId: msg.name || "mock-agent",
|
|
80
|
-
})
|
|
81
|
-
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
82
94
|
}
|
|
95
|
+
|
|
96
|
+
// JSON-RPC dispatch by method
|
|
97
|
+
switch (method) {
|
|
98
|
+
case "map/connect": {
|
|
99
|
+
this._respond(ws, id, {
|
|
100
|
+
sessionId: `session-${nextId()}`,
|
|
101
|
+
capabilities: {},
|
|
102
|
+
protocolVersion: "1.0",
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case "map/agents/register": {
|
|
108
|
+
const agent = {
|
|
109
|
+
id: params?.agentId || params?.name || nextId(),
|
|
110
|
+
state: "idle",
|
|
111
|
+
name: params?.name || "agent",
|
|
112
|
+
role: params?.role || "agent",
|
|
113
|
+
};
|
|
114
|
+
this._respond(ws, id, { agent });
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case "map/agents/spawn": {
|
|
119
|
+
const agent = {
|
|
120
|
+
id: params?.agentId || nextId(),
|
|
121
|
+
state: "idle",
|
|
122
|
+
name: params?.name || "spawned",
|
|
123
|
+
role: params?.role || "agent",
|
|
124
|
+
};
|
|
125
|
+
this.spawnedAgents.push({ ...params, _timestamp: Date.now() });
|
|
126
|
+
this._respond(ws, id, { agent });
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case "map/agents/unregister": {
|
|
131
|
+
this.callExtensions.push({
|
|
132
|
+
method: "map/agents/unregister",
|
|
133
|
+
params,
|
|
134
|
+
_timestamp: Date.now(),
|
|
135
|
+
});
|
|
136
|
+
this._respond(ws, id, { ok: true });
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case "map/agents/update": {
|
|
141
|
+
this.stateUpdates.push({ ...params, _timestamp: Date.now() });
|
|
142
|
+
this._respond(ws, id, { ok: true });
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
case "map/send": {
|
|
147
|
+
this.sentMessages.push({ ...params, _timestamp: Date.now() });
|
|
148
|
+
this._respond(ws, id, { messageId: nextId() });
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case "trajectory/checkpoint": {
|
|
153
|
+
if (this.trajectorySupported) {
|
|
154
|
+
this._respond(ws, id, { ok: true });
|
|
155
|
+
} else {
|
|
156
|
+
this._respondError(ws, id, -32601, "Method not found: trajectory/checkpoint");
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
default: {
|
|
162
|
+
// Check if it's a callExtension-style method
|
|
163
|
+
if (method) {
|
|
164
|
+
this.callExtensions.push({ method, params, _timestamp: Date.now() });
|
|
165
|
+
this._respond(ws, id, { ok: true });
|
|
166
|
+
} else {
|
|
167
|
+
this._respondError(ws, id, -32601, `Unknown method: ${method}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
_respond(ws, id, result) {
|
|
174
|
+
try {
|
|
175
|
+
ws.send(JSON.stringify({ jsonrpc: "2.0", id, result }));
|
|
176
|
+
} catch { /* client gone */ }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
_respondError(ws, id, code, message) {
|
|
180
|
+
try {
|
|
181
|
+
ws.send(JSON.stringify({
|
|
182
|
+
jsonrpc: "2.0",
|
|
183
|
+
id,
|
|
184
|
+
error: { code, message },
|
|
185
|
+
}));
|
|
186
|
+
} catch { /* client gone */ }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Send a MAP message notification to all connected clients.
|
|
191
|
+
* Simulates an external agent sending a message into the scope.
|
|
192
|
+
*/
|
|
193
|
+
sendToAll(payload, meta = {}) {
|
|
194
|
+
const notification = this._buildMessageNotification(payload, meta);
|
|
195
|
+
for (const ws of this.connections) {
|
|
196
|
+
try {
|
|
197
|
+
ws.send(notification);
|
|
198
|
+
} catch { /* ignore */ }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Send a MAP message notification to a specific client.
|
|
204
|
+
*/
|
|
205
|
+
sendToClient(ws, payload, meta = {}) {
|
|
206
|
+
try {
|
|
207
|
+
ws.send(this._buildMessageNotification(payload, meta));
|
|
208
|
+
} catch { /* ignore */ }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Build a MAP message notification in the exact JSON-RPC 2.0 format
|
|
213
|
+
* the SDK expects: no top-level `id` (notification, not request),
|
|
214
|
+
* method "map/message", params.message with id/from/to/timestamp/payload/meta.
|
|
215
|
+
*/
|
|
216
|
+
_buildMessageNotification(payload, meta = {}) {
|
|
217
|
+
return JSON.stringify({
|
|
218
|
+
jsonrpc: "2.0",
|
|
219
|
+
method: "map/message",
|
|
220
|
+
params: {
|
|
221
|
+
message: {
|
|
222
|
+
id: nextId(),
|
|
223
|
+
from: meta.from || "external-agent",
|
|
224
|
+
to: meta.to || { scope: "default" },
|
|
225
|
+
timestamp: new Date().toISOString(),
|
|
226
|
+
payload,
|
|
227
|
+
meta,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Set response delay for resilience testing.
|
|
235
|
+
*/
|
|
236
|
+
setResponseDelay(ms) {
|
|
237
|
+
this.responseDelayMs = ms;
|
|
83
238
|
}
|
|
84
239
|
|
|
85
240
|
/**
|
|
@@ -95,8 +250,19 @@ export class MockMapServer {
|
|
|
95
250
|
);
|
|
96
251
|
}
|
|
97
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Get received messages filtered by JSON-RPC method.
|
|
255
|
+
*/
|
|
256
|
+
getByMethod(method) {
|
|
257
|
+
return this.receivedMessages.filter((m) => m.data?.method === method);
|
|
258
|
+
}
|
|
259
|
+
|
|
98
260
|
clearMessages() {
|
|
99
261
|
this.receivedMessages = [];
|
|
262
|
+
this.spawnedAgents = [];
|
|
263
|
+
this.sentMessages = [];
|
|
264
|
+
this.callExtensions = [];
|
|
265
|
+
this.stateUpdates = [];
|
|
100
266
|
}
|
|
101
267
|
|
|
102
268
|
/**
|
|
@@ -104,15 +270,25 @@ export class MockMapServer {
|
|
|
104
270
|
*/
|
|
105
271
|
stop() {
|
|
106
272
|
return new Promise((resolve) => {
|
|
273
|
+
// Force-terminate all connections immediately to prevent
|
|
274
|
+
// auto-reconnecting clients from keeping the server alive.
|
|
107
275
|
for (const ws of this.connections) {
|
|
108
276
|
try {
|
|
109
|
-
ws.
|
|
277
|
+
ws.terminate();
|
|
110
278
|
} catch {
|
|
111
279
|
// ignore
|
|
112
280
|
}
|
|
113
281
|
}
|
|
282
|
+
this.connections = [];
|
|
283
|
+
|
|
114
284
|
if (this.wss) {
|
|
285
|
+
// Reject any new connections that arrive during shutdown
|
|
286
|
+
this.wss.on("connection", (ws) => {
|
|
287
|
+
try { ws.terminate(); } catch { /* ignore */ }
|
|
288
|
+
});
|
|
115
289
|
this.wss.close(() => resolve());
|
|
290
|
+
// Safety timeout — resolve after 3s even if wss.close() hangs
|
|
291
|
+
setTimeout(resolve, 3000);
|
|
116
292
|
} else {
|
|
117
293
|
resolve();
|
|
118
294
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sidecar.mjs — Test helper for starting/stopping real sidecar processes
|
|
3
|
+
*
|
|
4
|
+
* Spawns the actual map-sidecar.mjs as a detached child process,
|
|
5
|
+
* waits for sockets to appear, and provides cleanup.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import net from "net";
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const PLUGIN_DIR = path.resolve(__dirname, "../..");
|
|
16
|
+
const SIDECAR_SCRIPT = path.join(PLUGIN_DIR, "scripts", "map-sidecar.mjs");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Send a command to a UNIX socket and return the parsed response.
|
|
20
|
+
* Longer timeout than production (5s) for test reliability.
|
|
21
|
+
*/
|
|
22
|
+
export function sendCommand(socketPath, command, timeoutMs = 5000) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const client = net.createConnection(socketPath, () => {
|
|
25
|
+
client.write(JSON.stringify(command) + "\n");
|
|
26
|
+
});
|
|
27
|
+
let buffer = "";
|
|
28
|
+
client.on("data", (data) => {
|
|
29
|
+
buffer += data.toString();
|
|
30
|
+
const idx = buffer.indexOf("\n");
|
|
31
|
+
if (idx !== -1) {
|
|
32
|
+
const line = buffer.slice(0, idx);
|
|
33
|
+
client.end();
|
|
34
|
+
try {
|
|
35
|
+
resolve(JSON.parse(line));
|
|
36
|
+
} catch {
|
|
37
|
+
resolve(null);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
client.on("error", () => resolve(null));
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
client.destroy();
|
|
44
|
+
resolve(null);
|
|
45
|
+
}, timeoutMs);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wait for a condition to become true, polling at intervalMs.
|
|
51
|
+
*/
|
|
52
|
+
async function waitFor(predicate, timeoutMs = 10000, intervalMs = 200) {
|
|
53
|
+
const start = Date.now();
|
|
54
|
+
while (Date.now() - start < timeoutMs) {
|
|
55
|
+
if (await predicate()) return true;
|
|
56
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compute the expected socket paths for a sidecar running in a workspace.
|
|
63
|
+
* Mirrors the path resolution in src/paths.mjs when .swarm/claude-swarm/ exists.
|
|
64
|
+
*/
|
|
65
|
+
function getSocketPaths(workspaceDir, sessionId) {
|
|
66
|
+
// Resolve symlinks (macOS: /tmp -> /private/tmp) so paths match the sidecar's process.cwd()
|
|
67
|
+
const realDir = fs.realpathSync(workspaceDir);
|
|
68
|
+
const mapDir = path.join(realDir, ".swarm", "claude-swarm", "tmp", "map");
|
|
69
|
+
if (sessionId) {
|
|
70
|
+
const dir = path.join(mapDir, "sessions", sessionId);
|
|
71
|
+
return {
|
|
72
|
+
socketPath: path.join(dir, "sidecar.sock"),
|
|
73
|
+
inboxSocketPath: path.join(dir, "inbox.sock"),
|
|
74
|
+
pidPath: path.join(dir, "sidecar.pid"),
|
|
75
|
+
mapDir,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
socketPath: path.join(mapDir, "sidecar.sock"),
|
|
80
|
+
inboxSocketPath: path.join(mapDir, "inbox.sock"),
|
|
81
|
+
pidPath: path.join(mapDir, "sidecar.pid"),
|
|
82
|
+
mapDir,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Start the real sidecar process for testing.
|
|
88
|
+
*
|
|
89
|
+
* @param {object} options
|
|
90
|
+
* @param {string} options.workspaceDir - workspace with .swarm/claude-swarm/ directory
|
|
91
|
+
* @param {number} options.mockServerPort - port of the mock MAP server
|
|
92
|
+
* @param {string} [options.scope] - MAP scope (default: "swarm:test")
|
|
93
|
+
* @param {string} [options.systemId] - system ID (default: "system-test")
|
|
94
|
+
* @param {string} [options.sessionId] - per-session sidecar ID
|
|
95
|
+
* @param {object} [options.inboxConfig] - inbox config object (passed as --inbox-config JSON)
|
|
96
|
+
* @param {number} [options.inactivityTimeoutMs] - inactivity timeout override
|
|
97
|
+
* @returns {Promise<object>} handle with socketPath, pid, cleanup, etc.
|
|
98
|
+
*/
|
|
99
|
+
export async function startTestSidecar(options) {
|
|
100
|
+
const {
|
|
101
|
+
workspaceDir,
|
|
102
|
+
mockServerPort,
|
|
103
|
+
scope = "swarm:test",
|
|
104
|
+
systemId = "system-test",
|
|
105
|
+
sessionId,
|
|
106
|
+
inboxConfig,
|
|
107
|
+
inactivityTimeoutMs,
|
|
108
|
+
} = options;
|
|
109
|
+
|
|
110
|
+
const paths = getSocketPaths(workspaceDir, sessionId);
|
|
111
|
+
|
|
112
|
+
// Ensure map directory exists
|
|
113
|
+
fs.mkdirSync(path.dirname(paths.pidPath), { recursive: true });
|
|
114
|
+
|
|
115
|
+
// Build CLI args
|
|
116
|
+
const args = [
|
|
117
|
+
SIDECAR_SCRIPT,
|
|
118
|
+
"--server", `ws://localhost:${mockServerPort}`,
|
|
119
|
+
"--scope", scope,
|
|
120
|
+
"--system-id", systemId,
|
|
121
|
+
];
|
|
122
|
+
if (sessionId) {
|
|
123
|
+
args.push("--session-id", sessionId);
|
|
124
|
+
}
|
|
125
|
+
if (inboxConfig) {
|
|
126
|
+
args.push("--inbox-config", JSON.stringify(inboxConfig));
|
|
127
|
+
}
|
|
128
|
+
if (inactivityTimeoutMs) {
|
|
129
|
+
args.push("--inactivity-timeout", String(inactivityTimeoutMs));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Spawn detached
|
|
133
|
+
const child = spawn("node", args, {
|
|
134
|
+
detached: true,
|
|
135
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
136
|
+
cwd: workspaceDir,
|
|
137
|
+
});
|
|
138
|
+
child.unref();
|
|
139
|
+
|
|
140
|
+
// Collect stderr for debugging
|
|
141
|
+
let stderr = "";
|
|
142
|
+
child.stderr.on("data", (data) => {
|
|
143
|
+
stderr += data.toString();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Write PID file
|
|
147
|
+
fs.writeFileSync(paths.pidPath, String(child.pid));
|
|
148
|
+
|
|
149
|
+
// Wait for lifecycle socket to appear and respond to ping
|
|
150
|
+
const lifecycleReady = await waitFor(async () => {
|
|
151
|
+
if (!fs.existsSync(paths.socketPath)) return false;
|
|
152
|
+
const resp = await sendCommand(paths.socketPath, { action: "ping" });
|
|
153
|
+
return resp?.ok === true;
|
|
154
|
+
}, 15000);
|
|
155
|
+
|
|
156
|
+
if (!lifecycleReady) {
|
|
157
|
+
// Try to clean up
|
|
158
|
+
try { process.kill(child.pid, "SIGTERM"); } catch { /* ignore */ }
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Sidecar lifecycle socket not ready after 15s.\nStderr: ${stderr}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If inbox is configured, wait for inbox socket too
|
|
165
|
+
let inboxReady = false;
|
|
166
|
+
if (inboxConfig) {
|
|
167
|
+
inboxReady = await waitFor(
|
|
168
|
+
() => fs.existsSync(paths.inboxSocketPath),
|
|
169
|
+
10000
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
pid: child.pid,
|
|
175
|
+
child,
|
|
176
|
+
stderr: () => stderr,
|
|
177
|
+
...paths,
|
|
178
|
+
inboxReady,
|
|
179
|
+
cleanup: () => stopTestSidecar({ pid: child.pid, ...paths }),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Stop a test sidecar and clean up.
|
|
185
|
+
*/
|
|
186
|
+
export async function stopTestSidecar(handle) {
|
|
187
|
+
const { pid, socketPath, inboxSocketPath, pidPath } = handle;
|
|
188
|
+
|
|
189
|
+
// Send SIGTERM
|
|
190
|
+
try {
|
|
191
|
+
process.kill(pid, "SIGTERM");
|
|
192
|
+
} catch {
|
|
193
|
+
// Already dead
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Wait for process to die
|
|
197
|
+
await waitFor(() => {
|
|
198
|
+
try {
|
|
199
|
+
process.kill(pid, 0);
|
|
200
|
+
return false;
|
|
201
|
+
} catch {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}, 5000);
|
|
205
|
+
|
|
206
|
+
// Clean up files
|
|
207
|
+
for (const f of [socketPath, inboxSocketPath, pidPath]) {
|
|
208
|
+
try { fs.unlinkSync(f); } catch { /* ignore */ }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if a process is alive.
|
|
214
|
+
*/
|
|
215
|
+
export function isProcessAlive(pid) {
|
|
216
|
+
try {
|
|
217
|
+
process.kill(pid, 0);
|
|
218
|
+
return true;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -17,9 +17,10 @@ export function createWorkspace(options = {}) {
|
|
|
17
17
|
config = undefined,
|
|
18
18
|
gitInit = true,
|
|
19
19
|
files = {},
|
|
20
|
+
tmpdir = os.tmpdir(),
|
|
20
21
|
} = options;
|
|
21
22
|
|
|
22
|
-
const dir = fs.mkdtempSync(path.join(
|
|
23
|
+
const dir = fs.mkdtempSync(path.join(tmpdir, prefix));
|
|
23
24
|
|
|
24
25
|
if (gitInit) {
|
|
25
26
|
execSync("git init -q", { cwd: dir, stdio: "ignore" });
|