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,149 @@
|
|
|
1
|
+
import { appendFile, now, readJSON, writeJSON } from "../lib/fs.js";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { getRunFile, isValidRunId, ensureRunExists } from "../lib/run.js";
|
|
4
|
+
import { acquireLock, releaseLock } from "../lib/lock.js";
|
|
5
|
+
|
|
6
|
+
const INDEX_PATH = ".zmemory/index.json";
|
|
7
|
+
const EVENTS_FILE = ".zmemory/logs/events.jsonl";
|
|
8
|
+
const ARCHIVE_DIR = ".zmemory/logs/archive";
|
|
9
|
+
const MAX_ACTIVE_EVENTS = 5000;
|
|
10
|
+
const ARCHIVE_BATCH = 2000;
|
|
11
|
+
|
|
12
|
+
function updateIndex(filePath, contentLine) {
|
|
13
|
+
if (!fs.existsSync(INDEX_PATH)) return; // index built lazily by search
|
|
14
|
+
try {
|
|
15
|
+
const raw = JSON.parse(fs.readFileSync(INDEX_PATH, "utf8"));
|
|
16
|
+
if (!Array.isArray(raw)) return;
|
|
17
|
+
|
|
18
|
+
const lower = contentLine.toLowerCase();
|
|
19
|
+
let entry = raw.find((e) => e.path === filePath);
|
|
20
|
+
|
|
21
|
+
if (!entry) {
|
|
22
|
+
entry = { path: filePath, content: lower };
|
|
23
|
+
raw.push(entry);
|
|
24
|
+
} else {
|
|
25
|
+
entry.content += lower;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fs.writeFileSync(INDEX_PATH, JSON.stringify(raw));
|
|
29
|
+
} catch {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function rotateEventsIfNeeded() {
|
|
33
|
+
try {
|
|
34
|
+
if (!fs.existsSync(EVENTS_FILE)) return;
|
|
35
|
+
const lines = fs.readFileSync(EVENTS_FILE, "utf8").split("\n").filter(Boolean);
|
|
36
|
+
if (lines.length <= MAX_ACTIVE_EVENTS) return;
|
|
37
|
+
|
|
38
|
+
fs.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
39
|
+
|
|
40
|
+
const archive = lines.slice(0, ARCHIVE_BATCH);
|
|
41
|
+
const keep = lines.slice(ARCHIVE_BATCH);
|
|
42
|
+
|
|
43
|
+
const existing = fs.readdirSync(ARCHIVE_DIR).filter(f => f.startsWith("events-") && f.endsWith(".jsonl"));
|
|
44
|
+
const next = String(existing.length + 1).padStart(5, "0");
|
|
45
|
+
const archivePath = `${ARCHIVE_DIR}/events-${next}.jsonl`;
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(archivePath, archive.join("\n") + "\n");
|
|
48
|
+
fs.writeFileSync(EVENTS_FILE, keep.join("\n") + (keep.length ? "\n" : ""));
|
|
49
|
+
} catch {}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function appendEvent(args) {
|
|
53
|
+
const get = (flag) => {
|
|
54
|
+
const i = args.indexOf(flag);
|
|
55
|
+
if (i === -1) return null;
|
|
56
|
+
const v = args[i + 1];
|
|
57
|
+
if (typeof v !== "string" || v.startsWith("--")) return null;
|
|
58
|
+
return v;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const run = get("--run");
|
|
62
|
+
const runFlagPresent = args.includes("--run");
|
|
63
|
+
const role = get("--role");
|
|
64
|
+
const type = get("--type");
|
|
65
|
+
const summary = get("--summary");
|
|
66
|
+
const files = get("--files");
|
|
67
|
+
const evidence = get("--evidence");
|
|
68
|
+
const errorSig = get("--error-signature");
|
|
69
|
+
|
|
70
|
+
const usage = "usage: zmemory event --role <role> --type <type> --summary <text> [--run <id>]";
|
|
71
|
+
|
|
72
|
+
if (!role || !role.trim() || !type || !type.trim() || !summary || !summary.trim()) {
|
|
73
|
+
console.log(usage);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const evt = {
|
|
78
|
+
ts: now(),
|
|
79
|
+
run_id: run,
|
|
80
|
+
role,
|
|
81
|
+
type,
|
|
82
|
+
summary,
|
|
83
|
+
files: files ? files.split(",") : undefined,
|
|
84
|
+
evidence: evidence || undefined,
|
|
85
|
+
error_signature: errorSig || undefined
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (runFlagPresent) {
|
|
89
|
+
if (!run) {
|
|
90
|
+
console.log(usage);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!isValidRunId(run)) {
|
|
94
|
+
console.log(usage);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
ensureRunExists(run);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.log(e.message === "run not found" ? "run not found" : "invalid run id");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const line = JSON.stringify(evt) + "\n";
|
|
106
|
+
|
|
107
|
+
if (run) {
|
|
108
|
+
try {
|
|
109
|
+
acquireLock(run);
|
|
110
|
+
} catch {
|
|
111
|
+
console.log("run locked by another agent");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
appendFile(EVENTS_FILE, line);
|
|
116
|
+
const p = getRunFile(run, "events.jsonl");
|
|
117
|
+
appendFile(p, line);
|
|
118
|
+
updateIndex(p, line);
|
|
119
|
+
updateIndex(EVENTS_FILE, line);
|
|
120
|
+
rotateEventsIfNeeded();
|
|
121
|
+
|
|
122
|
+
if (errorSig) {
|
|
123
|
+
const statePath = getRunFile(run, "state.json");
|
|
124
|
+
if (fs.existsSync(statePath)) {
|
|
125
|
+
const state = readJSON(statePath);
|
|
126
|
+
const prev = state.attempts?.current_error_signature;
|
|
127
|
+
if (!state.attempts) state.attempts = { current_error_signature: null, repeated_count: 0 };
|
|
128
|
+
|
|
129
|
+
if (prev === errorSig) {
|
|
130
|
+
state.attempts.repeated_count = (state.attempts.repeated_count || 0) + 1;
|
|
131
|
+
} else {
|
|
132
|
+
state.attempts.current_error_signature = errorSig;
|
|
133
|
+
state.attempts.repeated_count = 1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
writeJSON(statePath, state);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
releaseLock(run);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
appendFile(EVENTS_FILE, line);
|
|
144
|
+
updateIndex(EVENTS_FILE, line);
|
|
145
|
+
rotateEventsIfNeeded();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log("event appended");
|
|
149
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import { appendFile, now } from "../lib/fs.js";
|
|
3
|
+
|
|
4
|
+
export async function execCommand(args) {
|
|
5
|
+
const cmd = args.join(" ");
|
|
6
|
+
if (!cmd || !cmd.trim()) {
|
|
7
|
+
console.log("empty command");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = spawnSync(cmd, { shell: true, encoding: "utf8" });
|
|
12
|
+
|
|
13
|
+
const evt = {
|
|
14
|
+
ts: now(),
|
|
15
|
+
role: "executor",
|
|
16
|
+
type: "command_run",
|
|
17
|
+
summary: cmd,
|
|
18
|
+
success: result.status === 0
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
appendFile(".zmemory/logs/events.jsonl", JSON.stringify(evt) + "\n");
|
|
22
|
+
|
|
23
|
+
console.log(result.stdout);
|
|
24
|
+
if (result.stderr) console.error(result.stderr);
|
|
25
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { who } from "../lib/coordination/who.js";
|
|
3
|
+
|
|
4
|
+
export async function explainCommand(args) {
|
|
5
|
+
const file = args[0];
|
|
6
|
+
|
|
7
|
+
if (!file) {
|
|
8
|
+
console.log("usage: zmemory explain <file>");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
console.log(`# ZMemory Explain\n`);
|
|
13
|
+
console.log(`File: ${file}\n`);
|
|
14
|
+
|
|
15
|
+
const matches = who(file);
|
|
16
|
+
|
|
17
|
+
console.log("Active claims");
|
|
18
|
+
if (!matches.length) {
|
|
19
|
+
console.log(" none");
|
|
20
|
+
} else {
|
|
21
|
+
for (const m of matches) {
|
|
22
|
+
console.log(` pattern: ${m.pattern}`);
|
|
23
|
+
for (const a of m.agents || []) {
|
|
24
|
+
const mins = typeof a.expires_in_minutes === "number" ? `${a.expires_in_minutes}m` : "?";
|
|
25
|
+
console.log(` ${a.session} ${a.task || ""} (expires in: ${mins})`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log();
|
|
31
|
+
|
|
32
|
+
const eventsFile = ".zmemory/logs/events.jsonl";
|
|
33
|
+
console.log("Recent related activity");
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(eventsFile)) {
|
|
36
|
+
console.log(" none");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const lines = fs
|
|
41
|
+
.readFileSync(eventsFile, "utf8")
|
|
42
|
+
.split("\n")
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.slice(-50);
|
|
45
|
+
|
|
46
|
+
const related = [];
|
|
47
|
+
|
|
48
|
+
for (const l of lines) {
|
|
49
|
+
try {
|
|
50
|
+
const e = JSON.parse(l);
|
|
51
|
+
const files = e.files || [];
|
|
52
|
+
if (files.find(f => file.includes(f) || f.includes(file))) {
|
|
53
|
+
related.push(e);
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!related.length) {
|
|
59
|
+
console.log(" none");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const e of related.slice(-5)) {
|
|
64
|
+
const role = e.role || "agent";
|
|
65
|
+
const ts = e.ts || "";
|
|
66
|
+
console.log(` ${ts} ${role}: ${e.summary || ""}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { appendFile, now } from "../lib/fs.js";
|
|
2
|
+
|
|
3
|
+
export async function failureAdd(args) {
|
|
4
|
+
const text = args.join(" ").trim();
|
|
5
|
+
if (!text) {
|
|
6
|
+
console.log("usage: zmemory failure <text>");
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const entry = `\n## ${now()}\n${text}\n`;
|
|
10
|
+
appendFile(`.zmemory/failures.md`, entry);
|
|
11
|
+
console.log("failure recorded");
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export async function installGitHook() {
|
|
5
|
+
const hookDir = ".git/hooks";
|
|
6
|
+
|
|
7
|
+
if (!fs.existsSync(hookDir)) {
|
|
8
|
+
console.log("not a git repo");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const hook = `#!/bin/sh
|
|
13
|
+
node bin/zmemory.js event --role executor --type git_commit --summary "git commit"
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const file = path.join(hookDir, "post-commit");
|
|
17
|
+
fs.writeFileSync(file, hook);
|
|
18
|
+
fs.chmodSync(file, 0o755);
|
|
19
|
+
|
|
20
|
+
console.log("git hook installed");
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { getRunFile, isValidRunId, ensureRunExists } from "../lib/run.js";
|
|
3
|
+
|
|
4
|
+
export async function generateHandoff(args) {
|
|
5
|
+
const i = args.indexOf("--run");
|
|
6
|
+
const run = i !== -1 ? args[i + 1] : null;
|
|
7
|
+
|
|
8
|
+
if (!run) {
|
|
9
|
+
console.log("missing --run");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!isValidRunId(run)) {
|
|
14
|
+
console.log("invalid run id");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
ensureRunExists(run);
|
|
20
|
+
} catch {
|
|
21
|
+
console.log("run not found");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let state;
|
|
26
|
+
try {
|
|
27
|
+
state = JSON.parse(fs.readFileSync(getRunFile(run, "state.json"), "utf8"));
|
|
28
|
+
} catch {
|
|
29
|
+
console.log("run not found");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let task;
|
|
34
|
+
try {
|
|
35
|
+
task = fs.readFileSync(getRunFile(run, "task.md"), "utf8");
|
|
36
|
+
} catch {
|
|
37
|
+
console.log("run not found");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// normalize task text (strip leading heading if present)
|
|
42
|
+
const taskText = task.split("\n").slice(1).join(" ").trim() || task.trim();
|
|
43
|
+
|
|
44
|
+
let events = [];
|
|
45
|
+
const eventsPath = getRunFile(run, "events.jsonl");
|
|
46
|
+
if (fs.existsSync(eventsPath)) {
|
|
47
|
+
events = fs
|
|
48
|
+
.readFileSync(eventsPath, "utf8")
|
|
49
|
+
.split("\n")
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.slice(-5);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const filesTouched = new Set();
|
|
55
|
+
events.forEach((l) => {
|
|
56
|
+
try {
|
|
57
|
+
const e = JSON.parse(l);
|
|
58
|
+
(e.files || []).forEach((f) => filesTouched.add(f));
|
|
59
|
+
} catch {}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const completed = Array.isArray(state.completed) ? state.completed : [];
|
|
63
|
+
const blockers = Array.isArray(state.blockers) ? state.blockers : [];
|
|
64
|
+
const attempts = state.attempts || {};
|
|
65
|
+
|
|
66
|
+
const handoff = `# ZMemory Handoff
|
|
67
|
+
|
|
68
|
+
## Task
|
|
69
|
+
${taskText}
|
|
70
|
+
|
|
71
|
+
## Status
|
|
72
|
+
${state.status}
|
|
73
|
+
|
|
74
|
+
## Attempts
|
|
75
|
+
Error signature: ${attempts.current_error_signature || "none"}
|
|
76
|
+
Repeated count: ${attempts.repeated_count || 0}
|
|
77
|
+
|
|
78
|
+
## Completed Steps
|
|
79
|
+
${completed.join("\n")}
|
|
80
|
+
|
|
81
|
+
## Blockers
|
|
82
|
+
${blockers.join("\n")}
|
|
83
|
+
|
|
84
|
+
## Files Touched
|
|
85
|
+
${Array.from(filesTouched).join("\n")}
|
|
86
|
+
|
|
87
|
+
## Recent Events
|
|
88
|
+
${events.join("\n")}
|
|
89
|
+
|
|
90
|
+
## Next Action
|
|
91
|
+
${state.next_action}
|
|
92
|
+
|
|
93
|
+
## Stop Conditions
|
|
94
|
+
- Escalate to reviewer if repeated failures >=2
|
|
95
|
+
- Escalate to brain if repeated failures >=3
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
fs.writeFileSync(getRunFile(run, "handoff.md"), handoff);
|
|
99
|
+
|
|
100
|
+
console.log(handoff);
|
|
101
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
|
|
3
|
+
export async function healthCommand() {
|
|
4
|
+
const report = {
|
|
5
|
+
zmemory_exists: fs.existsSync(".zmemory"),
|
|
6
|
+
runs_dir: fs.existsSync(".zmemory/runs"),
|
|
7
|
+
events_log: fs.existsSync(".zmemory/logs/events.jsonl"),
|
|
8
|
+
tasks: fs.existsSync(".zmemory/tasks.json"),
|
|
9
|
+
agents: fs.existsSync(".zmemory/agents.json")
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
console.log("ZMemory health check\n");
|
|
13
|
+
|
|
14
|
+
Object.entries(report).forEach(([k, v]) => {
|
|
15
|
+
console.log(k.padEnd(20), v ? "OK" : "missing");
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function helpCommand() {
|
|
2
|
+
console.log("ZMemory");
|
|
3
|
+
console.log("Shared project memory for AI agents\n");
|
|
4
|
+
|
|
5
|
+
console.log("Core:");
|
|
6
|
+
console.log(" zmemory setup Interactive platform setup");
|
|
7
|
+
console.log(" zmemory init Initialize .zmemory in this repo");
|
|
8
|
+
console.log(" zmemory resume Bootstrap agent context\n");
|
|
9
|
+
|
|
10
|
+
console.log("Coordination:");
|
|
11
|
+
console.log(" zmemory claim <pattern> Claim files or modules");
|
|
12
|
+
console.log(" zmemory who <file> See who is working on a file");
|
|
13
|
+
console.log(" zmemory release --session Release claims\n");
|
|
14
|
+
|
|
15
|
+
console.log("Activity:");
|
|
16
|
+
console.log(" zmemory event Append activity event");
|
|
17
|
+
console.log(" zmemory explain <file> Explain activity around a file\n");
|
|
18
|
+
|
|
19
|
+
console.log("Agents:");
|
|
20
|
+
console.log(" zmemory agent register");
|
|
21
|
+
console.log(" zmemory agent list\n");
|
|
22
|
+
|
|
23
|
+
console.log("Diagnostics:");
|
|
24
|
+
console.log(" zmemory doctor\n");
|
|
25
|
+
|
|
26
|
+
console.log("Updates:");
|
|
27
|
+
console.log(" zmemory update");
|
|
28
|
+
console.log(" zmemory upgrade\n");
|
|
29
|
+
|
|
30
|
+
console.log("Flags:");
|
|
31
|
+
console.log(" zmemory -v | --version");
|
|
32
|
+
console.log(" zmemory -h | --help\n");
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { buildFileIndex, readFileIndex } from "../lib/fileIndex.js";
|
|
2
|
+
|
|
3
|
+
export async function indexCommand(args) {
|
|
4
|
+
const sub = args[0];
|
|
5
|
+
|
|
6
|
+
if (sub === "build") {
|
|
7
|
+
const files = buildFileIndex();
|
|
8
|
+
console.log("indexed", files.length, "files");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (sub === "show") {
|
|
13
|
+
const files = readFileIndex();
|
|
14
|
+
files.slice(0, 50).forEach((f) => console.log(f));
|
|
15
|
+
console.log("total:", files.length);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log("usage: zmemory index build|show");
|
|
20
|
+
}
|
|
21
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ensureDir, writeFile, writeJSON } from "../lib/fs.js";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export async function initProject() {
|
|
5
|
+
const base = ".zmemory";
|
|
6
|
+
|
|
7
|
+
ensureDir(base);
|
|
8
|
+
ensureDir(path.join(base, "runs/active"));
|
|
9
|
+
ensureDir(path.join(base, "runs/archived"));
|
|
10
|
+
ensureDir(path.join(base, "logs"));
|
|
11
|
+
ensureDir(path.join(base, "packets"));
|
|
12
|
+
ensureDir(path.join(base, "skills"));
|
|
13
|
+
|
|
14
|
+
writeJSON(path.join(base, "config.json"), {
|
|
15
|
+
project_name: "project",
|
|
16
|
+
memory_mode: "local",
|
|
17
|
+
default_context_budget_tokens: 8000,
|
|
18
|
+
default_roles: ["brain", "executor", "reviewer", "tester"],
|
|
19
|
+
event_log_format: "jsonl",
|
|
20
|
+
allow_mcp: true
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
writeFile(path.join(base, "project.md"), "# Project Memory\n");
|
|
24
|
+
writeFile(path.join(base, "decisions.md"), "# Decisions\n");
|
|
25
|
+
writeFile(path.join(base, "failures.md"), "# Failures\n");
|
|
26
|
+
writeFile(path.join(base, "patterns.md"), "# Patterns\n");
|
|
27
|
+
writeFile(path.join(base, "commands.md"), "# Useful Commands\n");
|
|
28
|
+
|
|
29
|
+
writeFile(path.join(base, "logs/events.jsonl"), "");
|
|
30
|
+
|
|
31
|
+
// create ignore template
|
|
32
|
+
writeFile(
|
|
33
|
+
".zmemoryignore",
|
|
34
|
+
"node_modules\ndist\n.git\n"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
console.log("ZMemory initialized");
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
|
|
3
|
+
// Simple installer that prepares a repo to use ZMemory
|
|
4
|
+
export async function installCommand() {
|
|
5
|
+
if (!fs.existsSync(".zmemory")) {
|
|
6
|
+
fs.mkdirSync(".zmemory", { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// ensure bootstrap so skills + logs exist
|
|
10
|
+
const bootstrap = await import("./bootstrap.js");
|
|
11
|
+
await bootstrap.bootstrapCommand?.();
|
|
12
|
+
|
|
13
|
+
const readme = `# ZMemory Integration
|
|
14
|
+
|
|
15
|
+
This repository uses ZMemory for AI agent coordination.
|
|
16
|
+
|
|
17
|
+
Common commands:
|
|
18
|
+
|
|
19
|
+
zmemory bootstrap
|
|
20
|
+
zmemory plan "goal"
|
|
21
|
+
zmemory worker codex
|
|
22
|
+
zmemory dashboard
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
fs.writeFileSync(".zmemory/README.md", readme);
|
|
26
|
+
|
|
27
|
+
console.log("ZMemory installed in repository");
|
|
28
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getRunFile, getRunBase } from "../lib/run.js";
|
|
4
|
+
|
|
5
|
+
export async function nextAction(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 statePath;
|
|
15
|
+
try {
|
|
16
|
+
statePath = getRunFile(run, "state.json");
|
|
17
|
+
} catch {
|
|
18
|
+
console.log("invalid run id");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(statePath)) {
|
|
23
|
+
console.log("run not found");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const state = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
28
|
+
|
|
29
|
+
console.log("Run:", run);
|
|
30
|
+
try {
|
|
31
|
+
const base = getRunBase(run);
|
|
32
|
+
const taskPath = path.join(base, "task.md");
|
|
33
|
+
if (fs.existsSync(taskPath)) {
|
|
34
|
+
const txt = fs.readFileSync(taskPath, "utf8");
|
|
35
|
+
const task = txt.split("\n").slice(1).join(" ").trim();
|
|
36
|
+
if (task) console.log("Task:", task);
|
|
37
|
+
}
|
|
38
|
+
} catch {}
|
|
39
|
+
console.log("Status:", state.status);
|
|
40
|
+
console.log("Current Step:", state.current_step);
|
|
41
|
+
|
|
42
|
+
console.log("\nNext Action:");
|
|
43
|
+
console.log(state.next_action || "none");
|
|
44
|
+
|
|
45
|
+
const blockers = state.blockers || [];
|
|
46
|
+
if (blockers.length) {
|
|
47
|
+
console.log("\nBlockers:");
|
|
48
|
+
blockers.forEach((b) => console.log("-", b));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const attempts = state.attempts || {};
|
|
52
|
+
const repeated = attempts.repeated_count || 0;
|
|
53
|
+
|
|
54
|
+
if (repeated >= 3) {
|
|
55
|
+
console.log("\nSuggestion: escalate to brain (repeated failures >=3)");
|
|
56
|
+
} else if (repeated >= 2) {
|
|
57
|
+
console.log("\nSuggestion: request reviewer (repeated failures >=2)");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { addTask } from "../lib/tasks.js";
|
|
2
|
+
import { scanModules } from "../lib/repoScan.js";
|
|
3
|
+
|
|
4
|
+
// Very simple deterministic planner.
|
|
5
|
+
// Splits a large goal into multiple tasks heuristically.
|
|
6
|
+
|
|
7
|
+
export async function planCommand(args) {
|
|
8
|
+
const goal = args.join(" ");
|
|
9
|
+
|
|
10
|
+
if (!goal) {
|
|
11
|
+
console.log("usage: zmemory plan <goal>");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log("Planning goal:", goal);
|
|
16
|
+
|
|
17
|
+
const modules = scanModules();
|
|
18
|
+
|
|
19
|
+
if (!modules.length) {
|
|
20
|
+
const t = addTask(`Implement: ${goal}`);
|
|
21
|
+
console.log("created task:", t.id, "-", t.title);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
modules.forEach((m) => {
|
|
26
|
+
const title = `${goal} in ${m}`;
|
|
27
|
+
const task = addTask(title, [m]);
|
|
28
|
+
console.log("created task:", task.id, "-", title);
|
|
29
|
+
});
|
|
30
|
+
}
|