ei-tui 1.6.5 → 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 +114 -179
- package/package.json +2 -2
- package/src/cli.ts +2 -1
- package/src/integrations/claude-code/importer.ts +4 -8
- package/src/integrations/codex/importer.ts +4 -8
- package/src/integrations/cursor/importer.ts +4 -8
- package/src/integrations/opencode/importer.ts +2 -5
- package/src/integrations/pi/importer.ts +4 -8
- package/tui/README.md +49 -28
package/README.md
CHANGED
|
@@ -1,188 +1,41 @@
|
|
|
1
1
|
# Ei
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
**What are you hoping to do with Ei today?**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## I Want To Give My Coding Agents Memory
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
##
|
|
11
|
+
## I Want My Coding Agents To Have Personality
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
15
|
+
## I Want My Persona To Remember Me
|
|
22
16
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
## I Want My Persona To Talk To Each Other
|
|
28
22
|
|
|
29
|
-
|
|
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
|
-
|
|
25
|
+
## I Want To Use the TUI and the Web
|
|
32
26
|
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
TUI setup: `/auth slack` — see the [TUI README → Slack Integration](./tui/README.md#slack-integration) for details.
|
|
222
63
|
|
|
223
|
-
|
|
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.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"author": "Flare576",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@opentui/core": "^0.1.79",
|
|
64
64
|
"@opentui/solid": "^0.1.79",
|
|
65
65
|
"fastembed": "^2.1.0",
|
|
66
|
-
"solid-js": "1.9.9",
|
|
66
|
+
"solid-js": "^1.9.9",
|
|
67
67
|
"yaml": "^2.8.2"
|
|
68
68
|
}
|
|
69
69
|
}
|
package/src/cli.ts
CHANGED
|
@@ -151,7 +151,7 @@ async function main(): Promise<void> {
|
|
|
151
151
|
const { RemoteSync } = await import("./storage/remote.js");
|
|
152
152
|
const { decodeAllEmbeddings, encodeAllEmbeddings } = await import("./storage/embeddings.js");
|
|
153
153
|
const { join } = await import("path");
|
|
154
|
-
const { readFile, writeFile, rename,
|
|
154
|
+
const { readFile, writeFile, rename, mkdir } = await import("fs/promises");
|
|
155
155
|
|
|
156
156
|
const dataPath = getDataPath();
|
|
157
157
|
const statePath = join(dataPath, "state.json");
|
|
@@ -217,6 +217,7 @@ async function main(): Promise<void> {
|
|
|
217
217
|
process.exit(1);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
await mkdir(dataPath, { recursive: true });
|
|
220
221
|
const tempPath = `${backupPath}.tmp.${Date.now()}`;
|
|
221
222
|
await writeFile(tempPath, JSON.stringify(encodeAllEmbeddings(result.state), null, 2), "utf-8");
|
|
222
223
|
await rename(tempPath, backupPath);
|
|
@@ -211,14 +211,10 @@ export async function importClaudeCodeSessions(
|
|
|
211
211
|
const persona = ensureClaudeCodePersona(stateManager, eiInterface);
|
|
212
212
|
result.personaCreated = !personaExistedBefore;
|
|
213
213
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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,
|
|
206
|
-
if (
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
##
|
|
7
|
+
## What do you want to do?
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
13
|
+
**Chat with a Persona** — Full TUI experience — heartbeats, rooms, the whole thing. [Jump to Installation →](#installation)
|
|
14
14
|
|
|
15
|
-
|
|
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
|
-
|
|
17
|
+
You need Bun to run the TUI:
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
```bash
|
|
20
|
+
# Install Bun (if you don't have it)
|
|
21
|
+
curl -fsSL https://bun.sh/install | bash
|
|
22
|
+
```
|
|
22
23
|
|
|
23
|
-
|
|
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
|
-
|
|
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 |
|