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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for contained layout support
|
|
3
|
+
*
|
|
4
|
+
* Verifies that minimem correctly handles the contained layout where
|
|
5
|
+
* MEMORY.md, memory/, config.json, index.db, and .gitignore all live
|
|
6
|
+
* in the same directory (.minimem/ or .swarm/minimem/).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, beforeEach, afterEach, expect } from "vitest";
|
|
10
|
+
import fs from "node:fs/promises";
|
|
11
|
+
import fsSync from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
|
|
15
|
+
import { resolveConfigSubdir, isInitialized } from "../config.js";
|
|
16
|
+
import { discoverMemoryDir, resolveMemoryDir } from "../shared.js";
|
|
17
|
+
import { init } from "../commands/init.js";
|
|
18
|
+
import { getSyncStatePath } from "../sync/state.js";
|
|
19
|
+
import { getConflictsDir } from "../sync/conflicts.js";
|
|
20
|
+
import { getSyncLogPath } from "../commands/conflicts.js";
|
|
21
|
+
|
|
22
|
+
describe("contained layout", () => {
|
|
23
|
+
let tempDir: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "minimem-contained-test-"));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ── discoverMemoryDir ───────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
describe("discoverMemoryDir", () => {
|
|
36
|
+
it("returns base when MEMORY.md exists at root (classic layout)", async () => {
|
|
37
|
+
await fs.writeFile(path.join(tempDir, "MEMORY.md"), "# Memory\n");
|
|
38
|
+
expect(discoverMemoryDir(tempDir)).toBe(tempDir);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("returns .swarm/minimem/ when it has config.json", async () => {
|
|
42
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
43
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
44
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
45
|
+
|
|
46
|
+
expect(discoverMemoryDir(tempDir)).toBe(swarmDir);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("returns .minimem/ when it has config.json", async () => {
|
|
50
|
+
const minimemDir = path.join(tempDir, ".minimem");
|
|
51
|
+
await fs.mkdir(minimemDir, { recursive: true });
|
|
52
|
+
await fs.writeFile(path.join(minimemDir, "config.json"), "{}");
|
|
53
|
+
|
|
54
|
+
expect(discoverMemoryDir(tempDir)).toBe(minimemDir);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("prefers classic layout over contained when MEMORY.md exists at root", async () => {
|
|
58
|
+
// Both exist: classic wins
|
|
59
|
+
await fs.writeFile(path.join(tempDir, "MEMORY.md"), "# Memory\n");
|
|
60
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
61
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
62
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
63
|
+
|
|
64
|
+
expect(discoverMemoryDir(tempDir)).toBe(tempDir);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("prefers .swarm/minimem/ over .minimem/", async () => {
|
|
68
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
69
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
70
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
71
|
+
|
|
72
|
+
const minimemDir = path.join(tempDir, ".minimem");
|
|
73
|
+
await fs.mkdir(minimemDir, { recursive: true });
|
|
74
|
+
await fs.writeFile(path.join(minimemDir, "config.json"), "{}");
|
|
75
|
+
|
|
76
|
+
expect(discoverMemoryDir(tempDir)).toBe(swarmDir);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("returns base when no memory structure exists (fallback)", () => {
|
|
80
|
+
expect(discoverMemoryDir(tempDir)).toBe(tempDir);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("ignores .swarm/minimem/ without config.json", async () => {
|
|
84
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
85
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
86
|
+
// No config.json → should not discover
|
|
87
|
+
|
|
88
|
+
expect(discoverMemoryDir(tempDir)).toBe(tempDir);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ── resolveConfigSubdir ─────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
describe("resolveConfigSubdir", () => {
|
|
95
|
+
it("returns '.' when config.json exists at root (contained)", async () => {
|
|
96
|
+
await fs.writeFile(path.join(tempDir, "config.json"), "{}");
|
|
97
|
+
|
|
98
|
+
expect(resolveConfigSubdir(tempDir)).toBe(".");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("returns '.swarm/minimem' when config.json exists there", async () => {
|
|
102
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
103
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
104
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
105
|
+
|
|
106
|
+
expect(resolveConfigSubdir(tempDir)).toBe(path.join(".swarm", "minimem"));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("returns '.minimem' as default fallback", () => {
|
|
110
|
+
expect(resolveConfigSubdir(tempDir)).toBe(".minimem");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("prefers contained over .swarm/minimem", async () => {
|
|
114
|
+
// config.json at root AND in .swarm/minimem → contained wins
|
|
115
|
+
await fs.writeFile(path.join(tempDir, "config.json"), "{}");
|
|
116
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
117
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
118
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
119
|
+
|
|
120
|
+
expect(resolveConfigSubdir(tempDir)).toBe(".");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("respects MINIMEM_CONFIG_DIR env var", async () => {
|
|
124
|
+
const original = process.env.MINIMEM_CONFIG_DIR;
|
|
125
|
+
try {
|
|
126
|
+
process.env.MINIMEM_CONFIG_DIR = "custom/path";
|
|
127
|
+
expect(resolveConfigSubdir(tempDir)).toBe("custom/path");
|
|
128
|
+
} finally {
|
|
129
|
+
if (original === undefined) {
|
|
130
|
+
delete process.env.MINIMEM_CONFIG_DIR;
|
|
131
|
+
} else {
|
|
132
|
+
process.env.MINIMEM_CONFIG_DIR = original;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("ignores .swarm/minimem dir without config.json", async () => {
|
|
138
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
139
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
140
|
+
// No config.json
|
|
141
|
+
|
|
142
|
+
expect(resolveConfigSubdir(tempDir)).toBe(".minimem");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── init command ────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
describe("init creates contained layout", () => {
|
|
149
|
+
it("creates config.json at target root (not nested)", async () => {
|
|
150
|
+
await init(tempDir, { force: false, skipSync: true });
|
|
151
|
+
|
|
152
|
+
expect(fsSync.existsSync(path.join(tempDir, "config.json"))).toBe(true);
|
|
153
|
+
expect(fsSync.existsSync(path.join(tempDir, "MEMORY.md"))).toBe(true);
|
|
154
|
+
expect(fsSync.existsSync(path.join(tempDir, "memory"))).toBe(true);
|
|
155
|
+
expect(fsSync.existsSync(path.join(tempDir, ".gitignore"))).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("does not create a nested .minimem/ subdirectory", async () => {
|
|
159
|
+
await init(tempDir, { force: false, skipSync: true });
|
|
160
|
+
|
|
161
|
+
expect(fsSync.existsSync(path.join(tempDir, ".minimem"))).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("is detected as initialized by isInitialized", async () => {
|
|
165
|
+
await init(tempDir, { force: false, skipSync: true });
|
|
166
|
+
|
|
167
|
+
expect(await isInitialized(tempDir)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("config has correct default values", async () => {
|
|
171
|
+
await init(tempDir, { force: false, skipSync: true });
|
|
172
|
+
|
|
173
|
+
const config = JSON.parse(
|
|
174
|
+
fsSync.readFileSync(path.join(tempDir, "config.json"), "utf-8"),
|
|
175
|
+
);
|
|
176
|
+
expect(config.embedding.provider).toBe("auto");
|
|
177
|
+
expect(config.hybrid.enabled).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ── sync paths with contained layout ────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
describe("sync paths resolve correctly for contained layout", () => {
|
|
184
|
+
beforeEach(async () => {
|
|
185
|
+
// Set up contained layout: config.json at root
|
|
186
|
+
await fs.writeFile(
|
|
187
|
+
path.join(tempDir, "config.json"),
|
|
188
|
+
JSON.stringify({ embedding: { provider: "auto" } }),
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("getSyncStatePath resolves to root (not .minimem/)", () => {
|
|
193
|
+
const statePath = getSyncStatePath(tempDir);
|
|
194
|
+
expect(statePath).toBe(path.join(tempDir, "sync-state.json"));
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("getConflictsDir resolves to root/conflicts (not .minimem/conflicts)", () => {
|
|
198
|
+
const conflictsDir = getConflictsDir(tempDir);
|
|
199
|
+
expect(conflictsDir).toBe(path.join(tempDir, "conflicts"));
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("getSyncLogPath resolves to root (not .minimem/)", () => {
|
|
203
|
+
const logPath = getSyncLogPath(tempDir);
|
|
204
|
+
expect(logPath).toBe(path.join(tempDir, "sync.log"));
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("sync paths resolve correctly for default layout", () => {
|
|
209
|
+
beforeEach(async () => {
|
|
210
|
+
// Default layout: .minimem/ subdir exists, no config.json at root
|
|
211
|
+
await fs.mkdir(path.join(tempDir, ".minimem"), { recursive: true });
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("getSyncStatePath resolves to .minimem/", () => {
|
|
215
|
+
const statePath = getSyncStatePath(tempDir);
|
|
216
|
+
expect(statePath).toBe(path.join(tempDir, ".minimem", "sync-state.json"));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("getConflictsDir resolves to .minimem/conflicts", () => {
|
|
220
|
+
const conflictsDir = getConflictsDir(tempDir);
|
|
221
|
+
expect(conflictsDir).toBe(path.join(tempDir, ".minimem", "conflicts"));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("getSyncLogPath resolves to .minimem/", () => {
|
|
225
|
+
const logPath = getSyncLogPath(tempDir);
|
|
226
|
+
expect(logPath).toBe(path.join(tempDir, ".minimem", "sync.log"));
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe("sync paths resolve correctly for .swarm/minimem layout", () => {
|
|
231
|
+
beforeEach(async () => {
|
|
232
|
+
const swarmDir = path.join(tempDir, ".swarm", "minimem");
|
|
233
|
+
await fs.mkdir(swarmDir, { recursive: true });
|
|
234
|
+
await fs.writeFile(path.join(swarmDir, "config.json"), "{}");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("getSyncStatePath resolves to .swarm/minimem/", () => {
|
|
238
|
+
const statePath = getSyncStatePath(tempDir);
|
|
239
|
+
expect(statePath).toBe(
|
|
240
|
+
path.join(tempDir, ".swarm", "minimem", "sync-state.json"),
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("getConflictsDir resolves to .swarm/minimem/conflicts", () => {
|
|
245
|
+
const conflictsDir = getConflictsDir(tempDir);
|
|
246
|
+
expect(conflictsDir).toBe(
|
|
247
|
+
path.join(tempDir, ".swarm", "minimem", "conflicts"),
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ── end-to-end: init + discovery ────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
describe("end-to-end: init then discover", () => {
|
|
255
|
+
it("init creates structure that discoverMemoryDir finds", async () => {
|
|
256
|
+
const projectDir = path.join(tempDir, "myproject");
|
|
257
|
+
const minimemDir = path.join(projectDir, ".minimem");
|
|
258
|
+
await fs.mkdir(projectDir, { recursive: true });
|
|
259
|
+
|
|
260
|
+
// Init inside .minimem/ (like swarmkit would)
|
|
261
|
+
await init(minimemDir, { force: false, skipSync: true });
|
|
262
|
+
|
|
263
|
+
// Discovery from project root should find it
|
|
264
|
+
expect(discoverMemoryDir(projectDir)).toBe(minimemDir);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("init creates structure that discoverMemoryDir finds under .swarm/", async () => {
|
|
268
|
+
const projectDir = path.join(tempDir, "myproject");
|
|
269
|
+
const swarmDir = path.join(projectDir, ".swarm", "minimem");
|
|
270
|
+
await fs.mkdir(projectDir, { recursive: true });
|
|
271
|
+
|
|
272
|
+
// Init inside .swarm/minimem/ (like swarmkit with prefix)
|
|
273
|
+
await init(swarmDir, { force: false, skipSync: true });
|
|
274
|
+
|
|
275
|
+
// Discovery from project root should find it
|
|
276
|
+
expect(discoverMemoryDir(projectDir)).toBe(swarmDir);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("init creates structure detected by resolveConfigSubdir as contained", async () => {
|
|
280
|
+
await init(tempDir, { force: false, skipSync: true });
|
|
281
|
+
|
|
282
|
+
// resolveConfigSubdir should return "." (contained)
|
|
283
|
+
expect(resolveConfigSubdir(tempDir)).toBe(".");
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for conflict CLI commands and sync logging
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, beforeEach, afterEach, expect } from "vitest";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
appendSyncLog,
|
|
12
|
+
readSyncLog,
|
|
13
|
+
getSyncLogPath,
|
|
14
|
+
type SyncLogEntry,
|
|
15
|
+
} from "../conflicts.js";
|
|
16
|
+
|
|
17
|
+
describe("sync logging", () => {
|
|
18
|
+
let tempDir: string;
|
|
19
|
+
let memoryDir: string;
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "minimem-log-test-"));
|
|
23
|
+
memoryDir = path.join(tempDir, "memory");
|
|
24
|
+
await fs.mkdir(memoryDir, { recursive: true });
|
|
25
|
+
await fs.mkdir(path.join(memoryDir, ".minimem"), { recursive: true });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
try {
|
|
30
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
31
|
+
} catch {
|
|
32
|
+
// Ignore cleanup errors
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("appendSyncLog", () => {
|
|
37
|
+
it("should create log file if not exists", async () => {
|
|
38
|
+
const entry: SyncLogEntry = {
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
operation: "push",
|
|
41
|
+
result: "success",
|
|
42
|
+
pushed: 3,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
await appendSyncLog(memoryDir, entry);
|
|
46
|
+
|
|
47
|
+
const logPath = getSyncLogPath(memoryDir);
|
|
48
|
+
const exists = await fs.access(logPath).then(() => true).catch(() => false);
|
|
49
|
+
expect(exists).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should append multiple entries", async () => {
|
|
53
|
+
const entry1: SyncLogEntry = {
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
operation: "push",
|
|
56
|
+
result: "success",
|
|
57
|
+
pushed: 2,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const entry2: SyncLogEntry = {
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
operation: "pull",
|
|
63
|
+
result: "success",
|
|
64
|
+
pulled: 3,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await appendSyncLog(memoryDir, entry1);
|
|
68
|
+
await appendSyncLog(memoryDir, entry2);
|
|
69
|
+
|
|
70
|
+
const entries = await readSyncLog(memoryDir);
|
|
71
|
+
expect(entries.length).toBe(2);
|
|
72
|
+
expect(entries[0].operation).toBe("push");
|
|
73
|
+
expect(entries[1].operation).toBe("pull");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should store error messages", async () => {
|
|
77
|
+
const entry: SyncLogEntry = {
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
operation: "push",
|
|
80
|
+
result: "failure",
|
|
81
|
+
errors: ["File not found", "Permission denied"],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
await appendSyncLog(memoryDir, entry);
|
|
85
|
+
|
|
86
|
+
const entries = await readSyncLog(memoryDir);
|
|
87
|
+
expect(entries.length).toBe(1);
|
|
88
|
+
expect(entries[0].errors).toEqual(["File not found", "Permission denied"]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should record conflict count", async () => {
|
|
92
|
+
const entry: SyncLogEntry = {
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
operation: "push",
|
|
95
|
+
result: "partial",
|
|
96
|
+
pushed: 2,
|
|
97
|
+
conflicts: 1,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
await appendSyncLog(memoryDir, entry);
|
|
101
|
+
|
|
102
|
+
const entries = await readSyncLog(memoryDir);
|
|
103
|
+
expect(entries[0].conflicts).toBe(1);
|
|
104
|
+
expect(entries[0].result).toBe("partial");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("readSyncLog", () => {
|
|
109
|
+
it("should return empty array when no log exists", async () => {
|
|
110
|
+
const entries = await readSyncLog(memoryDir);
|
|
111
|
+
expect(entries).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should parse JSONL format", async () => {
|
|
115
|
+
const logPath = getSyncLogPath(memoryDir);
|
|
116
|
+
const entries = [
|
|
117
|
+
{ timestamp: "2024-01-01T10:00:00Z", operation: "push", result: "success", pushed: 1 },
|
|
118
|
+
{ timestamp: "2024-01-01T11:00:00Z", operation: "pull", result: "success", pulled: 2 },
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
await fs.mkdir(path.dirname(logPath), { recursive: true });
|
|
122
|
+
await fs.writeFile(
|
|
123
|
+
logPath,
|
|
124
|
+
entries.map((e) => JSON.stringify(e)).join("\n") + "\n"
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const read = await readSyncLog(memoryDir);
|
|
128
|
+
expect(read.length).toBe(2);
|
|
129
|
+
expect(read[0].pushed).toBe(1);
|
|
130
|
+
expect(read[1].pulled).toBe(2);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("getSyncLogPath", () => {
|
|
135
|
+
it("should return path in .minimem directory", () => {
|
|
136
|
+
const logPath = getSyncLogPath(memoryDir);
|
|
137
|
+
expect(logPath.includes(".minimem")).toBeTruthy();
|
|
138
|
+
expect(logPath.endsWith("sync.log")).toBeTruthy();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* minimem append - Quick append to today's daily log
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Minimem } from "../../minimem.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveMemoryDir,
|
|
8
|
+
exitWithError,
|
|
9
|
+
loadConfig,
|
|
10
|
+
buildMinimemConfig,
|
|
11
|
+
isInitialized,
|
|
12
|
+
formatPath,
|
|
13
|
+
} from "../config.js";
|
|
14
|
+
|
|
15
|
+
export type AppendOptions = {
|
|
16
|
+
dir?: string;
|
|
17
|
+
global?: boolean;
|
|
18
|
+
file?: string;
|
|
19
|
+
provider?: string;
|
|
20
|
+
session?: string;
|
|
21
|
+
sessionSource?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function append(
|
|
25
|
+
text: string,
|
|
26
|
+
options: AppendOptions,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
const memoryDir = resolveMemoryDir({ dir: options.dir, global: options.global });
|
|
29
|
+
|
|
30
|
+
// Check if initialized
|
|
31
|
+
if (!(await isInitialized(memoryDir))) {
|
|
32
|
+
exitWithError(
|
|
33
|
+
`${formatPath(memoryDir)} is not initialized.`,
|
|
34
|
+
`Run: minimem init${options.dir ? ` ${options.dir}` : ""}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Add session marker if explicit session provided
|
|
39
|
+
let finalText = text;
|
|
40
|
+
if (options.session) {
|
|
41
|
+
const timestamp = new Date().toISOString();
|
|
42
|
+
const sourceInfo = options.sessionSource ? ` source=${options.sessionSource}` : "";
|
|
43
|
+
finalText = `<!-- ${timestamp} session=${options.session}${sourceInfo} -->\n${text}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Load config and create Minimem instance
|
|
47
|
+
const cliConfig = await loadConfig(memoryDir);
|
|
48
|
+
const config = buildMinimemConfig(memoryDir, cliConfig, {
|
|
49
|
+
provider: options.provider,
|
|
50
|
+
watch: false,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let minimem: Minimem | null = null;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
minimem = await Minimem.create(config);
|
|
57
|
+
|
|
58
|
+
let targetPath: string;
|
|
59
|
+
|
|
60
|
+
if (options.file) {
|
|
61
|
+
// Append to specific file
|
|
62
|
+
targetPath = options.file;
|
|
63
|
+
await minimem.appendFile(targetPath, finalText);
|
|
64
|
+
} else {
|
|
65
|
+
// Append to today's daily log
|
|
66
|
+
targetPath = await minimem.appendToday(finalText);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`Appended to ${targetPath}`);
|
|
70
|
+
if (options.session) {
|
|
71
|
+
console.log(` Session: ${options.session}`);
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
minimem?.close();
|
|
75
|
+
}
|
|
76
|
+
}
|