diff --git a/README.md b/README.md index 71fcc1e..c4b3348 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ model should have `"input": ["text", "image"]`. Other `/vision` subcommands: | `/vision cache ` | Clear the cache or show stats (memory + disk entries) | | `/vision fallback \|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). @@ -102,7 +102,7 @@ DELEGATE mode (text-only primary) is resilient + cheap: Configure both via the `/vision` panel or `/vision fallback `. - **Custom system prompt.** A per-workflow framing prepended to the vision-model request (`/vision system-prompt `, 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 diff --git a/extensions/vision.ts b/extensions/vision.ts index 008cc62..edc833c 100644 --- a/extensions/vision.ts +++ b/extensions/vision.ts @@ -28,6 +28,7 @@ import { Container, type Component, Input, + Key, SettingsList, type SettingItem, SelectItem, @@ -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 ", "warning"); } @@ -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; → set directly. (Hotkey: alt+shift+v)", + "Switch the DELEGATE vision model inline. No arg → picker; → set directly. (Hotkey: ctrl+shift+i)", handler: async (args, ctx) => { const value = args.trim(); if (!value) { @@ -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); diff --git a/package.json b/package.json index 9cd9f99..4bc6ee0 100644 --- a/package.json +++ b/package.json @@ -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", @@ -53,4 +53,4 @@ "tsx": "^4.19.0", "typescript": "^5.6.0" } -} \ No newline at end of file +} diff --git a/tests/integration.test.ts b/tests/integration.test.ts index e0eec25..81fe42c 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -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,