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.
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/adapters/claude-code-adapter.sh +11 -0
- package/adapters/codex-adapter.sh +12 -0
- package/adapters/cursor-adapter.sh +14 -0
- package/adapters/opencode-adapter.sh +9 -0
- package/bin/zmemory.js +426 -0
- package/package.json +42 -0
- package/src/bus/server.js +51 -0
- package/src/commands/agent.js +42 -0
- package/src/commands/archive.js +32 -0
- package/src/commands/bootstrap.js +7 -0
- package/src/commands/bus.js +6 -0
- package/src/commands/claim.js +66 -0
- package/src/commands/config.js +29 -0
- package/src/commands/context.js +269 -0
- package/src/commands/daemon.js +6 -0
- package/src/commands/decision.js +12 -0
- package/src/commands/doctor.js +100 -0
- package/src/commands/event.js +149 -0
- package/src/commands/exec.js +25 -0
- package/src/commands/explain.js +68 -0
- package/src/commands/failure.js +12 -0
- package/src/commands/gitHook.js +22 -0
- package/src/commands/handoff.js +101 -0
- package/src/commands/health.js +18 -0
- package/src/commands/help.js +33 -0
- package/src/commands/index.js +21 -0
- package/src/commands/init.js +38 -0
- package/src/commands/install.js +28 -0
- package/src/commands/locks.js +9 -0
- package/src/commands/next.js +59 -0
- package/src/commands/orchestrator.js +6 -0
- package/src/commands/plan.js +30 -0
- package/src/commands/resume.js +79 -0
- package/src/commands/route.js +67 -0
- package/src/commands/runs.js +47 -0
- package/src/commands/search.js +159 -0
- package/src/commands/setup.js +148 -0
- package/src/commands/skill.js +103 -0
- package/src/commands/startRun.js +82 -0
- package/src/commands/state.js +68 -0
- package/src/commands/status.js +51 -0
- package/src/commands/stream.js +27 -0
- package/src/commands/summary.js +65 -0
- package/src/commands/sync.js +20 -0
- package/src/commands/tasks.js +89 -0
- package/src/commands/timeline.js +75 -0
- package/src/commands/upgrade.js +19 -0
- package/src/commands/version.js +12 -0
- package/src/commands/who.js +26 -0
- package/src/commands/worker.js +71 -0
- package/src/commands/workspaces.js +25 -0
- package/src/daemon/orchestrator.js +34 -0
- package/src/daemon/zmemoryd.js +17 -0
- package/src/dashboard/server.js +40 -0
- package/src/integrations/agent-hooks.md +39 -0
- package/src/integrations/auto-bootstrap.js +90 -0
- package/src/integrations/harness-adapter.md +39 -0
- package/src/lib/agents.js +36 -0
- package/src/lib/config.js +15 -0
- package/src/lib/coordination/agents.js +49 -0
- package/src/lib/coordination/claims.js +127 -0
- package/src/lib/coordination/who.js +49 -0
- package/src/lib/fileIndex.js +36 -0
- package/src/lib/fileLocks.js +44 -0
- package/src/lib/fs.js +140 -0
- package/src/lib/lock.js +103 -0
- package/src/lib/repoScan.js +19 -0
- package/src/lib/run.js +63 -0
- package/src/lib/sqlite.js +7 -0
- package/src/lib/tasks.js +77 -0
- package/src/lib/teamSync.js +36 -0
- package/src/lib/workspaces.js +29 -0
- package/src/mcp/autostart.js +12 -0
- package/src/mcp/server.js +420 -0
- 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,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,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
|
+
}
|