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.
- package/CHANGELOG.md +70 -0
- package/README.md +106 -17
- package/dist/{aider-context-J557IHIP.js → aider-context-6IDE3R7U.js} +1 -1
- package/dist/{chunk-PEH54LYC.js → chunk-645NBY6L.js} +42 -5
- package/dist/chunk-73IBCRFI.js +215 -0
- package/dist/{chunk-ZVWRIVWQ.js → chunk-B4UOE64J.js} +29 -11
- package/dist/{chunk-XFE6ZANP.js → chunk-FKY6HIT2.js} +1 -1
- package/dist/chunk-RJC6RNXJ.js +1405 -0
- package/dist/{chunk-4XA6ENNL.js → chunk-VLTWBTQ7.js} +14 -15
- package/dist/chunk-ZUC6OXSL.js +178 -0
- package/dist/cli.js +276 -1258
- package/dist/{core-TSXA5XZH.js → core-77F2BVYV.js} +2 -2
- package/dist/{cursor-mdc-VEOFFDVO.js → cursor-mdc-EEO7PYZ3.js} +1 -1
- package/dist/{exporter-AWXS34AS.js → exporter-ZYJ4WM2F.js} +1 -1
- package/dist/{importer-3Q5M6QBL.js → importer-4UWQDH4W.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/mcp-client-ROOJF76V.js +9 -0
- package/dist/mcp-config-QD4NPVXB.js +12 -0
- package/dist/{migrate-UKCO6BUU.js → migrate-KJ5K5NWO.js} +1 -1
- package/dist/{plugin-loader-STTGYIL5.js → plugin-loader-SQQB6V74.js} +69 -23
- package/dist/resolver-H7GXVP73.js +21 -0
- package/dist/serve.js +2 -2
- package/dist/{server-A6MUVKQK.js → server-2ZQKXJ5M.js} +74 -6
- package/dist/{windsurf-rules-RWPKBHRD.js → windsurf-rules-XF7MYF6J.js} +1 -1
- package/dist/{wizard-AOXWMSXW.js → wizard-UH27IO4I.js} +2 -2
- package/package.json +3 -2
- package/dist/{tuner-KFNNGKG3.js → tuner-Y2YENAZC.js} +3 -3
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
27
|
-
import "./chunk-
|
|
26
|
+
} from "./chunk-B4UOE64J.js";
|
|
27
|
+
import "./chunk-645NBY6L.js";
|
|
28
28
|
export {
|
|
29
29
|
GraphStore,
|
|
30
30
|
SUPPORTED_EXTENSIONS,
|
|
@@ -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
|
-
|
|
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.
|
|
42
|
-
return { plugin: null, reason:
|
|
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.
|
|
45
|
-
return { plugin: null, reason:
|
|
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
|
-
|
|
48
|
-
|
|
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 (
|
|
51
|
-
|
|
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
|
-
|
|
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-
|
|
13
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
27
|
-
import "./chunk-
|
|
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-
|
|
10
|
+
const { getStore } = await import("./core-77F2BVYV.js");
|
|
11
11
|
const store = await getStore(projectRoot);
|
|
12
12
|
try {
|
|
13
13
|
const allNodes = store.getAllNodes();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engramx",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "The context spine for AI coding agents.
|
|
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",
|