engramx 3.0.2 → 3.4.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 +41 -0
- package/README.md +49 -9
- package/dist/cli.js +97 -9
- package/dist/cost-CSILPTZT.js +227 -0
- package/dist/{wizard-UH27IO4I.js → wizard-WGBAIZLF.js} +60 -3
- package/package.json +1 -1
- package/dist/{server-2ZQKXJ5M.js → server-LEYILLJ2.js} +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,47 @@ All notable changes to engram are documented here. Format based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [3.4.0] — 2026-05-02 — "Universal Spine"
|
|
10
|
+
|
|
11
|
+
The release that turns engram from a Claude Code tool into a universal context spine across every major AI coding tool. Same engram, same graph, same 89.1% reduction — now plugged into 8 IDEs out of the box.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Universal init detector.** `src/setup/detect.ts` adds three new detectors: `detectCline` (probes for VS Code's Cline globalStorage), `detectZed` (probes for Zed's config and `.zed/settings.json`), `detectCodex` (probes for `~/.codex` and `AGENTS.md`). `detectAllIdes()` now returns 8 entries (was 5). Per-IDE setup hints in `wizard.ts` now include the exact MCP config snippet for Cline.
|
|
16
|
+
- **Anthropic Claude Code plugin manifest.** `plugins/anthropic-marketplace/` — submission-ready `marketplace.json`, `plugin.json`, three skills (`/engram:cost`, `/engram:query`, `/engram:mistakes`), and the MCP server config that registers `engram-serve` automatically. Verified against `code.claude.com/docs/en/plugins`.
|
|
17
|
+
- **VS Code / Cursor extension.** `extensions/vscode/` — thin wrapper around the engramx CLI. Six commands (init, gen-mdc, gen, cost, dashboard, doctor), status-bar entry, two configuration settings. Compiles to a single `out/extension.js` and packs via `vsce` for OpenVSX. Works in VS Code, Cursor, and any VS Code fork.
|
|
18
|
+
- **Cline integration documented.** `docs/integrations/cline.md` — Cline supports MCP natively, so the integration is one config-snippet away. Cross-linked from `docs/integrations/README.md`.
|
|
19
|
+
- **engramx-continue.** Sister npm package at `adapters/continue/` — Continue.dev `@engram` context provider with HTTP and CLI fallback transports. Tarball verified clean (2.2 KB, 4 files).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **README.md.** Top-of-file callout now leads with the May 2026 market context (Cursor pricing crisis + Claude Code rate-limit pain) so the positioning matches what users are actually searching for. IDE matrix expanded from 8 to 11. "How It Compares" rewritten as the May 2026 8-row competitive matrix (engramx vs Cursor index / Aider repo map / Cline / Continue / Mem0 / claude-mem / CartoGopher); legacy table moved to a collapsed details block.
|
|
24
|
+
- **docs/install.html.** Title, meta description, OG title + description, hero pill, nav link, and IDE matrix all refreshed for v3.4 framing.
|
|
25
|
+
- **GitHub repo description and topics.** Description shortened and re-led with "context spine that 10x's every AI coding session." Topics maxed at 20 with `cline` and `universal-spine` added.
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
- Net-new: 3 detector tests in `tests/setup/detect.test.ts` (detectCline / detectZed / detectCodex).
|
|
29
|
+
- Total: **910 passing** (was 907 baseline).
|
|
30
|
+
|
|
31
|
+
### Process
|
|
32
|
+
|
|
33
|
+
This is the first release that walks the new 8-phase release ritual codified at `~/.claude/skills/engram-release/SKILL.md`. Phase 3 (public surface refresh) caught README + install.html + topics drift in one pass. Future releases follow the same checklist by default.
|
|
34
|
+
|
|
35
|
+
### Why
|
|
36
|
+
|
|
37
|
+
Three things broke at the same time in 2026. Cursor went usage-based and people started getting $1,400 surprise bills. Anthropic tightened Claude Code limits, then quietly tested removing the product from the $20 Pro plan. AI coding fragmented into 8 IDEs with no common context layer. v3.4 puts engram into all of them — one install, one graph, every tool benefits. Audit at `~/Desktop/Projects/Engram/00-strategy/2026-05-02-strategic-audit-v34-pivot.md`.
|
|
38
|
+
|
|
39
|
+
## [3.3.0] — 2026-05-02 — "Cost Lens"
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- New `engram cost` subcommand: aggregates token-savings telemetry from existing `.engram/hook-log.jsonl` files across one or many project roots. Outputs a terminal table, JSON, or a weekly Markdown digest at `~/.engram/cost-report-YYYY-Www.md`.
|
|
43
|
+
- New `src/cost/` module: `types.ts`, `aggregator.ts`, `formatter.ts`, `digest.ts`, `instrument.ts`. Pure functions, hermetic tests, NaN-safe math.
|
|
44
|
+
- Dispatch instrumentation in `src/intercept/dispatch.ts` — every PreToolUse log entry now carries `wouldHaveRead`, `injected`, and `tokensSaved` fields when applicable.
|
|
45
|
+
- 31 new tests across `tests/cost.test.ts` and `tests/cost-instrument.test.ts`, hermetic.
|
|
46
|
+
|
|
47
|
+
### Why
|
|
48
|
+
Cost Lens is the baseline for the v3.3 → v4.0 roadmap. Future features (Bridge, Mesh, Vector) get evaluated against the real-world impact, not a single static benchmark. PRD: `01-prds/03-engram-mesh-ruflo-integration-PRD.md`.
|
|
49
|
+
|
|
9
50
|
## [3.0.2] — 2026-04-24 — "MCP Registry"
|
|
10
51
|
|
|
11
52
|
Chore release. No runtime changes. Adds the `mcpName` field to `package.json`
|
package/README.md
CHANGED
|
@@ -47,16 +47,28 @@
|
|
|
47
47
|
<a href="https://www.npmjs.com/package/engramx"><img src="https://img.shields.io/npm/v/engramx?color=blue" alt="npm version"></a>
|
|
48
48
|
<img src="https://img.shields.io/badge/license-Apache%202.0-blue" alt="License">
|
|
49
49
|
<img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="Node">
|
|
50
|
-
<img src="https://img.shields.io/badge/tests-
|
|
50
|
+
<img src="https://img.shields.io/badge/tests-910%20passing-brightgreen" alt="Tests">
|
|
51
51
|
<img src="https://img.shields.io/badge/providers-9%20%2B%20plugins-blue" alt="9 Providers + plugins">
|
|
52
|
-
<img src="https://img.shields.io/badge/token%20savings-
|
|
52
|
+
<img src="https://img.shields.io/badge/token%20savings-89.1%25%20measured-orange" alt="89.1% measured savings">
|
|
53
53
|
<img src="https://img.shields.io/badge/native%20deps-zero-green" alt="Zero native deps">
|
|
54
54
|
<img src="https://img.shields.io/badge/LLM%20cost-$0-green" alt="Zero LLM cost">
|
|
55
55
|
</p>
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
## Why this exists, May 2026
|
|
60
|
+
|
|
61
|
+
Three things broke at the same time. Cursor went usage-based and people started getting $1,400 surprise bills. Anthropic tightened Claude Code limits, then quietly tested removing it from the $20 Pro plan. Half the AI coding crowd migrated from one tool to the other, hit the new ceiling within a week, and started looking for any way to make a session last longer.
|
|
62
|
+
|
|
63
|
+
Engramx is what makes the session last longer. It indexes your codebase into a local SQLite knowledge graph once. Then it intercepts file reads at the agent boundary and replaces them with a structural summary the agent already has the working memory for. Same edit, same diff, same code shipped — fewer tokens consumed in the round trip.
|
|
64
|
+
|
|
65
|
+
On a real 87-file repo, the measured reduction is **89.1%**. That's not a marketing number. The benchmark is committed to this repo as `bench/real-world.ts` and runs against any project you point it at. Independent migration guides ([dev.to/56kode](https://dev.to/56_kode/why-were-moving-from-cursor-to-claude-code-and-why-you-should-too-9kh), [SpectrumAI Lab](https://spectrumailab.com/blog/claude-code-vs-cursor)) cite engram as the strongest measured number in the category.
|
|
66
|
+
|
|
67
|
+
Works in 8 IDEs and counting — Claude Code, Cursor, Cline, Continue.dev, Aider, Windsurf, Zed, OpenAI Codex CLI. One install, one graph, every tool benefits. Apache 2.0. Local SQLite. Nothing leaves your machine.
|
|
68
|
+
|
|
69
|
+
> **v3.4 "Universal Spine" shipped 2026-05-02** — multi-IDE detector covers 8 tools, Anthropic Claude Code plugin (`/plugin install engram`), VS Code / Cursor extension on OpenVSX, `engramx-continue` on npm, Cline integration documented. Cost Lens telemetry from v3.3.0 now feeds a weekly Markdown digest at `~/.engram/cost-report-YYYY-Www.md`. 910 tests, CI green on Ubuntu + Windows × Node 20 + 22. See [CHANGELOG.md](CHANGELOG.md) for the v3.3 + v3.4 diff.
|
|
70
|
+
|
|
71
|
+
> **EngramX v3.0 "Spine" shipped 2026-04-24** — the biggest release before v3.4. The spine is **extensible**: any MCP server becomes an EngramX provider via a 10-line plugin file. **Pre-mortem mistake-guard** warns before you repeat a bug. **Bi-temporal mistake memory** — refactored-away mistakes stop firing. **Anthropic Auto-Memory bridge** reads Claude Code's own consolidated memory. **SSE-streaming** packets render progressively. `engram gen` dual-emits `AGENTS.md` + `CLAUDE.md` by default.
|
|
60
72
|
|
|
61
73
|
---
|
|
62
74
|
|
|
@@ -368,14 +380,19 @@ engram hooks install # auto-rebuild graph on every git commit
|
|
|
368
380
|
|
|
369
381
|
## IDE Integrations
|
|
370
382
|
|
|
383
|
+
`engram setup` auto-detects every supported IDE on your machine and prints the right next-step for each. You don't have to remember which command to run — the detector knows.
|
|
384
|
+
|
|
371
385
|
| IDE | Integration | Setup |
|
|
372
386
|
|-----|------------|-------|
|
|
373
|
-
| **Claude Code** | Hook-based interception (native, automatic) | `engram install-hook` |
|
|
374
|
-
| **Cursor** | MDC snapshot + native MCP | `engram gen-mdc` · [docs/integrations/cursor-mcp.md](docs/integrations/cursor-mcp.md) |
|
|
375
|
-
| **
|
|
376
|
-
| **
|
|
377
|
-
| **Aider** | Context file generation | `engram gen-aider` |
|
|
387
|
+
| **Claude Code** | Hook-based interception (native, automatic) — **plus** `/plugin install engram` for slash-commands | `engram install-hook` · [docs/integrations/claude-code.md](docs/integrations/claude-code.md) |
|
|
388
|
+
| **Cursor** | MDC snapshot + native MCP + VS Code extension on OpenVSX | `engram gen-mdc` · [docs/integrations/cursor-mcp.md](docs/integrations/cursor-mcp.md) |
|
|
389
|
+
| **Cline** | MCP server (5M+ VS Code installs, no native answer to token burn) | [docs/integrations/cline.md](docs/integrations/cline.md) |
|
|
390
|
+
| **Continue.dev** | `@engram` context provider via [`engramx-continue`](https://www.npmjs.com/package/engramx-continue) | [docs/integrations/continue.md](docs/integrations/continue.md) |
|
|
391
|
+
| **Aider** | Context file generation | `engram gen-aider` · [docs/integrations/aider.md](docs/integrations/aider.md) |
|
|
378
392
|
| **Windsurf** (Codeium) | `.windsurfrules` snapshot + MCP | `engram gen-windsurfrules` |
|
|
393
|
+
| **Zed** | Context server (`/engram`) | `engram context-server` · [docs/integrations/zed.md](docs/integrations/zed.md) |
|
|
394
|
+
| **OpenAI Codex CLI** | `AGENTS.md` auto-emit (universal Linux Foundation standard) | `engram gen` (default emits both `AGENTS.md` + `CLAUDE.md`) |
|
|
395
|
+
| **VS Code (any agent)** | Status-bar entry + 6 commands wrapping the CLI | `code --install-extension engram-vscode` (OpenVSX) |
|
|
379
396
|
| **Neovim** | MCP via codecompanion / avante | [docs/integrations/neovim.md](docs/integrations/neovim.md) |
|
|
380
397
|
| **Emacs** | MCP via gptel-mcp | [docs/integrations/emacs.md](docs/integrations/emacs.md) |
|
|
381
398
|
|
|
@@ -385,17 +402,40 @@ Per-IDE setup guides are in [`docs/integrations/`](docs/integrations/).
|
|
|
385
402
|
|
|
386
403
|
## How It Compares
|
|
387
404
|
|
|
405
|
+
The "context spine" slot — local-first, code-aware, works in any MCP runtime, with a reproducible benchmark — is currently unowned. Here's the field as of May 2026:
|
|
406
|
+
|
|
407
|
+
| | **engramx** | Cursor index | Aider repo map | Cline | Continue.dev | Mem0 | claude-mem | CartoGopher |
|
|
408
|
+
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
409
|
+
| Works in any MCP runtime | ✅ | IDE-locked | Aider only | VS Code only | VS Code only | ✅ | Claude Code only | ✅ |
|
|
410
|
+
| Local-first (nothing leaves machine) | ✅ | cloud-synced | ✅ | ✅ | ✅ | optional | ✅ | ✅ |
|
|
411
|
+
| Code-aware AST graph | ✅ | proprietary | ✅ | — | — | — | — | ✅ |
|
|
412
|
+
| Reproducible benchmark | ✅ **89.1%** | — | — | — | — | — | — | claims 88% |
|
|
413
|
+
| Bi-temporal mistake memory | ✅ | — | — | — | — | — | partial | — |
|
|
414
|
+
| `AGENTS.md` + `CLAUDE.md` dual-emit | ✅ | — | — | — | — | — | — | — |
|
|
415
|
+
| Single npm install | ✅ | full IDE | pip | VS Code ext | VS Code ext | pip / npm | claude plugin | Go binary |
|
|
416
|
+
| License | Apache 2.0 | proprietary | Apache 2.0 | Apache 2.0 | Apache 2.0 | Apache 2.0 | MIT | unknown |
|
|
417
|
+
| GitHub stars (May 2026) | 108 | proprietary | 39K | 61.2K | 32.4K | 47.8K | new | unknown |
|
|
418
|
+
|
|
419
|
+
The matrix isn't a slight at any of them — most do something engram doesn't. Cursor's index is great inside Cursor. Aider's repo map is great in Aider. Cline's full-file rewrite model is honest about what it is. The point is that nobody else covers all eight rows. Engram is the only tool that does.
|
|
420
|
+
|
|
421
|
+
For the legacy comparison vs `Continue @RepoMap` / `Cursor .cursorrules` / `@199-bio/engram` (small repo-map approaches), see the matrix below.
|
|
422
|
+
|
|
423
|
+
<details>
|
|
424
|
+
<summary><strong>Legacy detailed comparison</strong></summary>
|
|
425
|
+
|
|
388
426
|
| | engram | Continue @RepoMap | Cursor .cursorrules | Aider repo-map | @199-bio/engram |
|
|
389
427
|
|---|---|---|---|---|---|
|
|
390
428
|
| **Interception model** | Hook-based, automatic on every Read | Fetched at @-mention time | Static file, manual | Per-session map | MCP server, called explicitly |
|
|
391
429
|
| **Cache strategy** | SQLite at SessionStart, <5ms per read | No cache — live fetch | No cache | Per-session only | No cache |
|
|
392
430
|
| **Persistent memory** | Decisions, mistakes, patterns across sessions | No | Manual text file | No | No |
|
|
393
|
-
| **Multiple providers** |
|
|
431
|
+
| **Multiple providers** | 9 (AST, git, mistakes, MemPalace, Context7, Obsidian, LSP, Anthropic Memory, MCP plugins) | Repo structure only | No | Repo structure only | Graph query only |
|
|
394
432
|
| **Mistake tracking** | LSP diagnostics → mistake nodes, ⚠️ on Edit | No | No | No | No |
|
|
395
433
|
| **Survives compaction** | Yes (PreCompact hook) | No | Yes (static file) | No | No |
|
|
396
434
|
| **LLM cost** | $0 | $0 | $0 | $0 | $0 |
|
|
397
435
|
| **Native deps** | Zero | No | No | No | No |
|
|
398
436
|
|
|
437
|
+
</details>
|
|
438
|
+
|
|
399
439
|
---
|
|
400
440
|
|
|
401
441
|
## Install + Configuration
|
package/dist/cli.js
CHANGED
|
@@ -1393,6 +1393,51 @@ async function handleCwdChanged(payload) {
|
|
|
1393
1393
|
}
|
|
1394
1394
|
}
|
|
1395
1395
|
|
|
1396
|
+
// src/cost/instrument.ts
|
|
1397
|
+
import { statSync as statSync3 } from "fs";
|
|
1398
|
+
var CHARS_PER_TOKEN = 4;
|
|
1399
|
+
function tokensFromChars(chars) {
|
|
1400
|
+
if (!Number.isFinite(chars) || chars <= 0) return 0;
|
|
1401
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
1402
|
+
}
|
|
1403
|
+
function extractInjectedTokens(result) {
|
|
1404
|
+
if (!result || typeof result !== "object") return 0;
|
|
1405
|
+
try {
|
|
1406
|
+
const hook = result.hookSpecificOutput;
|
|
1407
|
+
if (!hook || typeof hook !== "object") return 0;
|
|
1408
|
+
const reason = hook.permissionDecisionReason;
|
|
1409
|
+
if (typeof reason === "string" && reason.length > 0) {
|
|
1410
|
+
return tokensFromChars(reason.length);
|
|
1411
|
+
}
|
|
1412
|
+
const ctx = hook.additionalContext;
|
|
1413
|
+
if (typeof ctx === "string" && ctx.length > 0) {
|
|
1414
|
+
return tokensFromChars(ctx.length);
|
|
1415
|
+
}
|
|
1416
|
+
} catch {
|
|
1417
|
+
}
|
|
1418
|
+
return 0;
|
|
1419
|
+
}
|
|
1420
|
+
function estimateWouldHaveReadTokens(tool, filePath) {
|
|
1421
|
+
if (tool !== "Read") return 0;
|
|
1422
|
+
if (!filePath || typeof filePath !== "string") return 0;
|
|
1423
|
+
try {
|
|
1424
|
+
const size = statSync3(filePath).size;
|
|
1425
|
+
return tokensFromChars(size);
|
|
1426
|
+
} catch {
|
|
1427
|
+
return 0;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
function composeCostFields(tool, filePath, result) {
|
|
1431
|
+
const injected = extractInjectedTokens(result);
|
|
1432
|
+
const wouldHaveRead = estimateWouldHaveReadTokens(tool, filePath);
|
|
1433
|
+
const tokensSaved = Math.max(0, wouldHaveRead - injected);
|
|
1434
|
+
const out = {};
|
|
1435
|
+
if (wouldHaveRead > 0) out.wouldHaveRead = wouldHaveRead;
|
|
1436
|
+
if (injected > 0) out.injected = injected;
|
|
1437
|
+
if (tokensSaved > 0) out.tokensSaved = tokensSaved;
|
|
1438
|
+
return out;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1396
1441
|
// src/intercept/dispatch.ts
|
|
1397
1442
|
function validatePayload(raw) {
|
|
1398
1443
|
if (raw === null || typeof raw !== "object") return null;
|
|
@@ -1468,11 +1513,13 @@ async function dispatchPreToolUse(payload) {
|
|
|
1468
1513
|
if (projectRoot) {
|
|
1469
1514
|
const decision = extractPreToolDecision(result);
|
|
1470
1515
|
const filePath = typeof handlerPayload.tool_input?.file_path === "string" ? handlerPayload.tool_input.file_path : void 0;
|
|
1516
|
+
const cost = composeCostFields(tool, filePath, result);
|
|
1471
1517
|
logHookEvent(projectRoot, {
|
|
1472
1518
|
event: "PreToolUse",
|
|
1473
1519
|
tool,
|
|
1474
1520
|
path: filePath,
|
|
1475
|
-
decision
|
|
1521
|
+
decision,
|
|
1522
|
+
...cost
|
|
1476
1523
|
});
|
|
1477
1524
|
}
|
|
1478
1525
|
}
|
|
@@ -1494,7 +1541,7 @@ function extractPreToolDecision(result) {
|
|
|
1494
1541
|
|
|
1495
1542
|
// src/dashboard.ts
|
|
1496
1543
|
import chalk from "chalk";
|
|
1497
|
-
import { existsSync as existsSync5, statSync as
|
|
1544
|
+
import { existsSync as existsSync5, statSync as statSync4 } from "fs";
|
|
1498
1545
|
import { join as join5, resolve as resolve6, basename as basename4 } from "path";
|
|
1499
1546
|
var AMBER = chalk.hex("#d97706");
|
|
1500
1547
|
var DIM = chalk.dim;
|
|
@@ -1617,7 +1664,7 @@ function startDashboard(projectRoot, options = {}) {
|
|
|
1617
1664
|
try {
|
|
1618
1665
|
const logPath = join5(root, ".engram", "hook-log.jsonl");
|
|
1619
1666
|
if (existsSync5(logPath)) {
|
|
1620
|
-
const currentSize =
|
|
1667
|
+
const currentSize = statSync4(logPath).size;
|
|
1621
1668
|
if (currentSize !== lastSize) {
|
|
1622
1669
|
cachedEntries = readHookLog(root);
|
|
1623
1670
|
lastSize = currentSize;
|
|
@@ -1680,7 +1727,7 @@ import {
|
|
|
1680
1727
|
readFileSync as readFileSync3,
|
|
1681
1728
|
writeFileSync,
|
|
1682
1729
|
renameSync,
|
|
1683
|
-
statSync as
|
|
1730
|
+
statSync as statSync5
|
|
1684
1731
|
} from "fs";
|
|
1685
1732
|
import { join as join6 } from "path";
|
|
1686
1733
|
var ENGRAM_MARKER_START = "<!-- engram:structural-facts:start -->";
|
|
@@ -1757,7 +1804,7 @@ function writeEngramSectionToMemoryMd(projectRoot, engramSection) {
|
|
|
1757
1804
|
try {
|
|
1758
1805
|
let existing = "";
|
|
1759
1806
|
if (existsSync6(memoryPath)) {
|
|
1760
|
-
const st =
|
|
1807
|
+
const st = statSync5(memoryPath);
|
|
1761
1808
|
if (st.size > MAX_MEMORY_FILE_BYTES) {
|
|
1762
1809
|
return false;
|
|
1763
1810
|
}
|
|
@@ -2166,6 +2213,47 @@ program.command("bench").description("Run token reduction benchmark").option("-p
|
|
|
2166
2213
|
}
|
|
2167
2214
|
console.log();
|
|
2168
2215
|
});
|
|
2216
|
+
program.command("cost").description("Show token-savings telemetry from engram hook logs").option(
|
|
2217
|
+
"-p, --project <path...>",
|
|
2218
|
+
"One or more project roots. Defaults to current dir if omitted."
|
|
2219
|
+
).option("--digest", "Write weekly Markdown digest to ~/.engram/").option("--json", "Emit machine-readable JSON instead of a terminal table").action(
|
|
2220
|
+
async (opts) => {
|
|
2221
|
+
const cost = await import("./cost-CSILPTZT.js");
|
|
2222
|
+
const roots = opts.project && opts.project.length > 0 ? opts.project.map((p) => pathResolve2(p)) : [pathResolve2(".")];
|
|
2223
|
+
if (opts.digest) {
|
|
2224
|
+
const result = cost.writeWeeklyDigest(roots);
|
|
2225
|
+
console.log(
|
|
2226
|
+
chalk2.green(
|
|
2227
|
+
`wrote ${result.isoWeek} digest \u2192 ${result.path} (${result.rows.length} project${result.rows.length === 1 ? "" : "s"})`
|
|
2228
|
+
)
|
|
2229
|
+
);
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
const rows = cost.summarizeProjects(roots);
|
|
2233
|
+
if (opts.json) {
|
|
2234
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
console.log(chalk2.bold("\nengram cost lens\n"));
|
|
2238
|
+
console.log(cost.formatTable(rows));
|
|
2239
|
+
const totalSaved = rows.reduce(
|
|
2240
|
+
(a, r) => a + r.summary.tokensSaved,
|
|
2241
|
+
0
|
|
2242
|
+
);
|
|
2243
|
+
const totalEvents = rows.reduce((a, r) => a + r.summary.events, 0);
|
|
2244
|
+
const totalUsd = rows.reduce(
|
|
2245
|
+
(a, r) => a + r.summary.approxUsdSaved,
|
|
2246
|
+
0
|
|
2247
|
+
);
|
|
2248
|
+
console.log(
|
|
2249
|
+
chalk2.dim(
|
|
2250
|
+
`
|
|
2251
|
+
total: ${cost.formatNumber(totalSaved)} tokens saved \xB7 ${cost.formatUsd(totalUsd)} \xB7 ${totalEvents} events
|
|
2252
|
+
`
|
|
2253
|
+
)
|
|
2254
|
+
);
|
|
2255
|
+
}
|
|
2256
|
+
);
|
|
2169
2257
|
var hooks = program.command("hooks").description("Manage git hooks");
|
|
2170
2258
|
hooks.command("install").description("Install post-commit and post-checkout hooks").argument("[path]", "Project directory", ".").action((p) => console.log(install(p)));
|
|
2171
2259
|
hooks.command("uninstall").description("Remove engram git hooks").argument("[path]", "Project directory", ".").action((p) => console.log(uninstall(p)));
|
|
@@ -2761,7 +2849,7 @@ program.command("stress-test").description("Run stress tests: memory, concurrenc
|
|
|
2761
2849
|
}
|
|
2762
2850
|
});
|
|
2763
2851
|
program.command("server").description("Start engram HTTP REST server (binds to 127.0.0.1 only)").option("--http", "Enable HTTP server (default)").option("--port <port>", "HTTP port", "7337").option("-p, --project <path>", "Project directory", ".").action(async (opts) => {
|
|
2764
|
-
const { startHttpServer } = await import("./server-
|
|
2852
|
+
const { startHttpServer } = await import("./server-LEYILLJ2.js");
|
|
2765
2853
|
await startHttpServer(pathResolve2(opts.project), parseInt(opts.port, 10));
|
|
2766
2854
|
});
|
|
2767
2855
|
program.command("ui").description("Open the web dashboard (auto-starts HTTP server if needed)").option("--port <port>", "HTTP port", "7337").option("-p, --project <path>", "Project directory", ".").option("--no-open", "Don't launch browser, just print the URL").action(async (opts) => {
|
|
@@ -2989,7 +3077,7 @@ pluginCmd.command("list").description("List installed provider plugins").action(
|
|
|
2989
3077
|
}
|
|
2990
3078
|
});
|
|
2991
3079
|
pluginCmd.command("install").description("Install a plugin by copying its .mjs file into ~/.engram/plugins/").argument("<file>", "Path to plugin .mjs file").action(async (file) => {
|
|
2992
|
-
const { copyFileSync: copyFileSync2, statSync:
|
|
3080
|
+
const { copyFileSync: copyFileSync2, statSync: statSync6 } = await import("fs");
|
|
2993
3081
|
const { basename: basename6 } = await import("path");
|
|
2994
3082
|
const { getPluginsDir, ensurePluginsDir, validatePlugin } = await import("./plugin-loader-SQQB6V74.js");
|
|
2995
3083
|
const { pathToFileURL } = await import("url");
|
|
@@ -2998,7 +3086,7 @@ pluginCmd.command("install").description("Install a plugin by copying its .mjs f
|
|
|
2998
3086
|
console.error(chalk2.red(`File not found: ${absPath}`));
|
|
2999
3087
|
process.exit(1);
|
|
3000
3088
|
}
|
|
3001
|
-
if (!
|
|
3089
|
+
if (!statSync6(absPath).isFile()) {
|
|
3002
3090
|
console.error(chalk2.red(`Not a file: ${absPath}`));
|
|
3003
3091
|
process.exit(1);
|
|
3004
3092
|
}
|
|
@@ -3193,7 +3281,7 @@ program.command("setup").description("Zero-friction first-run wizard (init + ins
|
|
|
3193
3281
|
"local"
|
|
3194
3282
|
).action(
|
|
3195
3283
|
async (opts) => {
|
|
3196
|
-
const { runSetup } = await import("./wizard-
|
|
3284
|
+
const { runSetup } = await import("./wizard-WGBAIZLF.js");
|
|
3197
3285
|
const scope = opts.scope === "local" || opts.scope === "project" || opts.scope === "user" ? opts.scope : "local";
|
|
3198
3286
|
const result = await runSetup({
|
|
3199
3287
|
projectPath: opts.project,
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// src/cost/types.ts
|
|
2
|
+
var DEFAULT_COST_CONFIG = {
|
|
3
|
+
inputUsdPerMillion: 3,
|
|
4
|
+
currency: "USD"
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/cost/aggregator.ts
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
var LOG_FILES = ["hook-log.jsonl", "hook-log.jsonl.1"];
|
|
11
|
+
function readEvents(projectRoot) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const name of LOG_FILES) {
|
|
14
|
+
const p = join(projectRoot, ".engram", name);
|
|
15
|
+
if (!existsSync(p)) continue;
|
|
16
|
+
let raw = "";
|
|
17
|
+
try {
|
|
18
|
+
raw = readFileSync(p, "utf8");
|
|
19
|
+
} catch {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
for (const line of raw.split("\n")) {
|
|
23
|
+
if (!line.trim()) continue;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(line);
|
|
26
|
+
out.push(toCostEvent(parsed));
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function toCostEvent(raw) {
|
|
34
|
+
const tokensSaved = numOrUndef(raw.tokensSaved);
|
|
35
|
+
const injected = numOrUndef(raw.injected);
|
|
36
|
+
const wouldHaveRead = numOrUndef(
|
|
37
|
+
raw.wouldHaveRead
|
|
38
|
+
);
|
|
39
|
+
return {
|
|
40
|
+
ts: typeof raw.ts === "string" ? raw.ts : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
41
|
+
event: typeof raw.event === "string" ? raw.event : "unknown",
|
|
42
|
+
tool: strOrUndef(raw.tool),
|
|
43
|
+
path: strOrUndef(raw.path),
|
|
44
|
+
wouldHaveRead,
|
|
45
|
+
injected,
|
|
46
|
+
tokensSaved
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function numOrUndef(v) {
|
|
50
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : void 0;
|
|
51
|
+
}
|
|
52
|
+
function strOrUndef(v) {
|
|
53
|
+
return typeof v === "string" ? v : void 0;
|
|
54
|
+
}
|
|
55
|
+
function summarize(events, config = DEFAULT_COST_CONFIG) {
|
|
56
|
+
let saved = 0;
|
|
57
|
+
let injected = 0;
|
|
58
|
+
let wouldHave = 0;
|
|
59
|
+
let firstTs = "";
|
|
60
|
+
let lastTs = "";
|
|
61
|
+
for (const e of events) {
|
|
62
|
+
if (e.tokensSaved) saved += e.tokensSaved;
|
|
63
|
+
if (e.injected) injected += e.injected;
|
|
64
|
+
if (e.wouldHaveRead) wouldHave += e.wouldHaveRead;
|
|
65
|
+
if (!firstTs || e.ts < firstTs) firstTs = e.ts;
|
|
66
|
+
if (!lastTs || e.ts > lastTs) lastTs = e.ts;
|
|
67
|
+
}
|
|
68
|
+
const denom = wouldHave > 0 ? wouldHave : saved + injected;
|
|
69
|
+
const reductionRatio = denom > 0 ? saved / denom : 0;
|
|
70
|
+
const approxUsdSaved = saved / 1e6 * config.inputUsdPerMillion;
|
|
71
|
+
return {
|
|
72
|
+
fromTs: firstTs,
|
|
73
|
+
toTs: lastTs,
|
|
74
|
+
events: events.length,
|
|
75
|
+
tokensSaved: saved,
|
|
76
|
+
tokensInjected: injected,
|
|
77
|
+
tokensWouldHave: wouldHave,
|
|
78
|
+
reductionRatio,
|
|
79
|
+
approxUsdSaved
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function summarizeProjects(projectRoots, config = DEFAULT_COST_CONFIG) {
|
|
83
|
+
return projectRoots.map((projectRoot) => ({
|
|
84
|
+
projectRoot,
|
|
85
|
+
summary: summarize(readEvents(projectRoot), config)
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/cost/formatter.ts
|
|
90
|
+
function formatNumber(n) {
|
|
91
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
|
|
92
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
93
|
+
return String(Math.round(n));
|
|
94
|
+
}
|
|
95
|
+
function formatUsd(n) {
|
|
96
|
+
if (n >= 1) return `$${n.toFixed(2)}`;
|
|
97
|
+
if (n >= 0.01) return `$${n.toFixed(3)}`;
|
|
98
|
+
return `$${n.toFixed(4)}`;
|
|
99
|
+
}
|
|
100
|
+
function formatPct(ratio) {
|
|
101
|
+
return `${(ratio * 100).toFixed(1)}%`;
|
|
102
|
+
}
|
|
103
|
+
function formatOneLine(s) {
|
|
104
|
+
return [
|
|
105
|
+
`${formatNumber(s.tokensSaved)} tokens saved`,
|
|
106
|
+
`${formatPct(s.reductionRatio)} reduction`,
|
|
107
|
+
`~${formatUsd(s.approxUsdSaved)}`,
|
|
108
|
+
`${s.events} events`
|
|
109
|
+
].join(" \xB7 ");
|
|
110
|
+
}
|
|
111
|
+
function formatTable(rows) {
|
|
112
|
+
if (rows.length === 0) return "(no projects with hook-log.jsonl)";
|
|
113
|
+
const lines = [];
|
|
114
|
+
lines.push("Project Tokens saved Reduction Approx USD Events");
|
|
115
|
+
lines.push("\u2500".repeat(86));
|
|
116
|
+
for (const r of rows) {
|
|
117
|
+
const name = truncate(basenameOf(r.projectRoot), 32).padEnd(34);
|
|
118
|
+
const saved = formatNumber(r.summary.tokensSaved).padStart(13);
|
|
119
|
+
const pct = formatPct(r.summary.reductionRatio).padStart(11);
|
|
120
|
+
const usd = formatUsd(r.summary.approxUsdSaved).padStart(12);
|
|
121
|
+
const ev = String(r.summary.events).padStart(7);
|
|
122
|
+
lines.push(`${name}${saved} ${pct} ${usd} ${ev}`);
|
|
123
|
+
}
|
|
124
|
+
return lines.join("\n");
|
|
125
|
+
}
|
|
126
|
+
function formatMarkdownDigest(rows, totals, isoWeek) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
lines.push(`# Engram Cost Digest \u2014 ${isoWeek}`);
|
|
129
|
+
lines.push("");
|
|
130
|
+
lines.push(`**Total tokens saved:** ${formatNumber(totals.tokensSaved)} (${formatPct(totals.reductionRatio)} reduction, ~${formatUsd(totals.approxUsdSaved)})`);
|
|
131
|
+
lines.push("");
|
|
132
|
+
lines.push("## Per-project");
|
|
133
|
+
lines.push("");
|
|
134
|
+
lines.push("| Project | Tokens saved | Reduction | Approx USD | Events |");
|
|
135
|
+
lines.push("|---|---:|---:|---:|---:|");
|
|
136
|
+
for (const r of rows) {
|
|
137
|
+
lines.push([
|
|
138
|
+
"",
|
|
139
|
+
basenameOf(r.projectRoot),
|
|
140
|
+
formatNumber(r.summary.tokensSaved),
|
|
141
|
+
formatPct(r.summary.reductionRatio),
|
|
142
|
+
formatUsd(r.summary.approxUsdSaved),
|
|
143
|
+
String(r.summary.events),
|
|
144
|
+
""
|
|
145
|
+
].join("|"));
|
|
146
|
+
}
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push("_Generated by `engram cost --digest` (v3.3 Cost Lens)_");
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
function basenameOf(p) {
|
|
152
|
+
const idx = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
153
|
+
return idx >= 0 ? p.slice(idx + 1) : p;
|
|
154
|
+
}
|
|
155
|
+
function truncate(s, n) {
|
|
156
|
+
return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/cost/digest.ts
|
|
160
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
161
|
+
import { homedir } from "os";
|
|
162
|
+
import { join as join2 } from "path";
|
|
163
|
+
function isoWeekLabel(d = /* @__PURE__ */ new Date()) {
|
|
164
|
+
const target = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
|
|
165
|
+
const dayNum = (target.getUTCDay() + 6) % 7;
|
|
166
|
+
target.setUTCDate(target.getUTCDate() - dayNum + 3);
|
|
167
|
+
const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
|
|
168
|
+
const weekNum = 1 + Math.round(
|
|
169
|
+
((target.getTime() - firstThursday.getTime()) / 864e5 - 3 + (firstThursday.getUTCDay() + 6) % 7) / 7
|
|
170
|
+
);
|
|
171
|
+
return `${target.getUTCFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
172
|
+
}
|
|
173
|
+
function writeWeeklyDigest(projectRoots, config = DEFAULT_COST_CONFIG, outDir = join2(homedir(), ".engram"), now = /* @__PURE__ */ new Date()) {
|
|
174
|
+
mkdirSync(outDir, { recursive: true });
|
|
175
|
+
const rows = summarizeProjects(projectRoots, config);
|
|
176
|
+
const totals = sumRows(rows, config);
|
|
177
|
+
const isoWeek = isoWeekLabel(now);
|
|
178
|
+
const md = formatMarkdownDigest(rows, totals, isoWeek);
|
|
179
|
+
const path = join2(outDir, `cost-report-${isoWeek}.md`);
|
|
180
|
+
writeFileSync(path, md, "utf8");
|
|
181
|
+
return { path, isoWeek, rows };
|
|
182
|
+
}
|
|
183
|
+
function sumRows(rows, config) {
|
|
184
|
+
let saved = 0;
|
|
185
|
+
let injected = 0;
|
|
186
|
+
let wouldHave = 0;
|
|
187
|
+
let events = 0;
|
|
188
|
+
let firstTs = "";
|
|
189
|
+
let lastTs = "";
|
|
190
|
+
for (const r of rows) {
|
|
191
|
+
saved += r.summary.tokensSaved;
|
|
192
|
+
injected += r.summary.tokensInjected;
|
|
193
|
+
wouldHave += r.summary.tokensWouldHave;
|
|
194
|
+
events += r.summary.events;
|
|
195
|
+
if (r.summary.fromTs && (!firstTs || r.summary.fromTs < firstTs)) {
|
|
196
|
+
firstTs = r.summary.fromTs;
|
|
197
|
+
}
|
|
198
|
+
if (r.summary.toTs && (!lastTs || r.summary.toTs > lastTs)) {
|
|
199
|
+
lastTs = r.summary.toTs;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const denom = wouldHave > 0 ? wouldHave : saved + injected;
|
|
203
|
+
return {
|
|
204
|
+
fromTs: firstTs,
|
|
205
|
+
toTs: lastTs,
|
|
206
|
+
events,
|
|
207
|
+
tokensSaved: saved,
|
|
208
|
+
tokensInjected: injected,
|
|
209
|
+
tokensWouldHave: wouldHave,
|
|
210
|
+
reductionRatio: denom > 0 ? saved / denom : 0,
|
|
211
|
+
approxUsdSaved: saved / 1e6 * config.inputUsdPerMillion
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export {
|
|
215
|
+
DEFAULT_COST_CONFIG,
|
|
216
|
+
formatMarkdownDigest,
|
|
217
|
+
formatNumber,
|
|
218
|
+
formatOneLine,
|
|
219
|
+
formatPct,
|
|
220
|
+
formatTable,
|
|
221
|
+
formatUsd,
|
|
222
|
+
isoWeekLabel,
|
|
223
|
+
readEvents,
|
|
224
|
+
summarize,
|
|
225
|
+
summarizeProjects,
|
|
226
|
+
writeWeeklyDigest
|
|
227
|
+
};
|
|
@@ -99,13 +99,66 @@ function detectAider(projectRoot) {
|
|
|
99
99
|
status: !installed ? "not detected" : configured ? ".aider-context.md present" : "detected \u2014 run `engram gen-aider`"
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
function detectCline() {
|
|
103
|
+
const candidates = [
|
|
104
|
+
join(homedir(), "Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev"),
|
|
105
|
+
join(homedir(), ".config/Code/User/globalStorage/saoudrizwan.claude-dev"),
|
|
106
|
+
join(homedir(), "AppData/Roaming/Code/User/globalStorage/saoudrizwan.claude-dev")
|
|
107
|
+
];
|
|
108
|
+
const installed = candidates.some(existsSync);
|
|
109
|
+
let configured = false;
|
|
110
|
+
if (installed) {
|
|
111
|
+
try {
|
|
112
|
+
configured = candidates.filter(existsSync).some((p) => {
|
|
113
|
+
const settings = join(p, "settings", "cline_mcp_settings.json");
|
|
114
|
+
return existsSync(settings) && readFileSync(settings, "utf-8").includes("engram");
|
|
115
|
+
});
|
|
116
|
+
} catch {
|
|
117
|
+
configured = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
name: "Cline",
|
|
122
|
+
installed,
|
|
123
|
+
configured,
|
|
124
|
+
status: !installed ? "not detected" : configured ? "engram MCP server registered" : "detected \u2014 add engram-serve to cline_mcp_settings.json"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function detectZed(projectRoot) {
|
|
128
|
+
const candidates = [
|
|
129
|
+
join(homedir(), ".config/zed"),
|
|
130
|
+
join(homedir(), "Library/Application Support/Zed"),
|
|
131
|
+
join(homedir(), "AppData/Roaming/Zed")
|
|
132
|
+
];
|
|
133
|
+
const installed = candidates.some(existsSync);
|
|
134
|
+
const configured = existsSync(join(projectRoot, ".zed", "settings.json"));
|
|
135
|
+
return {
|
|
136
|
+
name: "Zed",
|
|
137
|
+
installed,
|
|
138
|
+
configured,
|
|
139
|
+
status: !installed ? "not detected" : configured ? "Zed project settings present" : "detected \u2014 add engram context server"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function detectCodex(projectRoot) {
|
|
143
|
+
const installed = existsSync(join(homedir(), ".codex"));
|
|
144
|
+
const configured = existsSync(join(projectRoot, "AGENTS.md"));
|
|
145
|
+
return {
|
|
146
|
+
name: "Codex CLI",
|
|
147
|
+
installed,
|
|
148
|
+
configured,
|
|
149
|
+
status: !installed ? "not detected" : configured ? "AGENTS.md present" : "detected \u2014 run `engram gen` to create AGENTS.md"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
102
152
|
function detectAllIdes(projectRoot) {
|
|
103
153
|
return [
|
|
104
154
|
detectClaudeCode(projectRoot),
|
|
105
155
|
detectCursor(projectRoot),
|
|
106
|
-
|
|
156
|
+
detectCline(),
|
|
107
157
|
detectContinue(),
|
|
108
|
-
|
|
158
|
+
detectWindsurf(projectRoot),
|
|
159
|
+
detectAider(projectRoot),
|
|
160
|
+
detectZed(projectRoot),
|
|
161
|
+
detectCodex(projectRoot)
|
|
109
162
|
];
|
|
110
163
|
}
|
|
111
164
|
|
|
@@ -214,7 +267,11 @@ async function offerIdeAdapters(opts, rl) {
|
|
|
214
267
|
const suggest = {
|
|
215
268
|
Cursor: "engram gen-mdc",
|
|
216
269
|
Windsurf: "engram gen-windsurfrules",
|
|
217
|
-
Aider: "engram gen-aider"
|
|
270
|
+
Aider: "engram gen-aider",
|
|
271
|
+
"Codex CLI": "engram gen --target agents",
|
|
272
|
+
Cline: "Add to cline_mcp_settings.json: { engram: { command: 'engram-serve', args: ['" + root + "'] } }",
|
|
273
|
+
"Continue.dev": "Add to ~/.continue/config.json: { contextProviders: [{ name: 'engramx-continue' }] }",
|
|
274
|
+
Zed: "Register engram-serve as a Zed context server (see Docs/integrations/zed.md)"
|
|
218
275
|
};
|
|
219
276
|
const suggested = [];
|
|
220
277
|
for (const ide of unconfigured) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engramx",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"mcpName": "io.github.NickCirv/engram",
|
|
5
5
|
"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.",
|
|
6
6
|
"repository": {
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ContextCache,
|
|
3
|
-
getContextCache
|
|
4
|
-
} from "./chunk-CIQQ5Y3S.js";
|
|
5
1
|
import {
|
|
6
2
|
getOrCreateToken,
|
|
7
3
|
isHostValid,
|
|
@@ -9,6 +5,10 @@ import {
|
|
|
9
5
|
parseCookies,
|
|
10
6
|
safeEqual
|
|
11
7
|
} from "./chunk-N6PPKOPK.js";
|
|
8
|
+
import {
|
|
9
|
+
ContextCache,
|
|
10
|
+
getContextCache
|
|
11
|
+
} from "./chunk-CIQQ5Y3S.js";
|
|
12
12
|
import {
|
|
13
13
|
summarizeHookLog
|
|
14
14
|
} from "./chunk-FKY6HIT2.js";
|