Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ model should have `"input": ["text", "image"]`. Other `/vision` subcommands:
| `/vision cache <clear\|show>` | Clear the cache or show stats (memory + disk entries) |
| `/vision fallback <provider/model>\|clear` | Set/clear a fallback vision model |
| `/vision clear` | Reset config to defaults |
| `/vision-use [provider/model]` | Switch the DELEGATE vision model inline (no arg → picker). **Hotkey: `alt+shift+v`** (rebindable via `keybindings.json`) |
| `/vision-use [provider/model]` | Switch the DELEGATE vision model inline (no arg → picker). **Hotkey: `ctrl+shift+i`** (rebindable via `keybindings.json`; on Mac, `alt`-based combos need `macos-option-as-alt=true`) |

Config is stored at `~/.pi/agent/vision.json` (not `vision-tool.json`, so it
doesn't collide with the community package during transition).
Expand All @@ -102,7 +102,7 @@ DELEGATE mode (text-only primary) is resilient + cheap:
Configure both via the `/vision` panel or `/vision fallback <provider/model>`.
- **Custom system prompt.** A per-workflow framing prepended to the
vision-model request (`/vision system-prompt <text>`, or the panel row).
- **Inline model switch.** `alt+shift+v` (or `/vision-use`) switches the
- **Inline model switch.** `ctrl+shift+i` (or `/vision-use`) switches the
DELEGATE vision model mid-session without opening the full panel.

## How it works
Expand Down
13 changes: 8 additions & 5 deletions extensions/vision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Container,
type Component,
Input,
Key,
SettingsList,
type SettingItem,
SelectItem,
Expand Down Expand Up @@ -566,7 +567,7 @@ export default function visionExtension(pi: ExtensionAPI): void {
ctx.ui.notify("Vision cache cleared (memory + disk).", "info");
} else if (action === "show") {
const s = cache.stats();
ctx.ui.notify(`Vision cache: ${s.memoryEntries} memory, ${s.diskEntries} disk (max ${s.maxEntries}, persisted ${s.persisted}).`, "info");
ctx.ui.notify(`Vision cache: ${s.memoryEntries} memory, ${s.diskEntries} disk (max ${s.maxEntries}, persisted ${s.persisted}). Memory is session-scoped; enable \"Persist cache to disk\" for cross-session hits.`, "info");
} else {
ctx.ui.notify("Usage: /vision cache <clear|show>", "warning");
}
Expand Down Expand Up @@ -604,13 +605,15 @@ export default function visionExtension(pi: ExtensionAPI): void {
},
});

// ── /vision-use command + alt+shift+v hotkey (SPEC-2 gap #5: inline switch) ─
// ── /vision-use command + ctrl+shift+i hotkey (SPEC-2 gap #5: inline switch) ─
// Both switch the DELEGATE vision model mid-session without the full panel.
// Tool visibility is unaffected (it tracks the PRIMARY model's capability,
// not the vision model) so no resync is needed.
// not the vision model) so no resync is needed. The hotkey uses ctrl+shift+i
// (not alt+) so it works on Mac terminals where Option≠Alt by default
// (e.g. Ghostty macos-option-as-alt=false). Rebindable via keybindings.json.
pi.registerCommand("vision-use", {
description:
"Switch the DELEGATE vision model inline. No arg → picker; <provider/model> → set directly. (Hotkey: alt+shift+v)",
"Switch the DELEGATE vision model inline. No arg → picker; <provider/model> → set directly. (Hotkey: ctrl+shift+i)",
handler: async (args, ctx) => {
const value = args.trim();
if (!value) {
Expand All @@ -629,7 +632,7 @@ export default function visionExtension(pi: ExtensionAPI): void {
},
});

pi.registerShortcut("alt+shift+v", {
pi.registerShortcut(Key.ctrlShift("i"), {
description: "Switch vision model (inline picker)",
handler: async (ctx) => {
const picked = await pickVisionModel(ctx);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getpipher/vision",
"version": "0.2.0",
"version": "0.2.1",
"description": "Capability-aware vision + paste extension for the pi coding agent. Delegates image analysis to a vision model only when the active primary model is text-only; passes images through natively for multimodal models (zero delegation).",
"keywords": [
"pi-package",
Expand Down Expand Up @@ -53,4 +53,4 @@
"tsx": "^4.19.0",
"typescript": "^5.6.0"
}
}
}
6 changes: 3 additions & 3 deletions tests/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,13 +656,13 @@ test("T20: primary 4xx (no retry) → fallback fires via tool execute + details.
}
});

test("T24: alt+shift+v shortcut registered + invokes pickVisionModel → config updated", async () => {
test("T24: ctrl+shift+i shortcut registered + invokes pickVisionModel → config updated", async () => {
const pi = createMockPi();
visionFactory(pi as unknown as ExtensionAPI);
pasteFactory(pi as unknown as ExtensionAPI);
await pi.emit("session_start", { type: "session_start", reason: "startup" }, makeCtx({ model: TEXT_ONLY }));
const shortcut = pi.shortcuts.get("alt+shift+v");
assert.ok(shortcut, "alt+shift+v shortcut registered");
const shortcut = pi.shortcuts.get("ctrl+shift+i");
assert.ok(shortcut, "ctrl+shift+i shortcut registered");
let notified = "";
const sc = makeCtx({
model: TEXT_ONLY,
Expand Down
Loading