@dahch/muninn 0.0.0-stage → 0.2.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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +395 -2
  3. package/dist/cli.d.ts +23 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +186 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/check.d.ts +3 -0
  8. package/dist/commands/check.d.ts.map +1 -0
  9. package/dist/commands/check.js +56 -0
  10. package/dist/commands/check.js.map +1 -0
  11. package/dist/commands/memory.d.ts +6 -0
  12. package/dist/commands/memory.d.ts.map +1 -0
  13. package/dist/commands/memory.js +374 -0
  14. package/dist/commands/memory.js.map +1 -0
  15. package/dist/commands/setup.d.ts +3 -0
  16. package/dist/commands/setup.d.ts.map +1 -0
  17. package/dist/commands/setup.js +397 -0
  18. package/dist/commands/setup.js.map +1 -0
  19. package/dist/contracts/compiler.d.ts +71 -0
  20. package/dist/contracts/compiler.d.ts.map +1 -0
  21. package/dist/contracts/compiler.js +362 -0
  22. package/dist/contracts/compiler.js.map +1 -0
  23. package/dist/contracts/index.d.ts +2 -0
  24. package/dist/contracts/index.d.ts.map +1 -0
  25. package/dist/contracts/index.js +2 -0
  26. package/dist/contracts/index.js.map +1 -0
  27. package/dist/db/client.d.ts +100 -0
  28. package/dist/db/client.d.ts.map +1 -0
  29. package/dist/db/client.js +318 -0
  30. package/dist/db/client.js.map +1 -0
  31. package/dist/db/schema.sql +99 -0
  32. package/dist/index.d.ts +23 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +23 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/indexer/ast-indexer.d.ts +55 -0
  37. package/dist/indexer/ast-indexer.d.ts.map +1 -0
  38. package/dist/indexer/ast-indexer.js +445 -0
  39. package/dist/indexer/ast-indexer.js.map +1 -0
  40. package/dist/indexer/index.d.ts +2 -0
  41. package/dist/indexer/index.d.ts.map +1 -0
  42. package/dist/indexer/index.js +2 -0
  43. package/dist/indexer/index.js.map +1 -0
  44. package/dist/mcp/index.d.ts +2 -0
  45. package/dist/mcp/index.d.ts.map +1 -0
  46. package/dist/mcp/index.js +2 -0
  47. package/dist/mcp/index.js.map +1 -0
  48. package/dist/mcp/server.d.ts +254 -0
  49. package/dist/mcp/server.d.ts.map +1 -0
  50. package/dist/mcp/server.js +636 -0
  51. package/dist/mcp/server.js.map +1 -0
  52. package/dist/service/index.d.ts +2 -0
  53. package/dist/service/index.d.ts.map +1 -0
  54. package/dist/service/index.js +2 -0
  55. package/dist/service/index.js.map +1 -0
  56. package/dist/service/memory-service.d.ts +320 -0
  57. package/dist/service/memory-service.d.ts.map +1 -0
  58. package/dist/service/memory-service.js +1511 -0
  59. package/dist/service/memory-service.js.map +1 -0
  60. package/dist/setup/index.d.ts +6 -0
  61. package/dist/setup/index.d.ts.map +1 -0
  62. package/dist/setup/index.js +6 -0
  63. package/dist/setup/index.js.map +1 -0
  64. package/dist/setup/json-target.d.ts +30 -0
  65. package/dist/setup/json-target.d.ts.map +1 -0
  66. package/dist/setup/json-target.js +263 -0
  67. package/dist/setup/json-target.js.map +1 -0
  68. package/dist/setup/portable.d.ts +56 -0
  69. package/dist/setup/portable.d.ts.map +1 -0
  70. package/dist/setup/portable.js +365 -0
  71. package/dist/setup/portable.js.map +1 -0
  72. package/dist/setup/registry.d.ts +54 -0
  73. package/dist/setup/registry.d.ts.map +1 -0
  74. package/dist/setup/registry.js +225 -0
  75. package/dist/setup/registry.js.map +1 -0
  76. package/dist/setup/rules-target.d.ts +31 -0
  77. package/dist/setup/rules-target.d.ts.map +1 -0
  78. package/dist/setup/rules-target.js +270 -0
  79. package/dist/setup/rules-target.js.map +1 -0
  80. package/dist/setup/toml-target.d.ts +62 -0
  81. package/dist/setup/toml-target.d.ts.map +1 -0
  82. package/dist/setup/toml-target.js +869 -0
  83. package/dist/setup/toml-target.js.map +1 -0
  84. package/dist/util/path-safety.d.ts +94 -0
  85. package/dist/util/path-safety.d.ts.map +1 -0
  86. package/dist/util/path-safety.js +162 -0
  87. package/dist/util/path-safety.js.map +1 -0
  88. package/dist/util/text.d.ts +9 -0
  89. package/dist/util/text.d.ts.map +1 -0
  90. package/dist/util/text.js +19 -0
  91. package/dist/util/text.js.map +1 -0
  92. package/package.json +87 -4
@@ -0,0 +1,318 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import crypto from "node:crypto";
5
+ import { fileURLToPath } from "node:url";
6
+ import Database from "better-sqlite3";
7
+ import { assertDocPath } from "../util/path-safety.js";
8
+ export const SCHEMA_SQL = `-- Muninn Engine Database Schema
9
+ -- Version 1.0.0
10
+
11
+ -- Projects table
12
+ CREATE TABLE IF NOT EXISTS projects (
13
+ id TEXT PRIMARY KEY,
14
+ name TEXT NOT NULL,
15
+ git_remote TEXT,
16
+ root_path TEXT NOT NULL UNIQUE,
17
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
18
+ );
19
+
20
+ CREATE INDEX IF NOT EXISTS idx_projects_root_path ON projects(root_path);
21
+
22
+ -- Observations table
23
+ CREATE TABLE IF NOT EXISTS observations (
24
+ id TEXT PRIMARY KEY,
25
+ project_id TEXT NOT NULL,
26
+ category TEXT CHECK(category IN ('decision', 'convention', 'discovery', 'bugfix', 'architecture')) NOT NULL,
27
+ title TEXT NOT NULL,
28
+ content TEXT NOT NULL,
29
+ topic_key TEXT,
30
+ status TEXT CHECK(status IN ('active', 'superseded')) NOT NULL DEFAULT 'active',
31
+ superseded_by TEXT,
32
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
33
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
34
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
35
+ );
36
+
37
+ CREATE INDEX IF NOT EXISTS idx_observations_project_id ON observations(project_id);
38
+ CREATE INDEX IF NOT EXISTS idx_observations_updated_at ON observations(updated_at);
39
+ CREATE INDEX IF NOT EXISTS idx_observations_project_updated ON observations(project_id, updated_at DESC, created_at DESC);
40
+
41
+ -- Observations FTS5 virtual table
42
+ CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
43
+ title,
44
+ content,
45
+ topic_key,
46
+ content='observations',
47
+ content_rowid='rowid'
48
+ );
49
+
50
+ -- Triggers for automatic FTS5 synchronization
51
+ CREATE TRIGGER IF NOT EXISTS obs_ai AFTER INSERT ON observations BEGIN
52
+ INSERT INTO observations_fts(rowid, title, content, topic_key)
53
+ VALUES (new.rowid, new.title, new.content, new.topic_key);
54
+ END;
55
+
56
+ CREATE TRIGGER IF NOT EXISTS obs_ad AFTER DELETE ON observations BEGIN
57
+ INSERT INTO observations_fts(observations_fts, rowid, title, content, topic_key)
58
+ VALUES ('delete', old.rowid, old.title, old.content, old.topic_key);
59
+ END;
60
+
61
+ CREATE TRIGGER IF NOT EXISTS obs_au AFTER UPDATE ON observations BEGIN
62
+ INSERT INTO observations_fts(observations_fts, rowid, title, content, topic_key)
63
+ VALUES ('delete', old.rowid, old.title, old.content, old.topic_key);
64
+ INSERT INTO observations_fts(rowid, title, content, topic_key)
65
+ VALUES (new.rowid, new.title, new.content, new.topic_key);
66
+ END;
67
+
68
+ -- Entities table
69
+ CREATE TABLE IF NOT EXISTS entities (
70
+ id TEXT PRIMARY KEY,
71
+ project_id TEXT NOT NULL,
72
+ entity_type TEXT CHECK(entity_type IN ('file', 'function', 'class', 'interface', 'module')) NOT NULL,
73
+ identifier TEXT NOT NULL,
74
+ file_path TEXT NOT NULL,
75
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
76
+ );
77
+
78
+ CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
79
+ CREATE INDEX IF NOT EXISTS idx_entities_identifier ON entities(identifier);
80
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_entities_project_identifier ON entities(project_id, identifier);
81
+
82
+ -- Observation Entities join table
83
+ CREATE TABLE IF NOT EXISTS observation_entities (
84
+ observation_id TEXT NOT NULL,
85
+ entity_id TEXT NOT NULL,
86
+ PRIMARY KEY(observation_id, entity_id),
87
+ FOREIGN KEY(observation_id) REFERENCES observations(id) ON DELETE CASCADE,
88
+ FOREIGN KEY(entity_id) REFERENCES entities(id) ON DELETE CASCADE
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_observation_entities_entity_id ON observation_entities(entity_id);
92
+
93
+ -- Entity Dependencies table
94
+ -- Relaciones topológicas entre símbolos (A llama/importa a B, o A implementa a B)
95
+ CREATE TABLE IF NOT EXISTS entity_dependencies (
96
+ source_entity_id TEXT NOT NULL,
97
+ target_entity_id TEXT NOT NULL,
98
+ relation_type TEXT CHECK(relation_type IN ('imports', 'calls', 'implements', 'extends', 'references')) NOT NULL,
99
+ PRIMARY KEY(source_entity_id, target_entity_id, relation_type),
100
+ FOREIGN KEY(source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
101
+ FOREIGN KEY(target_entity_id) REFERENCES entities(id) ON DELETE CASCADE
102
+ );
103
+
104
+ CREATE INDEX IF NOT EXISTS idx_entity_deps_source ON entity_dependencies(source_entity_id);
105
+ CREATE INDEX IF NOT EXISTS idx_entity_deps_target ON entity_dependencies(target_entity_id);
106
+ `;
107
+ /**
108
+ * Robustly load schema SQL from disk if available, otherwise fallback to embedded SCHEMA_SQL.
109
+ */
110
+ export function loadSchemaSql() {
111
+ try {
112
+ const currentDir = typeof __dirname !== "undefined"
113
+ ? __dirname
114
+ : path.dirname(fileURLToPath(import.meta.url));
115
+ const schemaFile = path.join(currentDir, "schema.sql");
116
+ if (fs.existsSync(schemaFile)) {
117
+ return fs.readFileSync(schemaFile, "utf-8");
118
+ }
119
+ }
120
+ catch {
121
+ // Ignore and fallback to embedded schema
122
+ }
123
+ return SCHEMA_SQL;
124
+ }
125
+ /**
126
+ * Strips embedded basic auth credentials (e.g. https://user:pass@host/... -> https://host/...)
127
+ * from a git remote URL before saving git_remote.
128
+ */
129
+ export function sanitizeGitRemote(rawUrl) {
130
+ try {
131
+ const parsed = new URL(rawUrl);
132
+ if (parsed.username || parsed.password) {
133
+ if (parsed.protocol === "ssh:" && !parsed.password) {
134
+ return rawUrl;
135
+ }
136
+ parsed.username = "";
137
+ parsed.password = "";
138
+ return parsed.toString();
139
+ }
140
+ return rawUrl;
141
+ }
142
+ catch {
143
+ // Regex fallback for non-standard URLs or git SCP-like syntax
144
+ return rawUrl.replace(/^([a-zA-Z+-]+:\/\/)[^/@]+@/, "$1");
145
+ }
146
+ }
147
+ /**
148
+ * Searches upward from startDir for the nearest directory or parent containing a .git folder or file.
149
+ */
150
+ export function findGitRoot(startDir = process.cwd()) {
151
+ let current = path.resolve(startDir);
152
+ while (true) {
153
+ const gitPath = path.join(current, ".git");
154
+ if (fs.existsSync(gitPath)) {
155
+ return current;
156
+ }
157
+ const parent = path.dirname(current);
158
+ if (parent === current) {
159
+ break;
160
+ }
161
+ current = parent;
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Inspects a git repository root to extract the default origin remote URL if present.
167
+ */
168
+ export function tryGetGitRemote(gitRoot) {
169
+ try {
170
+ const configPath = path.join(gitRoot, ".git", "config");
171
+ if (fs.existsSync(configPath)) {
172
+ const content = fs.readFileSync(configPath, "utf-8");
173
+ const match = content.match(/\[remote "origin"\][^\[]*url\s*=\s*([^\n\r]+)/);
174
+ if (match && match[1]) {
175
+ return sanitizeGitRemote(match[1].trim());
176
+ }
177
+ }
178
+ }
179
+ catch {
180
+ // Ignore error
181
+ }
182
+ return null;
183
+ }
184
+ /**
185
+ * Resolves the database path for Muninn.
186
+ * - If customPath is given, return it (e.g. ':memory:' or specific path).
187
+ * - Check if cwd (or startDir) or any parent directory has .git. If so, resolve to `<git_root>/.huginn/muninn.db`.
188
+ * - Fallback to `join(homedir(), '.huginn', 'muninn.db')`.
189
+ */
190
+ export function resolveDatabasePath(customPath, startDir = process.cwd()) {
191
+ if (customPath !== undefined && customPath !== "") {
192
+ return customPath;
193
+ }
194
+ const gitRoot = findGitRoot(startDir);
195
+ if (gitRoot) {
196
+ return path.join(gitRoot, ".huginn", "muninn.db");
197
+ }
198
+ return path.join(os.homedir(), ".huginn", "muninn.db");
199
+ }
200
+ /**
201
+ * Initializes and returns a SQLite database instance using better-sqlite3.
202
+ * Configures foreign_keys = ON, journal_mode = WAL, busy_timeout = 5000, recursive_triggers = ON, and executes schema.sql DDL.
203
+ */
204
+ export function getDatabase(dbPath, startDir) {
205
+ // The database and the project record must come from ONE root (ADR-49): the
206
+ // caller passes the same directory it hands to `ensureProject`.
207
+ const resolvedPath = resolveDatabasePath(dbPath, startDir);
208
+ if (resolvedPath !== ":memory:") {
209
+ // A cloned repository can ship `.huginn` — or the database file itself — as a
210
+ // symlink and redirect the SQLite write outside the project (SEC-005). The
211
+ // path is screened exactly like the harness state, the receipts and the
212
+ // promotion backup; the lexical path is kept for the open so callers see the
213
+ // path they named.
214
+ assertDocPath(resolvedPath, {
215
+ projectPath: findGitRoot(startDir ?? process.cwd()) ?? undefined,
216
+ label: "the Muninn database",
217
+ action: "write",
218
+ });
219
+ const dir = path.dirname(resolvedPath);
220
+ if (!fs.existsSync(dir)) {
221
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
222
+ }
223
+ }
224
+ const db = new Database(resolvedPath, { timeout: 5000 });
225
+ try {
226
+ db.pragma("foreign_keys = ON");
227
+ db.pragma("journal_mode = WAL");
228
+ db.pragma("busy_timeout = 5000");
229
+ db.pragma("recursive_triggers = ON");
230
+ const schema = loadSchemaSql();
231
+ db.exec(schema);
232
+ // Databases created before decision supersession lack these columns. Keep
233
+ // the migration column-by-column and idempotent, so reopening never touches
234
+ // an already migrated row, index, or trigger.
235
+ const columns = new Set(db
236
+ .prepare("PRAGMA table_info(observations)")
237
+ .all()
238
+ .map((column) => column.name));
239
+ if (!columns.has("status")) {
240
+ db.exec("ALTER TABLE observations ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'superseded'))");
241
+ }
242
+ if (!columns.has("superseded_by")) {
243
+ db.exec("ALTER TABLE observations ADD COLUMN superseded_by TEXT");
244
+ }
245
+ }
246
+ catch (err) {
247
+ db.close();
248
+ throw err;
249
+ }
250
+ return db;
251
+ }
252
+ /**
253
+ * Opens an **existing** Muninn database without any initialization side effects.
254
+ *
255
+ * This is the access path for read-only bootstrap: it resolves the same default
256
+ * location as {@link getDatabase}, but never creates a directory or file, never
257
+ * applies write-capable pragmas, never runs schema DDL/migrations and never
258
+ * inserts a project row. Opening a missing, inaccessible or corrupt file fails
259
+ * with the underlying SQLite error; callers that must fail open catch it and
260
+ * return an empty session brief with a note.
261
+ */
262
+ export function getReadOnlyDatabase(dbPath, startDir) {
263
+ const resolvedPath = resolveDatabasePath(dbPath, startDir);
264
+ if (resolvedPath === ":memory:") {
265
+ throw new Error("A read-only session brief requires an existing database file, not :memory:.");
266
+ }
267
+ // Bootstrap must not follow a repository-shipped symlink either. The screen
268
+ // does not write anything; a refused path simply becomes an unavailable brief.
269
+ assertDocPath(resolvedPath, {
270
+ projectPath: findGitRoot(startDir ?? process.cwd()) ?? undefined,
271
+ label: "the Muninn database",
272
+ action: "read",
273
+ });
274
+ const db = new Database(resolvedPath, {
275
+ readonly: true,
276
+ fileMustExist: true,
277
+ timeout: 5000,
278
+ });
279
+ try {
280
+ // A connection-local pragma only; it does not write to the database file.
281
+ db.pragma("busy_timeout = 5000");
282
+ }
283
+ catch (err) {
284
+ db.close();
285
+ throw err;
286
+ }
287
+ return db;
288
+ }
289
+ /** Alias kept explicit for callers that prefer the `open…` naming convention. */
290
+ export const openReadOnlyDatabase = getReadOnlyDatabase;
291
+ /**
292
+ * Finds an existing project by root_path or creates a new project record idempotently.
293
+ */
294
+ export function ensureProject(db, options) {
295
+ // Same fallback root as the database resolution (ADR-49): when no explicit
296
+ // root is given, both derive from `startDir` (or the process cwd).
297
+ const base = options?.startDir ? path.resolve(options.startDir) : process.cwd();
298
+ const rootPath = options?.rootPath ? path.resolve(options.rootPath) : (findGitRoot(base) ?? base);
299
+ const existing = db
300
+ .prepare("SELECT id, name, git_remote, root_path, created_at FROM projects WHERE root_path = ?")
301
+ .get(rootPath);
302
+ if (existing) {
303
+ return existing;
304
+ }
305
+ const name = options?.name ?? (path.basename(rootPath) || "project");
306
+ const rawRemote = options?.gitRemote ?? tryGetGitRemote(rootPath) ?? null;
307
+ const gitRemote = rawRemote ? sanitizeGitRemote(rawRemote) : null;
308
+ const id = crypto.randomUUID();
309
+ db.prepare("INSERT INTO projects (id, name, git_remote, root_path) VALUES (?, ?, ?, ?)").run(id, name, gitRemote, rootPath);
310
+ const created = db
311
+ .prepare("SELECT id, name, git_remote, root_path, created_at FROM projects WHERE id = ?")
312
+ .get(id);
313
+ if (!created) {
314
+ throw new Error(`Failed to create project record for rootPath: ${rootPath}`);
315
+ }
316
+ return created;
317
+ }
318
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ -- Muninn Engine Database Schema
2
+ -- Version 1.0.0
3
+
4
+ -- Projects table
5
+ CREATE TABLE IF NOT EXISTS projects (
6
+ id TEXT PRIMARY KEY,
7
+ name TEXT NOT NULL,
8
+ git_remote TEXT,
9
+ root_path TEXT NOT NULL UNIQUE,
10
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
11
+ );
12
+
13
+ CREATE INDEX IF NOT EXISTS idx_projects_root_path ON projects(root_path);
14
+
15
+ -- Observations table
16
+ CREATE TABLE IF NOT EXISTS observations (
17
+ id TEXT PRIMARY KEY,
18
+ project_id TEXT NOT NULL,
19
+ category TEXT CHECK(category IN ('decision', 'convention', 'discovery', 'bugfix', 'architecture')) NOT NULL,
20
+ title TEXT NOT NULL,
21
+ content TEXT NOT NULL,
22
+ topic_key TEXT,
23
+ status TEXT CHECK(status IN ('active', 'superseded')) NOT NULL DEFAULT 'active',
24
+ superseded_by TEXT,
25
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
26
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
27
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_observations_project_id ON observations(project_id);
31
+ CREATE INDEX IF NOT EXISTS idx_observations_updated_at ON observations(updated_at);
32
+ CREATE INDEX IF NOT EXISTS idx_observations_project_updated ON observations(project_id, updated_at DESC, created_at DESC);
33
+
34
+ -- Observations FTS5 virtual table
35
+ CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
36
+ title,
37
+ content,
38
+ topic_key,
39
+ content='observations',
40
+ content_rowid='rowid'
41
+ );
42
+
43
+ -- Triggers for automatic FTS5 synchronization
44
+ CREATE TRIGGER IF NOT EXISTS obs_ai AFTER INSERT ON observations BEGIN
45
+ INSERT INTO observations_fts(rowid, title, content, topic_key)
46
+ VALUES (new.rowid, new.title, new.content, new.topic_key);
47
+ END;
48
+
49
+ CREATE TRIGGER IF NOT EXISTS obs_ad AFTER DELETE ON observations BEGIN
50
+ INSERT INTO observations_fts(observations_fts, rowid, title, content, topic_key)
51
+ VALUES ('delete', old.rowid, old.title, old.content, old.topic_key);
52
+ END;
53
+
54
+ CREATE TRIGGER IF NOT EXISTS obs_au AFTER UPDATE ON observations BEGIN
55
+ INSERT INTO observations_fts(observations_fts, rowid, title, content, topic_key)
56
+ VALUES ('delete', old.rowid, old.title, old.content, old.topic_key);
57
+ INSERT INTO observations_fts(rowid, title, content, topic_key)
58
+ VALUES (new.rowid, new.title, new.content, new.topic_key);
59
+ END;
60
+
61
+ -- Entities table
62
+ CREATE TABLE IF NOT EXISTS entities (
63
+ id TEXT PRIMARY KEY,
64
+ project_id TEXT NOT NULL,
65
+ entity_type TEXT CHECK(entity_type IN ('file', 'function', 'class', 'interface', 'module')) NOT NULL,
66
+ identifier TEXT NOT NULL,
67
+ file_path TEXT NOT NULL,
68
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
69
+ );
70
+
71
+ CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
72
+ CREATE INDEX IF NOT EXISTS idx_entities_identifier ON entities(identifier);
73
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_entities_project_identifier ON entities(project_id, identifier);
74
+
75
+ -- Observation Entities join table
76
+ CREATE TABLE IF NOT EXISTS observation_entities (
77
+ observation_id TEXT NOT NULL,
78
+ entity_id TEXT NOT NULL,
79
+ PRIMARY KEY(observation_id, entity_id),
80
+ FOREIGN KEY(observation_id) REFERENCES observations(id) ON DELETE CASCADE,
81
+ FOREIGN KEY(entity_id) REFERENCES entities(id) ON DELETE CASCADE
82
+ );
83
+
84
+ CREATE INDEX IF NOT EXISTS idx_observation_entities_entity_id ON observation_entities(entity_id);
85
+
86
+ -- Entity Dependencies table
87
+ -- Relaciones topológicas entre símbolos (A llama/importa a B, o A implementa a B)
88
+ CREATE TABLE IF NOT EXISTS entity_dependencies (
89
+ source_entity_id TEXT NOT NULL,
90
+ target_entity_id TEXT NOT NULL,
91
+ relation_type TEXT CHECK(relation_type IN ('imports', 'calls', 'implements', 'extends', 'references')) NOT NULL,
92
+ PRIMARY KEY(source_entity_id, target_entity_id, relation_type),
93
+ FOREIGN KEY(source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
94
+ FOREIGN KEY(target_entity_id) REFERENCES entities(id) ON DELETE CASCADE
95
+ );
96
+
97
+ CREATE INDEX IF NOT EXISTS idx_entity_deps_source ON entity_dependencies(source_entity_id);
98
+ CREATE INDEX IF NOT EXISTS idx_entity_deps_target ON entity_dependencies(target_entity_id);
99
+
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Public API of the Muninn memory engine.
3
+ *
4
+ * Everything below is safe to import from a Node or Bun process:
5
+ *
6
+ * ```ts
7
+ * import { MemoryService, indexFilesIntoMuninn } from "@dahch/muninn";
8
+ *
9
+ * const memory = new MemoryService({ projectRoot: process.cwd() });
10
+ * indexFilesIntoMuninn(memory, ["src/app.ts"], { projectRoot: process.cwd() });
11
+ * console.log(memory.search({ query: "auth", limit: 5 }));
12
+ * ```
13
+ *
14
+ * Subpath entry points (`@dahch/muninn/db`, `/service`, `/indexer`, `/mcp`,
15
+ * `/contracts`, `/util`) expose the same modules without the barrel.
16
+ */
17
+ export * from "./db/client.js";
18
+ export * from "./service/index.js";
19
+ export * from "./indexer/index.js";
20
+ export * from "./mcp/index.js";
21
+ export * from "./contracts/index.js";
22
+ export * from "./util/text.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Public API of the Muninn memory engine.
3
+ *
4
+ * Everything below is safe to import from a Node or Bun process:
5
+ *
6
+ * ```ts
7
+ * import { MemoryService, indexFilesIntoMuninn } from "@dahch/muninn";
8
+ *
9
+ * const memory = new MemoryService({ projectRoot: process.cwd() });
10
+ * indexFilesIntoMuninn(memory, ["src/app.ts"], { projectRoot: process.cwd() });
11
+ * console.log(memory.search({ query: "auth", limit: 5 }));
12
+ * ```
13
+ *
14
+ * Subpath entry points (`@dahch/muninn/db`, `/service`, `/indexer`, `/mcp`,
15
+ * `/contracts`, `/util`) expose the same modules without the barrel.
16
+ */
17
+ export * from "./db/client.js";
18
+ export * from "./service/index.js";
19
+ export * from "./indexer/index.js";
20
+ export * from "./mcp/index.js";
21
+ export * from "./contracts/index.js";
22
+ export * from "./util/text.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { EntityType, DependencyRelationType } from "../db/client.js";
2
+ import type { IMemoryService } from "../service/memory-service.js";
3
+ export interface ExtractedDependency {
4
+ sourceIdentifier: string;
5
+ targetIdentifier: string;
6
+ relationType: DependencyRelationType;
7
+ }
8
+ export interface ExtractedSymbol {
9
+ name: string;
10
+ identifier: string;
11
+ filePath: string;
12
+ entityType: EntityType;
13
+ startLine: number;
14
+ endLine: number;
15
+ isExported: boolean;
16
+ dependencies?: ExtractedDependency[];
17
+ }
18
+ export interface AstExtractionResult {
19
+ filePath: string;
20
+ symbols: ExtractedSymbol[];
21
+ dependencies: ExtractedDependency[];
22
+ }
23
+ export interface IndexSummary {
24
+ indexedFiles: number;
25
+ indexedSymbols: number;
26
+ indexedDependencies: number;
27
+ }
28
+ /**
29
+ * Normalizes file path to forward slashes.
30
+ */
31
+ export declare function normalizePath(p: string): string;
32
+ /**
33
+ * Normalizes relative module specifiers against the importing file's directory.
34
+ * Maps .js, .mjs, .cjs to .ts, and .jsx to .tsx, while keeping extensionless specifiers bare.
35
+ */
36
+ export declare function normalizeModuleSpecifier(filePath: string, specifier: string): string;
37
+ /**
38
+ * Extracts symbols and dependencies from TypeScript / JavaScript source code.
39
+ */
40
+ export declare function extractSymbolsFromSource(filePath: string, sourceText: string): ExtractedSymbol[];
41
+ /**
42
+ * Comprehensive AST traversal returning both symbols and inter-symbol dependencies.
43
+ */
44
+ export declare function extractAstData(filePath: string, sourceText: string): AstExtractionResult;
45
+ /**
46
+ * Indexes a collection of files into Muninn's entities and entity_dependencies tables.
47
+ *
48
+ * Execution is cleanly separated into two stages (REV-001):
49
+ * 1. Synchronous file reading, filtering, validation, and AST parsing are performed in-memory outside any database transaction.
50
+ * 2. SQLite batch operations are executed inside a short-lived transaction with zombie entity pruning (REV-003, SEC-003).
51
+ */
52
+ export declare function indexFilesIntoMuninn(memoryService: IMemoryService, filePaths: string[], options?: {
53
+ projectRoot?: string;
54
+ }): IndexSummary;
55
+ //# sourceMappingURL=ast-indexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-indexer.d.ts","sourceRoot":"","sources":["../../src/indexer/ast-indexer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,UAAU,EACV,sBAAsB,EAEvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,YAAY,EAAE,mBAAmB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,CAiBR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,eAAe,EAAE,CAGnB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,mBAAmB,CAyQrB;AAmBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,cAAc,EAC7B,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,YAAY,CAwOd"}