pi-better-openai 0.1.4 → 0.1.6

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
@@ -2,40 +2,18 @@
2
2
 
3
3
  A pi extension for OpenAI subscription workflows: fast mode, usage visibility, footer polish, and image generation through `openai-codex` auth.
4
4
 
5
- ## Screenshots
6
-
7
- <!-- Add screenshots here. -->
8
-
9
5
  ## Install
10
6
 
11
- Install from a local checkout:
7
+ Install from GitHub:
12
8
 
13
9
  ```bash
14
- pi install /path/to/pi-better-openai
10
+ pi install github:mattleong/pi-better-openai
15
11
  ```
16
12
 
17
- For a project-local install instead of a global install, add `-l`:
13
+ Or install from npm:
18
14
 
19
15
  ```bash
20
- pi install -l /path/to/pi-better-openai
21
- ```
22
-
23
- Reload pi after installing:
24
-
25
- ```text
26
- /reload
27
- ```
28
-
29
- Sign in to OpenAI Codex subscription auth if you want usage stats or image generation:
30
-
31
- ```text
32
- /login openai-codex
33
- ```
34
-
35
- Start pi with fast mode enabled:
36
-
37
- ```bash
38
- pi --fast
16
+ pi install npm:pi-better-openai
39
17
  ```
40
18
 
41
19
  ## Features
@@ -50,3 +28,10 @@ pi --fast
50
28
  - `/openai-image <prompt>` generates an image directly.
51
29
  - `/openai-usage` shows current OpenAI subscription usage.
52
30
  - `/openai-settings` opens settings, diagnostics, and config details.
31
+
32
+ ## Screenshots
33
+
34
+ <!-- Add screenshots here. -->
35
+
36
+ <img width="983" height="851" alt="Screenshot 2026-04-29 at 11 53 23 PM" src="https://github.com/user-attachments/assets/07a2fb87-ef48-4396-8b12-124825c8d360" />
37
+ <img width="1327" height="102" alt="Screenshot 2026-04-29 at 11 34 49 PM" src="https://github.com/user-attachments/assets/22042782-c94e-491d-b5af-095f7f0810f9" />
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { extname, isAbsolute, join, resolve } from "node:path";
4
+ import { extname, isAbsolute, join, resolve, sep } from "node:path";
5
5
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
6
6
  import type { ResolvedConfig } from "./config.ts";
7
7
  import { isRecord } from "./config.ts";
@@ -23,7 +23,7 @@ export type ImageOutputFormat = typeof IMAGE_OUTPUT_FORMATS[number];
23
23
  const TOOL_PARAMS = {
24
24
  type: "object",
25
25
  properties: {
26
- prompt: { type: "string", description: "Image generation/editing prompt." },
26
+ prompt: { type: "string", description: "Image generation/editing prompt. Pass the user's wording verbatim unless they explicitly ask you to refine or expand it." },
27
27
  action: { type: "string", enum: IMAGE_ACTIONS, description: "Whether to generate a new image, edit/reference provided images, or let the model decide." },
28
28
  images: {
29
29
  type: "array",
@@ -338,6 +338,39 @@ async function requestCodexImage(params: ToolParams, ctx: ExtensionContext, cfg:
338
338
  return { ...parsed, prompt: params.prompt, savedPath, model, action, outputFormat };
339
339
  }
340
340
 
341
+ function displayPath(path: string): string {
342
+ const home = homedir();
343
+ if (!home) return path;
344
+ if (path === home) return "~";
345
+ const homePrefix = home.endsWith(sep) ? home : `${home}${sep}`;
346
+ return path.startsWith(homePrefix) ? `~/${path.slice(homePrefix.length)}` : path;
347
+ }
348
+
349
+ function textFromMessageContent(content: unknown): string | undefined {
350
+ if (typeof content === "string") return content.trim() || undefined;
351
+ if (!Array.isArray(content)) return undefined;
352
+ const text = content
353
+ .filter((part) => isRecord(part) && part.type === "text" && typeof part.text === "string")
354
+ .map((part) => (part as { text: string }).text)
355
+ .join("\n")
356
+ .trim();
357
+ return text || undefined;
358
+ }
359
+
360
+ function latestUserPromptFromEntries(entries: unknown[]): string | undefined {
361
+ for (let i = entries.length - 1; i >= 0; i--) {
362
+ const entry = entries[i];
363
+ if (!isRecord(entry) || entry.type !== "message" || !isRecord(entry.message) || entry.message.role !== "user") continue;
364
+ const text = textFromMessageContent(entry.message.content);
365
+ if (text) return text;
366
+ }
367
+ return undefined;
368
+ }
369
+
370
+ function resolveToolPrompt(params: ToolParams, ctx: ExtensionContext): string {
371
+ return latestUserPromptFromEntries(ctx.sessionManager.getEntries()) ?? params.prompt;
372
+ }
373
+
341
374
  function resultText(result: CodexImageResult): string {
342
375
  const parts = [
343
376
  `Generated image via openai-codex/${result.model}.`,
@@ -345,7 +378,7 @@ function resultText(result: CodexImageResult): string {
345
378
  `Prompt: ${result.prompt}`
346
379
  ];
347
380
  if (result.revisedPrompt) parts.push(`Revised prompt: ${result.revisedPrompt}`);
348
- if (result.savedPath) parts.push(`Saved: ${result.savedPath}`);
381
+ if (result.savedPath) parts.push(`Saved: ${displayPath(result.savedPath)}`);
349
382
  return parts.join("\n");
350
383
  }
351
384
 
@@ -388,6 +421,35 @@ export function registerOpenAIImage(pi: ExtensionAPI, getConfig: (ctx: Extension
388
421
  };
389
422
  }
390
423
 
424
+ void import("@mariozechner/pi-tui").then(({ Box, Container, Image, Text }) => {
425
+ pi.registerMessageRenderer<CodexImageResult>("openai-image", (message, _options, theme) => {
426
+ const result = message.details;
427
+ const text = result && isRecord(result)
428
+ ? resultText(result as CodexImageResult)
429
+ : typeof message.content === "string"
430
+ ? message.content
431
+ : message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
432
+ const image = result && isRecord(result) && typeof result.data === "string" && typeof result.mimeType === "string"
433
+ ? { data: result.data, mimeType: result.mimeType, savedPath: typeof result.savedPath === "string" ? result.savedPath : undefined }
434
+ : Array.isArray(message.content)
435
+ ? message.content.find((part) => part.type === "image" && typeof part.data === "string" && typeof part.mimeType === "string")
436
+ : undefined;
437
+
438
+ const container = new Container();
439
+ const box = new Box(1, 1, (line) => theme.bg("customMessageBg", line));
440
+ box.addChild(new Text(`${theme.fg("accent", theme.bold("[openai-image]"))}\n\n${text}`, 0, 0));
441
+ if (image) {
442
+ box.addChild(new Image(image.data, image.mimeType, { fallbackColor: (line) => theme.fg("dim", line) }, {
443
+ maxWidthCells: 80,
444
+ maxHeightCells: 24,
445
+ filename: "savedPath" in image && typeof image.savedPath === "string" ? image.savedPath : undefined
446
+ }));
447
+ }
448
+ container.addChild(box);
449
+ return container;
450
+ });
451
+ }).catch(() => undefined);
452
+
391
453
  pi.registerCommand(OPENAI_IMAGE_COMMAND, {
392
454
  description: "Generate an image with OpenAI Codex image generation",
393
455
  handler: async (args, ctx) => {
@@ -417,14 +479,16 @@ export function registerOpenAIImage(pi: ExtensionAPI, getConfig: (ctx: Extension
417
479
  promptSnippet: "Generate or edit raster images via OpenAI Codex subscription auth.",
418
480
  promptGuidelines: [
419
481
  "Use openai_image when the user asks to generate or edit a raster image, photo, illustration, mockup, texture, sprite, or bitmap asset.",
482
+ "Pass the user's image prompt verbatim. Do not embellish, rewrite, add camera/style details, or add negative prompt terms unless the user explicitly asks you to refine the prompt.",
420
483
  "Use openai_image with images for local reference images or edit targets; save project assets into the workspace when requested."
421
484
  ],
422
485
  parameters: TOOL_PARAMS,
423
486
  async execute(_toolCallId, params: ToolParams, signal, onUpdate, ctx) {
424
487
  const cfg = getConfig(ctx);
425
488
  const model = resolveModel(params, ctx, cfg);
489
+ const requestParams = { ...params, prompt: resolveToolPrompt(params, ctx) };
426
490
  onUpdate?.({ content: [{ type: "text", text: `Requesting OpenAI image via openai-codex/${model}...` }] });
427
- const result = await generate(params, ctx, signal);
491
+ const result = await generate(requestParams, ctx, signal);
428
492
  return {
429
493
  content: [
430
494
  { type: "text", text: resultText(result) },
@@ -447,5 +511,7 @@ export const _imageTest = {
447
511
  imageMimeType,
448
512
  dataUrlParts,
449
513
  extractImageFromEvent,
514
+ displayPath,
515
+ latestUserPromptFromEntries,
450
516
  buildRequest
451
517
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-openai",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Personal pi extension that improves OpenAI with fast mode, usage stats, and footer polish.",
5
5
  "keywords": [
6
6
  "fast",