eminiar 0.11.0 β 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/plugin.json +1 -1
- package/README.md +51 -9
- package/bin/em-hook +75 -2
- package/dist/cli.js +3097 -174
- package/dist/hook.js +6942 -4284
- package/dist/server.js +5020 -1703
- package/dist/ui/app.global.js +67 -60
- package/dist/ui/styles.css +1014 -0
- package/hooks/hooks.json +9 -1
- package/package.json +1 -1
- 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"
|
|
@@ -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 coding agents: sandboxed execution, FTS5 + vector search, session continuity, code graph.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ryan Dick"
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ Claude Code is very capable, but three things slow it down on a real project:
|
|
|
40
40
|
|
|
41
41
|
## The solution
|
|
42
42
|
|
|
43
|
-
eminiar is a Claude Code plugin that also installs into Cursor. 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
44
|
`em_*` tools and a set of hooks.
|
|
45
45
|
Everything runs locally in one SQLite database per project.
|
|
46
46
|
|
|
@@ -49,9 +49,10 @@ Everything runs locally in one SQLite database per project.
|
|
|
49
49
|
| **Large output** | The full output goes into context | Output is indexed. Only the sections that match your `intent` come back |
|
|
50
50
|
| **Memory** | Lost at `/compact` or a new session | Markdown memories in your repo, given back to Claude when they are relevant |
|
|
51
51
|
| **Code structure** | `grep` and full-file reads | A knowledge graph of files, symbols, calls, routes and hotspots |
|
|
52
|
-
| **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 |
|
|
53
53
|
| **Library docs** | Web search on each question | A local, searchable index of the docs your project uses |
|
|
54
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 |
|
|
55
56
|
|
|
56
57
|
## What you get
|
|
57
58
|
|
|
@@ -63,6 +64,12 @@ indexed. Claude gets back only the sections that match its `intent`. Snippets ob
|
|
|
63
64
|
Code `Bash` deny rules, and `em_execute_file` refuses a `path` that one of your `Read` deny rules
|
|
64
65
|
matches ([which rule forms](docs/how-it-works.md#sandbox)).
|
|
65
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
|
+
|
|
66
73
|
### π Hybrid search β keyword and meaning together
|
|
67
74
|
|
|
68
75
|
SQLite FTS5 (porter + trigram, BM25, typo correction) is fused with local ONNX embeddings
|
|
@@ -77,6 +84,10 @@ and commit them. Decisions have an ADR-style status (`proposed`, `accepted`, `su
|
|
|
77
84
|
attach matching memories to your prompts, to file reads and edits, to failed commands and to
|
|
78
85
|
subagent briefs. **Claude does not have to remember to look.**
|
|
79
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
|
+
|
|
80
91
|
### πΈοΈ Code knowledge graph β a map before an edit
|
|
81
92
|
|
|
82
93
|
tree-sitter parses TypeScript, JavaScript, Python, Go and PHP into files, symbols, imports, calls,
|
|
@@ -85,12 +96,20 @@ Python decorator routes, Go routers, Laravel, Symfony and Slim). eminiar
|
|
|
85
96
|
adds fan-in, complexity, churn, a hotspot score, module clusters, import cycles and git co-change
|
|
86
97
|
coupling. Ask *"what breaks if I change this?"* with `em_graph_impact`.
|
|
87
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
|
+
|
|
88
103
|
### π Git history, read for Claude
|
|
89
104
|
|
|
90
105
|
`em_history` classifies every commit (feat, fix, refactor, perf β¦). It traces each fix back to
|
|
91
106
|
the commit that wrote the broken lines. Files get a risk score with reasons. Before Claude edits
|
|
92
107
|
a bug-prone file, it sees the record.
|
|
93
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
|
+
|
|
94
113
|
### π Documentation knowledge base
|
|
95
114
|
|
|
96
115
|
`em_docs` finds your local docs (READMEs, ADRs, OpenAPI, Postman) and the libraries, services
|
|
@@ -106,18 +125,29 @@ session with its compactions, which tools fill the context, insights with recomm
|
|
|
106
125
|
what eminiar's own tools saved in dollars. Everything stays local; cost is an estimate at list
|
|
107
126
|
price ([details](docs/usage.md)).
|
|
108
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
|
+
|
|
109
137
|
### π Local dashboard
|
|
110
138
|
|
|
111
139
|
`eminiar ui` opens a local web app with token savings, usage and cost, a source explorer with
|
|
112
|
-
impact analysis, a graph "galaxy", an architecture map, a history explorer
|
|
113
|
-
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.
|
|
114
142
|
|
|
115
143
|
| | |
|
|
116
144
|
|:---:|:---:|
|
|
117
|
-
| [<img alt="Galaxy: the knowledge graph around a file" src="docs/images/dashboard-galaxy-dark.webp" width="430">](docs/dashboard.md#galaxy) | [<img alt="
|
|
118
|
-
| **Galaxy:** the knowledge graph | **
|
|
119
|
-
| [<img alt="
|
|
120
|
-
| **
|
|
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 |
|
|
121
151
|
|
|
122
152
|
## What Claude sees
|
|
123
153
|
|
|
@@ -134,6 +164,17 @@ eminiar graph: src/cli.ts is imported by 6 files; em_graph_impact {node:"file:sr
|
|
|
134
164
|
them before you change an exported signature.
|
|
135
165
|
```
|
|
136
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
|
+
|
|
137
178
|
Each note appears once per context window. After a `/compact`, notes come back when they are
|
|
138
179
|
relevant again.
|
|
139
180
|
|
|
@@ -168,7 +209,7 @@ flowchart LR
|
|
|
168
209
|
CC["Claude Code"]
|
|
169
210
|
subgraph eminiar ["eminiar (local)"]
|
|
170
211
|
H["Hooks<br/>recall Β· routing Β· capture"]
|
|
171
|
-
S["MCP server<br/>
|
|
212
|
+
S["MCP server<br/>24 em_* tools"]
|
|
172
213
|
SB["Sandbox<br/>shell Β· JS Β· TS Β· Python β¦"]
|
|
173
214
|
DB[("SQLite per project<br/>FTS5 Β· vectors Β· graph")]
|
|
174
215
|
M["Memory files<br/>.eminiar/memories/*.md"]
|
|
@@ -202,6 +243,7 @@ Read more in [How it works](docs/how-it-works.md).
|
|
|
202
243
|
| [Knowledge graph and history](docs/knowledge-graph.md) | Graph, symbols, impact analysis, git history |
|
|
203
244
|
| [Documentation knowledge base](docs/documentation-kb.md) | Discover, index and look up docs |
|
|
204
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 |
|
|
205
247
|
| [Dashboard](docs/dashboard.md) | The `eminiar ui` web app |
|
|
206
248
|
| [Configuration](docs/configuration.md) | Every config key with its default |
|
|
207
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" "$@"
|