ei-tui 0.6.7 → 0.7.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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/cli/mcp.ts +35 -10
  3. package/src/cli/persona-filter.ts +42 -0
  4. package/src/cli.ts +18 -6
  5. package/src/core/handlers/human-extraction.ts +1 -0
  6. package/src/core/handlers/human-matching.ts +10 -0
  7. package/src/core/handlers/index.ts +2 -1
  8. package/src/core/handlers/persona-response.ts +5 -0
  9. package/src/core/handlers/utils.ts +4 -1
  10. package/src/core/orchestrators/ceremony.ts +2 -2
  11. package/src/core/orchestrators/human-extraction.ts +5 -0
  12. package/src/core/processor.ts +22 -2
  13. package/src/core/prompt-context-builder.ts +40 -10
  14. package/src/core/queue-manager.ts +18 -0
  15. package/src/core/room-manager.ts +21 -4
  16. package/src/core/state-manager.ts +26 -0
  17. package/src/core/types/data-items.ts +1 -0
  18. package/src/core/types/enums.ts +1 -0
  19. package/src/core/types/integrations.ts +1 -0
  20. package/src/core/types/rooms.ts +2 -0
  21. package/src/integrations/claude-code/importer.ts +3 -57
  22. package/src/integrations/cursor/importer.ts +2 -52
  23. package/src/integrations/opencode/importer.ts +1 -0
  24. package/src/prompts/response/sections.ts +1 -1
  25. package/src/prompts/response/types.ts +1 -0
  26. package/src/prompts/room/index.ts +2 -2
  27. package/src/prompts/room/sections.ts +4 -4
  28. package/src/prompts/room/types.ts +4 -0
  29. package/tui/src/commands/activate.tsx +7 -6
  30. package/tui/src/commands/context.tsx +188 -2
  31. package/tui/src/components/CYPTreeOverlay.tsx +357 -0
  32. package/tui/src/components/MAPScoreOverlay.tsx +300 -0
  33. package/tui/src/components/MessageList.tsx +14 -3
  34. package/tui/src/components/RoomMessageList.tsx +15 -3
  35. package/tui/src/context/ei.tsx +20 -0
  36. package/tui/src/util/cyp-tree.ts +62 -0
  37. package/tui/src/util/yaml-context.ts +87 -1
@@ -1,4 +1,4 @@
1
- import { For, Show, createMemo, createSignal, createEffect, on, onCleanup } from "solid-js";
1
+ import { For, Show, createMemo, createSignal, createEffect, on, onCleanup, onMount } from "solid-js";
2
2
  import { TextAttributes, type ScrollBoxRenderable } from "@opentui/core";
3
3
  import { useEi } from "../context/ei.js";
4
4
  import { useKeyboardNav } from "../context/keyboard.js";
@@ -27,9 +27,21 @@ function formatTime(timestamp: string): string {
27
27
  }
28
28
 
29
29
  export function RoomMessageList() {
30
- const { roomMessages, roomActivePath, personas, activeRoomId, getRoom, getQuotes, quotesVersion } = useEi();
30
+ const { roomMessages, roomActivePath, personas, activeRoomId, getRoom, getQuotes, quotesVersion, getHuman } = useEi();
31
31
  const { registerMessageScroll } = useKeyboardNav();
32
32
 
33
+ const [humanDisplayName, setHumanDisplayName] = createSignal("Human");
34
+
35
+ onMount(() => {
36
+ void getHuman().then(human => {
37
+ const name =
38
+ human.settings?.name_display ||
39
+ human.facts?.find(f => f.name === "Nickname/Preferred Name")?.description ||
40
+ "Human";
41
+ setHumanDisplayName(name);
42
+ });
43
+ });
44
+
33
45
  const personaNameMap = createMemo(() => {
34
46
  const map = new Map<string, string>();
35
47
  for (const p of personas()) {
@@ -99,7 +111,7 @@ export function RoomMessageList() {
99
111
  });
100
112
 
101
113
  const getSpeakerName = (msg: RoomMessage): string => {
102
- if (msg.role === "human") return "Human";
114
+ if (msg.role === "human") return humanDisplayName();
103
115
  if (msg.persona_id) return personaNameMap().get(msg.persona_id) ?? msg.persona_id;
104
116
  return "Persona";
105
117
  };
@@ -110,6 +110,8 @@ export interface EiContextValue {
110
110
  dismissWelcomeOverlay: () => void;
111
111
  deleteMessages: (personaId: string, messageIds: string[]) => Promise<void>;
112
112
  setMessageContextStatus: (personaId: string, messageId: string, status: ContextStatus) => Promise<void>;
113
+ deleteRoomMessages: (roomId: string, messageIds: string[]) => Promise<void>;
114
+ setRoomMessageContextStatus: (roomId: string, messageId: string, status: ContextStatus) => Promise<void>;
113
115
  recallPendingMessages: () => Promise<string>;
114
116
  getToolProviderList: () => ToolProvider[];
115
117
  getToolList: () => ToolDefinition[];
@@ -470,6 +472,22 @@ export const EiProvider: ParentComponent = (props) => {
470
472
  setStore("messages", store.messages.map(m => m.id === messageId ? { ...m, context_status: status } : m));
471
473
  };
472
474
 
475
+ const deleteRoomMessages = async (roomId: string, messageIds: string[]): Promise<void> => {
476
+ if (!processor) return;
477
+ processor.getStateManager().removeRoomMessages(roomId, messageIds);
478
+ if (roomId === store.activeRoomId) {
479
+ setStore("roomMessages", msgs => msgs.filter(m => !messageIds.includes(m.id)));
480
+ }
481
+ };
482
+
483
+ const setRoomMessageContextStatus = async (roomId: string, messageId: string, status: ContextStatus): Promise<void> => {
484
+ if (!processor) return;
485
+ processor.getStateManager().updateRoomMessage(roomId, messageId, { context_status: status });
486
+ if (roomId === store.activeRoomId) {
487
+ setStore("roomMessages", msgs => msgs.map(m => m.id === messageId ? { ...m, context_status: status } : m));
488
+ }
489
+ };
490
+
473
491
  const recallPendingMessages = async (): Promise<string> => {
474
492
  if (!processor) return "";
475
493
  const personaId = store.activePersonaId;
@@ -905,6 +923,8 @@ export const EiProvider: ParentComponent = (props) => {
905
923
  dismissWelcomeOverlay: () => setShowWelcomeOverlay(false),
906
924
  deleteMessages,
907
925
  setMessageContextStatus,
926
+ deleteRoomMessages,
927
+ setRoomMessageContextStatus,
908
928
  recallPendingMessages,
909
929
  getToolProviderList,
910
930
  getToolList,
@@ -0,0 +1,62 @@
1
+ import type { RoomMessage } from "../../../src/core/types.js";
2
+
3
+ export interface CYPTreeData {
4
+ ordered: RoomMessage[];
5
+ numToId: Map<number, string>;
6
+ idToNum: Map<string, number>;
7
+ childrenMap: Map<string, RoomMessage[]>;
8
+ }
9
+
10
+ export function buildCYPTree(messages: RoomMessage[]): CYPTreeData {
11
+ const childrenMap = new Map<string, RoomMessage[]>();
12
+
13
+ for (const m of messages) {
14
+ if (m.parent_id !== null && m.parent_id !== undefined) {
15
+ const existing = childrenMap.get(m.parent_id);
16
+ if (existing) {
17
+ existing.push(m);
18
+ } else {
19
+ childrenMap.set(m.parent_id, [m]);
20
+ }
21
+ }
22
+ }
23
+
24
+ const root = messages.find((m) => m.parent_id === null);
25
+ const ordered: RoomMessage[] = [];
26
+ const numToId = new Map<number, string>();
27
+ const idToNum = new Map<string, number>();
28
+
29
+ if (!root) {
30
+ return { ordered, numToId, idToNum, childrenMap };
31
+ }
32
+
33
+ const queue: RoomMessage[] = [root];
34
+ while (queue.length > 0) {
35
+ const current = queue.shift()!;
36
+ ordered.push(current);
37
+ const num = ordered.length;
38
+ numToId.set(num, current.id);
39
+ idToNum.set(current.id, num);
40
+ for (const child of childrenMap.get(current.id) ?? []) {
41
+ queue.push(child);
42
+ }
43
+ }
44
+
45
+ return { ordered, numToId, idToNum, childrenMap };
46
+ }
47
+
48
+ export function getSubtreeIds(
49
+ rootId: string,
50
+ childrenMap: Map<string, RoomMessage[]>
51
+ ): Set<string> {
52
+ const result = new Set<string>();
53
+ const queue = [rootId];
54
+ while (queue.length > 0) {
55
+ const id = queue.shift()!;
56
+ result.add(id);
57
+ for (const child of childrenMap.get(id) ?? []) {
58
+ queue.push(child.id);
59
+ }
60
+ }
61
+ return result;
62
+ }
@@ -1,5 +1,5 @@
1
1
  import YAML from "yaml";
2
- import type { Message } from "../../../src/core/types.js";
2
+ import type { Message, RoomMessage } from "../../../src/core/types.js";
3
3
  import { ContextStatus } from "../../../src/core/types.js";
4
4
 
5
5
  interface EditableMessage {
@@ -63,4 +63,90 @@ export function contextFromYAML(yamlContent: string): ContextYAMLResult {
63
63
  return { messages, deletedMessageIds };
64
64
  }
65
65
 
66
+ interface FfaEditableNode {
67
+ id: string;
68
+ role: "human" | "persona";
69
+ speaker?: string;
70
+ context_status: ContextStatus;
71
+ _delete?: boolean;
72
+ content?: string;
73
+ silence_reason?: string;
74
+ children?: FfaEditableNode[];
75
+ }
76
+
77
+ function buildNode(msg: RoomMessage, messages: RoomMessage[], speakerMap: Map<string, string>, isRoot = false): FfaEditableNode {
78
+ const children = messages
79
+ .filter((m) => m.parent_id === msg.id)
80
+ .map((child) => buildNode(child, messages, speakerMap));
81
+
82
+ const node: FfaEditableNode = {
83
+ id: msg.id,
84
+ role: msg.role === "human" ? "human" : "persona",
85
+ context_status: msg.context_status,
86
+ ...(!isRoot && { _delete: false }),
87
+ };
88
+
89
+ if (msg.role === "persona" && msg.persona_id) {
90
+ node.speaker = speakerMap.get(msg.persona_id) ?? msg.persona_id.slice(0, 8);
91
+ }
92
+
93
+ const text = getContent(msg);
94
+ if (text) node.content = text;
95
+ if (msg.silence_reason) node.silence_reason = msg.silence_reason;
96
+ if (children.length > 0) node.children = children;
97
+
98
+ return node;
99
+ }
100
+
101
+ export function ffaContextToYAML(
102
+ messages: RoomMessage[],
103
+ speakerMap: Map<string, string>
104
+ ): string {
105
+ const header = [
106
+ "# context_status: default | always | never",
107
+ "# _delete: true — removes this message and all its descendants",
108
+ ].join("\n");
109
+
110
+ const rootMsg = messages.find((m) => m.parent_id === null);
111
+ if (!rootMsg) return header + "\n[]";
66
112
 
113
+ return header + "\n" + YAML.stringify([buildNode(rootMsg, messages, speakerMap, true)], { lineWidth: 0 });
114
+ }
115
+
116
+ export interface FfaContextYAMLResult {
117
+ messages: Array<{ id: string; context_status: ContextStatus }>;
118
+ deletedMessageIds: string[];
119
+ implicitDeleteCount: number;
120
+ }
121
+
122
+ export function ffaContextFromYAML(yamlContent: string): FfaContextYAMLResult {
123
+ const data = YAML.parse(yamlContent) as FfaEditableNode[];
124
+
125
+ const deletedMessageIds: string[] = [];
126
+ const messages: Array<{ id: string; context_status: ContextStatus }> = [];
127
+ let implicitDeleteCount = 0;
128
+
129
+ function collectDeleted(node: FfaEditableNode, parentDeleted: boolean): void {
130
+ const selfDeleted = parentDeleted || !!node._delete;
131
+ if (selfDeleted) {
132
+ deletedMessageIds.push(node.id);
133
+ if (!node._delete) implicitDeleteCount++;
134
+ } else {
135
+ const normalized = (node.context_status ?? 'default').toString().toLowerCase() as ContextStatus;
136
+ messages.push({ id: node.id, context_status: normalized });
137
+ }
138
+ for (const child of node.children ?? []) {
139
+ collectDeleted(child, selfDeleted);
140
+ }
141
+ }
142
+
143
+ const nodes = data ?? [];
144
+ for (let i = 0; i < nodes.length; i++) {
145
+ const node = nodes[i];
146
+ const isRoot = i === 0;
147
+ if (isRoot && node._delete) continue; // root deletion not allowed — it anchors the room
148
+ collectDeleted(node, false);
149
+ }
150
+
151
+ return { messages, deletedMessageIds, implicitDeleteCount };
152
+ }