Files
oh-my-pi/packages/coding-agent/test/prompt-action-autocomplete.test.ts
T
can1357 7fb18faf4c fix(coding-agent): backported pi-mono changes (1feccfed..b21b42d0)
packages/ai:
- feat: expose provider responseId on AssistantMessage
- feat: lazy-load provider modules for faster startup
- fix: hash foreign Responses API tool call IDs exceeding 64-char limit
- fix: ignore null chunks in openai-completions streams
- fix: keep image tool results inline for Gemini 3+ and Antigravity
- fix: correct Bedrock Claude 4.6 context window to 200k
- fix: support prompt caching for Bedrock application inference profiles
- fix: add OpenRouter reasoning payload format
- fix: ignore placeholder Vertex API keys
- fix: skip AJV validation in restricted runtimes
- fix: Anthropic OAuth client injection and responseId extraction
- fix: Codex incomplete/failed response status handling

packages/agent:
- fix: defer steering until after tool execution completes

packages/tui:
- feat: namespaced keybinding IDs with KeybindingsManager conflict detection
- feat: configurable select list column sizing (#2154 by @markusylisiurunen)
- fix: stream truncateToWidth for large strings
- fix: skip Termux height redraws
- fix: stop evicting unrelated default keybindings
- fix: resolve raw backspace ambiguity on Windows Terminal
- fix: clear stale scrollback on session switch (#2155 by @Perlence)
- fix: remove trailing markdown block spacing (#2152 by @markusylisiurunen)

packages/coding-agent:
- feat: add resizable share sidebar (#2435 by @dmmulroy)
- feat: emit OSC 133 command-executed marker
- feat: reload custom themes from disk watcher
- feat: add --fork session flag
- feat: file mutation queue for serialized writes
- feat: initial message consolidation utility
- fix: keybindings migrated to namespaced IDs
- fix: resolve waitForRetry() race when auto-retry produces tool calls
- fix: handle slash-delimited /model refs
- fix: refresh active model after provider updates
- fix: extended transient error patterns for retry
2026-03-22 18:28:40 +01:00

114 lines
4.0 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { KeybindingsManager, setKeybindings, TUI_KEYBINDINGS } from "@oh-my-pi/pi-tui";
import { KeybindingsManager as AppKeybindingsManager } from "../src/config/keybindings";
import { createPromptActionAutocompleteProvider } from "../src/modes/prompt-action-autocomplete";
describe("prompt action autocomplete", () => {
beforeEach(() => {
setKeybindings(
new KeybindingsManager({
"tui.editor.cursorLineStart": { defaultKeys: ["home", "f6"], description: "Move cursor to line start" },
"tui.editor.cursorLineEnd": { defaultKeys: "f7", description: "Move cursor to line end" },
"tui.editor.undo": { defaultKeys: "f8", description: "Undo" },
}),
);
});
afterEach(() => {
setKeybindings(new KeybindingsManager(TUI_KEYBINDINGS));
});
it("shows prompt actions with configured shortcut hints", async () => {
const provider = createPromptActionAutocompleteProvider({
commands: [],
basePath: "/tmp",
keybindings: AppKeybindingsManager.inMemory({
"app.clipboard.copyLine": "ctrl+shift+l",
"app.clipboard.copyPrompt": ["alt+shift+c", "ctrl+shift+c"],
}),
copyCurrentLine: () => {},
copyPrompt: () => {},
undo: () => {},
moveCursorToMessageEnd: () => {},
moveCursorToMessageStart: () => {},
moveCursorToLineStart: () => {},
moveCursorToLineEnd: () => {},
});
const suggestions = await provider.getSuggestions(["#"], 0, 1);
expect(suggestions).not.toBeNull();
expect(suggestions?.prefix).toBe("#");
expect(suggestions?.items.map(item => item.label)).toEqual([
"Copy current line",
"Copy whole prompt",
"Undo",
"Move cursor to end of message",
"Move cursor to beginning of message",
"Move cursor to beginning of line",
"Move cursor to end of line",
]);
expect(suggestions?.items.find(item => item.label === "Copy current line")?.description).toBe("Ctrl+Shift+L");
expect(suggestions?.items.find(item => item.label === "Copy whole prompt")?.description).toBe(
"Alt+Shift+C/Ctrl+Shift+C",
);
expect(suggestions?.items.find(item => item.label === "Move cursor to beginning of line")?.description).toBe(
"Home/F6",
);
expect(suggestions?.items.find(item => item.label === "Move cursor to end of line")?.description).toBe("F7");
expect(suggestions?.items.find(item => item.label === "Undo")?.description).toBe("F8");
});
it("passes the typed trigger to undo and leaves text removal to the editor", async () => {
let undoCalls = 0;
let undoPrefix = "";
const provider = createPromptActionAutocompleteProvider({
commands: [],
basePath: "/tmp",
keybindings: AppKeybindingsManager.inMemory(),
copyCurrentLine: () => {},
copyPrompt: () => {},
undo: prefix => {
undoCalls += 1;
undoPrefix = prefix;
},
moveCursorToMessageEnd: () => {},
moveCursorToMessageStart: () => {},
moveCursorToLineStart: () => {},
moveCursorToLineEnd: () => {},
});
const suggestions = await provider.getSuggestions(["hello #undo"], 0, 11);
const item = suggestions?.items.find(entry => entry.label === "Undo");
expect(item).toBeDefined();
if (!item || !suggestions) {
throw new Error("expected undo suggestion");
}
const result = provider.applyCompletion(["hello #undo"], 0, 11, item, suggestions.prefix);
expect(result.lines).toEqual(["hello #undo"]);
expect(result.cursorLine).toBe(0);
expect(result.cursorCol).toBe(11);
result.onApplied?.();
expect(undoCalls).toBe(1);
expect(undoPrefix).toBe("#undo");
});
it("falls back to normal typing for literal hashtags with no matching action", async () => {
const provider = createPromptActionAutocompleteProvider({
commands: [],
basePath: "/tmp",
keybindings: AppKeybindingsManager.inMemory(),
copyCurrentLine: () => {},
copyPrompt: () => {},
undo: () => {},
moveCursorToMessageEnd: () => {},
moveCursorToMessageStart: () => {},
moveCursorToLineStart: () => {},
moveCursorToLineEnd: () => {},
});
const suggestions = await provider.getSuggestions(["release #v1"], 0, 11);
expect(suggestions).toBeNull();
});
});