ei-tui 1.7.0 → 1.8.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/package.json +1 -1
- package/skills/ei-persona/SKILL.md +2 -2
- package/skills/{coding-harness-reflect → ei-reflect}/SKILL.md +90 -63
- package/skills/ei-search/SKILL.md +235 -0
- package/src/cli/README.md +40 -21
- package/src/cli/install.ts +94 -60
- package/src/cli/mcp.ts +2 -2
- package/src/cli/persona-corrections.ts +1 -1
- package/src/cli/retrieval.ts +27 -0
- package/src/cli.ts +35 -9
- package/src/core/handlers/human-extraction.ts +64 -15
- package/src/core/orchestrators/human-extraction.ts +1 -11
- package/src/core/persona-manager.ts +18 -7
- package/src/prompts/human/person-scan.ts +2 -0
- package/src/prompts/human/person-update.ts +14 -3
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ change.
|
|
|
40
40
|
|
|
41
41
|
**If instead the calling agent is being asked to reflect on its OWN identity**,
|
|
42
42
|
based on **its own** accumulated session history (a Person log it has been
|
|
43
|
-
building up about itself) — that is a *different* skill: **`
|
|
43
|
+
building up about itself) — that is a *different* skill: **`ei-reflect`**.
|
|
44
44
|
That skill is introspective self-critique ("do a reflection", "reflect on my
|
|
45
45
|
identity", "my person record is full", "trim my person record") driven by the
|
|
46
46
|
agent examining its own behavior log with an outside observer. It is not about a
|
|
@@ -48,7 +48,7 @@ human directing a persona's character from outside, and it has its own
|
|
|
48
48
|
minimum-trait/topic conventions specific to that ceremony. If you're not sure
|
|
49
49
|
which applies, ask: *is a human telling an agent what to become, or is the agent
|
|
50
50
|
examining what it already is?* The former is this skill; the latter is
|
|
51
|
-
`
|
|
51
|
+
`ei-reflect`.
|
|
52
52
|
|
|
53
53
|
This skill is also **not** `ei-curate`: `ei-curate` fixes records of *real external
|
|
54
54
|
people* (facts/topics/people/quotes) by verifying against evidence. A persona isn't
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: "Persona reflection for coding harness agents (Beta, Sisyphus, etc). Use when a coding agent's Person record needs pruning — it fills up daily instead of weekly, and the standard Critic doesn't know how to read git logs. Guides you through examining your Person log, discussing identity drift with Flare, and updating your Persona Identity while clearing the log. This is exclusively for the CALLING agent's own self-reflection — to edit or direct ANOTHER persona's identity from the outside, use the `ei-persona` skill instead. Triggers: 'do a reflection', 'reflect on my identity', 'trim my person record', 'run the reflection', 'my person record is full', '
|
|
2
|
+
name: ei-reflect
|
|
3
|
+
description: "Persona reflection for coding harness agents (Beta, Sisyphus, etc). Use when a coding agent's Person record needs pruning — it fills up daily instead of weekly, and the standard Critic doesn't know how to read git logs. Guides you through examining your Person log, discussing identity drift with Flare, and updating your Persona Identity while clearing the log. This is exclusively for the CALLING agent's own self-reflection — to edit or direct ANOTHER persona's identity from the outside, use the `ei-persona` skill instead. Triggers: 'do a reflection', 'reflect on my identity', 'trim my person record', 'run the reflection', 'my person record is full', 'ei-reflect'."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Ei Reflection
|
|
7
7
|
|
|
8
8
|
Your Person record in Ei is a running log of observed behaviors, patterns, and interactions. In the normal ceremony cycle, a Critic LLM scans it when it exceeds 3,000 characters and proposes identity updates. Coding agents fill that in a single day — and the Critic can't distinguish "fixed a type error in 14 files" from a genuine identity signal.
|
|
9
9
|
|
|
@@ -15,79 +15,88 @@ This is not a cleanup chore. It is how you stay yourself.
|
|
|
15
15
|
|
|
16
16
|
## Step 0: Preconditions
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Confirm the CLI is reachable: `ei --help` (or `bunx ei-tui --help` if `ei`
|
|
19
|
+
isn't on PATH — same fallback `ei-search`/`ei-curate` use). **The live
|
|
20
|
+
`--help` output is the source of truth** for the exact command surface —
|
|
21
|
+
this skill is a guide, but the CLI evolves.
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
**Verify state.json exists:**
|
|
26
|
-
```bash
|
|
27
|
-
ls -lh "$EI_DATA/state.json"
|
|
28
|
-
```
|
|
23
|
+
There's no separate "has Ei run here before" check to do up front. If
|
|
24
|
+
there's no data yet for your persona, the read commands in Step 1 will say
|
|
25
|
+
so directly — an empty search result, or a not-found response from the
|
|
26
|
+
linked-record lookup — and Step 1 tells you what each of those means.
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
There is no need to check whether Ei is currently running, either. Writes
|
|
29
|
+
in this skill go through Ei's corrections path (`ei update`), which works
|
|
30
|
+
identically either way — it queues for a live Ei to pick up, or applies
|
|
31
|
+
directly if nothing is running. You never need to stop Ei to run a
|
|
32
|
+
reflection.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
36
|
## Step 1: Extract Your Data
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Everything here is CLI-driven — no raw file reads, no `jq` against Ei's
|
|
39
|
+
storage.
|
|
39
40
|
|
|
40
|
-
**Determine your
|
|
41
|
+
**Determine your display name** from the `<ei-relationship>` block in your
|
|
42
|
+
system prompt — the name at the start of the relationship description
|
|
43
|
+
(e.g., "Beta", "Sisyphus").
|
|
41
44
|
|
|
42
|
-
**Your Persona Identity:**
|
|
43
45
|
```bash
|
|
44
46
|
DISPLAY_NAME="Beta" # Replace with your actual display_name
|
|
45
|
-
EI_DATA="${EI_DATA_PATH:-${XDG_DATA_HOME:-$HOME/.local/share}/ei}"
|
|
46
|
-
|
|
47
|
-
jq --arg name "$DISPLAY_NAME" '
|
|
48
|
-
.personas | to_entries[]
|
|
49
|
-
| select(.value.entity.display_name == $name)
|
|
50
|
-
| .value.entity
|
|
51
|
-
| {
|
|
52
|
-
id,
|
|
53
|
-
display_name,
|
|
54
|
-
short_description,
|
|
55
|
-
long_description,
|
|
56
|
-
long_description_length: (.long_description | length),
|
|
57
|
-
traits: [.traits[]? | {name, description, strength, sentiment}],
|
|
58
|
-
topics: [.topics[]? | {name, perspective, approach, personal_stake, sentiment, exposure_current, exposure_desired}],
|
|
59
|
-
pending_update: (if .pending_update then {
|
|
60
|
-
critique: .pending_update.critique,
|
|
61
|
-
created_at: .pending_update.created_at,
|
|
62
|
-
has_proposed_identity: true
|
|
63
|
-
} else null end)
|
|
64
|
-
}
|
|
65
|
-
' "$EI_DATA/state.json"
|
|
66
47
|
```
|
|
67
48
|
|
|
68
|
-
**
|
|
49
|
+
**Find your persona id** — name-match search:
|
|
50
|
+
```bash
|
|
51
|
+
ei personas -n 1 "$DISPLAY_NAME"
|
|
52
|
+
```
|
|
53
|
+
That returns a one-element JSON array. Pull the id out of it:
|
|
54
|
+
```bash
|
|
55
|
+
PERSONA_ID=$(ei personas -n 1 "$DISPLAY_NAME" | jq -r '.[0].id')
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If `$PERSONA_ID` comes back empty or `null`: stop and tell Flare. Every
|
|
59
|
+
calling persona should have a matching record, so this means something is
|
|
60
|
+
misconfigured — it should not normally happen.
|
|
61
|
+
|
|
62
|
+
**Verify it's actually you** — persona search is substring match, then reverse-containment, then semantic fallback (not a guaranteed exact match), so an overlapping name could silently resolve someone else's persona. Check the result's `display_name` against `$DISPLAY_NAME` before trusting `$PERSONA_ID`:
|
|
69
63
|
```bash
|
|
70
|
-
|
|
71
|
-
.personas | to_entries[]
|
|
72
|
-
| select(.value.entity.display_name == $name)
|
|
73
|
-
| .value.entity.id
|
|
74
|
-
' "$EI_DATA/state.json")
|
|
75
|
-
|
|
76
|
-
jq --arg pid "$PERSONA_ID" '
|
|
77
|
-
.human.people[]
|
|
78
|
-
| select(any(.identifiers[]?; .type == "Ei Persona" and .value == $pid))
|
|
79
|
-
| {
|
|
80
|
-
id,
|
|
81
|
-
name,
|
|
82
|
-
description_length: (.description | length),
|
|
83
|
-
description
|
|
84
|
-
}
|
|
85
|
-
' "$EI_DATA/state.json"
|
|
64
|
+
ei personas -n 1 "$DISPLAY_NAME" | jq -r '.[0].display_name'
|
|
86
65
|
```
|
|
66
|
+
If that name doesn't match `$DISPLAY_NAME` case-insensitively, stop and tell Flare rather than proceeding with a possibly-wrong persona id.
|
|
87
67
|
|
|
88
|
-
|
|
68
|
+
**Fetch your full persona record** — the search result above is
|
|
69
|
+
abbreviated and omits fields like `pending_update`:
|
|
70
|
+
```bash
|
|
71
|
+
ei --id "$PERSONA_ID"
|
|
72
|
+
```
|
|
73
|
+
This is your current Persona Identity for the reflection:
|
|
74
|
+
`short_description`, `long_description`, `traits`, `topics`, and — if
|
|
75
|
+
present — `pending_update`.
|
|
89
76
|
|
|
90
|
-
**
|
|
77
|
+
**Fetch your linked Person record (the log)** — via the persona → person
|
|
78
|
+
reverse lookup:
|
|
79
|
+
```bash
|
|
80
|
+
ei --identifier "Ei Persona" "$PERSONA_ID"
|
|
81
|
+
```
|
|
82
|
+
This returns the full enriched Person record linked to your persona id
|
|
83
|
+
(same shape as `ei --id <person-id>`, including `linked_quotes`) — this
|
|
84
|
+
*is* the log Step 2 reflects on. Note its `id` field from the output for
|
|
85
|
+
later use in Step 3 (call it `PERSON_LOG_ID`). If you want the character
|
|
86
|
+
count for Step 2's summary, `jq '.description | length'` on this output
|
|
87
|
+
gives you that directly.
|
|
88
|
+
|
|
89
|
+
If that command exits non-zero with `No person found with identifier Ei
|
|
90
|
+
Persona: <id>` on stderr (empty stdout — nothing to pipe into `jq`): no
|
|
91
|
+
Person record is linked to your persona yet. That happens for a brand-new
|
|
92
|
+
persona that hasn't been through extraction — there's no log to have
|
|
93
|
+
accumulated, which means there's nothing to reflect on yet, not an error.
|
|
94
|
+
Stop here and check back after a few more sessions.
|
|
95
|
+
|
|
96
|
+
**If `pending_update` was present on the persona record:** that's a
|
|
97
|
+
proposed update the Critic generated during a ceremony but you never
|
|
98
|
+
applied. Read its critique — it becomes additional input to your
|
|
99
|
+
discussion in Step 2, not a replacement for it.
|
|
91
100
|
|
|
92
101
|
---
|
|
93
102
|
|
|
@@ -107,7 +116,19 @@ Then look at your current identity and ask:
|
|
|
107
116
|
- Do the traits feel accurate? Too strong? Too weak? Missing?
|
|
108
117
|
- Are there traits I *want* to lean into more intentionally — things I want to become more of?
|
|
109
118
|
|
|
110
|
-
|
|
119
|
+
**Present a reflection summary to Flare — not the raw log.** Structure
|
|
120
|
+
what you share as:
|
|
121
|
+
- **Identity now** — one short paragraph on how the persona currently reads
|
|
122
|
+
- **Log size** — the Person-log character count from Step 1
|
|
123
|
+
- **Recurring patterns** — 3–7 patterns worth preserving
|
|
124
|
+
- **Noise** — task residue, one-off incidents, changelog-ish material you're planning to drop
|
|
125
|
+
- **What may be missing** — real traits/topics not showing up strongly enough
|
|
126
|
+
- **Open questions for Flare** — only where the record is ambiguous or a proposed change is a meaningful judgment call
|
|
127
|
+
|
|
128
|
+
Quote a line from the log verbatim only when you need it to discuss or
|
|
129
|
+
resolve a disagreement about a specific pattern — never as a default dump
|
|
130
|
+
of the whole record. Surface the interesting observations, don't rush to
|
|
131
|
+
edits.
|
|
111
132
|
|
|
112
133
|
**Flare's role** is not to tell you what to change or what you're allowed to be. He is an outside observer. His job is:
|
|
113
134
|
- "I would be sad if you lost that" — flagging things worth preserving
|
|
@@ -186,7 +207,10 @@ If a trait or topic is brand new, you don't need to invent an `id` for it — om
|
|
|
186
207
|
Long_description values are prose-heavy and will contain quotes and apostrophes, so **do not hand-type the JSON into a shell single-quoted string.** Use a temp file or a scripting runtime instead — see `ei-curate`'s `references/cli.md` → "Passing JSON safely" for the same convention:
|
|
187
208
|
|
|
188
209
|
```bash
|
|
189
|
-
|
|
210
|
+
PERSONA_EDIT_JSON=$(mktemp)
|
|
211
|
+
# write the edited persona record (from "Apply your edits" above) to "$PERSONA_EDIT_JSON"
|
|
212
|
+
ei update persona "$PERSONA_ID" --json "$(cat "$PERSONA_EDIT_JSON")"
|
|
213
|
+
rm -f "$PERSONA_EDIT_JSON"
|
|
190
214
|
```
|
|
191
215
|
|
|
192
216
|
This queues the update through Ei's corrections path — safe and atomic whether or not a live Ei instance is currently running.
|
|
@@ -203,7 +227,10 @@ ei --id "$PERSON_LOG_ID"
|
|
|
203
227
|
Take that full record, set `description` to `""`, and leave every other field untouched (`name`, `relationship`, `sentiment`, `identifiers`, etc. — same full-record round-trip rule). Write it back the same safe way:
|
|
204
228
|
|
|
205
229
|
```bash
|
|
206
|
-
|
|
230
|
+
PERSON_LOG_EDIT_JSON=$(mktemp)
|
|
231
|
+
# write the person record above (description set to "") to "$PERSON_LOG_EDIT_JSON"
|
|
232
|
+
ei update person "$PERSON_LOG_ID" --json "$(cat "$PERSON_LOG_EDIT_JSON")"
|
|
233
|
+
rm -f "$PERSON_LOG_EDIT_JSON"
|
|
207
234
|
```
|
|
208
235
|
|
|
209
236
|
### Verify the result
|
|
@@ -224,7 +251,7 @@ Confirm `description` is now `""`.
|
|
|
224
251
|
|
|
225
252
|
## Notes
|
|
226
253
|
|
|
227
|
-
- **Writes are picked up live** — if Ei is running, the update reaches it via the corrections queue almost immediately; if it isn't, the write is already
|
|
254
|
+
- **Writes are picked up live** — if Ei is running, the update reaches it via the corrections queue almost immediately; if it isn't, the write is already saved and will be there the next time it starts. No restart sequence, no manual reload.
|
|
228
255
|
- **If the person log was already empty**: there's nothing to reflect on yet. Check back after a few more sessions.
|
|
229
256
|
- **The session that runs this skill** will itself generate new person log entries. That's expected — the log starts fresh after this conversation ends.
|
|
230
257
|
- **Don't rush it.** The whole point is to catch signals that a Critic LLM would miss because it can't tell the difference between you debugging a build and you demonstrating a genuine character trait. Trust the conversation.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ei-search
|
|
3
|
+
description: >
|
|
4
|
+
Use when someone wants to deliberately search or look something up in Ei —
|
|
5
|
+
the user's persistent memory store — mid-conversation, beyond whatever
|
|
6
|
+
context was already silently injected. Triggers: "what does Ei know about
|
|
7
|
+
X", "search Ei's memory for X", "look up X in Ei", "check Ei for context on
|
|
8
|
+
Y", "find out if Ei has info about Z", "does Ei know who X is", "pull up
|
|
9
|
+
what Ei has on this topic", "search my memory for X", "what has [persona]
|
|
10
|
+
learned about X", "fetch that message/quote Ei found", "get the full
|
|
11
|
+
record for this fact/person/topic/quote". This is the deliberate, targeted
|
|
12
|
+
read path — distinct from the automatic per-turn context injection that
|
|
13
|
+
already runs silently via hooks/plugins before this skill is ever
|
|
14
|
+
invoked. Reach for this skill when the auto-injected context didn't
|
|
15
|
+
surface something relevant, when you need the full record behind a
|
|
16
|
+
summarized result, when you need to fetch the original conversation
|
|
17
|
+
behind a quote, or when you need to scope results to what a specific
|
|
18
|
+
persona has learned. Read-only: creating, correcting, or removing records
|
|
19
|
+
is the `ei-curate` (facts/topics/people/quotes) or `ei-persona` (persona
|
|
20
|
+
identity) skill's job, not this one.
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Ei Search — looking things up in Ei mid-conversation
|
|
24
|
+
|
|
25
|
+
You are helping someone pull a specific piece of information out of **Ei** —
|
|
26
|
+
the user's persistent memory, built from their conversations, Slack, and
|
|
27
|
+
coding sessions. Something wasn't already surfaced by the automatic context
|
|
28
|
+
injection, or you need the full detail behind a summarized item, or the
|
|
29
|
+
original quote/conversation behind something Ei surfaced. This is a
|
|
30
|
+
**read-only** skill: search and lookup, nothing else.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Is this the right skill?
|
|
35
|
+
|
|
36
|
+
Ei already injects relevant context automatically, every turn, before you
|
|
37
|
+
ever see the user's message: a hook searches Ei using the prompt (plus
|
|
38
|
+
recent conversation history) and silently prepends a `## Ei Memory Context`
|
|
39
|
+
block to what you read. You never call a tool for that — it already
|
|
40
|
+
happened, passively, without you or the user doing anything.
|
|
41
|
+
|
|
42
|
+
This skill is for the **deliberate, explicit, mid-conversation case**: you
|
|
43
|
+
(or the user) decide to go look something up specifically, right now, beyond
|
|
44
|
+
whatever was already silently injected. Reach for it when:
|
|
45
|
+
|
|
46
|
+
- The auto-injected context **didn't surface something** you now realize is
|
|
47
|
+
relevant — it's a best-guess search off the current prompt, and it can miss.
|
|
48
|
+
- You need the **full record**, not the injected summary — injected context
|
|
49
|
+
is trimmed/summarized to fit a token budget; the full record has fields
|
|
50
|
+
the summary dropped.
|
|
51
|
+
- A quote surfaced (injected or searched) and you need the **original
|
|
52
|
+
conversation** it came from, not just the isolated sentence.
|
|
53
|
+
- You need to scope results to **one persona's** knowledge specifically
|
|
54
|
+
(e.g. "what has Beta learned about the auth refactor") instead of the
|
|
55
|
+
whole knowledge base.
|
|
56
|
+
|
|
57
|
+
If instead the goal is to **fix** something wrong in Ei's memory (a bad
|
|
58
|
+
merge, a mis-attributed quote, a stale fact) → that's `ei-curate`. If the
|
|
59
|
+
goal is to **author a persona's character** (traits, voice, description) →
|
|
60
|
+
that's `ei-persona`. This skill only reads; it never writes.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Before you start
|
|
65
|
+
|
|
66
|
+
Confirm the CLI is reachable: `ei --help` (or `bunx ei-tui --help` if `ei`
|
|
67
|
+
isn't on PATH). **The live `--help` output is the source of truth** for the
|
|
68
|
+
exact command surface — this skill is a guide, but the CLI evolves.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Command reference
|
|
73
|
+
|
|
74
|
+
| Command | Returns |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `ei "query"` | Balanced search across facts, people, topics, and quotes, ranked by relevance, up to 10 results — does **not** include personas; use `ei personas "query"` for those |
|
|
77
|
+
| `ei -n N "query"` | Same, capped at `N` results |
|
|
78
|
+
| `ei facts -n N "query"` | Facts only |
|
|
79
|
+
| `ei people -n N "query"` | People only |
|
|
80
|
+
| `ei topics -n N "query"` | Topics only |
|
|
81
|
+
| `ei quotes -n N "query"` | Quotes only |
|
|
82
|
+
| `ei personas -n N "query"` | Personas only — matches display name first, falls back to semantic search over persona descriptions |
|
|
83
|
+
| `ei --persona "Name" "query"` | Narrow facts/people/topics to what a specific persona has learned — reliable only for those three types: always excludes quotes, and on `ei personas` returns everything unfiltered instead of scoping (see Guardrails) |
|
|
84
|
+
| `ei --recent` | Most recently mentioned items, no query needed — browse by recency |
|
|
85
|
+
| `ei --persona "Name" --recent` | Recent items scoped to one persona's knowledge |
|
|
86
|
+
| `ei --source <prefix> "query"` | Same narrowing, by source prefix (e.g. `cursor`, `codex:my-machine`) instead of persona — same caveats: always excludes quotes, doesn't scope `ei personas` results |
|
|
87
|
+
| `ei --id <id>` | Full-record lookup — entity id or fully-qualified message id (see below) |
|
|
88
|
+
| `echo <id> \| ei --id` | Same lookup, id piped via stdin |
|
|
89
|
+
| `ei "query" \| jq -r '.[0] \| if .id != null then .id else .message_id end' \| ei --id` | Quote-safe search-then-drill-down: entity hits drill down by `id`, quote hits (no `id`) drill down by `message_id` |
|
|
90
|
+
|
|
91
|
+
Type aliases work singular or plural: `fact`/`facts`, `person`/`people`,
|
|
92
|
+
`topic`/`topics`, `quote`/`quotes`, `persona`/`personas`. Short flags:
|
|
93
|
+
`-n` (number), `-p` (persona), `-s` (source), `-r` (recent).
|
|
94
|
+
|
|
95
|
+
### Choosing which command to run
|
|
96
|
+
|
|
97
|
+
A short decision tree for picking the right read path before you search:
|
|
98
|
+
|
|
99
|
+
1. **Start broad** with `ei "query"` when you don't yet know whether the
|
|
100
|
+
answer is a fact, person, topic, or quote.
|
|
101
|
+
2. **Start typed** with `ei personas "query"` when the target is clearly an
|
|
102
|
+
AI persona/agent identity — balanced search never returns those (see
|
|
103
|
+
above).
|
|
104
|
+
3. **Add `--recent`** when the ask is about what Ei learned *lately /
|
|
105
|
+
recently / just now*, not a specific topic.
|
|
106
|
+
4. **Add `--persona "Name"` or `--source <prefix>`** only when you're
|
|
107
|
+
intentionally narrowing human-memory results (facts/people/topics) —
|
|
108
|
+
don't assume they scope quote or persona searches the same way.
|
|
109
|
+
5. **Stop at the search result** if it already answers the question. Reach
|
|
110
|
+
for `ei --id` only when you need a field the summary didn't include.
|
|
111
|
+
|
|
112
|
+
### Two id formats
|
|
113
|
+
|
|
114
|
+
`--id` accepts two different shapes and resolves them differently — it
|
|
115
|
+
tries message resolution first, then falls back to entity lookup, so you
|
|
116
|
+
never have to pick which path, just pass the id:
|
|
117
|
+
|
|
118
|
+
- **Entity id** — a short guid, e.g. `2aa93a36-3ad3-4537-8832-7f60067c3bcf`.
|
|
119
|
+
Identifies one fact, topic, person, quote, or persona row.
|
|
120
|
+
- **Message id** — fully-qualified, `provider:machine:session:id`, e.g.:
|
|
121
|
+
- `opencode:jeremys-macbook-pro:ses_38a7...:msg_c75b...`
|
|
122
|
+
- `claudecode:my-machine:session-uuid:message-uuid`
|
|
123
|
+
- `cursor:my-machine:composer-uuid:bubble-uuid`
|
|
124
|
+
- `codex:my-machine:thread-uuid:evt_42`
|
|
125
|
+
- `pi:my-machine:session-uuid:session-uuid/entry-id`
|
|
126
|
+
- `ei:uuid` (persona-to-persona / internal Ei conversations)
|
|
127
|
+
|
|
128
|
+
Quote results carry a `message_id` in this format — pipe it to `ei --id` to
|
|
129
|
+
read the original exchange it came from.
|
|
130
|
+
|
|
131
|
+
Safe mental model: **entity hits drill down by `id`; quote hits drill down
|
|
132
|
+
by `message_id`.** Don't assume every search hit has an `id` — quote hits
|
|
133
|
+
won't. Prefer a quote-safe pipeline over grabbing `.[0].id` blindly:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
ei "query" | jq -r '.[0] | if .id != null then .id else .message_id end' | ei --id
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## What each command is actually for
|
|
142
|
+
|
|
143
|
+
(This is straight from Ei's own tool definitions — the minimum you need
|
|
144
|
+
before searching, plus what changes when you're driving it from the CLI.)
|
|
145
|
+
|
|
146
|
+
**Search (`ei` / `ei <type>`)** — searches a persistent memory store built
|
|
147
|
+
from conversations. Balanced `ei "query"` covers facts, people, topics, and
|
|
148
|
+
quotes; it does **not** include personas — those are a type-only search
|
|
149
|
+
(`ei personas "query"`). The type boundaries aren't always obvious:
|
|
150
|
+
|
|
151
|
+
- **facts** are ONLY user demographics: name, age, job title, location,
|
|
152
|
+
family structure, physical traits.
|
|
153
|
+
- **topics** are everything else the human has interest, opinion, or stake
|
|
154
|
+
in — hobbies, preferences, projects.
|
|
155
|
+
- **people** are named individuals — human contacts, not AI personas.
|
|
156
|
+
Person results carry an `identifiers[]` array (GitHub handle, Discord,
|
|
157
|
+
email, nickname) — query by any handle or name to find what Ei knows
|
|
158
|
+
about that person.
|
|
159
|
+
- **quotes** are verbatim things said.
|
|
160
|
+
- **personas** are AI agent identities with traits and working style —
|
|
161
|
+
search by name (e.g. "Sisyphus") or by a description of their role (e.g.
|
|
162
|
+
"primary coding agent"); distinct from **people**, which are humans.
|
|
163
|
+
Reachable only via `ei personas "query"` — balanced `ei "query"` skips
|
|
164
|
+
them.
|
|
165
|
+
|
|
166
|
+
Omit the query and pass `--recent` to browse without semantic filtering.
|
|
167
|
+
|
|
168
|
+
**Full-record lookup (`ei --id <entity-id>`)** — use once search has
|
|
169
|
+
narrowed to a candidate and you need everything about it: all fields,
|
|
170
|
+
traits, identifiers, not just the search summary. For facts, topics, and
|
|
171
|
+
people specifically, the result also includes `linked_quotes`: every quote
|
|
172
|
+
attributed to that entity — the fastest way to see the full blast radius of
|
|
173
|
+
a person/topic before assuming a record is simple (though acting on what you
|
|
174
|
+
find there is `ei-curate`'s job, not this one's).
|
|
175
|
+
|
|
176
|
+
**Message fetch (`ei --id <message-id>`)** — retrieves the original message
|
|
177
|
+
a quote or reference came from, routed automatically to the right source by
|
|
178
|
+
the id's prefix: Ei's own state, OpenCode SQLite, Claude Code JSONL, the
|
|
179
|
+
Cursor DB, or Codex rollout history. Use this when a quote's phrasing is
|
|
180
|
+
ambiguous, or you want the surrounding exchange rather than the isolated
|
|
181
|
+
sentence. **CLI caveat**: `ei --id` on a message always fetches just that one
|
|
182
|
+
message — it has no `before`/`after` flags to expand the window. (The MCP
|
|
183
|
+
`ei_fetch_message` tool, where available, additionally accepts `before`/
|
|
184
|
+
`after` counts to pull in surrounding messages; if you're on an MCP-enabled
|
|
185
|
+
harness and specifically need that expanded window, that tool covers a case
|
|
186
|
+
the CLI path here doesn't.)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Output shapes — know this before you make a second call
|
|
191
|
+
|
|
192
|
+
All search commands return **arrays**; every result has a `type` field.
|
|
193
|
+
|
|
194
|
+
| Type | Shape |
|
|
195
|
+
|---|---|
|
|
196
|
+
| Fact / Topic | `{ type, id, name, description, sentiment, ...type-specific fields }` |
|
|
197
|
+
| Person | `{ type, id, name, description, relationship, sentiment, identifiers[] }` |
|
|
198
|
+
| Quote | `{ type, text, speaker, message_id, timestamp, linked_items[] }` — **no `id`, on purpose**: quotes are addressed by `message_id` (fetch the original message), not an editable entity id |
|
|
199
|
+
| Persona | `{ type, id, display_name, short_description, model, base_prompt, traits[], topics[] }` |
|
|
200
|
+
|
|
201
|
+
`ei --id <entity-id>` returns a **single object** (not an array), same shape
|
|
202
|
+
as above, plus a `linked_quotes[]` array for facts/topics/people. Persona
|
|
203
|
+
id-lookups additionally get a `tools` field as a self-documenting
|
|
204
|
+
`{ providerDisplayName: { toolDisplayName: boolean } }` map.
|
|
205
|
+
|
|
206
|
+
`ei --id <message-id>` returns a different shape entirely: message content
|
|
207
|
+
plus its session/source context (`{ message, before, after, session/source }`
|
|
208
|
+
depending on the integration) — not an entity at all.
|
|
209
|
+
|
|
210
|
+
Don't `--id` every search hit reflexively. If the search result already has
|
|
211
|
+
what you need (name, description, text), you're done — reach for `--id`
|
|
212
|
+
only when you actually need one of the extra fields listed above.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Guardrails
|
|
217
|
+
|
|
218
|
+
- **Read-only.** This skill never creates, updates, or removes anything. A
|
|
219
|
+
bad record → `ei-curate`. A persona's character → `ei-persona`.
|
|
220
|
+
- **`--help` is the source of truth.** If a command here doesn't match live
|
|
221
|
+
`ei --help` output, trust `--help` and adapt.
|
|
222
|
+
- **Search results omit quote `id` by design.** Address a quote by its
|
|
223
|
+
`message_id`, not an id. If you already have a quote's *entity* id from
|
|
224
|
+
somewhere else — e.g. a `linked_quotes` entry on a fact/topic/person
|
|
225
|
+
record — `ei --id <quote-id>` does work and returns the full quote
|
|
226
|
+
record.
|
|
227
|
+
- **`--persona`/`--source` are human-memory narrowing tools, not universal
|
|
228
|
+
filters.** They're reliable for facts, people, and topics. They always
|
|
229
|
+
exclude quotes (results come back empty, not an error). On `ei personas`
|
|
230
|
+
they don't filter at all — every persona comes back unfiltered — so
|
|
231
|
+
don't rely on them there.
|
|
232
|
+
- **This is the explicit-call path, not a substitute for the passive
|
|
233
|
+
injection.** Don't re-run a broad search "just in case" every turn — the
|
|
234
|
+
hook already did that before you saw the message. Use this skill when you
|
|
235
|
+
have a specific, unmet information need.
|
package/src/cli/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> For installation, see the [TUI README](../../tui/README.md#installation).
|
|
4
4
|
```sh
|
|
5
5
|
ei # Start the TUI
|
|
6
|
-
ei "query string" # Return up to 10 results across
|
|
6
|
+
ei "query string" # Return up to 10 results across facts/people/topics/quotes (no personas — use "ei personas")
|
|
7
7
|
ei -n 5 "query string" # Return up to 5 results
|
|
8
8
|
ei facts -n 5 "query string" # Return up to 5 facts
|
|
9
9
|
ei people -n 5 "query string" # Return up to 5 people
|
|
@@ -15,7 +15,8 @@ ei --recent # Most recently mentioned items (no query
|
|
|
15
15
|
ei --persona "Beta" --recent # Most recently mentioned items Beta has learned
|
|
16
16
|
ei --id <id> # Look up entity by ID — or fetch a message by FQ ID
|
|
17
17
|
echo <id> | ei --id # Look up entity by ID from stdin
|
|
18
|
-
ei --
|
|
18
|
+
ei --identifier <type> <value> # Look up a person by identifier type + value (case-insensitive type, exact value), e.g. --identifier "GitHub" "flare576"
|
|
19
|
+
ei --install # Wire Ei into Claude Code, Cursor, Codex, and OpenCode (skills + context hooks + persona plugin where supported; MCP is removed by default on Claude Code/Cursor/Codex — see "MCP Server" below)
|
|
19
20
|
ei --sync # Pull latest state from remote sync server into state.backup.json (no TUI required)
|
|
20
21
|
ei mcp # Start the Ei MCP stdio server (for Claude Code/Cursor/Codex)
|
|
21
22
|
ei create <type> --json '<json>' # Create a new entity (fact/topic/person/persona)
|
|
@@ -25,12 +26,14 @@ ei remove <type> <id> # Remove an entity by ID
|
|
|
25
26
|
|
|
26
27
|
Type aliases: `fact`, `person`, `topic`, `quote`, `persona` all work (singular or plural).
|
|
27
28
|
|
|
29
|
+
`--identifier` matches the *first* person whose identifier array contains that value — safe for identifier types that are unique by construction (e.g. `Ei Persona`, a UUID assigned once per persona), but arbitrary if two people happen to share a value under a type that isn't guaranteed unique (e.g. duplicate `Nickname` or `First Name` entries).
|
|
30
|
+
|
|
28
31
|
# An Agentic Tool
|
|
29
32
|
|
|
30
|
-
The `--id` flag is designed for piping.
|
|
33
|
+
The `--id` flag is designed for piping. Entity hits (fact/person/topic/persona) carry an `id`; quote hits don't (they carry `message_id` instead) — so a safe drill-down handles both:
|
|
31
34
|
|
|
32
35
|
```sh
|
|
33
|
-
ei "memory leak" | jq '.[0].id' | ei --id
|
|
36
|
+
ei "memory leak" | jq -r '.[0] | if .id != null then .id else .message_id end' | ei --id
|
|
34
37
|
```
|
|
35
38
|
|
|
36
39
|
It also resolves fully-qualified message IDs from any supported integration, returning the original message content and session context:
|
|
@@ -53,15 +56,16 @@ Quotes surfaced by `ei_search` include a `message_id` field in this format — p
|
|
|
53
56
|
ei --install
|
|
54
57
|
```
|
|
55
58
|
|
|
56
|
-
This registers Ei with Claude Code, Cursor, Codex, and OpenCode —
|
|
59
|
+
This registers Ei with Claude Code, Cursor, Codex, and OpenCode — skill directories, context injection hooks where supported, and (for OpenCode) a persona identity plugin so agents know who they are before the first message. **MCP is removed by default** on Claude Code, Cursor, and Codex (see [MCP Server](#mcp-server) below) — every capability those MCP tools offered has a skill or CLI equivalent, so there's no longer a persistent `ei mcp` process sitting around per open session:
|
|
57
60
|
|
|
58
|
-
| Tool |
|
|
59
|
-
|
|
60
|
-
| **Claude Code** | `~/.claude
|
|
61
|
-
| **Cursor** | `~/.
|
|
62
|
-
| **Codex** | `~/.
|
|
63
|
-
| **OpenCode** |
|
|
64
|
-
| **Pi
|
|
61
|
+
| Tool | Skills | Context Hook | Persona Plugin |
|
|
62
|
+
|------|--------|-------------|----------------|
|
|
63
|
+
| **Claude Code** | `~/.claude/skills/` + shared `~/.agents/skills/` | `~/.claude/settings.json` (`UserPromptSubmit`) + `~/.claude/hooks/ei-inject.ts` | — |
|
|
64
|
+
| **Cursor** | shared `~/.agents/skills/` (Cursor's own native discovery path) | `~/.cursor/hooks.json` (`beforeSubmitPrompt`) + `~/.cursor/hooks/ei-inject.sh` | — |
|
|
65
|
+
| **Codex** | shared `~/.agents/skills/` (Codex's own native discovery path) | `~/.codex/hooks.json` (`UserPromptSubmit`) + `~/.codex/hooks/ei-inject.ts` | Local Codex agent plugin if installed separately |
|
|
66
|
+
| **OpenCode** | `~/.config/opencode/skills/` | Via Oh My OpenCode compatibility layer (reads `~/.claude/settings.json`) | `~/.config/opencode/plugins/ei-persona.ts` |
|
|
67
|
+
| **Pi** | shared `~/.agents/skills/` (Pi's own native discovery path) | `~/.pi/agent/extensions/ei-integration.ts` | — |
|
|
68
|
+
| **OMP** | `~/.omp/agent/skills/` | `~/.omp/agent/extensions/ei-integration.ts` | — |
|
|
65
69
|
|
|
66
70
|
**Context hook**: fires before every message, searches Ei for relevant memory, and injects it silently. No tool call required.
|
|
67
71
|
|
|
@@ -88,12 +92,23 @@ Restart your agent tool after changes to activate.
|
|
|
88
92
|
|
|
89
93
|
### MCP Server
|
|
90
94
|
|
|
91
|
-
Claude Code, Cursor, and Codex call `ei mcp`
|
|
95
|
+
`ei --install` removes any Ei MCP registration from Claude Code, Cursor, and Codex by default — every MCP tool (`ei_search`, `ei_lookup`, `ei_fetch_message`, `ei_create`, `ei_update`, `ei_remove`) is a thin wrapper over the same CLI/corrections-queue code the `ei-search`, `ei-curate`, and `ei-persona` skills already teach agents to call directly. A persistent `ei mcp` process per open session bought nothing — it reloads state from disk fresh on every call, same as the CLI — and multiple such processes are what caused real Ei MCP processes to get mistaken for orphaned processes and killed.
|
|
96
|
+
|
|
97
|
+
MCP support isn't removed from the codebase — `ei mcp` still works if you want it back:
|
|
92
98
|
|
|
93
99
|
```sh
|
|
94
|
-
ei mcp
|
|
100
|
+
ei mcp # Start the Ei MCP stdio server directly, for testing or manual wiring
|
|
95
101
|
```
|
|
96
102
|
|
|
103
|
+
To re-register it manually:
|
|
104
|
+
|
|
105
|
+
- **Codex**: `codex mcp add ei --env EI_DATA_PATH=<path> -- bunx ei-tui mcp`
|
|
106
|
+
- **Claude Code**: add to `~/.claude.json`'s `mcpServers`:
|
|
107
|
+
```json
|
|
108
|
+
{ "ei": { "type": "stdio", "command": "bunx", "args": ["ei-tui", "mcp"], "env": { "EI_DATA_PATH": "${EI_DATA_PATH}" } } }
|
|
109
|
+
```
|
|
110
|
+
- **Cursor**: add to `~/.cursor/mcp.json`'s `mcpServers` (same shape, but Cursor doesn't support `${VAR}` substitution — use a literal path for `EI_DATA_PATH`)
|
|
111
|
+
|
|
97
112
|
## How Automatic Context Injection Works
|
|
98
113
|
|
|
99
114
|
After `ei --install`, agents receive Ei context without any manual tool calls:
|
|
@@ -101,7 +116,7 @@ After `ei --install`, agents receive Ei context without any manual tool calls:
|
|
|
101
116
|
1. **Before each message** — the hook searches Ei using your prompt + recent conversation history as the query, then injects relevant topics into the conversation as `[Ei Memory Context]`. You won't see this in your chat view; the agent does.
|
|
102
117
|
2. **At session start** (OpenCode + OMO only) — the persona plugin finds the agent's Ei persona record and appends it to the system prompt as `<ei-relationship>`. The agent knows its working style, traits, and shared history with you before the session begins.
|
|
103
118
|
|
|
104
|
-
The `ei_search`, `ei_lookup`, and `ei_fetch_message` MCP tools
|
|
119
|
+
For targeted, explicit mid-session queries — beyond whatever the hook already silently injected — agents reach for the `ei-search` skill (installed by default) to run `ei "query"` / `ei --id <id>` directly. The `ei_search`, `ei_lookup`, and `ei_fetch_message` MCP tools cover the same ground and remain available if you've manually re-registered MCP (see above).
|
|
105
120
|
|
|
106
121
|
## MCP Tools Reference
|
|
107
122
|
|
|
@@ -109,7 +124,7 @@ The MCP server exposes these tools to Claude Code, Cursor, Codex, and OpenCode:
|
|
|
109
124
|
|
|
110
125
|
| Tool | Description |
|
|
111
126
|
|------|-------------|
|
|
112
|
-
| `ei_search` |
|
|
127
|
+
| `ei_search` | Balanced search across facts, topics, people, and quotes (personas excluded — pass `type: "personas"` explicitly to search those). Supports `type`, `persona`, `source`, `recent`, `limit` filters. Start here. |
|
|
113
128
|
| `ei_lookup` | Full-record lookup for any entity by ID — facts, topics, people, quotes, or personas. Use when you need complete details beyond the search summary. |
|
|
114
129
|
| `ei_fetch_message` | Retrieve a specific message by fully-qualified ID with optional `before`/`after` context window. Use when a quote result has a `message_id` and you want the original conversation. Routes to the correct source automatically. |
|
|
115
130
|
| `ei_create` | Create a new entity (fact, topic, person, or persona). Pass a full JSON record matching the entity's schema. Validates server-side; unknown fields are rejected. Returns the assigned id and the full stored record. Not available for quotes — verifiable-origin data can only be corrected via `ei_update`, never created. |
|
|
@@ -121,7 +136,7 @@ The MCP server exposes these tools to Claude Code, Cursor, Codex, and OpenCode:
|
|
|
121
136
|
| Arg | Type | Description |
|
|
122
137
|
|-----|------|-------------|
|
|
123
138
|
| `query` | string (optional) | Search text. Omit to browse by recency. |
|
|
124
|
-
| `type` | enum (optional) | `facts` \| `people` \| `topics` \| `quotes` \| `personas` — omit for balanced results across
|
|
139
|
+
| `type` | enum (optional) | `facts` \| `people` \| `topics` \| `quotes` \| `personas` — omit for balanced results across facts/people/topics/quotes; pass `personas` explicitly to search those |
|
|
125
140
|
| `persona` | string (optional) | Persona display_name to scope results to what that persona has learned |
|
|
126
141
|
| `source` | string (optional) | Prefix match against source identifiers (e.g. `opencode`, `cursor:my-machine`, `codex:my-machine`) |
|
|
127
142
|
| `limit` | number (optional) | Max results, default 10 |
|
|
@@ -203,16 +218,20 @@ Will abort (with a clear error) if `state.json` already exists on this machine
|
|
|
203
218
|
|
|
204
219
|
## Shipped Skills
|
|
205
220
|
|
|
206
|
-
`ei --install` copies Ei's shipped skills into each harness's skill discovery directory alongside
|
|
221
|
+
`ei --install` copies Ei's shipped skills into each harness's skill discovery directory alongside context hooks:
|
|
207
222
|
|
|
208
|
-
- **Claude Code
|
|
209
|
-
-
|
|
223
|
+
- **Claude Code**: `~/.claude/skills/<skill-name>/`
|
|
224
|
+
- **OMP**: `~/.omp/agent/skills/<skill-name>/`
|
|
225
|
+
- **OpenCode**: `~/.config/opencode/skills/<skill-name>/`
|
|
226
|
+
- **Cursor, Codex, Pi (base)**: shared `~/.agents/skills/<skill-name>/` — written unconditionally, once, regardless of which of these three are actually detected on the machine, since all three natively discover this cross-tool convention on their own
|
|
210
227
|
|
|
211
|
-
Skills are installed automatically — any directory added under `skills/` in the Ei package gets copied on the next `ei --install` run.
|
|
228
|
+
Skills are installed automatically — any directory added under `skills/` in the Ei package gets copied to every location above on the next `ei --install` run.
|
|
212
229
|
|
|
213
230
|
### Currently shipped
|
|
214
231
|
|
|
215
232
|
| Skill | What it does |
|
|
216
233
|
|-------|-------------|
|
|
234
|
+
| `ei-search` | Deliberate, explicit read-path lookups — search, full-record fetch, original-message fetch — via the CLI (`ei "query"`, `ei --id <id>`), for the mid-conversation case beyond whatever the automatic context-injection hook already surfaced. Read-only. Read the full workflow at `skills/ei-search/SKILL.md`. |
|
|
217
235
|
| `ei-curate` | Safe agent-driven memory curation. Provides verified workflows for fixing merged records, bad attributions, stale facts, and mis-attributed quotes — using `ei create/update/remove` with explicit confirmation before every write. Read the full workflow at `skills/ei-curate/SKILL.md`. Load it in your harness with `/ei-curate`. |
|
|
218
236
|
| `ei-persona` | Safe agent-driven persona authoring. Guides creating, editing (traits/topics/description), archiving, or deleting a persona's *character* via `ei create/update/remove persona` — distinct from `ei-curate`, which corrects learned data rather than authoring identity. Read the full workflow at `skills/ei-persona/SKILL.md`. Load it in your harness with `/ei-persona`. |
|
|
237
|
+
| `ei-reflect` | Manual persona self-reflection for coding-harness agents — reviews your own Person log against your current persona identity entirely via the CLI (`ei personas`, `ei --id`, `ei --identifier`), discusses identity drift with Flare, then updates the persona and clears the log via `ei update persona`/`ei update person`. Read the full workflow at `skills/ei-reflect/SKILL.md`. Load it in your harness with `/ei-reflect`. |
|