engramx 1.0.1 → 2.0.1

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 (32) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +67 -7
  3. package/dist/{aider-context-TNGSXMVY.js → aider-context-BC5R2ZTA.js} +1 -1
  4. package/dist/cache-AK6CF3BC.js +10 -0
  5. package/dist/chunk-22INHMKB.js +31 -0
  6. package/dist/chunk-533LR4I7.js +220 -0
  7. package/dist/{chunk-QOG4K427.js → chunk-C6GBUOAL.js} +1 -1
  8. package/dist/chunk-CIQQ5Y3S.js +338 -0
  9. package/dist/chunk-KL6NSPVA.js +59 -0
  10. package/dist/{chunk-SBHGK5WA.js → chunk-PEH54LYC.js} +85 -3
  11. package/dist/{chunk-6SFMVYUN.js → chunk-SJT7VS2G.js} +127 -23
  12. package/dist/cli.js +383 -258
  13. package/dist/{core-77MHT3QV.js → core-6IY5L6II.js} +2 -2
  14. package/dist/{cursor-mdc-HWVUZUZH.js → cursor-mdc-GJ7E5LDD.js} +1 -1
  15. package/dist/{exporter-A3VSLS4U.js → exporter-GWU2GF23.js} +1 -1
  16. package/dist/grammars/tree-sitter-go.wasm +0 -0
  17. package/dist/grammars/tree-sitter-javascript.wasm +0 -0
  18. package/dist/grammars/tree-sitter-python.wasm +0 -0
  19. package/dist/grammars/tree-sitter-rust.wasm +0 -0
  20. package/dist/grammars/tree-sitter-tsx.wasm +0 -0
  21. package/dist/grammars/tree-sitter-typescript.wasm +0 -0
  22. package/dist/{importer-LU2YFZDY.js → importer-V62NGZRK.js} +1 -1
  23. package/dist/index.js +3 -3
  24. package/dist/{migrate-5ZJWF2HD.js → migrate-UKCO6BUU.js} +3 -1
  25. package/dist/plugin-loader-STTGYIL5.js +106 -0
  26. package/dist/serve.js +2 -2
  27. package/dist/server-6AOI7NQP.js +1370 -0
  28. package/dist/{tuner-2LVIEE5V.js → tuner-KFNNGKG3.js} +4 -2
  29. package/dist/windsurf-rules-C7SVDHBL.js +59 -0
  30. package/package.json +4 -3
  31. package/dist/chunk-CEAANHHX.js +0 -88
  32. package/dist/server-I3C74ZLB.js +0 -193
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  readConfig,
3
- readHookLog,
4
3
  writeConfig
5
- } from "./chunk-CEAANHHX.js";
4
+ } from "./chunk-22INHMKB.js";
5
+ import {
6
+ readHookLog
7
+ } from "./chunk-KL6NSPVA.js";
6
8
 
7
9
  // src/tuner/index.ts
8
10
  var MIN_CONFIDENCE_SAMPLES = 10;
@@ -0,0 +1,59 @@
1
+ // src/generators/windsurf-rules.ts
2
+ import { mkdirSync, writeFileSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
5
+ function buildSection(heading, lines) {
6
+ if (lines.length === 0) return "";
7
+ return [`## ${heading}`, "", ...lines, ""].join("\n");
8
+ }
9
+ async function generateWindsurfRules(projectRoot) {
10
+ const { getStore } = await import("./core-6IY5L6II.js");
11
+ const store = await getStore(projectRoot);
12
+ try {
13
+ const allNodes = store.getAllNodes();
14
+ const now = Date.now();
15
+ const cutoff = now - THIRTY_DAYS_MS;
16
+ const godNodes = store.getGodNodes(10);
17
+ const architectureLines = godNodes.map(
18
+ ({ node, degree }) => `- \`${node.label}\` (${node.kind}, ${degree} connections) \u2014 ${node.sourceFile}`
19
+ );
20
+ const hotFileLines = allNodes.filter(
21
+ (n) => n.kind === "pattern" && n.metadata.type === "hot_file"
22
+ ).slice(0, 10).map((n) => `- ${n.label}`);
23
+ const mistakeLines = allNodes.filter((n) => n.kind === "mistake").sort((a, b) => b.queryCount - a.queryCount).slice(0, 5).map((n) => `- ${n.label}`);
24
+ const decisionLines = allNodes.filter((n) => n.kind === "decision" && n.lastVerified >= cutoff).sort((a, b) => b.lastVerified - a.lastVerified).map((n) => `- ${n.label}`);
25
+ const patternLines = allNodes.filter(
26
+ (n) => n.kind === "pattern" && n.confidenceScore >= 0.8 && n.metadata.type !== "hot_file"
27
+ ).map((n) => `- ${n.label}`);
28
+ const sections = [
29
+ buildSection("Architecture", architectureLines),
30
+ buildSection("Known Landmines", mistakeLines),
31
+ buildSection("Active Decisions", decisionLines),
32
+ buildSection("Key Patterns", patternLines),
33
+ buildSection("Hot Files", hotFileLines)
34
+ ].filter((s) => s.length > 0);
35
+ const header = [
36
+ "# engram context",
37
+ "",
38
+ "> Auto-generated by engram. Do not edit manually.",
39
+ "> Regenerate: `engram gen-windsurfrules -p .`",
40
+ '> Prefer `engram query "..."` for structural queries \u2014 this file is a snapshot.',
41
+ ""
42
+ ].join("\n");
43
+ const content = header + sections.join("\n");
44
+ const outPath = join(projectRoot, ".windsurfrules");
45
+ mkdirSync(dirname(outPath), { recursive: true });
46
+ writeFileSync(outPath, content, "utf-8");
47
+ const nodeCount = architectureLines.length + hotFileLines.length + mistakeLines.length + decisionLines.length + patternLines.length;
48
+ return {
49
+ filePath: outPath,
50
+ sections: sections.length,
51
+ nodes: nodeCount
52
+ };
53
+ } finally {
54
+ store.close();
55
+ }
56
+ }
57
+ export {
58
+ generateWindsurfRules
59
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "engramx",
3
- "version": "1.0.1",
4
- "description": "The context spine for AI coding agents. 6 providers assembled into rich context packets per Read interception. Up to 90% session-level token savings. Local SQLite, zero native deps, zero cloud.",
3
+ "version": "2.0.1",
4
+ "description": "The context spine for AI coding agents. 8 providers + pluggable context sources, 3-layer memory cache, web dashboard, multi-IDE support (Claude Code, Cursor, Continue, Zed, Aider, Windsurf, Neovim, Emacs). 88.1% measured session-level token savings. Local SQLite, zero native deps, zero cloud.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/NickCirv/engram.git"
@@ -18,7 +18,8 @@
18
18
  },
19
19
  "main": "./dist/index.js",
20
20
  "scripts": {
21
- "build": "tsup",
21
+ "build": "tsup && node scripts/bundle-grammars.mjs",
22
+ "build:nogrammars": "tsup",
22
23
  "dev": "tsup --watch",
23
24
  "test": "vitest",
24
25
  "lint": "tsc --noEmit",
@@ -1,88 +0,0 @@
1
- // src/tuner/config.ts
2
- import { existsSync, readFileSync, writeFileSync } from "fs";
3
- import { join } from "path";
4
- var DEFAULTS = {
5
- confidenceThreshold: 0.7,
6
- totalTokenBudget: 600,
7
- providers: {}
8
- };
9
- function readConfig(projectRoot) {
10
- const configPath = join(projectRoot, ".engram", "config.json");
11
- if (!existsSync(configPath)) return DEFAULTS;
12
- try {
13
- const raw = JSON.parse(readFileSync(configPath, "utf-8"));
14
- return {
15
- ...DEFAULTS,
16
- ...raw,
17
- providers: { ...DEFAULTS.providers, ...raw.providers ?? {} }
18
- };
19
- } catch {
20
- return DEFAULTS;
21
- }
22
- }
23
- function writeConfig(projectRoot, config) {
24
- const configPath = join(projectRoot, ".engram", "config.json");
25
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
26
- }
27
-
28
- // src/intelligence/hook-log.ts
29
- import {
30
- appendFileSync,
31
- existsSync as existsSync2,
32
- renameSync,
33
- statSync,
34
- readFileSync as readFileSync2
35
- } from "fs";
36
- import { join as join2 } from "path";
37
- var HOOK_LOG_MAX_BYTES = 10 * 1024 * 1024;
38
- var LOG_FILENAME = "hook-log.jsonl";
39
- var LOG_ROTATED_FILENAME = "hook-log.jsonl.1";
40
- function logHookEvent(projectRoot, entry) {
41
- if (!projectRoot) return;
42
- try {
43
- const logPath = join2(projectRoot, ".engram", LOG_FILENAME);
44
- rotateIfNeeded(projectRoot);
45
- const line = JSON.stringify({
46
- ts: (/* @__PURE__ */ new Date()).toISOString(),
47
- ...entry
48
- }) + "\n";
49
- appendFileSync(logPath, line);
50
- } catch {
51
- }
52
- }
53
- function rotateIfNeeded(projectRoot) {
54
- try {
55
- const logPath = join2(projectRoot, ".engram", LOG_FILENAME);
56
- if (!existsSync2(logPath)) return;
57
- const size = statSync(logPath).size;
58
- if (size < HOOK_LOG_MAX_BYTES) return;
59
- const rotatedPath = join2(projectRoot, ".engram", LOG_ROTATED_FILENAME);
60
- renameSync(logPath, rotatedPath);
61
- } catch {
62
- }
63
- }
64
- function readHookLog(projectRoot) {
65
- try {
66
- const logPath = join2(projectRoot, ".engram", LOG_FILENAME);
67
- if (!existsSync2(logPath)) return [];
68
- const raw = readFileSync2(logPath, "utf-8");
69
- const entries = [];
70
- for (const line of raw.split("\n")) {
71
- if (!line.trim()) continue;
72
- try {
73
- entries.push(JSON.parse(line));
74
- } catch {
75
- }
76
- }
77
- return entries;
78
- } catch {
79
- return [];
80
- }
81
- }
82
-
83
- export {
84
- readConfig,
85
- writeConfig,
86
- logHookEvent,
87
- readHookLog
88
- };
@@ -1,193 +0,0 @@
1
- import {
2
- learn,
3
- query,
4
- stats
5
- } from "./chunk-6SFMVYUN.js";
6
- import "./chunk-SBHGK5WA.js";
7
-
8
- // src/server/http.ts
9
- import { createServer } from "http";
10
- import { writeFileSync, unlinkSync, mkdirSync, existsSync } from "fs";
11
- import { join } from "path";
12
- import { createRequire } from "module";
13
- var require2 = createRequire(import.meta.url);
14
- var PKG_VERSION = (() => {
15
- for (const p of ["../package.json", "../../package.json"]) {
16
- try {
17
- return require2(p).version;
18
- } catch {
19
- }
20
- }
21
- return "0.0.0";
22
- })();
23
- var PROVIDERS = [
24
- "structure",
25
- "mistakes",
26
- "git",
27
- "mempalace",
28
- "context7",
29
- "obsidian"
30
- ];
31
- function parseUrl(req) {
32
- return new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
33
- }
34
- async function readBody(req) {
35
- const chunks = [];
36
- for await (const chunk of req) chunks.push(chunk);
37
- return Buffer.concat(chunks).toString("utf-8");
38
- }
39
- function json(res, status, data) {
40
- res.writeHead(status, {
41
- "Content-Type": "application/json",
42
- "Access-Control-Allow-Origin": "*",
43
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
44
- "Access-Control-Allow-Headers": "Authorization, Content-Type"
45
- });
46
- res.end(JSON.stringify(data));
47
- }
48
- function checkAuth(req, res) {
49
- const token = process.env.ENGRAM_API_TOKEN;
50
- if (!token) return true;
51
- const header = req.headers.authorization ?? "";
52
- if (header === `Bearer ${token}`) return true;
53
- json(res, 401, { error: "Unauthorized" });
54
- return false;
55
- }
56
- function handleHealth(_req, res, startedAt) {
57
- json(res, 200, {
58
- ok: true,
59
- version: PKG_VERSION,
60
- uptime: Math.floor((Date.now() - startedAt) / 1e3)
61
- });
62
- }
63
- async function handleQuery(req, res, projectRoot) {
64
- const url = parseUrl(req);
65
- const q = url.searchParams.get("q");
66
- if (!q) {
67
- json(res, 400, { error: "Missing query parameter 'q'" });
68
- return;
69
- }
70
- const budget = parseInt(url.searchParams.get("budget") ?? "2000", 10);
71
- try {
72
- const result = await query(projectRoot, q, { tokenBudget: isNaN(budget) ? 2e3 : budget });
73
- json(res, 200, {
74
- text: result.text,
75
- estimatedTokens: result.estimatedTokens,
76
- providers: [...PROVIDERS]
77
- });
78
- } catch (err) {
79
- json(res, 500, { error: "Query failed", detail: String(err) });
80
- }
81
- }
82
- async function handleStats(_req, res, projectRoot) {
83
- try {
84
- const result = await stats(projectRoot);
85
- json(res, 200, result);
86
- } catch (err) {
87
- json(res, 500, { error: "Stats failed", detail: String(err) });
88
- }
89
- }
90
- function handleProviders(_req, res) {
91
- const list = PROVIDERS.map((name) => ({ name, available: true }));
92
- json(res, 200, list);
93
- }
94
- async function handleLearn(req, res, projectRoot) {
95
- let body;
96
- try {
97
- body = await readBody(req);
98
- } catch {
99
- json(res, 400, { error: "Failed to read request body" });
100
- return;
101
- }
102
- let parsed;
103
- try {
104
- parsed = JSON.parse(body);
105
- } catch {
106
- json(res, 400, { error: "Invalid JSON body" });
107
- return;
108
- }
109
- if (!parsed.content || typeof parsed.content !== "string") {
110
- json(res, 400, { error: "Missing 'content' in request body" });
111
- return;
112
- }
113
- try {
114
- await learn(projectRoot, parsed.content, parsed.file ?? "http-api");
115
- json(res, 201, { ok: true });
116
- } catch (err) {
117
- json(res, 500, { error: "Learn failed", detail: String(err) });
118
- }
119
- }
120
- function writePid(projectRoot) {
121
- const dir = join(projectRoot, ".engram");
122
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
123
- writeFileSync(join(dir, "http-server.pid"), String(process.pid), "utf-8");
124
- }
125
- function removePid(projectRoot) {
126
- try {
127
- unlinkSync(join(projectRoot, ".engram", "http-server.pid"));
128
- } catch {
129
- }
130
- }
131
- function createHttpServer(projectRoot, port) {
132
- return new Promise((resolve, reject) => {
133
- const startedAt = Date.now();
134
- const server = createServer(async (req, res) => {
135
- if (req.method === "OPTIONS") {
136
- res.writeHead(204, {
137
- "Access-Control-Allow-Origin": "*",
138
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
139
- "Access-Control-Allow-Headers": "Authorization, Content-Type"
140
- });
141
- res.end();
142
- return;
143
- }
144
- if (!checkAuth(req, res)) return;
145
- const url = parseUrl(req);
146
- const path = url.pathname;
147
- try {
148
- if (req.method === "GET" && path === "/health") {
149
- handleHealth(req, res, startedAt);
150
- } else if (req.method === "GET" && path === "/query") {
151
- await handleQuery(req, res, projectRoot);
152
- } else if (req.method === "GET" && path === "/stats") {
153
- await handleStats(req, res, projectRoot);
154
- } else if (req.method === "GET" && path === "/providers") {
155
- handleProviders(req, res);
156
- } else if (req.method === "POST" && path === "/learn") {
157
- await handleLearn(req, res, projectRoot);
158
- } else {
159
- json(res, 404, { error: "Not found" });
160
- }
161
- } catch (err) {
162
- json(res, 500, { error: "Internal server error", detail: String(err) });
163
- }
164
- });
165
- server.on("error", (err) => {
166
- removePid(projectRoot);
167
- reject(err);
168
- });
169
- server.listen(port, "127.0.0.1", () => {
170
- writePid(projectRoot);
171
- const cleanup = () => {
172
- removePid(projectRoot);
173
- server.close(() => process.exit(0));
174
- };
175
- process.on("SIGINT", cleanup);
176
- process.on("SIGTERM", cleanup);
177
- resolve();
178
- });
179
- });
180
- }
181
-
182
- // src/server/index.ts
183
- var DEFAULT_PORT = 7337;
184
- async function startHttpServer(projectRoot, port = DEFAULT_PORT) {
185
- await createHttpServer(projectRoot, port);
186
- process.stdout.write(
187
- `engram HTTP server listening on http://127.0.0.1:${port}
188
- `
189
- );
190
- }
191
- export {
192
- startHttpServer
193
- };