engramx 2.1.0 → 3.0.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.
@@ -11,8 +11,8 @@ import {
11
11
  path,
12
12
  query,
13
13
  stats
14
- } from "./chunk-ZVWRIVWQ.js";
15
- import "./chunk-PEH54LYC.js";
14
+ } from "./chunk-B4UOE64J.js";
15
+ import "./chunk-645NBY6L.js";
16
16
  export {
17
17
  benchmark,
18
18
  computeKeywordIDF,
@@ -33,7 +33,7 @@ function buildSection(heading, bullets) {
33
33
  return [`## ${heading}`, "", ...bullets, ""].join("\n");
34
34
  }
35
35
  async function generateCursorMdc(projectPath) {
36
- const { getStore } = await import("./core-TSXA5XZH.js");
36
+ const { getStore } = await import("./core-77F2BVYV.js");
37
37
  const store = await getStore(projectPath);
38
38
  try {
39
39
  const allNodes = store.getAllNodes();
@@ -12,7 +12,7 @@ function buildSection(heading, nodes) {
12
12
  return [`## ${heading}`, "", ...bullets, ""].join("\n");
13
13
  }
14
14
  async function exportCcs(projectRoot) {
15
- const { getStore } = await import("./core-TSXA5XZH.js");
15
+ const { getStore } = await import("./core-77F2BVYV.js");
16
16
  const store = await getStore(projectRoot);
17
17
  try {
18
18
  const allNodes = store.getAllNodes();
@@ -25,7 +25,7 @@ async function importCcs(projectRoot) {
25
25
  if (!existsSync(filePath)) {
26
26
  return { nodesCreated: 0, sectionsFound: 0 };
27
27
  }
28
- const { getStore } = await import("./core-TSXA5XZH.js");
28
+ const { getStore } = await import("./core-77F2BVYV.js");
29
29
  const store = await getStore(projectRoot);
30
30
  try {
31
31
  const raw = readFileSync(filePath, "utf-8");
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  generateSummary,
5
5
  install,
6
6
  uninstall
7
- } from "./chunk-4XA6ENNL.js";
7
+ } from "./chunk-VLTWBTQ7.js";
8
8
  import {
9
9
  GraphStore,
10
10
  SUPPORTED_EXTENSIONS,
@@ -23,8 +23,8 @@ import {
23
23
  sliceGraphemeSafe,
24
24
  stats,
25
25
  truncateGraphemeSafe
26
- } from "./chunk-ZVWRIVWQ.js";
27
- import "./chunk-PEH54LYC.js";
26
+ } from "./chunk-B4UOE64J.js";
27
+ import "./chunk-645NBY6L.js";
28
28
  export {
29
29
  GraphStore,
30
30
  SUPPORTED_EXTENSIONS,
@@ -0,0 +1,9 @@
1
+ import {
2
+ __internalsForTesting,
3
+ createMcpProvider
4
+ } from "./chunk-73IBCRFI.js";
5
+ import "./chunk-ZUC6OXSL.js";
6
+ export {
7
+ __internalsForTesting,
8
+ createMcpProvider
9
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ applyArgTemplate,
3
+ getMcpConfigPath,
4
+ loadMcpConfigs,
5
+ validateProviderConfig
6
+ } from "./chunk-ZUC6OXSL.js";
7
+ export {
8
+ applyArgTemplate,
9
+ getMcpConfigPath,
10
+ loadMcpConfigs,
11
+ validateProviderConfig
12
+ };
@@ -3,7 +3,7 @@ import {
3
3
  getSchemaVersion,
4
4
  rollback,
5
5
  runMigrations
6
- } from "./chunk-PEH54LYC.js";
6
+ } from "./chunk-645NBY6L.js";
7
7
  export {
8
8
  CURRENT_SCHEMA_VERSION,
9
9
  getSchemaVersion,
@@ -1,3 +1,10 @@
1
+ import {
2
+ createMcpProvider
3
+ } from "./chunk-73IBCRFI.js";
4
+ import {
5
+ validateProviderConfig
6
+ } from "./chunk-ZUC6OXSL.js";
7
+
1
8
  // src/providers/plugin-loader.ts
2
9
  import { existsSync, readdirSync, mkdirSync } from "fs";
3
10
  import { join } from "path";
@@ -23,34 +30,73 @@ function validatePlugin(mod) {
23
30
  return { plugin: null, reason: "default export is not an object" };
24
31
  }
25
32
  const p = candidate;
26
- const required = [
27
- "name",
28
- "label",
29
- "tier",
30
- "tokenBudget",
31
- "timeoutMs",
32
- "version",
33
- "resolve",
34
- "isAvailable"
35
- ];
36
- for (const field of required) {
37
- if (p[field] === void 0 || p[field] === null) {
38
- return { plugin: null, reason: `missing required field: ${field}` };
39
- }
33
+ if (typeof p.name !== "string" || p.name.length === 0) {
34
+ return { plugin: null, reason: "name must be a non-empty string" };
40
35
  }
41
- if (typeof p.resolve !== "function") {
42
- return { plugin: null, reason: "resolve must be a function" };
36
+ if (typeof p.label !== "string" || p.label.length === 0) {
37
+ return { plugin: null, reason: `[${p.name}] label must be a non-empty string` };
43
38
  }
44
- if (typeof p.isAvailable !== "function") {
45
- return { plugin: null, reason: "isAvailable must be a function" };
39
+ if (typeof p.version !== "string" || p.version.length === 0) {
40
+ return { plugin: null, reason: `[${p.name}] version must be a non-empty string` };
46
41
  }
47
- if (p.tier !== 1 && p.tier !== 2) {
48
- return { plugin: null, reason: `tier must be 1 or 2 (got ${String(p.tier)})` };
42
+ const hasMcpConfig = p.mcpConfig !== void 0 && p.mcpConfig !== null;
43
+ const hasResolve = typeof p.resolve === "function";
44
+ if (!hasMcpConfig && !hasResolve) {
45
+ return {
46
+ plugin: null,
47
+ reason: `[${p.name}] plugin needs either a resolve() function or an mcpConfig declaration`
48
+ };
49
49
  }
50
- if (typeof p.name !== "string" || p.name.length === 0) {
51
- return { plugin: null, reason: "name must be a non-empty string" };
50
+ if (hasResolve) {
51
+ const classicRequired = [
52
+ "tier",
53
+ "tokenBudget",
54
+ "timeoutMs",
55
+ "isAvailable"
56
+ ];
57
+ for (const field of classicRequired) {
58
+ if (p[field] === void 0 || p[field] === null) {
59
+ return { plugin: null, reason: `[${p.name}] missing required field: ${field}` };
60
+ }
61
+ }
62
+ if (typeof p.isAvailable !== "function") {
63
+ return { plugin: null, reason: `[${p.name}] isAvailable must be a function` };
64
+ }
65
+ if (p.tier !== 1 && p.tier !== 2) {
66
+ return { plugin: null, reason: `[${p.name}] tier must be 1 or 2 (got ${String(p.tier)})` };
67
+ }
68
+ return { plugin: candidate, reason: "" };
69
+ }
70
+ const rawConfig = p.mcpConfig;
71
+ const normalizedConfig = {
72
+ name: p.name,
73
+ label: p.label,
74
+ ...rawConfig
75
+ };
76
+ const validation = validateProviderConfig(normalizedConfig);
77
+ if (!validation.ok) {
78
+ return {
79
+ plugin: null,
80
+ reason: `[${p.name}] invalid mcpConfig: ${validation.reason}`
81
+ };
52
82
  }
53
- return { plugin: candidate, reason: "" };
83
+ const mcpProvider = createMcpProvider(
84
+ validation.value
85
+ );
86
+ const merged = {
87
+ name: p.name,
88
+ label: p.label,
89
+ version: p.version,
90
+ description: p.description,
91
+ author: p.author,
92
+ mcpConfig: p.mcpConfig,
93
+ tier: p.tier ?? mcpProvider.tier,
94
+ tokenBudget: p.tokenBudget ?? mcpProvider.tokenBudget,
95
+ timeoutMs: p.timeoutMs ?? mcpProvider.timeoutMs,
96
+ resolve: mcpProvider.resolve.bind(mcpProvider),
97
+ isAvailable: mcpProvider.isAvailable.bind(mcpProvider)
98
+ };
99
+ return { plugin: merged, reason: "" };
54
100
  }
55
101
  async function loadPlugins(dir) {
56
102
  const pluginsDir = dir ?? getPluginsDir();
@@ -0,0 +1,21 @@
1
+ import {
2
+ _resetAvailabilityCache,
3
+ _resetMcpProvidersCache,
4
+ boostByMistakes,
5
+ enforcePerProviderBudget,
6
+ resolveRichPacket,
7
+ resolveRichPacketStreaming,
8
+ warmAllProviders
9
+ } from "./chunk-RJC6RNXJ.js";
10
+ import "./chunk-22INHMKB.js";
11
+ import "./chunk-B4UOE64J.js";
12
+ import "./chunk-645NBY6L.js";
13
+ export {
14
+ _resetAvailabilityCache,
15
+ _resetMcpProvidersCache,
16
+ boostByMistakes,
17
+ enforcePerProviderBudget,
18
+ resolveRichPacket,
19
+ resolveRichPacketStreaming,
20
+ warmAllProviders
21
+ };
package/dist/serve.js CHANGED
@@ -9,8 +9,8 @@ import {
9
9
  query,
10
10
  stats,
11
11
  truncateGraphemeSafe
12
- } from "./chunk-ZVWRIVWQ.js";
13
- import "./chunk-PEH54LYC.js";
12
+ } from "./chunk-B4UOE64J.js";
13
+ import "./chunk-645NBY6L.js";
14
14
 
15
15
  // src/serve.ts
16
16
  function clampInt(value, defaultValue, min, max) {
@@ -11,20 +11,20 @@ import {
11
11
  } from "./chunk-N6PPKOPK.js";
12
12
  import {
13
13
  summarizeHookLog
14
- } from "./chunk-XFE6ZANP.js";
15
- import {
16
- getComponentStatus
17
- } from "./chunk-G4U3QOOW.js";
14
+ } from "./chunk-FKY6HIT2.js";
18
15
  import {
19
16
  readHookLog
20
17
  } from "./chunk-KL6NSPVA.js";
18
+ import {
19
+ getComponentStatus
20
+ } from "./chunk-G4U3QOOW.js";
21
21
  import {
22
22
  getStore,
23
23
  learn,
24
24
  query,
25
25
  stats
26
- } from "./chunk-ZVWRIVWQ.js";
27
- import "./chunk-PEH54LYC.js";
26
+ } from "./chunk-B4UOE64J.js";
27
+ import "./chunk-645NBY6L.js";
28
28
 
29
29
  // src/server/http.ts
30
30
  import { createServer } from "http";
@@ -1093,6 +1093,72 @@ async function handleQuery(req, res, projectRoot) {
1093
1093
  json(res, 500, { error: "Query failed", detail: String(err) });
1094
1094
  }
1095
1095
  }
1096
+ async function handleContextStream(req, res, projectRoot) {
1097
+ const url = parseUrl(req);
1098
+ const filePath = url.searchParams.get("file");
1099
+ if (!filePath) {
1100
+ json(res, 400, { error: "Missing required query parameter 'file'" });
1101
+ return;
1102
+ }
1103
+ const lastEventIdHeader = req.headers["last-event-id"];
1104
+ const resumeAfter = (() => {
1105
+ if (typeof lastEventIdHeader !== "string") return -1;
1106
+ const n = parseInt(lastEventIdHeader, 10);
1107
+ return isNaN(n) ? -1 : n;
1108
+ })();
1109
+ res.writeHead(200, {
1110
+ "Content-Type": "text/event-stream",
1111
+ "Cache-Control": "no-cache, no-transform",
1112
+ Connection: "keep-alive",
1113
+ "X-Accel-Buffering": "no",
1114
+ ...corsHeaders(req)
1115
+ });
1116
+ if (typeof res.flushHeaders === "function") res.flushHeaders();
1117
+ const context = {
1118
+ filePath,
1119
+ projectRoot,
1120
+ nodeIds: [],
1121
+ imports: [],
1122
+ hasTests: false,
1123
+ churnRate: 0
1124
+ };
1125
+ const { resolveRichPacketStreaming } = await import("./resolver-H7GXVP73.js");
1126
+ let eventId = 0;
1127
+ let disconnected = false;
1128
+ req.on("close", () => {
1129
+ disconnected = true;
1130
+ });
1131
+ try {
1132
+ for await (const event of resolveRichPacketStreaming(
1133
+ filePath,
1134
+ context
1135
+ )) {
1136
+ if (disconnected) break;
1137
+ if (eventId <= resumeAfter) {
1138
+ eventId++;
1139
+ continue;
1140
+ }
1141
+ const frame = `id: ${eventId}
1142
+ event: ${event.type}
1143
+ data: ${JSON.stringify(
1144
+ event.type === "provider" ? event.result : { providerCount: event.providerCount, durationMs: event.durationMs }
1145
+ )}
1146
+
1147
+ `;
1148
+ try {
1149
+ res.write(frame);
1150
+ } catch {
1151
+ return;
1152
+ }
1153
+ eventId++;
1154
+ }
1155
+ } finally {
1156
+ try {
1157
+ res.end();
1158
+ } catch {
1159
+ }
1160
+ }
1161
+ }
1096
1162
  async function handleStats(_req, res, projectRoot) {
1097
1163
  try {
1098
1164
  const result = await stats(projectRoot);
@@ -1405,6 +1471,8 @@ function createHttpServer(projectRoot, port) {
1405
1471
  await handleGraphGodNodes(req, res, projectRoot);
1406
1472
  } else if (req.method === "GET" && path === "/api/sse") {
1407
1473
  handleSSE(req, res, projectRoot);
1474
+ } else if (req.method === "GET" && path === "/context/stream") {
1475
+ await handleContextStream(req, res, projectRoot);
1408
1476
  } else if (req.method === "GET" && (path === "/ui" || path === "/ui/")) {
1409
1477
  res.writeHead(200, {
1410
1478
  "Content-Type": "text/html; charset=utf-8",
@@ -7,7 +7,7 @@ function buildSection(heading, lines) {
7
7
  return [`## ${heading}`, "", ...lines, ""].join("\n");
8
8
  }
9
9
  async function generateWindsurfRules(projectRoot) {
10
- const { getStore } = await import("./core-TSXA5XZH.js");
10
+ const { getStore } = await import("./core-77F2BVYV.js");
11
11
  const store = await getStore(projectRoot);
12
12
  try {
13
13
  const allNodes = store.getAllNodes();
@@ -9,8 +9,8 @@ import {
9
9
  import "./chunk-G4U3QOOW.js";
10
10
  import {
11
11
  init
12
- } from "./chunk-ZVWRIVWQ.js";
13
- import "./chunk-PEH54LYC.js";
12
+ } from "./chunk-B4UOE64J.js";
13
+ import "./chunk-645NBY6L.js";
14
14
 
15
15
  // src/setup/wizard.ts
16
16
  import chalk from "chalk";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "engramx",
3
- "version": "2.1.0",
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.",
3
+ "version": "3.0.0",
4
+ "description": "The context spine for AI coding agents. 9 built-in providers + mcpConfig plugin contract (wrap any MCP server in 10 lines), generic MCP-client aggregator (stdio), pre-mortem mistake-guard, bi-temporal mistake memory, Anthropic Auto-Memory bridge, SSE streaming context packets, dual-emit AGENTS.md+CLAUDE.md. 90.8% measured real-world token savings (reproducible bench included). Local SQLite, zero cloud.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/NickCirv/engram.git"
@@ -56,6 +56,7 @@
56
56
  "CHANGELOG.md"
57
57
  ],
58
58
  "dependencies": {
59
+ "@modelcontextprotocol/sdk": "^1.29.0",
59
60
  "chalk": "^5.6.2",
60
61
  "commander": "^14.0.3",
61
62
  "sql.js": "^1.14.1",
@@ -1,10 +1,10 @@
1
+ import {
2
+ readHookLog
3
+ } from "./chunk-KL6NSPVA.js";
1
4
  import {
2
5
  readConfig,
3
6
  writeConfig
4
7
  } from "./chunk-22INHMKB.js";
5
- import {
6
- readHookLog
7
- } from "./chunk-KL6NSPVA.js";
8
8
 
9
9
  // src/tuner/index.ts
10
10
  var MIN_CONFIDENCE_SAMPLES = 10;