omo-memory 0.1.7 → 0.1.8
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/README.md +1 -0
- package/dist/cli.js +6 -2
- package/dist/hookTemplates.js +2 -2
- package/dist/mcp.js +3 -1
- package/dist/memory.js +37 -112
- package/dist/memoryDb.js +72 -0
- package/dist/projectContext.js +27 -0
- package/dist/projectMigration.js +40 -0
- package/docs/adapter-integration.md +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ It gives lazycodex, omo-on-opencode, lfg, and future OMO adapters a shared local
|
|
|
11
11
|
|
|
12
12
|
- Project-local DB: `<project-root>/.omo/memory/state.sqlite`
|
|
13
13
|
- Project namespacing: by git remote + project root hash
|
|
14
|
+
- Move handling: if a project directory moves with its `.omo` ledger, existing rows are migrated to the new root automatically.
|
|
14
15
|
- Privacy default: local-only, no network sync, no secrets by design
|
|
15
16
|
- Intended adapters: Codex/lazycodex, OpenCode/OMO, GrokBuild/lfg
|
|
16
17
|
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { installHooks } from "./hooks.js";
|
|
4
|
-
import { bootstrapSession, doctorReport, exportMemory, initMemory, purgeMemory, recentEvents, recordEvent, startSession, writeHandoff } from "./memory.js";
|
|
5
4
|
import { runMcpServer } from "./mcp.js";
|
|
5
|
+
import { bootstrapSession, doctorReport, exportMemory, purgeMemory, recentEvents, recordEvent, startSession, writeHandoff } from "./memory.js";
|
|
6
|
+
import { initMemory } from "./memoryDb.js";
|
|
6
7
|
async function main(argv) {
|
|
7
8
|
const [command, subcommand, ...rest] = argv;
|
|
8
9
|
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
@@ -48,7 +49,10 @@ function runCommand(command, subcommand, rest) {
|
|
|
48
49
|
const summary = readFlag(rest, "--summary") ?? fail("event record requires --summary");
|
|
49
50
|
const payloadJson = readFlag(rest, "--payload-json");
|
|
50
51
|
const sessionId = readFlag(rest, "--session-id");
|
|
51
|
-
return {
|
|
52
|
+
return {
|
|
53
|
+
ok: true,
|
|
54
|
+
...recordEvent({ type, summary, ...(payloadJson === undefined ? {} : { payloadJson }), ...(sessionId === undefined ? {} : { sessionId }) }),
|
|
55
|
+
};
|
|
52
56
|
}
|
|
53
57
|
if (command === "recent") {
|
|
54
58
|
const limitRaw = readFlag([subcommand, ...rest].filter((value) => value !== undefined), "--limit");
|
package/dist/hookTemplates.js
CHANGED
|
@@ -84,7 +84,7 @@ try {
|
|
|
84
84
|
export const GROK_HOOK_SCRIPT = SESSION_BOOTSTRAP_SCRIPT.replace('?? "codex"', '?? "grok"').replace('?? "lazycodex"', '?? "lfg"');
|
|
85
85
|
export const GROK_PLUGIN_JSON = `{
|
|
86
86
|
"name": "omo-memory",
|
|
87
|
-
"version": "0.1.
|
|
87
|
+
"version": "0.1.8",
|
|
88
88
|
"description": "Project-local OMO Memory bootstrap hook and MCP server for Grok.",
|
|
89
89
|
"author": {
|
|
90
90
|
"name": "islee23520"
|
|
@@ -109,7 +109,7 @@ export const GROK_MCP_JSON = `{
|
|
|
109
109
|
`;
|
|
110
110
|
export const CODEX_PLUGIN_JSON = `{
|
|
111
111
|
"name": "omo-memory",
|
|
112
|
-
"version": "0.1.
|
|
112
|
+
"version": "0.1.8",
|
|
113
113
|
"description": "Session-start OMO Memory bootstrap hook for Codex.",
|
|
114
114
|
"author": "islee23520",
|
|
115
115
|
"homepage": "https://github.com/islee23520/omo-memory",
|
package/dist/mcp.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { bootstrapSession, exportMemory,
|
|
4
|
+
import { bootstrapSession, exportMemory, memoryPaths, PurgeConfirmationError, purgeMemory, recentEvents, recordEvent, startSession, writeHandoff, } from "./memory.js";
|
|
5
|
+
import { initMemory } from "./memoryDb.js";
|
|
6
|
+
import { resolveProjectContext } from "./projectContext.js";
|
|
5
7
|
export async function runMcpServer() {
|
|
6
8
|
const server = new McpServer({ name: "omo-memory", version: "0.1.2" });
|
|
7
9
|
server.registerTool("memory_init", {
|
package/dist/memory.js
CHANGED
|
@@ -1,94 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { redactSecrets, sanitizeGitRemote } from "./privacy.js";
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { migrate, openMemoryDb, SCHEMA_VERSION } from "./memoryDb.js";
|
|
3
|
+
import { redactSecrets } from "./privacy.js";
|
|
4
|
+
import { defaultDbPath, resolveProjectContext } from "./projectContext.js";
|
|
5
|
+
import { resolveStoredProject } from "./projectMigration.js";
|
|
7
6
|
export class PurgeConfirmationError extends Error {
|
|
8
7
|
constructor() {
|
|
9
8
|
super("purge requires --yes");
|
|
10
9
|
this.name = "PurgeConfirmationError";
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
|
-
const SCHEMA_VERSION = 1;
|
|
14
|
-
export function defaultDbPath() {
|
|
15
|
-
return process.env["OMO_MEMORY_DB"] ?? join(resolveProjectContext().repoRoot, ".omo", "memory", "state.sqlite");
|
|
16
|
-
}
|
|
17
12
|
export function memoryPaths() {
|
|
18
13
|
return { dbPath: defaultDbPath() };
|
|
19
14
|
}
|
|
20
|
-
export function openMemoryDb(dbPath = defaultDbPath()) {
|
|
21
|
-
mkdirSync(dirname(dbPath), { recursive: true });
|
|
22
|
-
const db = new Database(dbPath);
|
|
23
|
-
db.pragma("journal_mode = WAL");
|
|
24
|
-
return db;
|
|
25
|
-
}
|
|
26
|
-
export function migrate(db) {
|
|
27
|
-
db.exec(`
|
|
28
|
-
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
29
|
-
key TEXT PRIMARY KEY,
|
|
30
|
-
value TEXT NOT NULL
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
CREATE TABLE IF NOT EXISTS projects (
|
|
34
|
-
id TEXT PRIMARY KEY,
|
|
35
|
-
repo_root TEXT NOT NULL,
|
|
36
|
-
git_remote TEXT,
|
|
37
|
-
created_at TEXT NOT NULL,
|
|
38
|
-
last_seen_at TEXT NOT NULL
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
42
|
-
id TEXT PRIMARY KEY,
|
|
43
|
-
project_id TEXT NOT NULL,
|
|
44
|
-
host TEXT NOT NULL,
|
|
45
|
-
adapter TEXT NOT NULL,
|
|
46
|
-
started_at TEXT NOT NULL,
|
|
47
|
-
ended_at TEXT,
|
|
48
|
-
git_branch TEXT,
|
|
49
|
-
git_head TEXT,
|
|
50
|
-
FOREIGN KEY(project_id) REFERENCES projects(id)
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
CREATE TABLE IF NOT EXISTS events (
|
|
54
|
-
id TEXT PRIMARY KEY,
|
|
55
|
-
session_id TEXT,
|
|
56
|
-
project_id TEXT NOT NULL,
|
|
57
|
-
type TEXT NOT NULL,
|
|
58
|
-
summary TEXT NOT NULL,
|
|
59
|
-
payload_json TEXT,
|
|
60
|
-
created_at TEXT NOT NULL,
|
|
61
|
-
FOREIGN KEY(project_id) REFERENCES projects(id),
|
|
62
|
-
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
CREATE TABLE IF NOT EXISTS handoffs (
|
|
66
|
-
id TEXT PRIMARY KEY,
|
|
67
|
-
project_id TEXT NOT NULL,
|
|
68
|
-
session_id TEXT,
|
|
69
|
-
summary_md TEXT NOT NULL,
|
|
70
|
-
created_at TEXT NOT NULL,
|
|
71
|
-
FOREIGN KEY(project_id) REFERENCES projects(id),
|
|
72
|
-
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
|
73
|
-
);
|
|
74
|
-
`);
|
|
75
|
-
db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', ?)").run(String(SCHEMA_VERSION));
|
|
76
|
-
}
|
|
77
|
-
export function initMemory(dbPath = defaultDbPath()) {
|
|
78
|
-
const db = openMemoryDb(dbPath);
|
|
79
|
-
try {
|
|
80
|
-
migrate(db);
|
|
81
|
-
return { dbPath, schemaVersion: SCHEMA_VERSION };
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
db.close();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
15
|
export function doctorReport(dbPath = defaultDbPath()) {
|
|
88
16
|
const db = openMemoryDb(dbPath);
|
|
89
17
|
try {
|
|
90
18
|
migrate(db);
|
|
91
|
-
const project = resolveProjectContext();
|
|
19
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
92
20
|
const schemaVersion = Number(db.prepare("SELECT value FROM schema_meta WHERE key = 'schema_version'").pluck().get());
|
|
93
21
|
const count = (table) => Number(db.prepare(`SELECT COUNT(*) FROM ${table}`).pluck().get());
|
|
94
22
|
return {
|
|
@@ -107,15 +35,6 @@ export function doctorReport(dbPath = defaultDbPath()) {
|
|
|
107
35
|
db.close();
|
|
108
36
|
}
|
|
109
37
|
}
|
|
110
|
-
export function resolveProjectContext(cwd = process.cwd()) {
|
|
111
|
-
const repoRoot = gitValue(["rev-parse", "--show-toplevel"], cwd) ?? resolve(cwd);
|
|
112
|
-
const rawGitRemote = gitValue(["config", "--get", "remote.origin.url"], repoRoot);
|
|
113
|
-
const gitRemote = sanitizeGitRemote(rawGitRemote);
|
|
114
|
-
const gitBranch = gitValue(["rev-parse", "--abbrev-ref", "HEAD"], repoRoot);
|
|
115
|
-
const gitHead = gitValue(["rev-parse", "HEAD"], repoRoot);
|
|
116
|
-
const id = createHash("sha256").update(`${rawGitRemote ?? ""}\n${repoRoot}`).digest("hex").slice(0, 24);
|
|
117
|
-
return { id, repoRoot, gitRemote, gitBranch, gitHead };
|
|
118
|
-
}
|
|
119
38
|
export function upsertProject(db, project) {
|
|
120
39
|
const now = new Date().toISOString();
|
|
121
40
|
db.prepare(`
|
|
@@ -131,7 +50,7 @@ export function startSession(input, dbPath = defaultDbPath()) {
|
|
|
131
50
|
const db = openMemoryDb(dbPath);
|
|
132
51
|
try {
|
|
133
52
|
migrate(db);
|
|
134
|
-
const project = resolveProjectContext();
|
|
53
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
135
54
|
upsertProject(db, project);
|
|
136
55
|
const sessionId = randomUUID();
|
|
137
56
|
db.prepare(`
|
|
@@ -152,7 +71,7 @@ export function recordEvent(input, dbPath = defaultDbPath()) {
|
|
|
152
71
|
const db = openMemoryDb(dbPath);
|
|
153
72
|
try {
|
|
154
73
|
migrate(db);
|
|
155
|
-
const project = resolveProjectContext();
|
|
74
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
156
75
|
upsertProject(db, project);
|
|
157
76
|
const eventId = randomUUID();
|
|
158
77
|
db.prepare(`
|
|
@@ -169,14 +88,16 @@ export function recentEvents(limit, dbPath = defaultDbPath()) {
|
|
|
169
88
|
const db = openMemoryDb(dbPath);
|
|
170
89
|
try {
|
|
171
90
|
migrate(db);
|
|
172
|
-
const project = resolveProjectContext();
|
|
173
|
-
return db
|
|
91
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
92
|
+
return db
|
|
93
|
+
.prepare(`
|
|
174
94
|
SELECT id, type, summary, created_at AS createdAt, session_id AS sessionId
|
|
175
95
|
FROM events
|
|
176
96
|
WHERE project_id = ?
|
|
177
97
|
ORDER BY created_at DESC
|
|
178
98
|
LIMIT ?
|
|
179
|
-
`)
|
|
99
|
+
`)
|
|
100
|
+
.all(project.id, limit);
|
|
180
101
|
}
|
|
181
102
|
finally {
|
|
182
103
|
db.close();
|
|
@@ -186,7 +107,7 @@ export function writeHandoff(summaryMd, sessionId, dbPath = defaultDbPath()) {
|
|
|
186
107
|
const db = openMemoryDb(dbPath);
|
|
187
108
|
try {
|
|
188
109
|
migrate(db);
|
|
189
|
-
const project = resolveProjectContext();
|
|
110
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
190
111
|
upsertProject(db, project);
|
|
191
112
|
const handoffId = randomUUID();
|
|
192
113
|
db.prepare(`
|
|
@@ -203,19 +124,25 @@ export function exportMemory(dbPath = defaultDbPath()) {
|
|
|
203
124
|
const db = openMemoryDb(dbPath);
|
|
204
125
|
try {
|
|
205
126
|
migrate(db);
|
|
206
|
-
const project = resolveProjectContext();
|
|
207
|
-
const sessions = db
|
|
127
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
128
|
+
const sessions = db
|
|
129
|
+
.prepare(`
|
|
208
130
|
SELECT id, host, adapter, started_at AS startedAt, ended_at AS endedAt, git_branch AS gitBranch, git_head AS gitHead FROM sessions
|
|
209
131
|
WHERE project_id = ? ORDER BY started_at ASC, id ASC
|
|
210
|
-
`)
|
|
211
|
-
|
|
132
|
+
`)
|
|
133
|
+
.all(project.id);
|
|
134
|
+
const events = db
|
|
135
|
+
.prepare(`
|
|
212
136
|
SELECT id, session_id AS sessionId, type, summary, payload_json AS payloadJson, created_at AS createdAt FROM events
|
|
213
137
|
WHERE project_id = ? ORDER BY created_at ASC, id ASC
|
|
214
|
-
`)
|
|
215
|
-
|
|
138
|
+
`)
|
|
139
|
+
.all(project.id);
|
|
140
|
+
const handoffs = db
|
|
141
|
+
.prepare(`
|
|
216
142
|
SELECT id, session_id AS sessionId, summary_md AS summaryMd, created_at AS createdAt FROM handoffs
|
|
217
143
|
WHERE project_id = ? ORDER BY created_at ASC, id ASC
|
|
218
|
-
`)
|
|
144
|
+
`)
|
|
145
|
+
.all(project.id);
|
|
219
146
|
return {
|
|
220
147
|
schemaVersion: SCHEMA_VERSION,
|
|
221
148
|
exportedAt: new Date().toISOString(),
|
|
@@ -236,11 +163,17 @@ export function purgeMemory(input, dbPath = defaultDbPath()) {
|
|
|
236
163
|
const db = openMemoryDb(dbPath);
|
|
237
164
|
try {
|
|
238
165
|
migrate(db);
|
|
239
|
-
const project = resolveProjectContext();
|
|
166
|
+
const project = resolveStoredProject(db, resolveProjectContext());
|
|
240
167
|
const deleteProject = db.transaction(() => {
|
|
241
|
-
const events = db
|
|
242
|
-
|
|
243
|
-
|
|
168
|
+
const events = db
|
|
169
|
+
.prepare("DELETE FROM events WHERE project_id IN (SELECT id FROM projects WHERE id = ? OR repo_root = ?)")
|
|
170
|
+
.run(project.id, project.repoRoot).changes;
|
|
171
|
+
const handoffs = db
|
|
172
|
+
.prepare("DELETE FROM handoffs WHERE project_id IN (SELECT id FROM projects WHERE id = ? OR repo_root = ?)")
|
|
173
|
+
.run(project.id, project.repoRoot).changes;
|
|
174
|
+
const sessions = db
|
|
175
|
+
.prepare("DELETE FROM sessions WHERE project_id IN (SELECT id FROM projects WHERE id = ? OR repo_root = ?)")
|
|
176
|
+
.run(project.id, project.repoRoot).changes;
|
|
244
177
|
const projects = db.prepare("DELETE FROM projects WHERE id = ? OR repo_root = ?").run(project.id, project.repoRoot).changes;
|
|
245
178
|
return { events, handoffs, sessions, projects };
|
|
246
179
|
});
|
|
@@ -250,11 +183,3 @@ export function purgeMemory(input, dbPath = defaultDbPath()) {
|
|
|
250
183
|
db.close();
|
|
251
184
|
}
|
|
252
185
|
}
|
|
253
|
-
function gitValue(args, cwd) {
|
|
254
|
-
try {
|
|
255
|
-
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
}
|
package/dist/memoryDb.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { defaultDbPath } from "./projectContext.js";
|
|
5
|
+
export const SCHEMA_VERSION = 1;
|
|
6
|
+
export function openMemoryDb(dbPath = defaultDbPath()) {
|
|
7
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
8
|
+
const db = new Database(dbPath);
|
|
9
|
+
db.pragma("journal_mode = WAL");
|
|
10
|
+
return db;
|
|
11
|
+
}
|
|
12
|
+
export function migrate(db) {
|
|
13
|
+
db.exec(`
|
|
14
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
15
|
+
key TEXT PRIMARY KEY,
|
|
16
|
+
value TEXT NOT NULL
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
repo_root TEXT NOT NULL,
|
|
22
|
+
git_remote TEXT,
|
|
23
|
+
created_at TEXT NOT NULL,
|
|
24
|
+
last_seen_at TEXT NOT NULL
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
project_id TEXT NOT NULL,
|
|
30
|
+
host TEXT NOT NULL,
|
|
31
|
+
adapter TEXT NOT NULL,
|
|
32
|
+
started_at TEXT NOT NULL,
|
|
33
|
+
ended_at TEXT,
|
|
34
|
+
git_branch TEXT,
|
|
35
|
+
git_head TEXT,
|
|
36
|
+
FOREIGN KEY(project_id) REFERENCES projects(id)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
40
|
+
id TEXT PRIMARY KEY,
|
|
41
|
+
session_id TEXT,
|
|
42
|
+
project_id TEXT NOT NULL,
|
|
43
|
+
type TEXT NOT NULL,
|
|
44
|
+
summary TEXT NOT NULL,
|
|
45
|
+
payload_json TEXT,
|
|
46
|
+
created_at TEXT NOT NULL,
|
|
47
|
+
FOREIGN KEY(project_id) REFERENCES projects(id),
|
|
48
|
+
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS handoffs (
|
|
52
|
+
id TEXT PRIMARY KEY,
|
|
53
|
+
project_id TEXT NOT NULL,
|
|
54
|
+
session_id TEXT,
|
|
55
|
+
summary_md TEXT NOT NULL,
|
|
56
|
+
created_at TEXT NOT NULL,
|
|
57
|
+
FOREIGN KEY(project_id) REFERENCES projects(id),
|
|
58
|
+
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
|
59
|
+
);
|
|
60
|
+
`);
|
|
61
|
+
db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', ?)").run(String(SCHEMA_VERSION));
|
|
62
|
+
}
|
|
63
|
+
export function initMemory(dbPath = defaultDbPath()) {
|
|
64
|
+
const db = openMemoryDb(dbPath);
|
|
65
|
+
try {
|
|
66
|
+
migrate(db);
|
|
67
|
+
return { dbPath, schemaVersion: SCHEMA_VERSION };
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
db.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { sanitizeGitRemote } from "./privacy.js";
|
|
5
|
+
export function defaultDbPath() {
|
|
6
|
+
return process.env["OMO_MEMORY_DB"] ?? join(resolveProjectContext().repoRoot, ".omo", "memory", "state.sqlite");
|
|
7
|
+
}
|
|
8
|
+
export function resolveProjectContext(cwd = process.cwd()) {
|
|
9
|
+
const repoRoot = gitValue(["rev-parse", "--show-toplevel"], cwd) ?? resolve(cwd);
|
|
10
|
+
const rawGitRemote = gitValue(["config", "--get", "remote.origin.url"], repoRoot);
|
|
11
|
+
const gitRemote = sanitizeGitRemote(rawGitRemote);
|
|
12
|
+
const gitBranch = gitValue(["rev-parse", "--abbrev-ref", "HEAD"], repoRoot);
|
|
13
|
+
const gitHead = gitValue(["rev-parse", "HEAD"], repoRoot);
|
|
14
|
+
const id = createHash("sha256")
|
|
15
|
+
.update(`${rawGitRemote ?? ""}\n${repoRoot}`)
|
|
16
|
+
.digest("hex")
|
|
17
|
+
.slice(0, 24);
|
|
18
|
+
return { id, repoRoot, gitRemote, gitBranch, gitHead };
|
|
19
|
+
}
|
|
20
|
+
function gitValue(args, cwd) {
|
|
21
|
+
try {
|
|
22
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function resolveStoredProject(db, project) {
|
|
2
|
+
const current = readProjectById(db, project.id);
|
|
3
|
+
if (current !== undefined) {
|
|
4
|
+
updateProjectMetadata(db, project);
|
|
5
|
+
return project;
|
|
6
|
+
}
|
|
7
|
+
const candidate = findMovedProjectCandidate(db, project);
|
|
8
|
+
if (candidate === undefined || candidate.id === project.id)
|
|
9
|
+
return project;
|
|
10
|
+
updateProjectMetadata(db, { ...project, id: candidate.id });
|
|
11
|
+
return { ...project, id: candidate.id };
|
|
12
|
+
}
|
|
13
|
+
function findMovedProjectCandidate(db, project) {
|
|
14
|
+
const byRoot = readProjectByRepoRoot(db, project.repoRoot);
|
|
15
|
+
if (byRoot !== undefined)
|
|
16
|
+
return byRoot;
|
|
17
|
+
if (project.gitRemote !== null) {
|
|
18
|
+
const byRemote = readProjectByGitRemote(db, project.gitRemote);
|
|
19
|
+
if (byRemote !== undefined)
|
|
20
|
+
return byRemote;
|
|
21
|
+
}
|
|
22
|
+
const allProjects = db
|
|
23
|
+
.prepare("SELECT id, repo_root, git_remote, created_at FROM projects ORDER BY last_seen_at DESC, created_at DESC")
|
|
24
|
+
.all();
|
|
25
|
+
return allProjects.length === 1 ? allProjects[0] : undefined;
|
|
26
|
+
}
|
|
27
|
+
function readProjectById(db, id) {
|
|
28
|
+
return db.prepare("SELECT id, repo_root, git_remote, created_at FROM projects WHERE id = ?").get(id);
|
|
29
|
+
}
|
|
30
|
+
function readProjectByRepoRoot(db, repoRoot) {
|
|
31
|
+
return db.prepare("SELECT id, repo_root, git_remote, created_at FROM projects WHERE repo_root = ?").get(repoRoot);
|
|
32
|
+
}
|
|
33
|
+
function readProjectByGitRemote(db, gitRemote) {
|
|
34
|
+
return db
|
|
35
|
+
.prepare("SELECT id, repo_root, git_remote, created_at FROM projects WHERE git_remote = ? ORDER BY last_seen_at DESC, created_at DESC")
|
|
36
|
+
.get(gitRemote);
|
|
37
|
+
}
|
|
38
|
+
function updateProjectMetadata(db, project) {
|
|
39
|
+
db.prepare("UPDATE projects SET repo_root = ?, git_remote = ?, last_seen_at = ? WHERE id = ?").run(project.repoRoot, project.gitRemote, new Date().toISOString(), project.id);
|
|
40
|
+
}
|
|
@@ -11,6 +11,7 @@ No full transcript capture by default. Do not store API keys, tokens, `.env` con
|
|
|
11
11
|
- Record concise events with a stable `type`, a human-readable `summary`, and optional redacted JSON metadata in `payloadJson`.
|
|
12
12
|
- Store handoffs as summary markdown that another host can read without needing the originating transcript.
|
|
13
13
|
- Treat CLI and MCP as two entrypoints to the same core functions and schema.
|
|
14
|
+
- When a project directory moves with its `.omo` ledger, OMO Memory migrates matching project rows to the new root automatically.
|
|
14
15
|
- Use `OMO_MEMORY_DB` only when the caller explicitly chooses a different database path, such as an isolated smoke test.
|
|
15
16
|
|
|
16
17
|
## Adapter Metadata
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omo-memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Host-neutral local SQLite memory and session ledger for OMO adapters, exposed through CLI and MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsc -p tsconfig.json",
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "biome check .",
|
|
29
|
+
"format": "biome format --write .",
|
|
30
|
+
"check": "npm run lint && npm run typecheck && npm run smoke",
|
|
28
31
|
"start": "node dist/cli.js",
|
|
29
32
|
"prepack": "npm run build",
|
|
30
33
|
"presmoke": "npm run build",
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
"zod": "^4.4.3"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "^2.5.1",
|
|
43
47
|
"@types/better-sqlite3": "^7.6.13",
|
|
44
48
|
"@types/node": "^24.10.1",
|
|
45
49
|
"typescript": "^6.0.3"
|