ei-tui 1.6.1 → 1.6.3

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,12 +1,12 @@
1
1
  # Ei
2
2
 
3
- A local-first AI companion system with persistent personas and coding tool integrations (OpenCode, Claude Code, Cursor).
3
+ A local-first AI companion system with persistent personas and coding tool integrations (OpenCode, Claude Code, Cursor, Codex).
4
4
 
5
5
  You can access the Web version at [ei.flare576.com](https://ei.flare576.com).
6
6
 
7
7
  You can run the local version via `bunx ei-tui` — no install needed, always current (see [### TUI](#tui) for details).
8
8
 
9
- If you're here to give your coding tools (OpenCode, Claude Code, Cursor) 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 automatic context injection so your agents receive relevant memory before every message — no tool calls required.
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.
10
10
 
11
11
  ## What Does "Local First" Mean?
12
12
 
@@ -83,7 +83,7 @@ Ei can operate with three types of input, and three types of output.
83
83
  ^
84
84
  Sessions
85
85
  |
86
- [OpenCode / Claude Code / Cursor]
86
+ [OpenCode / Claude Code / Cursor / Codex]
87
87
  ```
88
88
 
89
89
  ```
@@ -91,7 +91,7 @@ Ei can operate with three types of input, and three types of output.
91
91
  |
92
92
  CLI Data
93
93
  v
94
- [OpenCode]
94
+ [OpenCode / Claude Code / Cursor / Codex]
95
95
  ```
96
96
 
97
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).
@@ -129,7 +129,7 @@ More information (including commands) can be found in the [TUI Readme](tui/READM
129
129
 
130
130
  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
131
 
132
- All three integrations are enabled via `/settings` in the TUI.
132
+ All four integrations are enabled via `/settings` in the TUI.
133
133
 
134
134
  #### OpenCode
135
135
 
@@ -165,6 +165,21 @@ Reads from Cursor's SQLite databases:
165
165
 
166
166
  All sessions map to a single "Cursor" persona.
167
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
+
168
183
  ---
169
184
 
170
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)
@@ -233,7 +248,7 @@ Personas can use tools. Not just read-from-memory tools — *actual* tools. Web
233
248
  |------|-------------|
234
249
  | `find_memory` | Semantic search of your personal memory — facts, traits, topics, people, quotes. Personas call this automatically when the conversation touches something they might know about you. Supports the `persona` filter to scope results to what a specific persona has learned. |
235
250
  | `fetch_memory` | Full-record lookup for a specific human entity (Fact, Topic, Person, or Quote) by ID. Use after `find_memory` to retrieve complete details. |
236
- | `fetch_message` | Retrieve a specific message by ID with optional surrounding context. Searches persona conversations and room messages. |
251
+ | `fetch_message` | Retrieve a specific message by fully-qualified ID with optional surrounding context. Searches Ei conversations and supported external coding-session stores. |
237
252
  | `file_read` | Read a file from your local filesystem *(TUI only)* |
238
253
  | `list_directory` | Explore folder structure *(TUI only)* |
239
254
  | `directory_tree` | Recursive directory tree *(TUI only)* |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ei-tui",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "author": "Flare576",
5
5
  "repository": {
6
6
  "type": "git",
package/src/cli/README.md CHANGED
@@ -15,8 +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 --install # Wire Ei into Claude Code, Cursor, and OpenCode (MCP + hooks + persona plugin)
19
- ei mcp # Start the Ei MCP stdio server (for Cursor/Claude Desktop)
18
+ ei --install # Wire Ei into Claude Code, Cursor, Codex, and OpenCode (MCP + hooks + persona plugin)
19
+ ei mcp # Start the Ei MCP stdio server (for Claude Code/Cursor/Codex)
20
20
  ```
21
21
 
22
22
  Type aliases: `fact`, `person`, `topic`, `quote`, `persona` all work (singular or plural).
@@ -35,6 +35,8 @@ It also resolves fully-qualified message IDs from any supported integration, ret
35
35
  ei --id "opencode:jeremys-macbook-pro:ses_38a7...:msg_c75b..."
36
36
  ei --id "claudecode:my-machine:session-uuid:message-uuid"
37
37
  ei --id "cursor:my-machine:composer-uuid:bubble-uuid"
38
+ ei --id "codex:my-machine:thread-uuid:evt_42"
39
+ ei --id "pi:my-machine:session-uuid:session-uuid/entry-id"
38
40
  ```
39
41
 
40
42
  Quotes surfaced by `ei_search` include a `message_id` field in this format — pipe it to `ei --id` to read the original conversation.
@@ -47,15 +49,17 @@ Quotes surfaced by `ei_search` include a `message_id` field in this format — p
47
49
  ei --install
48
50
  ```
49
51
 
50
- This registers Ei with Claude Code, Cursor, and OpenCode — MCP server config, context injection hooks, and (for OpenCode) a persona identity plugin so agents know who they are before the first message:
52
+ This registers Ei with Claude Code, Cursor, Codex, and OpenCode — MCP server config, context injection hooks where supported, and (for OpenCode) a persona identity plugin so agents know who they are before the first message:
51
53
 
52
54
  | Tool | MCP | Context Hook | Persona Plugin |
53
55
  |------|-----|-------------|----------------|
54
56
  | **Claude Code** | `~/.claude.json` | `~/.claude/settings.json` (`UserPromptSubmit`) + `~/.claude/hooks/ei-inject.ts` | — |
55
57
  | **Cursor** | `~/.cursor/mcp.json` | `~/.cursor/hooks.json` (`beforeSubmitPrompt`) + `~/.cursor/hooks/ei-inject.sh` | — |
58
+ | **Codex** | `~/.codex/config.toml` via `codex mcp add ei` | `~/.codex/hooks.json` (`UserPromptSubmit`) + `~/.codex/hooks/ei-inject.ts` | Local Codex agent plugin if installed separately |
56
59
  | **OpenCode** | manual (see below) | Via Oh My OpenCode compatibility layer (reads `~/.claude/settings.json`) | `~/.config/opencode/plugins/ei-persona.ts` |
60
+ | **Pi / OMP** | — (tools registered as native Pi extension) | `~/.pi/agent/extensions/ei-integration.ts` (Pi) or `~/.omp/agent/extensions/ei-integration.ts` (OMP) | — |
57
61
 
58
- **Context hook**: fires before every message, searches Ei for topics relevant to what you just asked, injects them silently. No tool call required.
62
+ **Context hook**: fires before every message, searches Ei for relevant memory, and injects it silently. No tool call required.
59
63
 
60
64
  **Persona plugin** (OpenCode + [Oh My OpenCode](https://github.com/code-yeongyu/oh-my-opencode) only): injects the agent's Ei relationship record directly into the system prompt at session start — traits, working style, shared context. The agent knows who it is *to you* before it reads a word of your message.
61
65
 
@@ -80,7 +84,7 @@ Restart your agent tool after changes to activate.
80
84
 
81
85
  ### MCP Server
82
86
 
83
- Claude Code and Cursor call `ei mcp` to start the MCP stdio server. You can run it directly to test:
87
+ Claude Code, Cursor, and Codex call `ei mcp` to start the MCP stdio server. You can run it directly to test:
84
88
 
85
89
  ```sh
86
90
  ei mcp
@@ -97,7 +101,7 @@ The `ei_search`, `ei_lookup`, and `ei_fetch_message` MCP tools are still availab
97
101
 
98
102
  ## MCP Tools Reference
99
103
 
100
- The MCP server exposes these tools to Claude Code, Cursor, and OpenCode:
104
+ The MCP server exposes these tools to Claude Code, Cursor, Codex, and OpenCode:
101
105
 
102
106
  | Tool | Description |
103
107
  |------|-------------|
@@ -112,7 +116,7 @@ The MCP server exposes these tools to Claude Code, Cursor, and OpenCode:
112
116
  | `query` | string (optional) | Search text. Omit to browse by recency. |
113
117
  | `type` | enum (optional) | `facts` \| `people` \| `topics` \| `quotes` \| `personas` — omit for balanced results across all types |
114
118
  | `persona` | string (optional) | Persona display_name to scope results to what that persona has learned |
115
- | `source` | string (optional) | Prefix match against source identifiers (e.g. `opencode`, `cursor:my-machine`) |
119
+ | `source` | string (optional) | Prefix match against source identifiers (e.g. `opencode`, `cursor:my-machine`, `codex:my-machine`) |
116
120
  | `limit` | number (optional) | Max results, default 10 |
117
121
  | `recent` | boolean (optional) | Sort by most recently mentioned instead of relevance |
118
122
 
package/src/cli/mcp.ts CHANGED
@@ -37,7 +37,7 @@ export function createMcpServer(): McpServer {
37
37
  .string()
38
38
  .optional()
39
39
  .describe(
40
- "Filter to entities from a specific source. Prefix match against namespaced source identifiers (e.g. 'cursor', 'opencode', 'opencode:my-machine', 'opencode:my-machine:ses_abc123')."
40
+ "Filter to entities from a specific source. Prefix match against namespaced source identifiers (e.g. 'cursor', 'codex', 'opencode', 'opencode:my-machine', 'codex:my-machine:thread-id')."
41
41
  ),
42
42
  limit: z
43
43
  .number()
@@ -106,7 +106,7 @@ export function createMcpServer(): McpServer {
106
106
  .string()
107
107
  .optional()
108
108
  .describe(
109
- "Filter to entities from a specific source. Prefix match against namespaced source identifiers (e.g. 'cursor', 'opencode', 'opencode:my-machine', 'opencode:my-machine:ses_abc123'). If the entity does not match, returns not found."
109
+ "Filter to entities from a specific source. Prefix match against namespaced source identifiers (e.g. 'cursor', 'codex', 'opencode', 'opencode:my-machine', 'codex:my-machine:thread-id'). If the entity does not match, returns not found."
110
110
  ),
111
111
  },
112
112
  },
@@ -134,7 +134,7 @@ export function createMcpServer(): McpServer {
134
134
  "ei_fetch_message",
135
135
  {
136
136
  description:
137
- "Retrieve a specific message by its fully-qualified ID, with optional surrounding conversation context. Use when ei_search returns a quote with a message_id and you want to read the original exchange. The 'before' and 'after' parameters expand the context window in either direction (default 0). Accepts IDs from any integrated source: 'ei:uuid' searches Ei state, 'opencode:machine:session:id' queries OpenCode SQLite, 'claudecode:...' scans Claude Code JSONL files, 'cursor:...' reads the Cursor DB.",
137
+ "Retrieve a specific message by its fully-qualified ID, with optional surrounding conversation context. Use when ei_search returns a quote with a message_id and you want to read the original exchange. The 'before' and 'after' parameters expand the context window in either direction (default 0). Accepts IDs from any integrated source: 'ei:uuid' searches Ei state, 'opencode:machine:session:id' queries OpenCode SQLite, 'claudecode:...' scans Claude Code JSONL files, 'cursor:...' reads the Cursor DB, and 'codex:...' reads Codex rollout history.",
138
138
  inputSchema: {
139
139
  id: z.string().describe("The ID of the message to retrieve"),
140
140
  before: z
@@ -499,6 +499,50 @@ export async function resolveExternalMessage(
499
499
  }
500
500
  }
501
501
 
502
+ case "codex": {
503
+ if (parsed.machine !== getMachineId()) {
504
+ return { error: `Message is from machine '${parsed.machine}', not available on this machine (${getMachineId()})` };
505
+ }
506
+ try {
507
+ const { CodexReader } = await import("../integrations/codex/reader.js");
508
+ const reader = new CodexReader();
509
+ const win = await reader.getMessageById(parsed.session!, parsed.nativeId, before, after);
510
+ if (!win) return null;
511
+ return {
512
+ type: "opencode_message",
513
+ message: { id: win.message.id, role: win.message.role, content: win.message.content, timestamp: win.message.timestamp },
514
+ before: win.before.map(m => ({ id: m.id, role: m.role, content: m.content, timestamp: m.timestamp })),
515
+ after: win.after.map(m => ({ id: m.id, role: m.role, content: m.content, timestamp: m.timestamp })),
516
+ session: { id: win.session.id, title: win.session.title, directory: win.session.cwd },
517
+ source: "codex",
518
+ };
519
+ } catch {
520
+ return null;
521
+ }
522
+ }
523
+
524
+ case "pi": {
525
+ if (parsed.machine !== getMachineId()) {
526
+ return { error: `Message is from machine '${parsed.machine}', not available on this machine (${getMachineId()})` };
527
+ }
528
+ try {
529
+ const { PiReader } = await import("../integrations/pi/reader.js");
530
+ const reader = new PiReader();
531
+ const win = await reader.getMessageById(parsed.session!, parsed.nativeId, before, after);
532
+ if (!win) return null;
533
+ return {
534
+ type: "opencode_message",
535
+ message: { id: win.message.id, role: win.message.role, content: win.message.content, timestamp: win.message.timestamp },
536
+ before: win.before.map(m => ({ id: m.id, role: m.role, content: m.content, timestamp: m.timestamp })),
537
+ after: win.after.map(m => ({ id: m.id, role: m.role, content: m.content, timestamp: m.timestamp })),
538
+ session: { id: win.session.id, title: win.session.title, directory: win.session.cwd },
539
+ source: "pi",
540
+ };
541
+ } catch {
542
+ return null;
543
+ }
544
+ }
545
+
502
546
  case "unknown":
503
547
  default: {
504
548
  // Backward compat: bare msg_xxx → treat as opencode (no machine qualifier)