ei-tui 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/handlers/human-matching.ts +12 -9
- package/src/prompts/human/topic-update.ts +1 -1
- package/tui/src/commands/me.tsx +12 -6
- package/tui/src/util/yaml-context.ts +66 -0
- package/tui/src/util/yaml-human.ts +274 -0
- package/tui/src/util/yaml-persona.ts +479 -0
- package/tui/src/util/yaml-provider.ts +215 -0
- package/tui/src/util/yaml-queue.ts +81 -0
- package/tui/src/util/yaml-quotes.ts +46 -0
- package/tui/src/util/yaml-serializers.ts +9 -1510
- package/tui/src/util/yaml-settings.ts +223 -0
- package/tui/src/util/yaml-shared.ts +32 -0
- package/tui/src/util/yaml-toolkit.ts +55 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import type {
|
|
3
|
+
HumanSettings,
|
|
4
|
+
CeremonyConfig,
|
|
5
|
+
OpenCodeSettings,
|
|
6
|
+
ProviderAccount,
|
|
7
|
+
} from "../../../src/core/types.js";
|
|
8
|
+
import type { ClaudeCodeSettings } from "../../../src/integrations/claude-code/types.js";
|
|
9
|
+
import type { CursorSettings } from "../../../src/integrations/cursor/types.js";
|
|
10
|
+
import { modelGuidToDisplay, displayToModelGuid } from "./yaml-shared.js";
|
|
11
|
+
|
|
12
|
+
interface EditableSettingsData {
|
|
13
|
+
default_model?: string | null;
|
|
14
|
+
oneshot_model?: string | null;
|
|
15
|
+
rewrite_model?: string | null;
|
|
16
|
+
time_mode?: "24h" | "12h" | "local" | "utc" | null;
|
|
17
|
+
name_display?: string | null;
|
|
18
|
+
default_heartbeat_ms?: number | null;
|
|
19
|
+
default_context_window_hours?: number | null;
|
|
20
|
+
message_min_count?: number | null;
|
|
21
|
+
message_max_age_days?: number | null;
|
|
22
|
+
ceremony?: {
|
|
23
|
+
time: string;
|
|
24
|
+
decay_rate?: number | null;
|
|
25
|
+
explore_threshold?: number | null;
|
|
26
|
+
dedup_threshold?: number | null;
|
|
27
|
+
event_window_hours?: number | null;
|
|
28
|
+
};
|
|
29
|
+
opencode?: {
|
|
30
|
+
integration?: boolean | null;
|
|
31
|
+
polling_interval_ms?: number | null;
|
|
32
|
+
last_sync?: string | null;
|
|
33
|
+
extraction_point?: string | null;
|
|
34
|
+
extraction_model?: string | null;
|
|
35
|
+
};
|
|
36
|
+
claudeCode?: {
|
|
37
|
+
integration?: boolean | null;
|
|
38
|
+
polling_interval_ms?: number | null;
|
|
39
|
+
last_sync?: string | null;
|
|
40
|
+
extraction_point?: string | null;
|
|
41
|
+
extraction_model?: string | null;
|
|
42
|
+
};
|
|
43
|
+
cursor?: {
|
|
44
|
+
integration?: boolean | null;
|
|
45
|
+
polling_interval_ms?: number | null;
|
|
46
|
+
last_sync?: string | null;
|
|
47
|
+
extraction_point?: string | null;
|
|
48
|
+
};
|
|
49
|
+
backup?: {
|
|
50
|
+
enabled?: boolean | null;
|
|
51
|
+
max_backups?: number | null;
|
|
52
|
+
interval_ms?: number | null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function settingsToYAML(settings: HumanSettings | undefined, accounts: ProviderAccount[]): string {
|
|
57
|
+
const guidToDisplay = (guid: string | undefined | null): string | null => {
|
|
58
|
+
if (!guid) return null;
|
|
59
|
+
return modelGuidToDisplay(guid, accounts);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const data: EditableSettingsData = {
|
|
63
|
+
default_model: guidToDisplay(settings?.default_model),
|
|
64
|
+
oneshot_model: guidToDisplay(settings?.oneshot_model),
|
|
65
|
+
rewrite_model: guidToDisplay(settings?.rewrite_model),
|
|
66
|
+
time_mode: settings?.time_mode ?? null,
|
|
67
|
+
name_display: settings?.name_display ?? null,
|
|
68
|
+
default_heartbeat_ms: settings?.default_heartbeat_ms ?? 1800000,
|
|
69
|
+
default_context_window_hours: settings?.default_context_window_hours ?? 8,
|
|
70
|
+
message_min_count: settings?.message_min_count ?? 200,
|
|
71
|
+
message_max_age_days: settings?.message_max_age_days ?? 14,
|
|
72
|
+
ceremony: {
|
|
73
|
+
time: settings?.ceremony?.time ?? "09:00",
|
|
74
|
+
decay_rate: settings?.ceremony?.decay_rate ?? null,
|
|
75
|
+
explore_threshold: settings?.ceremony?.explore_threshold ?? null,
|
|
76
|
+
dedup_threshold: settings?.ceremony?.dedup_threshold ?? null,
|
|
77
|
+
event_window_hours: settings?.ceremony?.event_window_hours ?? null,
|
|
78
|
+
},
|
|
79
|
+
opencode: {
|
|
80
|
+
integration: settings?.opencode?.integration ?? false,
|
|
81
|
+
polling_interval_ms: settings?.opencode?.polling_interval_ms ?? 60000,
|
|
82
|
+
extraction_model: guidToDisplay(settings?.opencode?.extraction_model) ?? 'default',
|
|
83
|
+
last_sync: settings?.opencode?.last_sync ?? null,
|
|
84
|
+
extraction_point: settings?.opencode?.extraction_point ?? null,
|
|
85
|
+
},
|
|
86
|
+
claudeCode: {
|
|
87
|
+
integration: settings?.claudeCode?.integration ?? false,
|
|
88
|
+
polling_interval_ms: settings?.claudeCode?.polling_interval_ms ?? 60000,
|
|
89
|
+
extraction_model: guidToDisplay(settings?.claudeCode?.extraction_model) ?? 'default',
|
|
90
|
+
last_sync: settings?.claudeCode?.last_sync ?? null,
|
|
91
|
+
extraction_point: settings?.claudeCode?.extraction_point ?? null,
|
|
92
|
+
},
|
|
93
|
+
cursor: {
|
|
94
|
+
integration: settings?.cursor?.integration ?? false,
|
|
95
|
+
polling_interval_ms: settings?.cursor?.polling_interval_ms ?? 60000,
|
|
96
|
+
last_sync: settings?.cursor?.last_sync ?? null,
|
|
97
|
+
extraction_point: settings?.cursor?.extraction_point ?? null,
|
|
98
|
+
},
|
|
99
|
+
backup: {
|
|
100
|
+
enabled: settings?.backup?.enabled ?? false,
|
|
101
|
+
max_backups: settings?.backup?.max_backups ?? 24,
|
|
102
|
+
interval_ms: settings?.backup?.interval_ms ?? 3600000,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return YAML.stringify(data, {
|
|
107
|
+
lineWidth: 0,
|
|
108
|
+
})
|
|
109
|
+
.replace(/^(\s+)(last_sync: .+)$/mg, '$1# [read-only] $2')
|
|
110
|
+
.replace(/^(\s+)(extraction_point: .+)$/mg, '$1# [read-only] $2')
|
|
111
|
+
.replace(/^(\s+)(extraction_model: .+)$/mg, '$1$2 # e.g. Anthropic:claude-haiku-4-5');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function settingsFromYAML(yamlContent: string, original: HumanSettings | undefined, accounts: ProviderAccount[]): HumanSettings {
|
|
115
|
+
const data = YAML.parse(yamlContent) as EditableSettingsData;
|
|
116
|
+
|
|
117
|
+
const nullToUndefined = <T>(value: T | null | undefined): T | undefined =>
|
|
118
|
+
value === null ? undefined : value;
|
|
119
|
+
|
|
120
|
+
const displayToGuid = (display: string | null | undefined): string | undefined => {
|
|
121
|
+
if (!display || display === 'default') return undefined;
|
|
122
|
+
return displayToModelGuid(display, accounts) ?? display;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
let ceremony: CeremonyConfig | undefined;
|
|
126
|
+
if (data.ceremony) {
|
|
127
|
+
ceremony = {
|
|
128
|
+
time: data.ceremony.time,
|
|
129
|
+
decay_rate: nullToUndefined(data.ceremony.decay_rate),
|
|
130
|
+
explore_threshold: nullToUndefined(data.ceremony.explore_threshold),
|
|
131
|
+
dedup_threshold: nullToUndefined(data.ceremony.dedup_threshold),
|
|
132
|
+
event_window_hours: nullToUndefined(data.ceremony.event_window_hours),
|
|
133
|
+
last_ceremony: original?.ceremony?.last_ceremony,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let opencode: OpenCodeSettings | undefined;
|
|
138
|
+
if (data.opencode) {
|
|
139
|
+
opencode = {
|
|
140
|
+
integration: nullToUndefined(data.opencode.integration),
|
|
141
|
+
polling_interval_ms: nullToUndefined(data.opencode.polling_interval_ms),
|
|
142
|
+
last_sync: original?.opencode?.last_sync,
|
|
143
|
+
extraction_point: original?.opencode?.extraction_point,
|
|
144
|
+
processed_sessions: original?.opencode?.processed_sessions,
|
|
145
|
+
extraction_model: displayToGuid(data.opencode.extraction_model),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let claudeCode: ClaudeCodeSettings | undefined;
|
|
150
|
+
if (data.claudeCode) {
|
|
151
|
+
claudeCode = {
|
|
152
|
+
integration: nullToUndefined(data.claudeCode.integration),
|
|
153
|
+
polling_interval_ms: nullToUndefined(data.claudeCode.polling_interval_ms),
|
|
154
|
+
last_sync: original?.claudeCode?.last_sync,
|
|
155
|
+
extraction_point: original?.claudeCode?.extraction_point,
|
|
156
|
+
processed_sessions: original?.claudeCode?.processed_sessions,
|
|
157
|
+
extraction_model: displayToGuid(data.claudeCode.extraction_model),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let cursor: CursorSettings | undefined;
|
|
162
|
+
if (data.cursor) {
|
|
163
|
+
cursor = {
|
|
164
|
+
integration: nullToUndefined(data.cursor.integration),
|
|
165
|
+
polling_interval_ms: nullToUndefined(data.cursor.polling_interval_ms),
|
|
166
|
+
last_sync: original?.cursor?.last_sync,
|
|
167
|
+
extraction_point: original?.cursor?.extraction_point,
|
|
168
|
+
processed_sessions: original?.cursor?.processed_sessions,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let backup: import('../../../src/core/types.js').BackupConfig | undefined;
|
|
173
|
+
if (data.backup) {
|
|
174
|
+
backup = {
|
|
175
|
+
enabled: nullToUndefined(data.backup.enabled),
|
|
176
|
+
max_backups: nullToUndefined(data.backup.max_backups),
|
|
177
|
+
interval_ms: nullToUndefined(data.backup.interval_ms),
|
|
178
|
+
last_backup: original?.backup?.last_backup,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
...original,
|
|
184
|
+
default_model: displayToGuid(data.default_model),
|
|
185
|
+
oneshot_model: displayToGuid(data.oneshot_model),
|
|
186
|
+
rewrite_model: displayToGuid(data.rewrite_model),
|
|
187
|
+
time_mode: nullToUndefined(data.time_mode),
|
|
188
|
+
name_display: nullToUndefined(data.name_display),
|
|
189
|
+
default_heartbeat_ms: nullToUndefined(data.default_heartbeat_ms),
|
|
190
|
+
default_context_window_hours: nullToUndefined(data.default_context_window_hours),
|
|
191
|
+
message_min_count: nullToUndefined(data.message_min_count),
|
|
192
|
+
message_max_age_days: nullToUndefined(data.message_max_age_days),
|
|
193
|
+
ceremony,
|
|
194
|
+
opencode,
|
|
195
|
+
claudeCode,
|
|
196
|
+
cursor,
|
|
197
|
+
backup,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function validateModelProvider(
|
|
202
|
+
modelSpec: string | undefined,
|
|
203
|
+
accounts: ProviderAccount[]
|
|
204
|
+
): string | undefined {
|
|
205
|
+
if (!modelSpec) return undefined;
|
|
206
|
+
|
|
207
|
+
const colonIdx = modelSpec.indexOf(":");
|
|
208
|
+
const providerPart = colonIdx >= 0 ? modelSpec.substring(0, colonIdx) : modelSpec;
|
|
209
|
+
const modelPart = colonIdx >= 0 ? modelSpec.substring(colonIdx + 1) : undefined;
|
|
210
|
+
|
|
211
|
+
const match = accounts.find(a => a.name.toLowerCase() === providerPart.toLowerCase());
|
|
212
|
+
|
|
213
|
+
if (!match) {
|
|
214
|
+
const available = accounts.map(a => a.name).join(", ");
|
|
215
|
+
throw new Error(
|
|
216
|
+
available
|
|
217
|
+
? `No provider named "${providerPart}". Available: ${available}`
|
|
218
|
+
: `No provider named "${providerPart}". Create one with /provider new`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return modelPart ? `${match.name}:${modelPart}` : match.name;
|
|
223
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// GUID <-> DISPLAY NAME HELPERS
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
import type { ProviderAccount } from "../../../src/core/types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert a model GUID to "ProviderName:modelName" display string.
|
|
9
|
+
* Falls back to the raw GUID if the model is not found.
|
|
10
|
+
*/
|
|
11
|
+
export function modelGuidToDisplay(guid: string, accounts: ProviderAccount[]): string {
|
|
12
|
+
for (const account of accounts) {
|
|
13
|
+
const model = (account.models ?? []).find(m => m.id === guid);
|
|
14
|
+
if (model) return `${account.name}:${model.name}`;
|
|
15
|
+
}
|
|
16
|
+
return guid; // fallback: return raw GUID if not found
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve "ProviderName:modelName" display string back to a model GUID.
|
|
21
|
+
* Returns undefined if no matching provider+model is found.
|
|
22
|
+
* Handles colons in model names by treating everything after the first colon as the model name.
|
|
23
|
+
*/
|
|
24
|
+
export function displayToModelGuid(display: string, accounts: ProviderAccount[]): string | undefined {
|
|
25
|
+
const colonIdx = display.indexOf(':');
|
|
26
|
+
if (colonIdx < 0) return undefined;
|
|
27
|
+
const providerName = display.substring(0, colonIdx);
|
|
28
|
+
const modelName = display.substring(colonIdx + 1);
|
|
29
|
+
const account = accounts.find(a => a.name === providerName);
|
|
30
|
+
const model = (account?.models ?? []).find(m => m.name === modelName);
|
|
31
|
+
return model?.id;
|
|
32
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import type { ToolProvider, ToolDefinition } from "../../../src/core/types.js";
|
|
3
|
+
|
|
4
|
+
interface EditableToolkitData {
|
|
5
|
+
display_name: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
config: Record<string, string>;
|
|
8
|
+
tools?: Record<string, boolean>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function toolkitToYAML(provider: ToolProvider, tools: ToolDefinition[]): string {
|
|
12
|
+
const toolsMap = tools.length > 0
|
|
13
|
+
? Object.fromEntries(tools.map(t => [t.display_name, t.enabled]))
|
|
14
|
+
: undefined;
|
|
15
|
+
if (provider.builtin) {
|
|
16
|
+
return YAML.stringify({ enabled: provider.enabled, tools: toolsMap }, { lineWidth: 0 });
|
|
17
|
+
}
|
|
18
|
+
const data: EditableToolkitData = {
|
|
19
|
+
display_name: provider.display_name,
|
|
20
|
+
enabled: provider.enabled,
|
|
21
|
+
config: { ...provider.config },
|
|
22
|
+
tools: toolsMap,
|
|
23
|
+
};
|
|
24
|
+
return YAML.stringify(data, { lineWidth: 0 });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ToolkitYAMLResult {
|
|
28
|
+
updates: Partial<Omit<ToolProvider, 'id' | 'created_at'>>;
|
|
29
|
+
toolUpdates: Array<{ id: string; enabled: boolean }>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function toolkitFromYAML(yamlContent: string, original: ToolProvider, tools: ToolDefinition[]): ToolkitYAMLResult {
|
|
33
|
+
const data = YAML.parse(yamlContent) as EditableToolkitData;
|
|
34
|
+
|
|
35
|
+
if (!data.display_name) {
|
|
36
|
+
if (!original.display_name) throw new Error("display_name is required");
|
|
37
|
+
data.display_name = original.display_name;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const updates: Partial<Omit<ToolProvider, 'id' | 'created_at'>> = {
|
|
41
|
+
display_name: data.display_name,
|
|
42
|
+
enabled: data.enabled ?? original.enabled,
|
|
43
|
+
config: data.config ?? {},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const toolUpdates: Array<{ id: string; enabled: boolean }> = [];
|
|
47
|
+
if (data.tools) {
|
|
48
|
+
for (const [displayName, enabled] of Object.entries(data.tools)) {
|
|
49
|
+
const tool = tools.find(t => t.display_name === displayName);
|
|
50
|
+
if (tool) toolUpdates.push({ id: tool.id, enabled: Boolean(enabled) });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { updates, toolUpdates };
|
|
55
|
+
}
|