ei-tui 0.1.9 → 0.1.12
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/cli.ts +58 -0
- package/src/core/handlers/index.ts +76 -21
- package/src/core/llm-client.ts +13 -2
- package/src/core/processor.ts +78 -4
- package/src/core/queue-processor.ts +4 -3
- package/src/core/types.ts +3 -1
- package/src/integrations/claude-code/importer.ts +317 -0
- package/src/integrations/claude-code/index.ts +10 -0
- package/src/integrations/claude-code/reader.ts +238 -0
- package/src/integrations/claude-code/types.ts +163 -0
- package/src/prompts/generation/persona.ts +5 -3
- package/src/prompts/generation/types.ts +1 -1
- package/src/prompts/human/fact-scan.ts +6 -6
- package/src/prompts/human/person-scan.ts +6 -6
- package/src/prompts/human/topic-scan.ts +4 -4
- package/src/prompts/human/trait-scan.ts +6 -6
- package/src/prompts/persona/traits.ts +2 -2
- package/tui/README.md +13 -0
- package/tui/src/util/yaml-serializers.ts +27 -3
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -126,6 +126,63 @@ async function installOpenCodeTool(): Promise<void> {
|
|
|
126
126
|
console.log(` Restart OpenCode to activate.`);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
async function installClaudeCodeMcp(): Promise<void> {
|
|
130
|
+
const home = process.env.HOME || "~";
|
|
131
|
+
const claudeJsonPath = join(home, ".claude.json");
|
|
132
|
+
|
|
133
|
+
// Prefer shelling out to `claude mcp add` — lets Claude Code manage its own config
|
|
134
|
+
// and avoids race conditions with a live state file.
|
|
135
|
+
try {
|
|
136
|
+
const which = Bun.spawnSync(["which", "claude"], { stdout: "pipe", stderr: "pipe" });
|
|
137
|
+
if (which.exitCode === 0) {
|
|
138
|
+
const result = Bun.spawnSync(
|
|
139
|
+
["claude", "mcp", "add", "--scope", "user", "--transport", "stdio", "ei", "--", "ei"],
|
|
140
|
+
{ stdout: "pipe", stderr: "pipe" }
|
|
141
|
+
);
|
|
142
|
+
if (result.exitCode === 0) {
|
|
143
|
+
console.log(`✓ Registered Ei as Claude Code MCP server (user scope)`);
|
|
144
|
+
console.log(` Restart Claude Code to activate.`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
console.warn(` claude mcp add failed (exit ${result.exitCode}), falling back to direct write`);
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// claude binary not found — fall through to direct write
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Fallback: direct atomic write to ~/.claude.json
|
|
154
|
+
let config: Record<string, unknown> = {};
|
|
155
|
+
try {
|
|
156
|
+
const text = await Bun.file(claudeJsonPath).text();
|
|
157
|
+
config = JSON.parse(text) as Record<string, unknown>;
|
|
158
|
+
} catch {
|
|
159
|
+
// File doesn't exist or isn't valid JSON — start fresh
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Resolve the ei binary: if running as compiled binary, argv[1] is our path;
|
|
163
|
+
// if running as 'bun src/cli.ts', fall back to 'ei' (assumed on PATH after npm install -g)
|
|
164
|
+
const isBunScript = process.argv[1]?.endsWith("/cli.ts") || process.argv[1]?.endsWith("/cli.js");
|
|
165
|
+
const command = isBunScript ? "ei" : (process.argv[1] ?? "ei");
|
|
166
|
+
|
|
167
|
+
const mcpServers = (config.mcpServers ?? {}) as Record<string, unknown>;
|
|
168
|
+
mcpServers["ei"] = {
|
|
169
|
+
type: "stdio",
|
|
170
|
+
command,
|
|
171
|
+
args: [],
|
|
172
|
+
env: {},
|
|
173
|
+
};
|
|
174
|
+
config.mcpServers = mcpServers;
|
|
175
|
+
|
|
176
|
+
// Atomic write: write to temp file then rename to avoid partial writes
|
|
177
|
+
const tmpPath = `${claudeJsonPath}.ei-install.tmp`;
|
|
178
|
+
await Bun.write(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
179
|
+
const { rename } = await import(/* @vite-ignore */ "fs/promises");
|
|
180
|
+
await rename(tmpPath, claudeJsonPath);
|
|
181
|
+
|
|
182
|
+
console.log(`✓ Installed Ei MCP server to ${claudeJsonPath}`);
|
|
183
|
+
console.log(` Restart Claude Code to activate.`);
|
|
184
|
+
}
|
|
185
|
+
|
|
129
186
|
async function main(): Promise<void> {
|
|
130
187
|
const args = process.argv.slice(2);
|
|
131
188
|
|
|
@@ -148,6 +205,7 @@ async function main(): Promise<void> {
|
|
|
148
205
|
|
|
149
206
|
if (args[0] === "--install") {
|
|
150
207
|
await installOpenCodeTool();
|
|
208
|
+
await installClaudeCodeMcp();
|
|
151
209
|
process.exit(0);
|
|
152
210
|
}
|
|
153
211
|
|
|
@@ -276,33 +276,88 @@ function handlePersonaGeneration(response: LLMResponse, state: StateManager): vo
|
|
|
276
276
|
|
|
277
277
|
const now = new Date().toISOString();
|
|
278
278
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
279
|
+
// Merge LLM traits into user-provided traits by name.
|
|
280
|
+
// User-provided fields win; LLM fills in what the user left blank.
|
|
281
|
+
const userTraitsByName = new Map(
|
|
282
|
+
(existingPartial.traits ?? []).filter(t => t.name?.trim()).map(t => [t.name!.toLowerCase().trim(), t])
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const mergedLlmTraits: Trait[] = (result?.traits || []).map(t => {
|
|
286
|
+
const userTrait = userTraitsByName.get(t.name?.toLowerCase().trim() ?? '');
|
|
287
|
+
return {
|
|
288
|
+
id: (userTrait as Trait | undefined)?.id ?? crypto.randomUUID(),
|
|
289
|
+
name: t.name,
|
|
290
|
+
description: userTrait?.description?.trim() || t.description,
|
|
291
|
+
sentiment: userTrait?.sentiment ?? t.sentiment ?? 0,
|
|
292
|
+
strength: userTrait?.strength ?? t.strength,
|
|
293
|
+
last_updated: now,
|
|
294
|
+
};
|
|
295
|
+
});
|
|
287
296
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
// Keep user-provided traits the LLM didn't return
|
|
298
|
+
const llmTraitNames = new Set(mergedLlmTraits.map(t => t.name?.toLowerCase().trim()));
|
|
299
|
+
const preservedUserTraits: Trait[] = (existingPartial.traits ?? [])
|
|
300
|
+
.filter(t => t.name?.trim() && !llmTraitNames.has(t.name.toLowerCase().trim()))
|
|
301
|
+
.map(t => ({
|
|
302
|
+
id: (t as Trait).id ?? crypto.randomUUID(),
|
|
303
|
+
name: t.name!,
|
|
304
|
+
description: t.description || '',
|
|
305
|
+
sentiment: t.sentiment ?? 0,
|
|
306
|
+
strength: t.strength,
|
|
307
|
+
last_updated: now,
|
|
308
|
+
}));
|
|
309
|
+
|
|
310
|
+
const mergedTraits: Trait[] = mergedLlmTraits.length > 0
|
|
311
|
+
? [...mergedLlmTraits, ...preservedUserTraits]
|
|
312
|
+
: (existingPartial.traits as Trait[] | undefined) ?? [];
|
|
313
|
+
|
|
314
|
+
// Merge LLM topics into user-provided topics by name.
|
|
315
|
+
// User-provided fields win; LLM fills in what the user left blank.
|
|
316
|
+
const userTopicsByName = new Map(
|
|
317
|
+
(existingPartial.topics ?? []).filter(t => t.name?.trim()).map(t => [t.name!.toLowerCase().trim(), t])
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const llmTopics: PersonaTopic[] = (result?.topics || []).map(t => {
|
|
321
|
+
const userTopic = userTopicsByName.get(t.name?.toLowerCase().trim() ?? '');
|
|
322
|
+
return {
|
|
323
|
+
id: (userTopic as PersonaTopic | undefined)?.id ?? crypto.randomUUID(),
|
|
324
|
+
name: t.name,
|
|
325
|
+
perspective: userTopic?.perspective?.trim() || t.perspective || '',
|
|
326
|
+
approach: userTopic?.approach?.trim() || t.approach || '',
|
|
327
|
+
personal_stake: userTopic?.personal_stake?.trim() || t.personal_stake || '',
|
|
328
|
+
sentiment: userTopic?.sentiment ?? t.sentiment ?? 0,
|
|
329
|
+
exposure_current: userTopic?.exposure_current ?? t.exposure_current ?? 0.5,
|
|
330
|
+
exposure_desired: userTopic?.exposure_desired ?? t.exposure_desired ?? 0.5,
|
|
331
|
+
last_updated: now,
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Keep user-provided topics the LLM didn't return (not in its output list)
|
|
336
|
+
const llmTopicNames = new Set(llmTopics.map(t => t.name?.toLowerCase().trim()));
|
|
337
|
+
const preservedUserTopics: PersonaTopic[] = (existingPartial.topics ?? [])
|
|
338
|
+
.filter(t => t.name?.trim() && !llmTopicNames.has(t.name.toLowerCase().trim()))
|
|
339
|
+
.map(t => ({
|
|
340
|
+
id: (t as PersonaTopic).id ?? crypto.randomUUID(),
|
|
341
|
+
name: t.name!,
|
|
342
|
+
perspective: t.perspective || '',
|
|
343
|
+
approach: t.approach || '',
|
|
344
|
+
personal_stake: t.personal_stake || '',
|
|
345
|
+
sentiment: t.sentiment ?? 0,
|
|
346
|
+
exposure_current: t.exposure_current ?? 0.5,
|
|
347
|
+
exposure_desired: t.exposure_desired ?? 0.5,
|
|
348
|
+
last_updated: now,
|
|
349
|
+
}));
|
|
350
|
+
|
|
351
|
+
const topics: PersonaTopic[] = llmTopics.length > 0
|
|
352
|
+
? [...llmTopics, ...preservedUserTopics]
|
|
353
|
+
: (existingPartial.topics as PersonaTopic[] | undefined) ?? [];
|
|
299
354
|
|
|
300
355
|
const updatedPartial: PartialPersona = {
|
|
301
356
|
...existingPartial,
|
|
302
357
|
short_description: result?.short_description ?? existingPartial.short_description,
|
|
303
358
|
long_description: existingPartial.long_description ?? result?.long_description,
|
|
304
|
-
traits:
|
|
305
|
-
topics
|
|
359
|
+
traits: mergedTraits.length > 0 ? mergedTraits : existingPartial.traits,
|
|
360
|
+
topics,
|
|
306
361
|
};
|
|
307
362
|
|
|
308
363
|
orchestratePersonaGeneration(updatedPartial, state);
|
package/src/core/llm-client.ts
CHANGED
|
@@ -259,7 +259,18 @@ export function parseJSONResponse(content: string): unknown {
|
|
|
259
259
|
|
|
260
260
|
export function cleanResponseContent(content: string): string {
|
|
261
261
|
return content
|
|
262
|
-
|
|
263
|
-
.replace(
|
|
262
|
+
// Complete paired blocks (space-tolerant, case-insensitive)
|
|
263
|
+
.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, "")
|
|
264
|
+
.replace(/<\s*thinking\s*>[\s\S]*?<\s*\/\s*thinking\s*>/gi, "")
|
|
265
|
+
// Seed-OSS (ByteDance) namespaced thinking tags — always paired
|
|
266
|
+
.replace(/<seed:think>[\s\S]*?<\/seed:think>/gi, "")
|
|
267
|
+
// Seed-OSS budget reflection tokens (may appear outside stripped think block)
|
|
268
|
+
.replace(/<seed:cot_budget_reflect>[\s\S]*?<\/seed:cot_budget_reflect>/gi, "")
|
|
269
|
+
// Orphaned closing tag with content before it (MiniMax / streaming accumulation)
|
|
270
|
+
.replace(/^[\s\S]*?<\s*\/\s*think(?:ing)?\s*>/i, "")
|
|
271
|
+
// Remaining orphaned closing tags
|
|
272
|
+
.replace(/<\s*\/\s*think(?:ing)?\s*>/gi, "")
|
|
273
|
+
// Remaining orphaned opening tags
|
|
274
|
+
.replace(/<\s*think(?:ing)?\s*>/gi, "")
|
|
264
275
|
.trim();
|
|
265
276
|
}
|
package/src/core/processor.ts
CHANGED
|
@@ -87,6 +87,7 @@ function stripHumanEmbeddings(human: HumanEntity): HumanEntity {
|
|
|
87
87
|
const DEFAULT_LOOP_INTERVAL_MS = 100;
|
|
88
88
|
const DEFAULT_CONTEXT_WINDOW_HOURS = 8;
|
|
89
89
|
const DEFAULT_OPENCODE_POLLING_MS = 1800000;
|
|
90
|
+
const DEFAULT_CLAUDE_CODE_POLLING_MS = 1800000;
|
|
90
91
|
|
|
91
92
|
let processorInstanceCount = 0;
|
|
92
93
|
|
|
@@ -127,6 +128,8 @@ export class Processor {
|
|
|
127
128
|
private lastOpenCodeSync = 0;
|
|
128
129
|
private lastDLQTrim = 0;
|
|
129
130
|
private openCodeImportInProgress = false;
|
|
131
|
+
private lastClaudeCodeSync = 0;
|
|
132
|
+
private claudeCodeImportInProgress = false;
|
|
130
133
|
private pendingConflict: StateConflictData | null = null;
|
|
131
134
|
|
|
132
135
|
constructor(ei: Ei_Interface) {
|
|
@@ -269,6 +272,10 @@ export class Processor {
|
|
|
269
272
|
console.log(`[Processor ${this.instanceId}] Aborting OpenCode import in progress`);
|
|
270
273
|
this.openCodeImportInProgress = false;
|
|
271
274
|
}
|
|
275
|
+
if (this.claudeCodeImportInProgress) {
|
|
276
|
+
console.log(`[Processor ${this.instanceId}] Aborting Claude Code import in progress`);
|
|
277
|
+
this.claudeCodeImportInProgress = false;
|
|
278
|
+
}
|
|
272
279
|
|
|
273
280
|
await this.stateManager.flush();
|
|
274
281
|
|
|
@@ -383,6 +390,10 @@ export class Processor {
|
|
|
383
390
|
if (this.isTUI && human.settings?.opencode?.integration && this.stateManager.queue_length() === 0) {
|
|
384
391
|
await this.checkAndSyncOpenCode(human, now);
|
|
385
392
|
}
|
|
393
|
+
|
|
394
|
+
if (this.isTUI && human.settings?.claudeCode?.integration && this.stateManager.queue_length() === 0) {
|
|
395
|
+
await this.checkAndSyncClaudeCode(human, now);
|
|
396
|
+
}
|
|
386
397
|
|
|
387
398
|
if (human.settings?.ceremony && shouldStartCeremony(human.settings.ceremony, this.stateManager)) {
|
|
388
399
|
// Auto-backup to remote before ceremony (if configured)
|
|
@@ -479,6 +490,60 @@ export class Processor {
|
|
|
479
490
|
});
|
|
480
491
|
}
|
|
481
492
|
|
|
493
|
+
private async checkAndSyncClaudeCode(human: HumanEntity, now: number): Promise<void> {
|
|
494
|
+
if (this.claudeCodeImportInProgress) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const claudeCode = human.settings?.claudeCode;
|
|
499
|
+
const pollingInterval = claudeCode?.polling_interval_ms ?? DEFAULT_CLAUDE_CODE_POLLING_MS;
|
|
500
|
+
const lastSync = claudeCode?.last_sync
|
|
501
|
+
? new Date(claudeCode.last_sync).getTime()
|
|
502
|
+
: 0;
|
|
503
|
+
const timeSinceSync = now - lastSync;
|
|
504
|
+
|
|
505
|
+
if (timeSinceSync < pollingInterval && this.lastClaudeCodeSync > 0) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this.lastClaudeCodeSync = now;
|
|
510
|
+
const syncTimestamp = new Date().toISOString();
|
|
511
|
+
this.stateManager.setHuman({
|
|
512
|
+
...this.stateManager.getHuman(),
|
|
513
|
+
settings: {
|
|
514
|
+
...this.stateManager.getHuman().settings,
|
|
515
|
+
claudeCode: {
|
|
516
|
+
...claudeCode,
|
|
517
|
+
last_sync: syncTimestamp,
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
this.claudeCodeImportInProgress = true;
|
|
523
|
+
import("../integrations/claude-code/importer.js")
|
|
524
|
+
.then(({ importClaudeCodeSessions }) =>
|
|
525
|
+
importClaudeCodeSessions({
|
|
526
|
+
stateManager: this.stateManager,
|
|
527
|
+
interface: this.interface,
|
|
528
|
+
})
|
|
529
|
+
)
|
|
530
|
+
.then((result) => {
|
|
531
|
+
if (result.sessionsProcessed > 0) {
|
|
532
|
+
console.log(
|
|
533
|
+
`[Processor] Claude Code sync complete: ${result.sessionsProcessed} sessions, ` +
|
|
534
|
+
`${result.topicsCreated} topics created, ${result.messagesImported} messages imported, ` +
|
|
535
|
+
`${result.extractionScansQueued} extraction scans queued`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
.catch((err) => {
|
|
540
|
+
console.warn(`[Processor] Claude Code sync failed:`, err);
|
|
541
|
+
})
|
|
542
|
+
.finally(() => {
|
|
543
|
+
this.claudeCodeImportInProgress = false;
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
482
547
|
private getModelForPersona(personaId?: string): string | undefined {
|
|
483
548
|
const human = this.stateManager.getHuman();
|
|
484
549
|
if (personaId) {
|
|
@@ -488,6 +553,11 @@ export class Processor {
|
|
|
488
553
|
return human.settings?.default_model;
|
|
489
554
|
}
|
|
490
555
|
|
|
556
|
+
private getOneshotModel(): string | undefined {
|
|
557
|
+
const human = this.stateManager.getHuman();
|
|
558
|
+
return human.settings?.oneshot_model || human.settings?.default_model;
|
|
559
|
+
}
|
|
560
|
+
|
|
491
561
|
private fetchMessagesForLLM(personaId: string): import("./types.js").ChatMessage[] {
|
|
492
562
|
const persona = this.stateManager.persona_getById(personaId);
|
|
493
563
|
if (!persona) return [];
|
|
@@ -695,6 +765,10 @@ export class Processor {
|
|
|
695
765
|
message += ` (attempt ${response.request.attempts}, retrying in ${Math.round(result.retryDelay / 1000)}s)`;
|
|
696
766
|
} else if (result.dropped) {
|
|
697
767
|
message += " (permanent failure \u2014 request removed)";
|
|
768
|
+
if (response.request.next_step === LLMNextStep.HandleOneShot) {
|
|
769
|
+
const guid = response.request.data.guid as string;
|
|
770
|
+
this.interface.onOneShotReturned?.(guid, "");
|
|
771
|
+
}
|
|
698
772
|
}
|
|
699
773
|
|
|
700
774
|
this.interface.onError?.({ code, message });
|
|
@@ -922,9 +996,9 @@ export class Processor {
|
|
|
922
996
|
const responsesToClear = [
|
|
923
997
|
LLMNextStep.HandlePersonaResponse,
|
|
924
998
|
LLMNextStep.HandlePersonaTraitExtraction,
|
|
925
|
-
LLMNextStep.
|
|
926
|
-
LLMNextStep.
|
|
927
|
-
|
|
999
|
+
LLMNextStep.HandleHeartbeatCheck, // clear stale heartbeat when user is active
|
|
1000
|
+
LLMNextStep.HandleEiHeartbeat, // clear stale Ei heartbeat when user is active
|
|
1001
|
+
// Note: TopicScan/Match/Update are ceremony-only — never clear them here
|
|
928
1002
|
];
|
|
929
1003
|
|
|
930
1004
|
let removedAny = false;
|
|
@@ -1535,7 +1609,7 @@ export class Processor {
|
|
|
1535
1609
|
system: systemPrompt,
|
|
1536
1610
|
user: userPrompt,
|
|
1537
1611
|
next_step: LLMNextStep.HandleOneShot,
|
|
1538
|
-
model: this.
|
|
1612
|
+
model: this.getOneshotModel(),
|
|
1539
1613
|
data: { guid },
|
|
1540
1614
|
});
|
|
1541
1615
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LLMRequest, LLMResponse, LLMRequestType, ProviderAccount, ChatMessage, Message } from "./types.js";
|
|
2
|
-
import { callLLMRaw, parseJSONResponse } from "./llm-client.js";
|
|
2
|
+
import { callLLMRaw, parseJSONResponse, cleanResponseContent } from "./llm-client.js";
|
|
3
3
|
import { hydratePromptPlaceholders } from "../prompts/message-utils.js";
|
|
4
4
|
|
|
5
5
|
type QueueProcessorState = "idle" | "busy";
|
|
@@ -131,16 +131,17 @@ export class QueueProcessor {
|
|
|
131
131
|
content: string,
|
|
132
132
|
finishReason: string | null
|
|
133
133
|
): LLMResponse {
|
|
134
|
+
const cleanedContent = cleanResponseContent(content);
|
|
134
135
|
switch (request.type) {
|
|
135
136
|
case "json" as LLMRequestType:
|
|
136
137
|
case "response" as LLMRequestType:
|
|
137
|
-
return this.handleJSONResponse(request,
|
|
138
|
+
return this.handleJSONResponse(request, cleanedContent, finishReason);
|
|
138
139
|
case "raw" as LLMRequestType:
|
|
139
140
|
default:
|
|
140
141
|
return {
|
|
141
142
|
request,
|
|
142
143
|
success: true,
|
|
143
|
-
content,
|
|
144
|
+
content: cleanedContent,
|
|
144
145
|
finish_reason: finishReason ?? undefined,
|
|
145
146
|
};
|
|
146
147
|
}
|
package/src/core/types.ts
CHANGED
|
@@ -194,6 +194,7 @@ export interface CeremonyConfig {
|
|
|
194
194
|
|
|
195
195
|
export interface HumanSettings {
|
|
196
196
|
default_model?: string;
|
|
197
|
+
oneshot_model?: string; // Model for AI-assist (wand) requests; falls back to default_model
|
|
197
198
|
queue_paused?: boolean;
|
|
198
199
|
skip_quote_delete_confirm?: boolean;
|
|
199
200
|
name_display?: string;
|
|
@@ -202,6 +203,7 @@ export interface HumanSettings {
|
|
|
202
203
|
sync?: SyncCredentials;
|
|
203
204
|
opencode?: OpenCodeSettings;
|
|
204
205
|
ceremony?: CeremonyConfig;
|
|
206
|
+
claudeCode?: import("../integrations/claude-code/types.js").ClaudeCodeSettings;
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
export interface HumanEntity {
|
|
@@ -248,7 +250,7 @@ export interface PersonaCreationInput {
|
|
|
248
250
|
long_description?: string;
|
|
249
251
|
short_description?: string;
|
|
250
252
|
traits?: Partial<Trait>[];
|
|
251
|
-
topics?: Partial<
|
|
253
|
+
topics?: Partial<PersonaTopic>[];
|
|
252
254
|
model?: string;
|
|
253
255
|
group_primary?: string;
|
|
254
256
|
groups_visible?: string[];
|