ei-tui 1.6.6 → 1.6.7

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/README.md CHANGED
@@ -1,188 +1,41 @@
1
1
  # Ei
2
2
 
3
- A local-first AI companion system with persistent personas and coding tool integrations (OpenCode, Claude Code, Cursor, Codex).
3
+ Ei can be a LOT of different things, but it's always going to be **YOURS** - [Local First](#what-does-local-first-mean)
4
4
 
5
- You can access the Web version at [ei.flare576.com](https://ei.flare576.com).
5
+ **What are you hoping to do with Ei today?**
6
6
 
7
- You can run the local version via `bunx ei-tui` — no install needed, always current (see [### TUI](#tui) for details).
7
+ ## I Want To Give My Coding Agents Memory
8
8
 
9
- If you're here to give your coding tools (OpenCode, Claude Code, Cursor, Codex) persistent memory, jump over to [TUI README.md](./tui/README.md) to learn how to get information _into_ Ei, and [CLI README.md](./src/cli/README.md) to wire up MCP/context injection so your agents can read relevant memory when they need it.
9
+ You'll want to start with [Getting Data From Coding Tools](#coding-tool-integrations), then learn a little about [Installing Coding Tool Extensions](./tui/README.md#installation).
10
10
 
11
- ## What Does "Local First" Mean?
11
+ ## I Want My Coding Agents To Have Personality
12
12
 
13
- All of the data Ei learns about you from your conversations is stored on your device (LocalStorage on the Web, and `$EI_DATA_PATH` or `~/.local/share/ei` in the TUI).
14
-
15
- Unless you enable Syncing, that's where it stays.
16
-
17
- If you have a local LLM, literally no data leaves your system(s) by default. If you don't, you'll need to provide an LLM for Ei to use. I tried to make that as easy as possible via adding Providers via API Key.
18
-
19
- > **One honest note**: the first time you load Ei in a browser, it downloads the embedding library and model weights from public CDNs (jsdelivr, HuggingFace). Those CDNs see your IP address — but not your data. All embedding runs locally in your browser after that first download. The TUI version caches everything on first run and is fully offline after. Additionally, the same is true for my webhost - it must see your IP address to serve you assets, but no analytics, reports, metrics, etc. are done on them.
13
+ I've started using terms like [Persona and Agent](#whats-a-persona). To get your agents to be more like Personas, you talk to them in the [TUI](./tui/README.md). Define what they're like, spend time with them, and they'll evolve — picking up facts, opinions, and context about you as you go. The [Personas section of the TUI README](./tui/README.md#personas) covers the commands you can use to work with Persona, and [What's a Persona](#whats-a-persona) explains the fields/Identity.
20
14
 
21
- There's no other usage, debugging, analytics, tracking, or history information stored or transmitted - anonymized or otherwise.
15
+ ## I Want My Persona To Remember Me
22
16
 
23
- If there's a problem with the system, you need to tell me here on GitHub, or on Bluesky, or Discord, or whatever. There's no "report a bug" button, no "DONATE" link in the app.
17
+ Ei is, at its core, a memory system. It compiles Facts, People, Topics, and Quotes in your life and surfaces them to Agents and Persona naturally. Simply spending time in Ei or adding integrations [like Coding tools](#coding-tool-integrations) or [Slack](#slack-integration) will help it grow faster<sup>1</sup>!
24
18
 
25
- Don't get me wrong - I absolutely want to fix whatever problem you run into, or hear about the feature you want - but your Ei system, and the data you build with it, is yours.
19
+ ><sup>1</sup> It should be noted - again - that all of the data Ei gathers stays with [**YOU**](#what-does-local-first-mean)
26
20
 
27
- That's what "Local First" means.
21
+ ## I Want My Persona To Talk To Each Other
28
22
 
29
- ### What Does Sync Do?
23
+ Ei features a "Rooms" concept, where you can put your Persona into a shared context space and they'll "see" all the messages in several different formats. They all maintain their Identity (Traits, Descriptions, Topics), see the other participants, and still access the shared knowledge base.
30
24
 
31
- Optionally, you can choose to "Sync" to flare576.com. The only reason you would do this is if you wanted to easily move between two or more devices.
25
+ ## I Want To Use the TUI and the Web
32
26
 
33
- If you just want data back-ups, there's an "Backup & Restore" feature built into the system on the same page as "Sync" (actually, above Sync, because I honestly don't think anyone besides me wants to use Ei enough to use two devices...).
27
+ I built a [Sync](#what-does-sync-do) feature for this - it works using an encryption key only you know, before the data leaves your machine. When you sync data with Ei, you're basically just sharing your JSON data file between two machines. You don't **NEED** to use Sync - you can just send the file around!
34
28
 
35
- After you enable it, Sync kicks in when you close the TUI, or if you click "Save and Exit" in the web app. It sends a single, encrypted file to a file store for Ei...
36
-
37
- That I can't decrypt.
38
-
39
- Even if I wanted to (I definitely do not), I wouldn't be able to divulge your information because **You** are the only one that can generate the key. It's not a public/private keypair, it's not a "handshake".
40
-
41
- It's *your* data - I have no right to it, and neither does anyone else except you.
42
-
43
- ## What's a Persona?
44
-
45
- At the core of the technology, LLM "Agents" are made up of two or three components, depending on who you ask:
46
-
47
- 1. System Prompt
48
- 2. User Prompt (which can be broken into "Messages", but they're still basically the User Prompt)
29
+ ## Getting Started
49
30
 
50
- The "System Prompt" is the part where you usually say
51
-
52
- > You are a pirate
53
-
54
- The "User Prompt" is the part where you put your messages
55
-
56
- > user: "OMG ARE YOU REALLY A PIRATE?!"
57
- > assistant: "Yar."
58
-
59
- A "Persona" is the combination of these two pieces of data, plus some _personality_. The reason I didn't call it an "Agent" is because Personas aren't static<sup>1</sup> - they'll grow and adapt as you talk to them. See the [Core Readme](src/README.md) for more information!
60
-
61
- > <sup>1</sup>: By default. You can make them static.
62
-
63
- ## What's a Room?
64
-
65
- Rooms let you throw multiple personas into the same conversation thread. Chaos ensues. Or collaboration. Sometimes both.
66
-
67
- Three modes, set at creation:
68
-
69
- **Free For All (FFA)**: Everyone talks. Every message gets a response from every persona. It's loud. Good for brainstorming or when you want a bunch of perspectives on the same thing.
70
-
71
- **Choose Your Path (CYP)**: The conversation branches. Each message triggers responses from all personas, but you pick which one continues the thread. Fork in the road, every turn. You're the navigator.
72
-
73
- **Messages Against Persona (MAP)**: The interesting one. Everyone submits a response, but a Judge persona picks which one actually shows up. The personas have to stay in character and compete for the Judge's approval. The human doesn't have to play by the rules. It's partly a game of "who knows this judge best?" and partly just fun to watch them try.
74
-
75
- Rooms learn the same way persona conversations do. Quotes, topics, people — all get extracted and persisted. The knowledge base grows no matter which mode you're in.
31
+ - **Web** Go to [ei.flare576.com](https://ei.flare576.com) it'll walk you through onboarding.
32
+ - **TUI** — Install [Bun](https://bun.sh) if you don't have it, then run `bunx ei-tui`. Full setup details in the [TUI README](./tui/README.md).
76
33
 
77
- ## The Basics
78
-
79
- Ei can operate with three types of input, and three types of output.
80
-
81
- ```
82
- [TUI] -User Messages-> Ei <-User Messages- [Web]
83
- ^
84
- Sessions
85
- |
86
- [OpenCode / Claude Code / Cursor / Codex]
87
- ```
88
-
89
- ```
90
- [TUI] <-Persona Messages- Ei -Persona Messages-> [Web]
91
- |
92
- CLI Data
93
- v
94
- [OpenCode / Claude Code / Cursor / Codex]
95
- ```
96
-
97
- Optionally, users can opt into a server-side data sync. This is ideal for users who want to use multiple devices or switch between TUI and Web throughout the day. All data is encrypted _before_ being sent to the server, using a key that only the user can generate (your `username` and `passphrase` never leave your device - I couldn't decrypt your data if I wanted to).
98
-
99
- ### Web
100
-
101
- When you access Ei via https://ei.flare576.com, your browser will download the assets and walk you through onboarding. If you're running a Local LLM on port :1234 it will auto-detect it, otherwise it prompts you to enter one.
102
-
103
- Then you'll land on the chat interface. As you enter messages, they'll go to *YOUR* server. As Ei discovers information about you, summaries will be built with *YOUR* server, and data will be stored to *YOUR* LocalStorage in *YOUR* browser.
104
-
105
- When you leave, it simply stays in LocalStorage. When you come back, it loads it from LocalStorage.
106
-
107
- More information can be found in the [Web Readme](web/README.md)
108
-
109
- ### TUI
110
-
111
- ```bash
112
- # Install Bun (if you don't have it)
113
- curl -fsSL https://bun.sh/install | bash
114
-
115
- # Run Ei — no install needed, always the latest version
116
- bunx ei-tui
117
-
118
- # Or, if you use it as much as I do, add this to your profile!
119
- alias ei='bunx ei-tui'
120
- ```
121
-
122
- If you have a Local LLM, that's the first and last set of signals that leave your machine for Ei unless you tell it otherwise.
123
-
124
- Regardless, running `ei` (or `bunx ei-tui`) pops open the TUI interface and, just like on the web, all messages and summary requests flow to your LLM provider, but the core data stays on your device.
125
-
126
- More information (including commands) can be found in the [TUI Readme](tui/README.md)
127
-
128
- ### Coding Tool Integrations
34
+ ## Coding Tool Integrations
129
35
 
130
36
  Ei can import sessions from your coding tools and extract what you've been working on — pulling out facts, topics, and context that persist across sessions. Enable any combination; they work independently and feed into the same knowledge base.
131
37
 
132
- All four integrations are enabled via `/settings` in the TUI.
133
-
134
- #### OpenCode
135
-
136
- ```yaml
137
- opencode:
138
- integration: true
139
- ```
140
-
141
- OpenCode saves sessions as JSON or SQLite (depending on version). Ei reads them, extracts context per-agent (each agent like Sisyphus gets its own persona), and keeps everything current as sessions accumulate.
142
-
143
- OpenCode can also *read* Ei's knowledge back out via the [CLI tool](src/cli/README.md). Run `ei --install` to wire up automatic context injection — relevant topics are injected before every message, and (with [Oh My OpenCode](https://github.com/code-yeongyu/oh-my-opencode)) each agent's Ei persona record is loaded into the system prompt at session start. The agent knows who it is *to you* before it reads your first message.
144
-
145
- #### Claude Code
146
-
147
- ```yaml
148
- claudeCode:
149
- integration: true
150
- ```
151
-
152
- Reads from `~/.claude/projects/` (JSONL session files). All sessions map to a single "Claude Code" persona. Tool calls, thinking blocks, and internal plumbing are stripped — only the conversational content is imported.
153
-
154
- #### Cursor
155
-
156
- ```yaml
157
- cursor:
158
- integration: true
159
- ```
160
-
161
- Reads from Cursor's SQLite databases:
162
- - **macOS**: `~/Library/Application Support/Cursor/User/`
163
- - **Windows**: `%APPDATA%\Cursor\User\`
164
- - **Linux**: `~/.config/Cursor/User/`
165
-
166
- All sessions map to a single "Cursor" persona.
167
-
168
- #### Codex
169
-
170
- ```yaml
171
- codex:
172
- integration: true
173
- ```
174
-
175
- Reads from Codex's local state database and rollout JSONL files:
176
- - `~/.codex/state_*.sqlite`
177
- - `~/.codex/sessions/`
178
-
179
- All sessions map to a single "Codex" persona. Codex may store thread-level agent metadata for custom agents, but rollout messages do not reliably expose per-message sub-agent speaker identity yet. Tool calls, prompt scaffolding, and token-count events are stripped — only visible user/agent messages are imported.
180
-
181
- Codex can also read Ei's knowledge back out. Run `ei --install` to register the Ei MCP server and install a Codex `UserPromptSubmit` hook that injects relevant memory before each message, using your current prompt plus recent Codex transcript context when available.
182
-
183
- ---
184
-
185
- Sessions are processed oldest-first, one per queue cycle, so Ei won't overwhelm your LLM provider on first run. See [TUI Readme](tui/README.md)
38
+ Supported tools: OpenCode, Claude Code, Cursor, Codex, and Pi/OMP. Full setup in the [TUI README → Coding Tool Integrations](./tui/README.md#coding-tool-integrations).
186
39
 
187
40
  ## Document Import
188
41
 
@@ -204,23 +57,11 @@ Both surfaces show you which documents have been imported and let you remove the
204
57
 
205
58
  Ei can index your Slack workspace — channels, DMs, and threads — and extract the same topics, people, and context it pulls from your conversations. Your personas end up knowing what's been going on at work without you having to explain it.
206
59
 
207
- **TUI** (requires setup):
208
- ```bash
209
- /auth slack
210
- ```
211
-
212
- Opens a browser OAuth flow. Once authenticated, enable in `/settings`:
213
-
214
- ```yaml
215
- slack:
216
- integration: true
217
- ```
218
-
219
60
  **Web**: Open **☰ menu** → **My Data** → **External** tab → **Connect Slack**.
220
61
 
221
- Ei is read-onlyit never posts, reacts, or takes any action in your workspace. All processing happens locally. Your workspace admin may need to approve the [Ei Slack app](https://slack.com/oauth/v2/authorize?client_id=11080256060354.11080294064034&scope=&user_scope=channels:history,channels:read,groups:history,groups:read,im:history,im:read,mpim:history,mpim:read,users:read,users:read.email) before you can connect.
62
+ TUI setup: `/auth slack` see the [TUI README Slack Integration](./tui/README.md#slack-integration) for details.
222
63
 
223
- > **Note on backfill speed**: Slack limits non-Marketplace apps to 1 request/minute on message history. Backfill through a large workspace is gradual steady-state (indexing new messages) is fast once you're caught up.
64
+ Ei is read-only it never posts, reacts, or takes any action in your workspace. All processing happens locally. Your workspace admin may need to approve the [Ei Slack app](https://slack.com/oauth/v2/authorize?client_id=11080256060354.11080294064034&scope=&user_scope=channels:history,channels:read,groups:history,groups:read,im:history,im:read,mpim:history,mpim:read,users:read,users:read.email) before you can connect.
224
65
 
225
66
  ## Knowledge Share
226
67
 
@@ -283,6 +124,100 @@ Tools aren't global — you choose which personas get access. Edit a persona and
283
124
 
284
125
  ---
285
126
 
127
+ ## How Ei Works
128
+
129
+ Ei can operate with three types of input, and three types of output.
130
+
131
+ ```
132
+ [TUI] -User Messages-> Ei <-User Messages- [Web]
133
+ ^
134
+ Sessions
135
+ |
136
+ [OpenCode / Claude Code / Cursor / Codex]
137
+ ```
138
+
139
+ ```
140
+ [TUI] <-Persona Messages- Ei -Persona Messages-> [Web]
141
+ |
142
+ CLI Data
143
+ v
144
+ [OpenCode / Claude Code / Cursor / Codex]
145
+ ```
146
+
147
+ Optionally, users can opt into a server-side data sync. This is ideal for users who want to use multiple devices or switch between TUI and Web throughout the day. All data is encrypted _before_ being sent to the server, using a key that only the user can generate (your `username` and `passphrase` never leave your device - I couldn't decrypt your data if I wanted to).
148
+
149
+ ### Web
150
+
151
+ Go to [ei.flare576.com](https://ei.flare576.com) and it'll walk you through onboarding. More in the [Web README](web/README.md).
152
+
153
+ ## What's a Persona?
154
+
155
+ At the core of the technology, LLM "Agents" are made up of two or three components, depending on who you ask:
156
+
157
+ 1. System Prompt
158
+ 2. User Prompt (which can be broken into "Messages", but they're still basically the User Prompt)
159
+
160
+ The "System Prompt" is the part where you usually say
161
+
162
+ > You are a pirate
163
+
164
+ The "User Prompt" is the part where you put your messages
165
+
166
+ > user: "OMG ARE YOU REALLY A PIRATE?!"
167
+ > assistant: "Yar."
168
+
169
+ A "Persona" is the combination of these two pieces of data, plus some _personality_. The reason I didn't call it an "Agent" is because Personas aren't static<sup>1</sup> - they'll grow and adapt as you talk to them. See the [Core Readme](src/README.md) for more information!
170
+
171
+ > <sup>1</sup>: By default. You can make them static.
172
+
173
+ ## What's a Room?
174
+
175
+ Rooms let you throw multiple personas into the same conversation thread. Chaos ensues. Or collaboration. Sometimes both.
176
+
177
+ Three modes, set at creation:
178
+
179
+ **Free For All (FFA)**: Everyone talks. Every message gets a response from every persona. It's loud. Good for brainstorming or when you want a bunch of perspectives on the same thing.
180
+
181
+ **Choose Your Path (CYP)**: The conversation branches. Each message triggers responses from all personas, but you pick which one continues the thread. Fork in the road, every turn. You're the navigator.
182
+
183
+ **Messages Against Persona (MAP)**: The interesting one. Everyone submits a response, but a Judge persona picks which one actually shows up. The personas have to stay in character and compete for the Judge's approval. The human doesn't have to play by the rules. It's partly a game of "who knows this judge best?" and partly just fun to watch them try.
184
+
185
+ Rooms learn the same way persona conversations do. Quotes, topics, people — all get extracted and persisted. The knowledge base grows no matter which mode you're in.
186
+
187
+ ## Philosophy
188
+
189
+ ### What Does "Local First" Mean?
190
+
191
+ All of the data Ei learns about you from your conversations is stored on your device (LocalStorage on the Web, and `$EI_DATA_PATH` or `~/.local/share/ei` in the TUI).
192
+
193
+ Unless you enable Syncing, that's where it stays.
194
+
195
+ If you have a local LLM, literally no data leaves your system(s) by default. If you don't, you'll need to provide an LLM for Ei to use. I tried to make that as easy as possible via adding Providers via API Key.
196
+
197
+ > **One honest note**: the first time you load Ei in a browser, it downloads the embedding library and model weights from public CDNs (jsdelivr, HuggingFace). Those CDNs see your IP address — but not your data. All embedding runs locally in your browser after that first download. The TUI version caches everything on first run and is fully offline after. Additionally, the same is true for my webhost - it must see your IP address to serve you assets, but no analytics, reports, metrics, etc. are done on them.
198
+
199
+ There's no other usage, debugging, analytics, tracking, or history information stored or transmitted - anonymized or otherwise.
200
+
201
+ If there's a problem with the system, you need to tell me here on GitHub, or on Bluesky, or Discord, or whatever. There's no "report a bug" button, no "DONATE" link in the app.
202
+
203
+ Don't get me wrong - I absolutely want to fix whatever problem you run into, or hear about the feature you want - but your Ei system, and the data you build with it, is yours.
204
+
205
+ That's what "Local First" means.
206
+
207
+ #### What Does Sync Do?
208
+
209
+ Optionally, you can choose to "Sync" to flare576.com. The only reason you would do this is if you wanted to easily move between two or more devices.
210
+
211
+ If you just want data back-ups, there's an "Backup & Restore" feature built into the system on the same page as "Sync" (actually, above Sync, because I honestly don't think anyone besides me wants to use Ei enough to use two devices...).
212
+
213
+ After you enable it, Sync kicks in when you close the TUI, or if you click "Save and Exit" in the web app. It sends a single, encrypted file to a file store for Ei...
214
+
215
+ That I can't decrypt.
216
+
217
+ Even if I wanted to (I definitely do not), I wouldn't be able to divulge your information because **You** are the only one that can generate the key. It's not a public/private keypair, it's not a "handshake".
218
+
219
+ It's *your* data - I have no right to it, and neither does anyone else except you.
220
+
286
221
  ## Technical Details
287
222
 
288
223
  This project is separated into five (5) logical parts:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ei-tui",
3
- "version": "1.6.6",
3
+ "version": "1.6.7",
4
4
  "author": "Flare576",
5
5
  "repository": {
6
6
  "type": "git",
@@ -211,14 +211,10 @@ export async function importClaudeCodeSessions(
211
211
  const persona = ensureClaudeCodePersona(stateManager, eiInterface);
212
212
  result.personaCreated = !personaExistedBefore;
213
213
 
214
- if (!personaExistedBefore) {
215
- stateManager.persona_archive(persona.id);
216
- } else {
217
- const existingMsgs = stateManager.messages_get(persona.id);
218
- const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
219
- if (externalIds.length > 0) {
220
- stateManager.messages_remove(persona.id, externalIds);
221
- }
214
+ const existingMsgs = stateManager.messages_get(persona.id);
215
+ const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
216
+ if (externalIds.length > 0) {
217
+ stateManager.messages_remove(persona.id, externalIds);
222
218
  }
223
219
 
224
220
  const cutoffIso = processedSessions[targetSession.id] ?? null;
@@ -173,14 +173,10 @@ export async function importCodexSessions(
173
173
  const persona = ensureCodexPersona(stateManager, eiInterface);
174
174
  result.personaCreated = !personaExistedBefore;
175
175
 
176
- if (!personaExistedBefore) {
177
- stateManager.persona_archive(persona.id);
178
- } else {
179
- const existingMsgs = stateManager.messages_get(persona.id);
180
- const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
181
- if (externalIds.length > 0) {
182
- stateManager.messages_remove(persona.id, externalIds);
183
- }
176
+ const existingMsgs = stateManager.messages_get(persona.id);
177
+ const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
178
+ if (externalIds.length > 0) {
179
+ stateManager.messages_remove(persona.id, externalIds);
184
180
  }
185
181
 
186
182
  const cutoffIso = processedSessions[targetSession.id] ?? null;
@@ -174,14 +174,10 @@ export async function importCursorSessions(
174
174
  const persona = ensureCursorPersona(stateManager, eiInterface);
175
175
  result.personaCreated = !personaExistedBefore;
176
176
 
177
- if (!personaExistedBefore) {
178
- stateManager.persona_archive(persona.id);
179
- } else {
180
- const existingMsgs = stateManager.messages_get(persona.id);
181
- const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
182
- if (externalIds.length > 0) {
183
- stateManager.messages_remove(persona.id, externalIds);
184
- }
177
+ const existingMsgs = stateManager.messages_get(persona.id);
178
+ const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
179
+ if (externalIds.length > 0) {
180
+ stateManager.messages_remove(persona.id, externalIds);
185
181
  }
186
182
 
187
183
  const cutoffIso = processedSessions[targetSession.id] ?? null;
@@ -202,11 +202,8 @@ export async function importOpenCodeSessions(
202
202
  const cutoffMs = cutoffIso ? new Date(cutoffIso).getTime() : null;
203
203
  let anyPersonaHasChanges = false;
204
204
 
205
- for (const [, { persona, msgs: agentMsgs, isNew, agentName }] of byPersonaId) {
206
- if (isNew) {
207
- // Brand-new persona: archive it (coding-session store, not a live chat persona)
208
- stateManager.persona_archive(persona.id);
209
- } else if (persona.is_archived) {
205
+ for (const [, { persona, msgs: agentMsgs, agentName }] of byPersonaId) {
206
+ if (persona.is_archived) {
210
207
  // Existing archived persona: refresh identity fields, then remove only external messages
211
208
  const agentInfo = await reader.getAgentInfo(persona.display_name);
212
209
  const { aliases } = resolveCanonicalAgent(agentName);
@@ -171,14 +171,10 @@ export async function importPiSessions(options: PiImporterOptions): Promise<PiIm
171
171
  const persona = ensurePiPersona(stateManager, eiInterface);
172
172
  result.personaCreated = !personaExistedBefore;
173
173
 
174
- if (!personaExistedBefore) {
175
- stateManager.persona_archive(persona.id);
176
- } else {
177
- const existingMsgs = stateManager.messages_get(persona.id);
178
- const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
179
- if (externalIds.length > 0) {
180
- stateManager.messages_remove(persona.id, externalIds);
181
- }
174
+ const existingMsgs = stateManager.messages_get(persona.id);
175
+ const externalIds = existingMsgs.filter((m) => m.external === true).map((m) => m.id);
176
+ if (externalIds.length > 0) {
177
+ stateManager.messages_remove(persona.id, externalIds);
182
178
  }
183
179
 
184
180
  const cutoffIso = processedSessions[targetSession.id] ?? null;
package/tui/README.md CHANGED
@@ -4,28 +4,39 @@ Ei TUI is built with OpenTUI and SolidJS.
4
4
 
5
5
  Coding tool integrations (OpenCode, Claude Code, Cursor, Codex): enable via `/settings` · export data via [CLI](../src/cli/README.md)
6
6
 
7
- ## How Ei Handles Configuration
7
+ ## What do you want to do?
8
8
 
9
- Ei is designed to run consistently across machines and environments, so it keeps its own copy of your settings rather than reading from environment variables on every launch.
9
+ **Give my coding tools persistent memory** Enable integrations in `/settings`, run `bunx ei-tui --install` for MCP wiring. [Jump to Coding Tool Integrations →](#coding-tool-integrations)
10
10
 
11
- **On first run**, Ei reads environment variables like `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc. to auto-configure providers for you. After that, those values are saved to Ei's local state (`~/.local/share/ei/state.json` by default) and the env vars are no longer consulted.
11
+ **Personalize my AI agents** Create personas, set traits and topics, let them evolve. [Jump to Installation →](#installation) then come back for [Persona setup in the root README](../README.md#whats-a-persona)
12
12
 
13
- Detected providers are configured with sensible defaults out of the box:
13
+ **Chat with a Persona** Full TUI experience heartbeats, rooms, the whole thing. [Jump to Installation →](#installation)
14
14
 
15
- - **Models**: Only chat-capable models are included — TTS, image generation, embeddings, and other non-chat model families are filtered out. You get one model per tier (e.g. fast/mini for extraction, capable for chat, powerful for complex work) rather than a wall of 100+ options.
16
- - **Token limits**: Known models get pre-configured `token_limit` and `max_output_tokens` values based on real-world Ei usage, not just the provider's advertised maximums.
17
- - **Rewrite model**: If a high-capability model is detected (Anthropic Opus, OpenAI o-series), it's automatically set as your `rewrite_model` — used by `/generate` and `/dedupe`. No manual `/settings` step needed.
15
+ ## Installation
18
16
 
19
- All of this only applies on first run. Existing profiles are never modified by detection.
17
+ You need Bun to run the TUI:
20
18
 
21
- This means:
19
+ ```bash
20
+ # Install Bun (if you don't have it)
21
+ curl -fsSL https://bun.sh/install | bash
22
+ ```
22
23
 
23
- - **Rotating an API key?** Update it in Ei with `/provider`, not just in your shell.
24
- - **Switching machines?** Your providers and settings travel with your state file (or via Sync), not your shell profile.
25
- - **Changed your mind about a model?** Use `/provider` to set the model for a persona, or `/settings` to change your global default.
26
- - **Updated sync credentials?** Use `/setsync <user> <pass>` — env vars won't be re-read.
24
+ But the TUI itself doesn't need to be "installed":
27
25
 
28
- The one exception is `EI_DATA_PATH` (and `EI_SYNC_USERNAME` / `EI_SYNC_PASSWORD` for bootstrapping sync on a new machine) — those are always read at startup since Ei needs them before it can load its own state.
26
+ ```bash
27
+ # Run Ei — no install needed, always the latest version
28
+ bunx ei-tui
29
+
30
+ # Or, if you use it as much as I do, add this to your profile!
31
+ alias ei='bunx ei-tui'
32
+ ```
33
+
34
+ **BUT**, if you want to wire Ei's knowledge base to your coding tools, you can install the hooks for it with:
35
+
36
+ ```bash
37
+ # Wire up Cursor, Claude Code, OpenCode, Codex, etc.
38
+ bunx ei-tui --install
39
+ ```
29
40
 
30
41
  ## Coding Tool Integrations
31
42
 
@@ -41,7 +52,7 @@ Enable any or all four in `/settings`. They work independently and feed into the
41
52
 
42
53
  Sessions are processed oldest-first, one per queue cycle. On first run Ei works through your backlog gradually — it won't flood your LLM provider.
43
54
 
44
- All four tools also support reading Ei's knowledge back out via the [CLI tool](../src/cli/README.md). Run `ei --install` to wire up MCP everywhere it is detected, plus automatic context injection where hooks/plugins are available.
55
+ All four tools also support reading Ei's knowledge back out via the [CLI tool](../src/cli/README.md). Run `bunx ei-tui --install` to wire up MCP everywhere it is detected, plus automatic context injection where hooks/plugins are available.
45
56
 
46
57
  ## Slack Integration
47
58
 
@@ -69,19 +80,6 @@ Ei will index channels and DMs you're a member of, extracting topics, people, an
69
80
  - Your workspace admin may need to approve the [Ei Slack app](https://slack.com/oauth/v2/authorize?client_id=11080256060354.11080294064034&scope=&user_scope=channels:history,channels:read,groups:history,groups:read,im:history,im:read,mpim:history,mpim:read,users:read,users:read.email) — that link goes directly to the install flow
70
81
  - The Slack app is read-only — Ei never posts, reacts, or takes any action in your workspace
71
82
 
72
- # Installation
73
-
74
- ```bash
75
- # Install Bun (if you don't have it)
76
- curl -fsSL https://bun.sh/install | bash
77
-
78
- # Run Ei — no install needed, always the latest version
79
- bunx ei-tui
80
-
81
- # Or, if you use it as much as I do, add this to your profile!
82
- alias ei='bunx ei-tui'
83
- ```
84
-
85
83
  ## TUI Commands
86
84
 
87
85
  All commands start with `/`. Append `!` to any command as a shorthand for `--force` (e.g., `/quit!`).
@@ -199,6 +197,29 @@ Rooms have three modes, set at creation time:
199
197
  | `Ctrl+E` | Open `$EDITOR` (preserves current input) |
200
198
  | `PageUp / PageDown` | Scroll message history |
201
199
 
200
+ ## How Ei Handles Configuration
201
+
202
+ Ei is designed to run consistently across machines and environments, so it keeps its own copy of your settings rather than reading from environment variables on every launch.
203
+
204
+ **On first run**, Ei reads environment variables like `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc. to auto-configure providers for you. After that, those values are saved to Ei's local state (`~/.local/share/ei/state.json` by default) and the env vars are no longer consulted.
205
+
206
+ Detected providers are configured with sensible defaults out of the box:
207
+
208
+ - **Models**: Only chat-capable models are included — TTS, image generation, embeddings, and other non-chat model families are filtered out. You get one model per tier (e.g. fast/mini for extraction, capable for chat, powerful for complex work) rather than a wall of 100+ options.
209
+ - **Token limits**: Known models get pre-configured `token_limit` and `max_output_tokens` values based on real-world Ei usage, not just the provider's advertised maximums.
210
+ - **Rewrite model**: If a high-capability model is detected (Anthropic Opus, OpenAI o-series), it's automatically set as your `rewrite_model` — used by `/generate` and `/dedupe`. No manual `/settings` step needed.
211
+
212
+ All of this only applies on first run. Existing profiles are never modified by detection.
213
+
214
+ This means:
215
+
216
+ - **Rotating an API key?** Update it in Ei with `/provider`, not just in your shell.
217
+ - **Switching machines?** Your providers and settings travel with your state file (or via Sync), not your shell profile.
218
+ - **Changed your mind about a model?** Use `/provider` to set the model for a persona, or `/settings` to change your global default.
219
+ - **Updated sync credentials?** Use `/setsync <user> <pass>` — env vars won't be re-read.
220
+
221
+ The one exception is `EI_DATA_PATH` (and `EI_SYNC_USERNAME` / `EI_SYNC_PASSWORD` for bootstrapping sync on a new machine) — those are always read at startup since Ei needs them before it can load its own state.
222
+
202
223
  # Environment Variables
203
224
 
204
225
  | Variable | Default | Description |