ei-tui 1.6.7 → 1.6.8

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli/install.ts +147 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ei-tui",
3
- "version": "1.6.7",
3
+ "version": "1.6.8",
4
4
  "author": "Flare576",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,15 +34,26 @@ export async function installMcpClients(): Promise<void> {
34
34
  console.log(`ℹ️ OpenCode not detected — skipping OpenCode plugin install.`);
35
35
  }
36
36
 
37
- const hasPi = await Bun.file(join(home, ".pi", "agent", "settings.json")).exists() ||
37
+ const hasPi =
38
+ await Bun.file(join(home, ".pi", "agent", "settings.json")).exists() ||
38
39
  await Bun.file(join(home, ".pi", "agent", "auth.json")).exists();
39
- const hasOmp = await Bun.file(join(home, ".omp", "agent", "settings.json")).exists() ||
40
- await Bun.file(join(home, ".omp", "agent", "auth.json")).exists();
41
40
 
42
- if (hasPi || hasOmp) {
41
+ if (hasPi) {
43
42
  await installPi();
44
43
  } else {
45
- console.log(`ℹ️ Pi/OMP not detected — skipping Pi extension install.`);
44
+ console.log(`ℹ️ Pi not detected — skipping Pi extension install.`);
45
+ }
46
+
47
+ const hasOmp =
48
+ await Bun.file(join(home, ".omp", "agent", "settings.json")).exists() ||
49
+ await Bun.file(join(home, ".omp", "agent", "auth.json")).exists() ||
50
+ await Bun.file(join(home, ".omp", "agent", "config.yml")).exists() ||
51
+ await Bun.file(join(home, ".omp", "agent", "agent.db")).exists();
52
+
53
+ if (hasOmp) {
54
+ await installOmp();
55
+ } else {
56
+ console.log(`ℹ️ OMP not detected — skipping OMP extension install.`);
46
57
  }
47
58
  }
48
59
 
@@ -289,7 +300,12 @@ if (input.session_id && input.hook_source) {
289
300
 
290
301
  const args = raw ? ["-n", "5", ...sessionArgs, raw] : ["--recent", "-n", "5"];
291
302
 
292
- const output = await $\`bunx ei-tui@latest \${args}\`.quiet().text().catch(() => "");
303
+ async function runEi(commandArgs) {
304
+ const direct = await $\`ei \${commandArgs}\`.quiet().text().catch(() => "");
305
+ if (direct.trim()) return direct;
306
+ return await $\`bunx ei-tui@latest \${commandArgs}\`.quiet().text().catch(() => "");
307
+ }
308
+ const output = await runEi(args);
293
309
  if (output.trim()) process.stdout.write(\`\\n\${heading}\\n\${output.trim()}\\n\`);
294
310
  `;
295
311
 
@@ -431,12 +447,18 @@ exit 0
431
447
 
432
448
  async function installPi(): Promise<void> {
433
449
  const home = process.env.HOME || "~";
434
- const dataPath = process.env.EI_DATA_PATH ?? join(home, ".local", "share", "ei");
435
450
 
436
451
  const extensionContent = `import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
437
452
  import { Type } from "typebox";
438
453
  import { $ } from "bun";
439
454
 
455
+ const runEi = async (cmdArgs: string[]): Promise<string> => {
456
+ const direct = await $\`ei \${cmdArgs}\`.quiet().text().catch(() => "");
457
+ if (direct.trim()) return direct;
458
+ return $\`bunx ei-tui@latest \${cmdArgs}\`.quiet().text().catch(() => "");
459
+ };
460
+
461
+
440
462
  export default function eiIntegration(pi: ExtensionAPI) {
441
463
  pi.on("before_agent_start", async (event, ctx) => {
442
464
  const entries = ctx.sessionManager.getEntries();
@@ -457,11 +479,7 @@ export default function eiIntegration(pi: ExtensionAPI) {
457
479
  ? ["-n", "5", "--", prompt]
458
480
  : ["--recent", "-n", "5"];
459
481
 
460
- const output = await $\`bunx ei-tui@latest \${args}\`
461
- .env({ ...process.env, EI_DATA_PATH: "${dataPath}" })
462
- .quiet()
463
- .text()
464
- .catch(() => "");
482
+ const output = await runEi(args).catch(() => "");
465
483
 
466
484
  if (!output.trim()) return undefined;
467
485
 
@@ -502,11 +520,7 @@ export default function eiIntegration(pi: ExtensionAPI) {
502
520
  const args = params.type
503
521
  ? [params.type, "-n", "5", "--", params.query]
504
522
  : ["-n", "5", "--", params.query];
505
- const output = await $\`bunx ei-tui@latest \${args}\`
506
- .env({ ...process.env, EI_DATA_PATH: "${dataPath}" })
507
- .quiet()
508
- .text()
509
- .catch(() => "No results found");
523
+ const output = await runEi(args).catch(() => "");
510
524
  return {
511
525
  content: [{ type: "text" as const, text: output.trim() || "No results found" }],
512
526
  details: {},
@@ -522,11 +536,7 @@ export default function eiIntegration(pi: ExtensionAPI) {
522
536
  id: Type.String({ description: "Entity ID from ei_search results" }),
523
537
  }),
524
538
  async execute(_id, params, _signal, _onUpdate, _ctx) {
525
- const output = await $\`bunx ei-tui@latest --id \${params.id}\`
526
- .env({ ...process.env, EI_DATA_PATH: "${dataPath}" })
527
- .quiet()
528
- .text()
529
- .catch(() => "Not found");
539
+ const output = await runEi(["--id", params.id]).catch(() => "");
530
540
  return {
531
541
  content: [{ type: "text" as const, text: output.trim() || "Not found" }],
532
542
  details: {},
@@ -536,26 +546,121 @@ export default function eiIntegration(pi: ExtensionAPI) {
536
546
  }
537
547
  `;
538
548
 
539
- const piExtDir = join(home, ".pi", "agent", "extensions");
540
- const ompExtDir = join(home, ".omp", "agent", "extensions");
549
+ const extDir = join(home, ".pi", "agent", "extensions");
541
550
  const extFilename = "ei-integration.ts";
542
551
 
543
- const hasPiAgent = await Bun.file(join(home, ".pi", "agent", "auth.json")).exists() ||
544
- await Bun.file(join(home, ".pi", "agent", "settings.json")).exists();
545
- const hasOmpAgent = await Bun.file(join(home, ".omp", "agent", "auth.json")).exists() ||
546
- await Bun.file(join(home, ".omp", "agent", "settings.json")).exists();
552
+ await Bun.$`mkdir -p ${extDir}`;
553
+ await Bun.write(join(extDir, extFilename), extensionContent);
554
+ console.log(`✓ Installed Ei extension to ~/.pi/agent/extensions/${extFilename}`);
555
+ }
556
+
557
+ async function installOmp(): Promise<void> {
558
+ const home = process.env.HOME || "~";
547
559
 
548
- if (hasPiAgent) {
549
- await Bun.$`mkdir -p ${piExtDir}`;
550
- await Bun.write(join(piExtDir, extFilename), extensionContent);
551
- console.log(`✓ Installed Ei extension to ~/.pi/agent/extensions/${extFilename}`);
552
- }
560
+ const extensionContent = `import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
561
+ import { Type } from "typebox";
562
+ import { $ } from "bun";
553
563
 
554
- if (hasOmpAgent) {
555
- await Bun.$`mkdir -p ${ompExtDir}`;
556
- await Bun.write(join(ompExtDir, extFilename), extensionContent);
557
- console.log(`✓ Installed Ei extension to ~/.omp/agent/extensions/${extFilename}`);
558
- }
564
+ const runEi = async (cmdArgs: string[]): Promise<string> => {
565
+ const direct = await $\`ei \${cmdArgs}\`.quiet().text().catch(() => "");
566
+ if (direct.trim()) return direct;
567
+ return $\`bunx ei-tui@latest \${cmdArgs}\`.quiet().text().catch(() => "");
568
+ };
569
+
570
+
571
+ export default function eiIntegration(pi: ExtensionAPI) {
572
+ pi.on("before_agent_start", async (event, ctx) => {
573
+ const entries = ctx.sessionManager.getEntries();
574
+ const recentMsgs = entries
575
+ .filter((e: any) => e.type === "message" && (e.message?.role === "user" || e.message?.role === "assistant"))
576
+ .slice(-5)
577
+ .map((e: any) => {
578
+ const role = e.message?.role ?? "unknown";
579
+ const text = Array.isArray(e.message?.content)
580
+ ? e.message.content.filter((b: any) => b.type === "text").map((b: any) => b.text).join(" ")
581
+ : (e.message?.content ?? "");
582
+ return \`\${role}: \${text.slice(0, 200)}\`;
583
+ })
584
+ .join("\\n");
585
+
586
+ const prompt = event.prompt ?? "";
587
+ const args = prompt
588
+ ? ["-n", "5", "--", prompt]
589
+ : ["--recent", "-n", "5"];
590
+
591
+ const output = await runEi(args).catch(() => "");
592
+
593
+ if (!output.trim()) return undefined;
594
+
595
+ const heading = [
596
+ "## Ei Memory Context",
597
+ "*(The user cannot see this block. It is injected automatically before their message.)*",
598
+ "*(If you reference anything from it, briefly explain where it came from.)*",
599
+ "",
600
+ "Ei is a personal knowledge base built from your coding sessions, Slack, documents, and conversations.",
601
+ "The following items MAY be relevant to your current task — use ei_search or ei_lookup for targeted queries.",
602
+ ].join("\\n");
603
+
604
+ return {
605
+ message: {
606
+ customType: "ei-context",
607
+ content: \`\${heading}\\n\\n\${output.trim()}\`,
608
+ display: false,
609
+ },
610
+ };
611
+ });
612
+
613
+ pi.registerTool({
614
+ name: "ei_search",
615
+ label: "Search Ei Memory",
616
+ description: "Semantic search of Ei's personal knowledge base — facts, topics, people, quotes across all sources. Use when you need context about the user, their work, or anything Ei has learned.",
617
+ promptSnippet: "Search Ei's personal memory for relevant facts, topics, people, or quotes.",
618
+ parameters: Type.Object({
619
+ query: Type.String({ description: "Natural language search query" }),
620
+ type: Type.Optional(Type.Union([
621
+ Type.Literal("facts"),
622
+ Type.Literal("topics"),
623
+ Type.Literal("people"),
624
+ Type.Literal("quotes"),
625
+ Type.Literal("personas"),
626
+ ], { description: "Filter to a specific data type. Omit for balanced results across all types." })),
627
+ }),
628
+ async execute(_id, params, _signal, _onUpdate, _ctx) {
629
+ const args = params.type
630
+ ? [params.type, "-n", "5", "--", params.query]
631
+ : ["-n", "5", "--", params.query];
632
+ const output = await runEi(args).catch(() => "");
633
+ return {
634
+ content: [{ type: "text" as const, text: output.trim() || "No results found" }],
635
+ details: {},
636
+ };
637
+ },
638
+ });
639
+
640
+ pi.registerTool({
641
+ name: "ei_lookup",
642
+ label: "Lookup Ei Entity",
643
+ description: "Full-record lookup for a specific Ei entity (Fact, Topic, Person, Quote, or Persona) by ID. Use after ei_search to retrieve complete details for an item.",
644
+ parameters: Type.Object({
645
+ id: Type.String({ description: "Entity ID from ei_search results" }),
646
+ }),
647
+ async execute(_id, params, _signal, _onUpdate, _ctx) {
648
+ const output = await runEi(["--id", params.id]).catch(() => "");
649
+ return {
650
+ content: [{ type: "text" as const, text: output.trim() || "Not found" }],
651
+ details: {},
652
+ };
653
+ },
654
+ });
655
+ }
656
+ `;
657
+
658
+ const extDir = join(home, ".omp", "agent", "extensions");
659
+ const extFilename = "ei-integration.ts";
660
+
661
+ await Bun.$`mkdir -p ${extDir}`;
662
+ await Bun.write(join(extDir, extFilename), extensionContent);
663
+ console.log(`✓ Installed Ei extension to ~/.omp/agent/extensions/${extFilename}`);
559
664
  }
560
665
 
561
666
  async function installOpenCodePlugin(): Promise<void> {
@@ -603,7 +708,8 @@ export function extractAgentName(systemPrompt: string): string | null {
603
708
  // tolerates OMO renaming agents without requiring a hardcoded alias map.
604
709
  export async function resolveEiPersona(rawName: string): Promise<PersonaResult | null> {
605
710
  try {
606
- const out = await $\`bunx ei-tui@latest personas -n 5 \${rawName}\`.text()
711
+ const direct = await $\`ei personas -n 5 \${rawName}\`.quiet().text().catch(() => "")
712
+ const out = direct.trim() ? direct : await $\`bunx ei-tui@latest personas -n 5 \${rawName}\`.text()
607
713
  const candidates = JSON.parse(out.trim()) as PersonaResult[]
608
714
  if (!Array.isArray(candidates) || candidates.length === 0) return null
609
715
  const rawLower = rawName.toLowerCase()
@@ -650,7 +756,8 @@ export default async function EiPersonaPlugin() {
650
756
  input: { sessionID?: string; model: { id: string; providerID: string; [key: string]: unknown } },
651
757
  output: { system: string[] },
652
758
  ): Promise<void> => {
653
- const rawName = extractAgentName(output.system[0] ?? "")
759
+ if (!Array.isArray(output.system) || typeof output.system[0] !== "string") return
760
+ const rawName = extractAgentName(output.system[0])
654
761
  if (!rawName) return
655
762
 
656
763
  const cacheKey = \`\${input.sessionID ?? "unknown"}:\${rawName}\`