zmemory 0.1.0

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/adapters/claude-code-adapter.sh +11 -0
  4. package/adapters/codex-adapter.sh +12 -0
  5. package/adapters/cursor-adapter.sh +14 -0
  6. package/adapters/opencode-adapter.sh +9 -0
  7. package/bin/zmemory.js +426 -0
  8. package/package.json +42 -0
  9. package/src/bus/server.js +51 -0
  10. package/src/commands/agent.js +42 -0
  11. package/src/commands/archive.js +32 -0
  12. package/src/commands/bootstrap.js +7 -0
  13. package/src/commands/bus.js +6 -0
  14. package/src/commands/claim.js +66 -0
  15. package/src/commands/config.js +29 -0
  16. package/src/commands/context.js +269 -0
  17. package/src/commands/daemon.js +6 -0
  18. package/src/commands/decision.js +12 -0
  19. package/src/commands/doctor.js +100 -0
  20. package/src/commands/event.js +149 -0
  21. package/src/commands/exec.js +25 -0
  22. package/src/commands/explain.js +68 -0
  23. package/src/commands/failure.js +12 -0
  24. package/src/commands/gitHook.js +22 -0
  25. package/src/commands/handoff.js +101 -0
  26. package/src/commands/health.js +18 -0
  27. package/src/commands/help.js +33 -0
  28. package/src/commands/index.js +21 -0
  29. package/src/commands/init.js +38 -0
  30. package/src/commands/install.js +28 -0
  31. package/src/commands/locks.js +9 -0
  32. package/src/commands/next.js +59 -0
  33. package/src/commands/orchestrator.js +6 -0
  34. package/src/commands/plan.js +30 -0
  35. package/src/commands/resume.js +79 -0
  36. package/src/commands/route.js +67 -0
  37. package/src/commands/runs.js +47 -0
  38. package/src/commands/search.js +159 -0
  39. package/src/commands/setup.js +148 -0
  40. package/src/commands/skill.js +103 -0
  41. package/src/commands/startRun.js +82 -0
  42. package/src/commands/state.js +68 -0
  43. package/src/commands/status.js +51 -0
  44. package/src/commands/stream.js +27 -0
  45. package/src/commands/summary.js +65 -0
  46. package/src/commands/sync.js +20 -0
  47. package/src/commands/tasks.js +89 -0
  48. package/src/commands/timeline.js +75 -0
  49. package/src/commands/upgrade.js +19 -0
  50. package/src/commands/version.js +12 -0
  51. package/src/commands/who.js +26 -0
  52. package/src/commands/worker.js +71 -0
  53. package/src/commands/workspaces.js +25 -0
  54. package/src/daemon/orchestrator.js +34 -0
  55. package/src/daemon/zmemoryd.js +17 -0
  56. package/src/dashboard/server.js +40 -0
  57. package/src/integrations/agent-hooks.md +39 -0
  58. package/src/integrations/auto-bootstrap.js +90 -0
  59. package/src/integrations/harness-adapter.md +39 -0
  60. package/src/lib/agents.js +36 -0
  61. package/src/lib/config.js +15 -0
  62. package/src/lib/coordination/agents.js +49 -0
  63. package/src/lib/coordination/claims.js +127 -0
  64. package/src/lib/coordination/who.js +49 -0
  65. package/src/lib/fileIndex.js +36 -0
  66. package/src/lib/fileLocks.js +44 -0
  67. package/src/lib/fs.js +140 -0
  68. package/src/lib/lock.js +103 -0
  69. package/src/lib/repoScan.js +19 -0
  70. package/src/lib/run.js +63 -0
  71. package/src/lib/sqlite.js +7 -0
  72. package/src/lib/tasks.js +77 -0
  73. package/src/lib/teamSync.js +36 -0
  74. package/src/lib/workspaces.js +29 -0
  75. package/src/mcp/autostart.js +12 -0
  76. package/src/mcp/server.js +420 -0
  77. package/src/mcp/tools.json +28 -0
@@ -0,0 +1,51 @@
1
+ import http from "http";
2
+ import fs from "fs";
3
+
4
+ // Very lightweight event bus using Server Sent Events
5
+ export function startEventBus() {
6
+ const port = 4322;
7
+ const clients = [];
8
+
9
+ const server = http.createServer((req, res) => {
10
+ if (req.url === "/events") {
11
+ res.writeHead(200, {
12
+ "Content-Type": "text/event-stream",
13
+ "Cache-Control": "no-cache",
14
+ Connection: "keep-alive"
15
+ });
16
+
17
+ clients.push(res);
18
+ return;
19
+ }
20
+
21
+ res.writeHead(404);
22
+ res.end();
23
+ });
24
+
25
+ server.listen(port);
26
+ console.log("ZMemory event bus running http://localhost:" + port);
27
+
28
+ const log = ".zmemory/logs/events.jsonl";
29
+ let size = fs.existsSync(log) ? fs.statSync(log).size : 0;
30
+
31
+ setInterval(() => {
32
+ if (!fs.existsSync(log)) return;
33
+ const stats = fs.statSync(log);
34
+
35
+ if (stats.size > size) {
36
+ const stream = fs.createReadStream(log, { start: size, end: stats.size });
37
+
38
+ let data = "";
39
+ stream.on("data", (c) => (data += c));
40
+
41
+ stream.on("end", () => {
42
+ clients.forEach((c) => {
43
+ c.write(`data: ${data}\n\n`);
44
+ });
45
+ });
46
+
47
+ size = stats.size;
48
+ }
49
+ }, 1000);
50
+ }
51
+
@@ -0,0 +1,42 @@
1
+ import { registerAgent, listAgents } from "../lib/coordination/agents.js";
2
+
3
+ export async function agentCommand(args) {
4
+ const sub = args[0];
5
+
6
+ if (sub === "register") {
7
+ const get = (f) => {
8
+ const i = args.indexOf(f);
9
+ return i !== -1 ? args[i + 1] : null;
10
+ };
11
+
12
+ const agent = get("--agent");
13
+ const session = get("--session");
14
+ const task = get("--task") || "";
15
+
16
+ if (!agent || !session) {
17
+ console.log("usage: zmemory agent register --agent <name> --session <id>");
18
+ return;
19
+ }
20
+
21
+ registerAgent({ agent, session, task });
22
+ console.log("agent registered", session);
23
+ return;
24
+ }
25
+
26
+ if (sub === "list" || sub === "agents") {
27
+ const agents = listAgents();
28
+
29
+ if (!agents.length) {
30
+ console.log("no active agents");
31
+ return;
32
+ }
33
+
34
+ console.log("Active agents\n");
35
+
36
+ for (const a of agents) {
37
+ console.log(a.session);
38
+ if (a.task) console.log(" task:", a.task);
39
+ console.log();
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,32 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { getRunBase, getArchivedRunBase } from "../lib/run.js";
4
+
5
+ export async function archiveRun(args) {
6
+ const i = args.indexOf("--run");
7
+ const run = i !== -1 ? args[i + 1] : null;
8
+
9
+ if (!run) {
10
+ console.log("missing --run");
11
+ return;
12
+ }
13
+
14
+ let active, archived;
15
+ try {
16
+ active = getRunBase(run);
17
+ archived = getArchivedRunBase(run);
18
+ } catch {
19
+ console.log("invalid run id");
20
+ return;
21
+ }
22
+
23
+ if (!fs.existsSync(active)) {
24
+ console.log("run not found");
25
+ return;
26
+ }
27
+
28
+ fs.mkdirSync(path.dirname(archived), { recursive: true });
29
+ fs.renameSync(active, archived);
30
+
31
+ console.log(`run archived: ${run}`);
32
+ }
@@ -0,0 +1,7 @@
1
+ import { ensureZMemory } from "../integrations/auto-bootstrap.js";
2
+
3
+ export async function bootstrapCommand() {
4
+ ensureZMemory();
5
+ console.log("ZMemory bootstrap complete");
6
+ }
7
+
@@ -0,0 +1,6 @@
1
+ import { startEventBus } from "../bus/server.js";
2
+
3
+ export async function busCommand() {
4
+ startEventBus();
5
+ }
6
+
@@ -0,0 +1,66 @@
1
+ import { claimPattern, listClaims, releaseClaims } from "../lib/coordination/claims.js";
2
+
3
+ export async function claimCommand(args) {
4
+ const sub = args[0];
5
+
6
+ // NEW UX: zmemory claim <pattern> --session <id>
7
+ // Backward compatible with old: zmemory claim claim <pattern>
8
+ if (sub && sub !== "claims" && sub !== "release") {
9
+ const pattern = sub === "claim" ? args[1] : sub;
10
+ const get = (f) => {
11
+ const i = args.indexOf(f);
12
+ return i !== -1 ? args[i + 1] : null;
13
+ };
14
+
15
+ const session = get("--session");
16
+ const task = get("--task") || "";
17
+
18
+ if (!pattern || !session) {
19
+ console.log("usage: zmemory claim <pattern> --session <id>");
20
+ return;
21
+ }
22
+
23
+ const res = claimPattern(pattern, { session, task });
24
+
25
+ if (res.agents.length > 1) {
26
+ console.log("warning: pattern already claimed\n");
27
+ }
28
+
29
+ console.log("claimed", pattern);
30
+ return;
31
+ }
32
+
33
+ if (sub === "claims") {
34
+ const claims = listClaims();
35
+
36
+ if (!claims.length) {
37
+ console.log("no claims");
38
+ return;
39
+ }
40
+
41
+ for (const c of claims) {
42
+ console.log(c.pattern);
43
+ for (const a of c.agents || []) {
44
+ console.log(" ", a.session, a.task || "");
45
+ }
46
+ console.log();
47
+ }
48
+ return;
49
+ }
50
+
51
+ if (sub === "release") {
52
+ const get = (f) => {
53
+ const i = args.indexOf(f);
54
+ return i !== -1 ? args[i + 1] : null;
55
+ };
56
+
57
+ const session = get("--session");
58
+ if (!session) {
59
+ console.log("usage: zmemory release --session <id>");
60
+ return;
61
+ }
62
+
63
+ releaseClaims(session);
64
+ console.log("claims released", session);
65
+ }
66
+ }
@@ -0,0 +1,29 @@
1
+ import { readConfig, writeConfig } from "../lib/config.js";
2
+
3
+ export async function configCommand(args) {
4
+ const sub = args[0];
5
+
6
+ if (sub === "show") {
7
+ console.log(JSON.stringify(readConfig(), null, 2));
8
+ return;
9
+ }
10
+
11
+ if (sub === "set") {
12
+ const key = args[1];
13
+ const value = args[2];
14
+
15
+ if (!key || !value || !key.trim() || !value.trim()) {
16
+ console.log("usage: zmemory config set <key> <value>");
17
+ return;
18
+ }
19
+
20
+ const cfg = readConfig();
21
+ cfg[key] = value;
22
+ writeConfig(cfg);
23
+
24
+ console.log("config updated", key, "=", value);
25
+ return;
26
+ }
27
+
28
+ console.log("usage: zmemory config show|set");
29
+ }
@@ -0,0 +1,269 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { assertValidRunId, getRunFile, readRunState } from "../lib/run.js";
4
+
5
+ export async function generateContext(args) {
6
+ const r = args.indexOf("--run");
7
+ const roleIndex = args.indexOf("--role");
8
+ const eventsIndex = args.indexOf("--include-events");
9
+ const budgetIndex = args.indexOf("--budget");
10
+
11
+ const run = r !== -1 ? args[r + 1] : null;
12
+ const role = roleIndex !== -1 ? args[roleIndex + 1] : "executor";
13
+ const includeEvents = eventsIndex !== -1 ? Number(args[eventsIndex + 1]) : 0;
14
+ const budget = budgetIndex !== -1 ? Number(args[budgetIndex + 1]) : null;
15
+
16
+ if (!run) {
17
+ console.log("missing --run");
18
+ return;
19
+ }
20
+
21
+ try {
22
+ assertValidRunId(run);
23
+ } catch {
24
+ console.log("invalid run id");
25
+ return;
26
+ }
27
+
28
+ if (typeof role !== "string" || role.trim() === "" || !/^[A-Za-z0-9_-]+$/.test(role)) {
29
+ console.log("invalid role");
30
+ return;
31
+ }
32
+
33
+ let state;
34
+ try {
35
+ state = readRunState(run);
36
+ } catch (e) {
37
+ console.log(e.message === "run not found" ? "run not found" : "invalid run id");
38
+ return;
39
+ }
40
+
41
+ const taskPath = getRunFile(run, "task.md");
42
+ if (!fs.existsSync(taskPath)) {
43
+ console.log("run not found");
44
+ return;
45
+ }
46
+ const task = fs.readFileSync(taskPath, "utf8");
47
+
48
+ let projectFacts = "";
49
+ const projectFile = `.zmemory/project.md`;
50
+ if (fs.existsSync(projectFile)) {
51
+ projectFacts = fs.readFileSync(projectFile, "utf8");
52
+ }
53
+
54
+ let patternsFacts = "";
55
+ const patternsFile = `.zmemory/patterns.md`;
56
+ if (fs.existsSync(patternsFile)) {
57
+ patternsFacts = fs.readFileSync(patternsFile, "utf8");
58
+ }
59
+
60
+ let commandFacts = "";
61
+ const commandsFile = `.zmemory/commands.md`;
62
+ if (fs.existsSync(commandsFile)) {
63
+ commandFacts = fs.readFileSync(commandsFile, "utf8");
64
+ }
65
+
66
+ let configFacts = "";
67
+ const configFile = `.zmemory/config.json`;
68
+ if (fs.existsSync(configFile)) {
69
+ try {
70
+ const cfg = JSON.parse(fs.readFileSync(configFile, "utf8"));
71
+ configFacts = `Project: ${cfg.project_name}\nMemory Mode: ${cfg.memory_mode}`;
72
+ } catch {}
73
+ }
74
+
75
+ let events = [];
76
+ if (includeEvents > 0) {
77
+ const p = getRunFile(run, "events.jsonl");
78
+ if (fs.existsSync(p)) {
79
+ events = fs
80
+ .readFileSync(p, "utf8")
81
+ .split("\n")
82
+ .filter(Boolean)
83
+ .slice(-includeEvents);
84
+ }
85
+ }
86
+
87
+ let body = "";
88
+
89
+ if (role === "executor") {
90
+ const attempts = state.attempts || {};
91
+ const allowed = Array.isArray(state.allowed_files) ? state.allowed_files : [];
92
+ const forbidden = Array.isArray(state.forbidden_files) ? state.forbidden_files : [];
93
+ body = `## Role
94
+ Executor
95
+
96
+ ## Task
97
+ ${task}
98
+
99
+ ## Current Run State
100
+ Status: ${state.status}
101
+ Current Step: ${state.current_step}
102
+ Next Action: ${state.next_action}
103
+
104
+ ## Scope
105
+ Allowed files:
106
+ ${allowed.join("\n")}
107
+
108
+ Forbidden files:
109
+ ${forbidden.join("\n")}
110
+
111
+ ## Acceptance Criteria
112
+ - Implement the requested task
113
+ - Do not modify forbidden files
114
+ - Keep changes minimal and scoped
115
+
116
+ ## Previous Attempts
117
+ Current error signature: ${attempts.current_error_signature || "none"}
118
+ Repeated count: ${attempts.repeated_count || 0}
119
+
120
+ ## Stop Conditions
121
+ - Scope expands beyond allowed files
122
+ - Same failure repeats twice
123
+ - Task requirements become ambiguous
124
+
125
+ ## Required Output
126
+ - Files changed
127
+ - Commands executed
128
+ - Test results
129
+ - Remaining blockers (if any)
130
+ `;
131
+ } else if (role === "reviewer") {
132
+ body = `## Role
133
+ Reviewer
134
+
135
+ ## Task
136
+ Review executor output for task:
137
+ ${task}
138
+
139
+ ## Review Checklist
140
+ - Scope respected
141
+ - Minimal patch
142
+ - Tests sufficient
143
+ - No hidden behavior change
144
+ `;
145
+ } else if (role === "tester") {
146
+ body = `## Role
147
+ Tester
148
+
149
+ ## Task
150
+ Validate implementation for:
151
+ ${task}
152
+
153
+ ## Required Output
154
+ - Commands executed
155
+ - Test results
156
+ - Failures if any
157
+ `;
158
+ } else if (role === "brain") {
159
+ body = `## Role
160
+ Brain
161
+
162
+ ## Task
163
+ ${task}
164
+
165
+ ## Responsibilities
166
+ - Plan solution steps
167
+ - Decide next role
168
+ - Define safe scope
169
+ `;
170
+ } else if (role === "router") {
171
+ const attempts = state.attempts || {};
172
+ const blockers = state.blockers || [];
173
+ let suggestion = "executor";
174
+
175
+ if (blockers.length > 0) suggestion = "brain";
176
+ else if ((attempts.repeated_count || 0) >= 3) suggestion = "brain";
177
+ else if ((attempts.repeated_count || 0) >= 2) suggestion = "reviewer";
178
+
179
+ body = `## Role
180
+ Router
181
+
182
+ ## Task
183
+ Route next action for:
184
+ ${task}
185
+
186
+ ## Run Signals
187
+ Repeated failures: ${attempts.repeated_count || 0}
188
+ Current error: ${attempts.current_error_signature || "none"}
189
+ Blockers: ${blockers.join(", ")}
190
+
191
+ ## Suggested Next Role
192
+ ${suggestion}
193
+
194
+ ## Available Roles
195
+ - executor
196
+ - reviewer
197
+ - tester
198
+ - brain
199
+ `;
200
+ } else {
201
+ if (role === "handoff_receiver") {
202
+ body = `## Role
203
+ Handoff Receiver
204
+
205
+ ## Task
206
+ Continue work for:
207
+ ${task}
208
+
209
+ ## Instructions
210
+ - Read the handoff packet
211
+ - Continue from the next action
212
+ - Do not repeat previous failed attempts
213
+ `;
214
+ } else {
215
+ body = `## Role
216
+ ${role}
217
+
218
+ ## Task
219
+ ${task}
220
+ `;
221
+ }
222
+ }
223
+
224
+ const ctx = `# ZMemory Context Packet
225
+
226
+ ${body}
227
+
228
+ ## Run Status
229
+ Status: ${state.status}
230
+ Step: ${state.current_step}
231
+ Next Action: ${state.next_action}
232
+
233
+ ## Project Facts
234
+ ${configFacts}
235
+
236
+ ${projectFacts}
237
+
238
+ ## Important Decisions
239
+ ${fs.existsSync('.zmemory/decisions.md') ? fs.readFileSync('.zmemory/decisions.md','utf8') : ''}
240
+
241
+ ## Known Failures
242
+ ${fs.existsSync('.zmemory/failures.md') ? fs.readFileSync('.zmemory/failures.md','utf8') : ''}
243
+
244
+ ## Known Patterns
245
+ ${patternsFacts}
246
+
247
+ ## Useful Commands
248
+ ${commandFacts}
249
+
250
+ ## Recent Events
251
+ ${events.join("\n")}
252
+ `;
253
+
254
+ let finalCtx = ctx;
255
+
256
+ if (budget && ctx.length > budget) {
257
+ finalCtx = ctx.slice(0, budget) + "\n...truncated...";
258
+ }
259
+
260
+ const packetPath = `.zmemory/packets/latest-context.md`;
261
+ fs.mkdirSync(path.dirname(packetPath), { recursive: true });
262
+ fs.writeFileSync(packetPath, finalCtx);
263
+
264
+ // also write role-specific packet inside run directory
265
+ const rolePacket = getRunFile(run, `context.${role}.md`);
266
+ fs.writeFileSync(rolePacket, finalCtx);
267
+
268
+ console.log(finalCtx);
269
+ }
@@ -0,0 +1,6 @@
1
+ import { startDaemon } from "../daemon/zmemoryd.js";
2
+
3
+ export async function daemonCommand() {
4
+ startDaemon();
5
+ }
6
+
@@ -0,0 +1,12 @@
1
+ import { appendFile, now } from "../lib/fs.js";
2
+
3
+ export async function decisionAdd(args) {
4
+ const text = args.join(" ").trim();
5
+ if (!text) {
6
+ console.log("usage: zmemory decision <text>");
7
+ return;
8
+ }
9
+ const entry = `\n## ${now()}\n${text}\n`;
10
+ appendFile(`.zmemory/decisions.md`, entry);
11
+ console.log("decision recorded");
12
+ }
@@ -0,0 +1,100 @@
1
+ import fs from "fs";
2
+
3
+ export async function doctor() {
4
+ const issues = [];
5
+ const warnings = [];
6
+
7
+ if (!fs.existsSync(".zmemory")) {
8
+ console.log("ZMemory not initialized");
9
+ return;
10
+ }
11
+
12
+ const required = [
13
+ ".zmemory/config.json",
14
+ ".zmemory/project.md",
15
+ ".zmemory/decisions.md",
16
+ ".zmemory/failures.md",
17
+ ".zmemory/logs/events.jsonl",
18
+ ".zmemory/runs/active",
19
+ ".zmemory/agents",
20
+ ".zmemory/claims"
21
+ ];
22
+
23
+ required.forEach((p) => {
24
+ if (!fs.existsSync(p)) issues.push(`missing: ${p}`);
25
+ });
26
+
27
+ const runsDir = ".zmemory/runs/active";
28
+ if (fs.existsSync(runsDir)) {
29
+ const runs = fs.readdirSync(runsDir);
30
+
31
+ runs.forEach((r) => {
32
+ const base = `${runsDir}/${r}`;
33
+ const requiredRunFiles = ["state.json", "task.md", "events.jsonl"];
34
+
35
+ requiredRunFiles.forEach((f) => {
36
+ const p = `${base}/${f}`;
37
+ if (!fs.existsSync(p)) {
38
+ issues.push(`run ${r} missing ${f}`);
39
+ }
40
+ });
41
+ });
42
+ }
43
+
44
+ // check logs writable
45
+ try {
46
+ const testFile = ".zmemory/logs/.doctor-write-test";
47
+ fs.writeFileSync(testFile, "ok");
48
+ fs.unlinkSync(testFile);
49
+ } catch {
50
+ issues.push("logs directory not writable");
51
+ }
52
+
53
+ // check claims health
54
+ const claimsDir = ".zmemory/claims";
55
+ if (fs.existsSync(claimsDir)) {
56
+ const files = fs.readdirSync(claimsDir);
57
+ for (const f of files) {
58
+ try {
59
+ JSON.parse(fs.readFileSync(`${claimsDir}/${f}`, "utf8"));
60
+ } catch {
61
+ issues.push(`corrupted claim file: ${f}`);
62
+ }
63
+ }
64
+ }
65
+
66
+ // check agents
67
+ const agentsDir = ".zmemory/agents";
68
+ if (fs.existsSync(agentsDir)) {
69
+ const agents = fs.readdirSync(agentsDir);
70
+ if (!agents.length) {
71
+ warnings.push("no active agents registered");
72
+ }
73
+ }
74
+
75
+ // check event log size (suggest rotation if large)
76
+ const eventsFile = ".zmemory/logs/events.jsonl";
77
+ if (fs.existsSync(eventsFile)) {
78
+ const size = fs.statSync(eventsFile).size;
79
+ if (size > 5 * 1024 * 1024) {
80
+ warnings.push("events.jsonl larger than 5MB (consider rotation)");
81
+ }
82
+ }
83
+
84
+ if (!issues.length) {
85
+ console.log("ZMemory doctor: OK");
86
+ if (warnings.length) {
87
+ console.log("\nWarnings:\n");
88
+ warnings.forEach((w) => console.log("-", w));
89
+ }
90
+ return;
91
+ }
92
+
93
+ console.log("ZMemory doctor found issues:\n");
94
+ issues.forEach((i) => console.log("-", i));
95
+
96
+ if (warnings.length) {
97
+ console.log("\nWarnings:\n");
98
+ warnings.forEach((w) => console.log("-", w));
99
+ }
100
+ }