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,68 @@
1
+ import fs from "fs";
2
+ import { getRunFile } from "../lib/run.js";
3
+ import { now, readJSON, writeJSON } from "../lib/fs.js";
4
+ import { acquireLock, releaseLock } from "../lib/lock.js";
5
+
6
+ export async function updateState(args) {
7
+ const get = (flag) => {
8
+ const i = args.indexOf(flag);
9
+ return i !== -1 ? args[i + 1] : null;
10
+ };
11
+
12
+ const run = get("--run");
13
+ if (!run) {
14
+ console.log("missing --run");
15
+ return;
16
+ }
17
+
18
+ let path;
19
+ try {
20
+ path = getRunFile(run, "state.json");
21
+ } catch {
22
+ console.log("invalid run id");
23
+ return;
24
+ }
25
+ if (!fs.existsSync(path)) {
26
+ console.log("run not found");
27
+ return;
28
+ }
29
+
30
+ try {
31
+ acquireLock(run);
32
+ } catch (e) {
33
+ console.log("run locked by another agent");
34
+ return;
35
+ }
36
+
37
+ let state;
38
+ try {
39
+ state = readJSON(path);
40
+
41
+ const status = get("--status");
42
+ const step = get("--current-step");
43
+ const next = get("--next-action");
44
+ const completed = get("--completed");
45
+ const blockers = get("--blockers");
46
+
47
+ if (status) state.status = status;
48
+ if (step) state.current_step = Number(step);
49
+ if (next) state.next_action = next;
50
+
51
+ if (completed) {
52
+ const items = completed.split(",").map((s) => s.trim()).filter(Boolean);
53
+ state.completed = [...(state.completed || []), ...items];
54
+ }
55
+
56
+ if (blockers) {
57
+ state.blockers = blockers.split(",").map((s) => s.trim());
58
+ }
59
+
60
+ state.updated_at = now();
61
+
62
+ writeJSON(path, state);
63
+ } finally {
64
+ releaseLock(run);
65
+ }
66
+
67
+ console.log("state updated");
68
+ }
@@ -0,0 +1,51 @@
1
+ import fs from "fs";
2
+ import { getRunFile, isValidRunId } from "../lib/run.js";
3
+
4
+ function readLastEntry(file) {
5
+ if (!fs.existsSync(file)) return null;
6
+ const lines = fs.readFileSync(file, "utf8").split("\n").filter(Boolean);
7
+ return lines.length ? lines[lines.length - 1] : null;
8
+ }
9
+
10
+ export async function status() {
11
+ if (!fs.existsSync(".zmemory")) {
12
+ console.log("ZMemory not initialized");
13
+ return;
14
+ }
15
+
16
+ const cfgPath = ".zmemory/config.json";
17
+ let project = "unknown";
18
+ if (fs.existsSync(cfgPath)) {
19
+ try {
20
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
21
+ project = cfg.project_name || project;
22
+ } catch {}
23
+ }
24
+
25
+ const runsPath = ".zmemory/runs/active";
26
+ const runs = fs.existsSync(runsPath) ? fs.readdirSync(runsPath) : [];
27
+
28
+ console.log("Project:", project);
29
+ console.log("\nActive Runs:");
30
+
31
+ runs.forEach((r) => {
32
+ if (!isValidRunId(r)) return;
33
+ let statePath;
34
+ try { statePath = getRunFile(r, "state.json"); } catch { return; }
35
+ if (fs.existsSync(statePath)) {
36
+ const state = JSON.parse(fs.readFileSync(statePath, "utf8"));
37
+ console.log(`- ${r} | status=${state.status} | next=${state.next_action || "none"}`);
38
+ } else {
39
+ console.log("-", r);
40
+ }
41
+ });
42
+
43
+ const lastDecision = readLastEntry(".zmemory/decisions.md");
44
+ const lastFailure = readLastEntry(".zmemory/failures.md");
45
+
46
+ console.log("\nLatest Decision:");
47
+ console.log(lastDecision || "none");
48
+
49
+ console.log("\nLatest Failure:");
50
+ console.log(lastFailure || "none");
51
+ }
@@ -0,0 +1,27 @@
1
+ import fs from "fs";
2
+
3
+ export async function streamEvents() {
4
+ const file = ".zmemory/logs/events.jsonl";
5
+
6
+ if (!fs.existsSync(file)) {
7
+ console.log("no event log");
8
+ return;
9
+ }
10
+
11
+ console.log("Streaming events (Ctrl+C to stop)...\n");
12
+
13
+ let size = fs.statSync(file).size;
14
+
15
+ setInterval(() => {
16
+ const stats = fs.statSync(file);
17
+ if (stats.size > size) {
18
+ const stream = fs.createReadStream(file, {
19
+ start: size,
20
+ end: stats.size
21
+ });
22
+ stream.pipe(process.stdout);
23
+ size = stats.size;
24
+ }
25
+ }, 1000);
26
+ }
27
+
@@ -0,0 +1,65 @@
1
+ import fs from "fs";
2
+ import { getRunBase } from "../lib/run.js";
3
+
4
+ export async function summary(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
+ let base;
14
+ try { base = getRunBase(run); } catch { console.log("invalid run id"); return; }
15
+ const statePath = `${base}/state.json`;
16
+ const taskPath = `${base}/task.md`;
17
+ const eventsPath = `${base}/events.jsonl`;
18
+
19
+ if (!fs.existsSync(statePath)) {
20
+ console.log("run not found");
21
+ return;
22
+ }
23
+
24
+ const state = JSON.parse(fs.readFileSync(statePath, "utf8"));
25
+
26
+ let task = "";
27
+ if (fs.existsSync(taskPath)) {
28
+ task = fs.readFileSync(taskPath, "utf8").trim();
29
+ }
30
+
31
+ console.log("Run:", run);
32
+ console.log("Status:", state.status);
33
+ console.log("Current Step:", state.current_step);
34
+
35
+ console.log("\nTask:");
36
+ console.log(task);
37
+
38
+ const completed = state.completed || [];
39
+ if (completed.length) {
40
+ console.log("\nCompleted Steps:");
41
+ completed.forEach((c) => console.log("-", c));
42
+ }
43
+
44
+ const blockers = state.blockers || [];
45
+ if (blockers.length) {
46
+ console.log("\nBlockers:");
47
+ blockers.forEach((b) => console.log("-", b));
48
+ }
49
+
50
+ if (fs.existsSync(eventsPath)) {
51
+ const events = fs
52
+ .readFileSync(eventsPath, "utf8")
53
+ .split("\n")
54
+ .filter(Boolean)
55
+ .slice(-10);
56
+
57
+ if (events.length) {
58
+ console.log("\nRecent Events:");
59
+ events.forEach((e) => console.log(e));
60
+ }
61
+ }
62
+
63
+ console.log("\nNext Action:");
64
+ console.log(state.next_action || "none");
65
+ }
@@ -0,0 +1,20 @@
1
+ import { syncPush, syncPull } from "../lib/teamSync.js";
2
+
3
+ export async function syncCommand(args) {
4
+ const sub = args[0];
5
+
6
+ if (sub === "push") {
7
+ syncPush();
8
+ console.log("zmemory pushed to remote directory");
9
+ return;
10
+ }
11
+
12
+ if (sub === "pull") {
13
+ syncPull();
14
+ console.log("zmemory pulled from remote directory");
15
+ return;
16
+ }
17
+
18
+ console.log("usage: zmemory sync push|pull");
19
+ }
20
+
@@ -0,0 +1,89 @@
1
+ import { addTask, addTaskWithDeps, listTasks, claimTask, completeTask } from "../lib/tasks.js";
2
+
3
+ export async function tasksCommand(args) {
4
+ const sub = args[0];
5
+
6
+ if (sub === "add") {
7
+ const usage = () => console.log("usage: zmemory tasks add <title> [--after id1,id2] [--files f1,f2]");
8
+
9
+ let deps = [];
10
+ let files = [];
11
+ const titleParts = [];
12
+
13
+ for (let i = 1; i < args.length; i++) {
14
+ const a = args[i];
15
+
16
+ if (a === "--after" || a === "--files") {
17
+ const val = args[i + 1];
18
+ if (typeof val !== "string" || !val.trim() || val.startsWith("--")) {
19
+ usage();
20
+ return;
21
+ }
22
+
23
+ const parts = val.split(",").map(s => s.trim()).filter(Boolean);
24
+ if (parts.length === 0) {
25
+ usage();
26
+ return;
27
+ }
28
+
29
+ if (a === "--after") deps = parts;
30
+ else files = parts;
31
+
32
+ i++;
33
+ continue;
34
+ }
35
+
36
+ titleParts.push(a);
37
+ }
38
+
39
+ const title = titleParts.join(" ").trim();
40
+ if (!title) {
41
+ usage();
42
+ return;
43
+ }
44
+
45
+ const task = deps.length
46
+ ? addTaskWithDeps(title, deps)
47
+ : addTask(title, files);
48
+ console.log("task created:", task.id);
49
+ return;
50
+ }
51
+
52
+ if (sub === "list") {
53
+ const tasks = listTasks();
54
+ tasks.forEach((t) => {
55
+ console.log(t.id, t.status, t.worker || "-");
56
+ if (t.depends_on && t.depends_on.length)
57
+ console.log(" depends_on:", t.depends_on.join(","));
58
+ console.log(" ", t.title);
59
+ });
60
+ return;
61
+ }
62
+
63
+ if (sub === "claim") {
64
+ const id = args[1];
65
+ const worker = args[2] || "agent";
66
+ if (!id || !String(id).trim()) {
67
+ console.log("usage: zmemory tasks claim <id> [worker]");
68
+ return;
69
+ }
70
+ const t = claimTask(id, worker);
71
+ if (!t) console.log("cannot claim task");
72
+ else console.log("task claimed:", id);
73
+ return;
74
+ }
75
+
76
+ if (sub === "done") {
77
+ const id = args[1];
78
+ if (!id || !String(id).trim()) {
79
+ console.log("usage: zmemory tasks done <id>");
80
+ return;
81
+ }
82
+ const t = completeTask(id);
83
+ if (!t) console.log("task not found");
84
+ else console.log("task completed:", id);
85
+ return;
86
+ }
87
+
88
+ console.log("usage: zmemory tasks add|list|claim|done");
89
+ }
@@ -0,0 +1,75 @@
1
+ import fs from "fs";
2
+ import { getRunFile, isValidRunId, ensureRunExists } from "../lib/run.js";
3
+
4
+ export async function runTimeline(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
+ const eventsPath = getRunFile(run, "events.jsonl");
26
+
27
+ if (!fs.existsSync(eventsPath)) {
28
+ console.log("no events");
29
+ return;
30
+ }
31
+
32
+ const lines = fs
33
+ .readFileSync(eventsPath, "utf8")
34
+ .split("\n")
35
+ .filter(Boolean);
36
+
37
+ if (!lines.length) {
38
+ console.log("no events");
39
+ return;
40
+ }
41
+
42
+ const events = [];
43
+
44
+ for (const l of lines) {
45
+ try {
46
+ const e = JSON.parse(l);
47
+ events.push(e);
48
+ } catch {}
49
+ }
50
+
51
+ events.sort((a, b) => (a.ts || "").localeCompare(b.ts || ""));
52
+
53
+ console.log(`# Run Timeline`);
54
+ console.log(`Run: ${run}\n`);
55
+
56
+ for (const e of events) {
57
+ const ts = e.ts || "unknown";
58
+ const role = e.role || "unknown";
59
+ const type = e.type || "event";
60
+ const summary = e.summary || "";
61
+
62
+ console.log(`${ts} [${role}] (${type})`);
63
+ if (summary) console.log(` ${summary}`);
64
+
65
+ if (Array.isArray(e.files) && e.files.length) {
66
+ console.log(` files: ${e.files.join(", ")}`);
67
+ }
68
+
69
+ if (e.error_signature) {
70
+ console.log(` error: ${e.error_signature}`);
71
+ }
72
+
73
+ console.log();
74
+ }
75
+ }
@@ -0,0 +1,19 @@
1
+ import { execSync } from "child_process";
2
+
3
+ export async function upgradeCommand() {
4
+ console.log("Checking for ZMemory updates...\n");
5
+
6
+ try {
7
+ execSync("npm install -g zmemory@latest", { stdio: "inherit" });
8
+ } catch {
9
+ console.log("\nUpgrade failed. Try manually:");
10
+ console.log("npm install -g zmemory@latest");
11
+ return;
12
+ }
13
+
14
+ try {
15
+ const { doctor } = await import("./doctor.js");
16
+ console.log("\nRunning diagnostics...\n");
17
+ await doctor();
18
+ } catch {}
19
+ }
@@ -0,0 +1,12 @@
1
+ import fs from "fs";
2
+ import { fileURLToPath } from "url";
3
+ import path from "path";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const pkgPath = path.resolve(__dirname, "../../package.json");
8
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
9
+
10
+ export async function versionCommand() {
11
+ console.log(`zmemory v${pkg.version}`);
12
+ }
@@ -0,0 +1,26 @@
1
+ import { who } from "../lib/coordination/who.js";
2
+
3
+ export async function whoCommand(args) {
4
+ const file = args[0];
5
+
6
+ if (!file) {
7
+ console.log("usage: zmemory who <file>");
8
+ return;
9
+ }
10
+
11
+ const matches = who(file);
12
+
13
+ if (!matches.length) {
14
+ console.log("no claims for", file);
15
+ return;
16
+ }
17
+
18
+ for (const m of matches) {
19
+ console.log(m.pattern);
20
+ for (const a of m.agents || []) {
21
+ const mins = typeof a.expires_in_minutes === "number" ? `${a.expires_in_minutes}m` : "?";
22
+ console.log(" ", a.session, a.task || "", `(expires in: ${mins})`);
23
+ }
24
+ console.log();
25
+ }
26
+ }
@@ -0,0 +1,71 @@
1
+ import http from "http";
2
+ import { claimTask, listTasks, completeTask } from "../lib/tasks.js";
3
+ import { lockFiles, releaseFiles } from "../lib/fileLocks.js";
4
+ import { appendFile, now } from "../lib/fs.js";
5
+
6
+ // Simple autonomous worker that listens to the event bus
7
+ // and attempts to claim pending tasks.
8
+
9
+ export async function workerCommand(args) {
10
+ const name = args[0] || "worker";
11
+
12
+ console.log("Starting worker:", name);
13
+
14
+ setInterval(() => {
15
+ const tasks = listTasks();
16
+ const pending = tasks.find((t) => {
17
+ if (t.status !== "pending") return false;
18
+
19
+ if (!t.depends_on || !t.depends_on.length) return true;
20
+
21
+ return t.depends_on.every((d) => {
22
+ const dep = tasks.find((x) => x.id === d);
23
+ return dep && dep.status === "done";
24
+ });
25
+ });
26
+
27
+ if (!pending) return;
28
+
29
+ const claimed = claimTask(pending.id, name);
30
+
31
+ if (!claimed) return;
32
+
33
+ if (claimed.file_scope && claimed.file_scope.length) {
34
+ try {
35
+ lockFiles(name, claimed.file_scope);
36
+ } catch {
37
+ return;
38
+ }
39
+ }
40
+
41
+ console.log("claimed task", claimed.id);
42
+
43
+ // simulate work
44
+ setTimeout(() => {
45
+ completeTask(claimed.id);
46
+
47
+ if (claimed.file_scope && claimed.file_scope.length) {
48
+ releaseFiles(name);
49
+ }
50
+
51
+ const evt = {
52
+ ts: now(),
53
+ role: "worker",
54
+ type: "task_completed",
55
+ summary: claimed.title,
56
+ task: claimed.id
57
+ };
58
+
59
+ appendFile(".zmemory/logs/events.jsonl", JSON.stringify(evt) + "\n");
60
+
61
+ console.log("completed task", claimed.id);
62
+ }, 2000);
63
+ }, 2000);
64
+
65
+ // keep process alive
66
+ http
67
+ .createServer((_, res) => {
68
+ res.end("worker alive");
69
+ })
70
+ .listen(0);
71
+ }
@@ -0,0 +1,25 @@
1
+ import { addWorkspace, listWorkspaces } from "../lib/workspaces.js";
2
+
3
+ export async function workspacesCommand(args) {
4
+ const sub = args[0];
5
+
6
+ if (sub === "add") {
7
+ const p = args[1];
8
+ if (typeof p !== "string" || p.trim().length === 0) {
9
+ console.log("usage: zmemory workspaces add <path>");
10
+ return;
11
+ }
12
+ const pathClean = p.trim();
13
+ addWorkspace(pathClean);
14
+ console.log("workspace added:", pathClean);
15
+ return;
16
+ }
17
+
18
+ if (sub === "list") {
19
+ const ws = listWorkspaces();
20
+ ws.forEach((w) => console.log(w));
21
+ return;
22
+ }
23
+
24
+ console.log("usage: zmemory workspaces add|list");
25
+ }
@@ -0,0 +1,34 @@
1
+ import { listTasks, claimTask, completeTask } from "../lib/tasks.js";
2
+ import { appendFile, now } from "../lib/fs.js";
3
+
4
+ // Very small orchestration loop that can coordinate workers
5
+ export function startOrchestrator() {
6
+ console.log("ZMemory orchestrator started");
7
+
8
+ setInterval(() => {
9
+ const tasks = listTasks();
10
+
11
+ const runnable = tasks.filter(
12
+ (t) => t.status === "pending" && (!t.depends_on || !t.depends_on.length)
13
+ );
14
+
15
+ runnable.forEach((task) => {
16
+ const claimed = claimTask(task.id, "orchestrator");
17
+ if (!claimed) return;
18
+
19
+ const evt = {
20
+ ts: now(),
21
+ role: "orchestrator",
22
+ type: "task_scheduled",
23
+ summary: task.title,
24
+ task: task.id
25
+ };
26
+
27
+ appendFile(".zmemory/logs/events.jsonl", JSON.stringify(evt) + "\n");
28
+
29
+ // immediately mark complete for prototype scheduling
30
+ completeTask(task.id);
31
+ });
32
+ }, 3000);
33
+ }
34
+
@@ -0,0 +1,17 @@
1
+ import { startDashboard } from "../dashboard/server.js";
2
+ import { startEventBus } from "../bus/server.js";
3
+ import { startOrchestrator } from "./orchestrator.js";
4
+ import { ensureZMemory } from "../integrations/auto-bootstrap.js";
5
+ import "../mcp/server.js"; // start MCP stdio server
6
+
7
+ // Unified daemon so harnesses only start one process
8
+ export function startDaemon() {
9
+ console.log("Starting ZMemory daemon...");
10
+ // ensure memory + autoloaded skill exist for harnesses
11
+ ensureZMemory();
12
+ startEventBus();
13
+ startOrchestrator();
14
+ startDashboard();
15
+ console.log("MCP interface ready (stdio)");
16
+ console.log("ZMemory daemon ready");
17
+ }
@@ -0,0 +1,40 @@
1
+ import http from "http";
2
+ import fs from "fs";
3
+
4
+ export function startDashboard() {
5
+ const port = 4321;
6
+
7
+ const server = http.createServer((req, res) => {
8
+ if (req.url === "/events") {
9
+ const data = fs.existsSync(".zmemory/logs/events.jsonl")
10
+ ? fs.readFileSync(".zmemory/logs/events.jsonl", "utf8")
11
+ : "";
12
+ res.writeHead(200, { "Content-Type": "text/plain" });
13
+ res.end(data);
14
+ return;
15
+ }
16
+
17
+ res.writeHead(200, { "Content-Type": "text/html" });
18
+ res.end(`
19
+ <html>
20
+ <body>
21
+ <h1>ZMemory Dashboard</h1>
22
+ <pre id="log"></pre>
23
+ <script>
24
+ async function load(){
25
+ const r = await fetch('/events');
26
+ const t = await r.text();
27
+ document.getElementById('log').textContent = t;
28
+ }
29
+ setInterval(load,1000);
30
+ load();
31
+ </script>
32
+ </body>
33
+ </html>
34
+ `);
35
+ });
36
+
37
+ server.listen(port);
38
+ console.log("dashboard running http://localhost:" + port);
39
+ }
40
+
@@ -0,0 +1,39 @@
1
+ # ZMemory Agent Hooks
2
+
3
+ This document explains how external AI coding agents should integrate with
4
+ ZMemory automatically.
5
+
6
+ Recommended workflow for agents:
7
+
8
+ 1. On startup
9
+
10
+ ```
11
+ zmemory resume
12
+ ```
13
+
14
+ 2. Before executing a task
15
+
16
+ ```
17
+ zmemory context --role executor --run <run_id>
18
+ ```
19
+
20
+ 3. After modifying files
21
+
22
+ ```
23
+ zmemory event --run <run_id> --role executor --type file_changed --summary "<change>"
24
+ ```
25
+
26
+ 4. After finishing a step
27
+
28
+ ```
29
+ zmemory state update --run <run_id> --next-action "<next step>"
30
+ ```
31
+
32
+ 5. Before terminating
33
+
34
+ ```
35
+ zmemory handoff --run <run_id>
36
+ ```
37
+
38
+ Agents can embed these commands directly in their execution loop.
39
+