eminiar 0.10.1 → 0.12.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/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/mcp.json +11 -0
- package/.cursor-plugin/plugin.json +23 -0
- package/README.md +62 -16
- package/bin/em-hook +75 -2
- package/dist/cli.js +3674 -557
- package/dist/hook.js +6986 -4104
- package/dist/server.js +5020 -1703
- package/dist/ui/app.global.js +67 -60
- package/dist/ui/styles.css +1014 -0
- package/hooks/cursor.json +36 -0
- package/hooks/hooks.json +9 -1
- package/package.json +7 -4
- package/rules/eminiar.mdc +15 -0
- package/skills/em-swarm/SKILL.md +100 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eminiar",
|
|
3
3
|
"displayName": "Eminiar",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "Local-first context, memory and knowledge graph for Claude Code: sandboxed execution, FTS5 + vector search, session continuity, code graph.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ryan Dick"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eminiar",
|
|
3
|
+
"displayName": "Eminiar",
|
|
4
|
+
"version": "0.12.0",
|
|
5
|
+
"description": "Local-first context, memory and knowledge graph for coding agents: sandboxed execution, FTS5 + vector search, session continuity, code graph.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Ryan Dick"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/rydn/eminiar#readme",
|
|
10
|
+
"repository": "https://github.com/rydn/eminiar",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"context",
|
|
14
|
+
"memory",
|
|
15
|
+
"search",
|
|
16
|
+
"sandbox",
|
|
17
|
+
"knowledge-graph"
|
|
18
|
+
],
|
|
19
|
+
"hooks": "./hooks/cursor.json",
|
|
20
|
+
"mcpServers": "./.cursor-plugin/mcp.json",
|
|
21
|
+
"skills": "./skills",
|
|
22
|
+
"rules": "./rules"
|
|
23
|
+
}
|
package/README.md
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# eminiar
|
|
4
4
|
|
|
5
|
-
**A memory, a code map and a clean context window for Claude Code — all on your machine.**
|
|
5
|
+
**A memory, a code map and a clean context window for Claude Code and Cursor — all on your machine.**
|
|
6
6
|
|
|
7
7
|
*Pronounced ee-MIN-ee-ar*
|
|
8
8
|
|
|
9
9
|
[](#license)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://docs.claude.com/en/docs/claude-code)
|
|
12
|
+
[](docs/getting-started.md#cursor)
|
|
12
13
|
[](docs/how-it-works.md#privacy-and-data)
|
|
13
14
|
|
|
14
15
|
[Quick start](#quick-start) · [Features](#what-you-get) · [How it works](docs/how-it-works.md) · [Tools](docs/tools.md) · [Docs](docs/README.md)
|
|
@@ -39,7 +40,8 @@ Claude Code is very capable, but three things slow it down on a real project:
|
|
|
39
40
|
|
|
40
41
|
## The solution
|
|
41
42
|
|
|
42
|
-
eminiar is a Claude Code plugin. It adds an MCP server with
|
|
43
|
+
eminiar is a Claude Code plugin that also installs into Cursor. It adds an MCP server with 24
|
|
44
|
+
`em_*` tools and a set of hooks.
|
|
43
45
|
Everything runs locally in one SQLite database per project.
|
|
44
46
|
|
|
45
47
|
| | Without eminiar | With eminiar |
|
|
@@ -47,9 +49,10 @@ Everything runs locally in one SQLite database per project.
|
|
|
47
49
|
| **Large output** | The full output goes into context | Output is indexed. Only the sections that match your `intent` come back |
|
|
48
50
|
| **Memory** | Lost at `/compact` or a new session | Markdown memories in your repo, given back to Claude when they are relevant |
|
|
49
51
|
| **Code structure** | `grep` and full-file reads | A knowledge graph of files, symbols, calls, routes and hotspots |
|
|
50
|
-
| **Git history** | Raw `git log` / `git blame` | Classified commits,
|
|
52
|
+
| **Git history** | Raw `git log` / `git blame` | Classified commits, fix tracing, and a graph from branches to the files, memories and docs they touched |
|
|
51
53
|
| **Library docs** | Web search on each question | A local, searchable index of the docs your project uses |
|
|
52
54
|
| **Getting knowledge** | Claude must remember to ask | Hooks push the relevant knowledge to Claude **automatically** |
|
|
55
|
+
| **Cost** | A guess, one session at a time | Tokens, cache hits and estimated cost for every model call, with insights |
|
|
53
56
|
|
|
54
57
|
## What you get
|
|
55
58
|
|
|
@@ -61,6 +64,12 @@ indexed. Claude gets back only the sections that match its `intent`. Snippets ob
|
|
|
61
64
|
Code `Bash` deny rules, and `em_execute_file` refuses a `path` that one of your `Read` deny rules
|
|
62
65
|
matches ([which rule forms](docs/how-it-works.md#sandbox)).
|
|
63
66
|
|
|
67
|
+
Hooks route noisy commands for Claude. A Bash line is judged one command at a time: output that
|
|
68
|
+
is already bounded (`| head`, `| wc`, `grep -l`, `cat` of a small file) and commands that change
|
|
69
|
+
files stay native. `git log`, `git blame` and `git show` go to `em_history`, and a `Grep` for a
|
|
70
|
+
symbol the graph knows goes to `em_symbols`. `em_stats` shows each routing decision and the rule
|
|
71
|
+
behind it, so you can see what was sent where.
|
|
72
|
+
|
|
64
73
|
### 🔎 Hybrid search — keyword and meaning together
|
|
65
74
|
|
|
66
75
|
SQLite FTS5 (porter + trigram, BM25, typo correction) is fused with local ONNX embeddings
|
|
@@ -75,6 +84,10 @@ and commit them. Decisions have an ADR-style status (`proposed`, `accepted`, `su
|
|
|
75
84
|
attach matching memories to your prompts, to file reads and edits, to failed commands and to
|
|
76
85
|
subagent briefs. **Claude does not have to remember to look.**
|
|
77
86
|
|
|
87
|
+
Recall matches a memory when the prompt names it, one of its tags, a file it mentions or a
|
|
88
|
+
symbol from that file. A memory can come back later in a long session. To see why a memory did
|
|
89
|
+
or did not appear, run `eminiar recall --explain "<prompt>"`.
|
|
90
|
+
|
|
78
91
|
### 🕸️ Code knowledge graph — a map before an edit
|
|
79
92
|
|
|
80
93
|
tree-sitter parses TypeScript, JavaScript, Python, Go and PHP into files, symbols, imports, calls,
|
|
@@ -83,12 +96,20 @@ Python decorator routes, Go routers, Laravel, Symfony and Slim). eminiar
|
|
|
83
96
|
adds fan-in, complexity, churn, a hotspot score, module clusters, import cycles and git co-change
|
|
84
97
|
coupling. Ask *"what breaks if I change this?"* with `em_graph_impact`.
|
|
85
98
|
|
|
99
|
+
Memories and documentation share the same graph. Each memory links to the files and symbols it
|
|
100
|
+
mentions, and each doc source links to the code that uses it. One graph answers *"what do we
|
|
101
|
+
know about this file?"*
|
|
102
|
+
|
|
86
103
|
### 📜 Git history, read for Claude
|
|
87
104
|
|
|
88
105
|
`em_history` classifies every commit (feat, fix, refactor, perf …). It traces each fix back to
|
|
89
106
|
the commit that wrote the broken lines. Files get a risk score with reasons. Before Claude edits
|
|
90
107
|
a bug-prone file, it sees the record.
|
|
91
108
|
|
|
109
|
+
The history graph connects branches to their commits, commits to the files they changed, and
|
|
110
|
+
those files to the memories and docs about them. Merged and deleted branches keep their commits.
|
|
111
|
+
A fix points at the commit it repairs.
|
|
112
|
+
|
|
92
113
|
### 📚 Documentation knowledge base
|
|
93
114
|
|
|
94
115
|
`em_docs` finds your local docs (READMEs, ADRs, OpenAPI, Postman) and the libraries, services
|
|
@@ -104,18 +125,29 @@ session with its compactions, which tools fill the context, insights with recomm
|
|
|
104
125
|
what eminiar's own tools saved in dollars. Everything stays local; cost is an estimate at list
|
|
105
126
|
price ([details](docs/usage.md)).
|
|
106
127
|
|
|
128
|
+
### 🐝 Agent swarms (opt-in)
|
|
129
|
+
|
|
130
|
+
Declare worker roles in `.eminiar/swarm.yaml`, with their prompts, models, tools, skills and
|
|
131
|
+
hooks. eminiar generates a Claude Code agent file per role. `/eminiar:em-swarm <swarm> <goal>`
|
|
132
|
+
makes the main session the coordinator: it plans small tasks, fans workers out in parallel and
|
|
133
|
+
reviews what comes back. Every worker starts with the memories, history risk and hot files for
|
|
134
|
+
its task. Capacity limits, a shared blackboard and a live Swarms tab in the dashboard come with
|
|
135
|
+
it. Nothing changes until you run `eminiar swarm init` ([details](docs/swarms.md)).
|
|
136
|
+
|
|
107
137
|
### 📊 Local dashboard
|
|
108
138
|
|
|
109
139
|
`eminiar ui` opens a local web app with token savings, usage and cost, a source explorer with
|
|
110
|
-
impact analysis, a graph "galaxy", an architecture map, a history explorer
|
|
111
|
-
a docs manager.
|
|
140
|
+
impact analysis, a graph "galaxy", an architecture map, a history explorer with a history
|
|
141
|
+
graph, a memory editor with a memory graph, and a docs manager.
|
|
112
142
|
|
|
113
143
|
| | |
|
|
114
144
|
|:---:|:---:|
|
|
115
|
-
| [<img alt="Galaxy: the knowledge graph around a file" src="docs/images/dashboard-galaxy-dark.webp" width="430">](docs/dashboard.md#galaxy) | [<img alt="
|
|
116
|
-
| **Galaxy:** the knowledge graph | **
|
|
117
|
-
| [<img alt="
|
|
118
|
-
| **
|
|
145
|
+
| [<img alt="Galaxy: the knowledge graph around a file" src="docs/images/dashboard-galaxy-dark.webp" width="430">](docs/dashboard.md#galaxy) | [<img alt="Usage: cost per day by model, cache hit rate, burn rate and session scores" src="docs/images/dashboard-usage-dark.webp" width="430">](docs/dashboard.md#usage) |
|
|
146
|
+
| **Galaxy:** the knowledge graph | **Usage:** cost, cache and burn rate |
|
|
147
|
+
| [<img alt="History graph: branches, their commits, and the files, memories and docs they touched" src="docs/images/dashboard-history-graph-dark.webp" width="430">](docs/dashboard.md#history) | [<img alt="Memory graph: memories linked to the files, tags and topics they are about" src="docs/images/dashboard-memory-graph-dark.webp" width="430">](docs/dashboard.md#memory) |
|
|
148
|
+
| **History graph:** branches to files and memories | **Memory graph:** what each memory is about |
|
|
149
|
+
| [<img alt="History: commits classified by kind, with the branch graph" src="docs/images/dashboard-history-dark.webp" width="430">](docs/dashboard.md#history) | [<img alt="Hotspots: fan-in, churn and complexity per file" src="docs/images/dashboard-hotspots-dark.webp" width="430">](docs/dashboard.md#architecture) |
|
|
150
|
+
| **History:** classified commits and fix tracing | **Hotspots:** where change is risky |
|
|
119
151
|
|
|
120
152
|
## What Claude sees
|
|
121
153
|
|
|
@@ -132,26 +164,39 @@ eminiar graph: src/cli.ts is imported by 6 files; em_graph_impact {node:"file:sr
|
|
|
132
164
|
them before you change an exported signature.
|
|
133
165
|
```
|
|
134
166
|
|
|
167
|
+
When a command would flood the context, routing names the tool to use instead:
|
|
168
|
+
|
|
169
|
+
```text
|
|
170
|
+
eminiar: `git diff .claude-plugin/plugin.json package.json README.md` usually prints a lot;
|
|
171
|
+
em_execute {language:"shell", code, intent} keeps the output out of context and returns only
|
|
172
|
+
matching sections. Retry with em_execute.
|
|
173
|
+
|
|
174
|
+
eminiar history: this commit includes files that deserve a second look:
|
|
175
|
+
- package.json is bug-prone (3/25 commits were fixes; 2 earlier changes later needed fixing)
|
|
176
|
+
```
|
|
177
|
+
|
|
135
178
|
Each note appears once per context window. After a `/compact`, notes come back when they are
|
|
136
179
|
relevant again.
|
|
137
180
|
|
|
138
181
|
## Quick start
|
|
139
182
|
|
|
140
|
-
**Requirements:** Node.js 22.12 or later, and Claude Code.
|
|
183
|
+
**Requirements:** Node.js 22.12 or later, and Claude Code or Cursor.
|
|
141
184
|
|
|
142
185
|
```sh
|
|
143
|
-
npx eminiar install
|
|
144
|
-
npx eminiar
|
|
186
|
+
npx eminiar install # registers the plugin, installs a stable copy, downloads the model
|
|
187
|
+
npx eminiar install --cursor # the same, plus hooks, MCP server and skills for Cursor
|
|
188
|
+
npx eminiar doctor # checks every part
|
|
145
189
|
```
|
|
146
190
|
|
|
147
|
-
Restart Claude Code. Then, in your project:
|
|
191
|
+
Restart Claude Code (or Cursor). Then, in your project:
|
|
148
192
|
|
|
149
193
|
```text
|
|
150
194
|
/em-onboard # writes onboarding memories and builds the docs knowledge base
|
|
151
195
|
```
|
|
152
196
|
|
|
153
|
-
That is all. Use Claude Code as usual. To see how much context eminiar saves, run
|
|
154
|
-
open the dashboard with `eminiar ui`.
|
|
197
|
+
That is all. Use Claude Code or Cursor as usual. To see how much context eminiar saves, run
|
|
198
|
+
`/em-stats` or open the dashboard with `eminiar ui`. One flag covers Cursor because the hook
|
|
199
|
+
runner speaks both dialects; see [Cursor](docs/getting-started.md#cursor) for what differs.
|
|
155
200
|
|
|
156
201
|
**For a team:** run `npx eminiar install --team` once and commit `eminiar.config.json` and
|
|
157
202
|
`.eminiar/`. Teammates then run `npx eminiar install`. See
|
|
@@ -164,7 +209,7 @@ flowchart LR
|
|
|
164
209
|
CC["Claude Code"]
|
|
165
210
|
subgraph eminiar ["eminiar (local)"]
|
|
166
211
|
H["Hooks<br/>recall · routing · capture"]
|
|
167
|
-
S["MCP server<br/>
|
|
212
|
+
S["MCP server<br/>24 em_* tools"]
|
|
168
213
|
SB["Sandbox<br/>shell · JS · TS · Python …"]
|
|
169
214
|
DB[("SQLite per project<br/>FTS5 · vectors · graph")]
|
|
170
215
|
M["Memory files<br/>.eminiar/memories/*.md"]
|
|
@@ -198,6 +243,7 @@ Read more in [How it works](docs/how-it-works.md).
|
|
|
198
243
|
| [Knowledge graph and history](docs/knowledge-graph.md) | Graph, symbols, impact analysis, git history |
|
|
199
244
|
| [Documentation knowledge base](docs/documentation-kb.md) | Discover, index and look up docs |
|
|
200
245
|
| [Usage insights](docs/usage.md) | Tokens, cache, cost, billing blocks, insights and savings per session |
|
|
246
|
+
| [Agent swarms](docs/swarms.md) | Opt-in worker swarms: the swarm file, topologies, the tool and CLI |
|
|
201
247
|
| [Dashboard](docs/dashboard.md) | The `eminiar ui` web app |
|
|
202
248
|
| [Configuration](docs/configuration.md) | Every config key with its default |
|
|
203
249
|
| [CLI](docs/cli.md) | The `eminiar` command |
|
package/bin/em-hook
CHANGED
|
@@ -2,14 +2,87 @@
|
|
|
2
2
|
# Hook entry shim. Node and package-root resolution live in _resolve.sh.
|
|
3
3
|
# A hook must never block Claude: if nothing can run, print an empty JSON object.
|
|
4
4
|
. "$(dirname "$0")/_resolve.sh"
|
|
5
|
+
# Swarm enforcement fails closed: when nothing can run, an Agent/Task call in a project whose
|
|
6
|
+
# .eminiar/swarm.yaml says `enforce: true` is denied. Reads stdin only here; every other call
|
|
7
|
+
# (and every other project) gets the empty answer below.
|
|
8
|
+
swarm_gate() {
|
|
9
|
+
case " $* " in *" cursor "*) return 0 ;; esac
|
|
10
|
+
payload=$(cat 2>/dev/null) || return 0
|
|
11
|
+
case "$payload" in *'"PreToolUse"'*) ;; *) return 0 ;; esac
|
|
12
|
+
printf '%s' "$payload" | grep -Eq '"tool_name"[[:space:]]*:[[:space:]]*"(Agent|Task)"' || return 0
|
|
13
|
+
# Payloads with "agent_id" (a call made inside a subagent) are denied too: the Node path
|
|
14
|
+
# allows nested calls only for allow-listed agents, and this shim cannot read allowed_agents,
|
|
15
|
+
# so a nested call gets the same deny text as a main-session one.
|
|
16
|
+
# Root order matches the hooks: CLAUDE_PROJECT_DIR (set by Claude Code per hook command)
|
|
17
|
+
# before EMINIAR_PROJECT_ROOT (may be inherited from an outer session).
|
|
18
|
+
root="${CLAUDE_PROJECT_DIR:-${EMINIAR_PROJECT_ROOT:-$PWD}}"
|
|
19
|
+
# Walk up to the first directory with a project marker (src/project.ts ROOT_MARKERS), else
|
|
20
|
+
# stay on the start dir, so this checks the root resolveProjectRoot would.
|
|
21
|
+
d=$root
|
|
22
|
+
while :; do
|
|
23
|
+
for m in .git package.json pnpm-workspace.yaml go.mod Cargo.toml pyproject.toml; do
|
|
24
|
+
[ -e "$d/$m" ] && break 2
|
|
25
|
+
done
|
|
26
|
+
p=${d%/*}
|
|
27
|
+
[ -n "$p" ] || p=/
|
|
28
|
+
[ "$p" != "$d" ] || { d=$root; break; }
|
|
29
|
+
d=$p
|
|
30
|
+
done
|
|
31
|
+
root=$d
|
|
32
|
+
main=
|
|
33
|
+
# A `claude --worktree` session: .git is a file ("gitdir: <main>/.git/worktrees/<n>"), and the
|
|
34
|
+
# uncommitted swarm file lives in the main checkout. Follow gitdir -> commondir to it. A .git
|
|
35
|
+
# file without a gitdir line, or a gitdir without commondir (a submodule), is not followed.
|
|
36
|
+
if [ -f "$root/.git" ]; then
|
|
37
|
+
cr=$(printf '\r')
|
|
38
|
+
gd=$(grep -m1 '^gitdir:' "$root/.git" 2>/dev/null)
|
|
39
|
+
gd=${gd#gitdir:}
|
|
40
|
+
gd=${gd# }
|
|
41
|
+
gd=${gd%"$cr"}
|
|
42
|
+
if [ -n "$gd" ]; then
|
|
43
|
+
case "$gd" in /*) ;; *) gd="$root/$gd" ;; esac
|
|
44
|
+
cdir=
|
|
45
|
+
[ -f "$gd/commondir" ] && read -r cdir < "$gd/commondir"
|
|
46
|
+
cdir=${cdir%"$cr"}
|
|
47
|
+
if [ -n "$cdir" ]; then
|
|
48
|
+
case "$cdir" in /*) ;; *) cdir="$gd/$cdir" ;; esac
|
|
49
|
+
main=$(cd "$cdir/.." 2>/dev/null && pwd)
|
|
50
|
+
[ "$main" = "$root" ] && main=
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
# The same rule as enforceIntent (src/swarm/config.ts): top-level `enforce: true|True|TRUE`
|
|
55
|
+
# (plain or quoted key, BOM, CRLF, space before the colon, trailing comment), and not
|
|
56
|
+
# `enabled: false|False|FALSE`. Flow-style or indented documents are not recognised here; a
|
|
57
|
+
# parsing Node reader decides those.
|
|
58
|
+
bom=$(printf '\357\273\277')
|
|
59
|
+
tail='[[:space:]]*:[[:space:]]+'
|
|
60
|
+
end='([[:space:]]+#.*)?[[:space:]]*$'
|
|
61
|
+
for r in "$root" "$main"; do
|
|
62
|
+
[ -n "$r" ] || continue
|
|
63
|
+
for f in "$r/.eminiar/swarm.yaml" "$r/.eminiar/swarm.yml"; do
|
|
64
|
+
[ -f "$f" ] || continue
|
|
65
|
+
grep -Eq "^($bom)?(enforce|\"enforce\"|'enforce')${tail}(true|True|TRUE)${end}" "$f" 2>/dev/null || return 0
|
|
66
|
+
grep -Eq "^($bom)?(enabled|\"enabled\"|'enabled')${tail}(false|False|FALSE)${end}" "$f" 2>/dev/null && return 0
|
|
67
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"eminiar swarm enforcement: eminiar'"'"'s hook cannot run (no Node.js or no installed copy; npx eminiar install fixes it), so this subagent is denied. Do the work in this session, or ask the user to fix the install or set enforce: false in .eminiar/swarm.yaml."}}'
|
|
68
|
+
return 1
|
|
69
|
+
done
|
|
70
|
+
done
|
|
71
|
+
return 0
|
|
72
|
+
}
|
|
73
|
+
# --gate: decide-only mode for a project-level onFailure:"block" entry. Nothing to run means
|
|
74
|
+
# failure (exit 1), so that entry blocks; the swarm_gate fallback below is for the plugin hook.
|
|
75
|
+
case " $* " in *" --gate "*) EM_GATE=1 ;; *) EM_GATE= ;; esac
|
|
5
76
|
if [ ! -x "$NODE" ]; then
|
|
6
77
|
echo "eminiar hook: no node binary found (run: npx eminiar install)" >&2
|
|
7
|
-
|
|
78
|
+
[ -n "$EM_GATE" ] && exit 1
|
|
79
|
+
swarm_gate "$@" && echo '{}'
|
|
8
80
|
exit 0
|
|
9
81
|
fi
|
|
10
82
|
if [ -z "$RUN" ]; then
|
|
11
83
|
echo "eminiar hook: $DIR has no node_modules and no stable copy is pinned (run: npx eminiar install)" >&2
|
|
12
|
-
|
|
84
|
+
[ -n "$EM_GATE" ] && exit 1
|
|
85
|
+
swarm_gate "$@" && echo '{}'
|
|
13
86
|
exit 0
|
|
14
87
|
fi
|
|
15
88
|
exec "$NODE" "$RUN/dist/hook.js" "$@"
|