From 093ca69c302497a25c02ca1449bb09cc0a6196e9 Mon Sep 17 00:00:00 2001 From: btema2 Date: Sun, 14 Jun 2026 16:53:13 +0200 Subject: [PATCH 1/7] feat: skill picker recently-used section with drill-down and fuzzy description search Add recently-used skills tracking across TUI, CLI, and web interfaces. Skills are recorded on selection and persisted to disk with 30-day TTL. - New recent-skills.ts utility with in-memory cache and fire-and-forget persistence - TUI DialogSkill: 'Recently Used' category at top of skill list - CLI RunSkillSelectBody: recent skills pinned at top, right arrow drill-down to show skill description in-place, left arrow to return - Web SlashPopover: 'Recently Used' section above full skill list - Fuzzy search now includes description field across TUI and web Right arrow dispatch is confirmed safe: useKeyboard global listeners fire before the element's own keypress handler via emitWithPriority. --- packages/app/src/components/prompt-input.tsx | 2 +- .../components/prompt-input/slash-popover.tsx | 52 +++++++- packages/core/src/util/recent-skills.ts | 111 ++++++++++++++++++ .../src/cli/cmd/run/footer.command.tsx | 95 ++++++++++++--- .../opencode/src/cli/cmd/run/footer.view.tsx | 2 + packages/tui/src/component/dialog-skill.tsx | 21 +++- packages/tui/src/ui/dialog-select.tsx | 4 +- 7 files changed, 264 insertions(+), 23 deletions(-) create mode 100644 packages/core/src/util/recent-skills.ts diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index cbec31d522f5..480e87db0bc0 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -767,7 +767,7 @@ export const PromptInput: Component = (props) => { } = useFilteredList({ items: slashCommands, key: (x) => x?.id, - filterKeys: ["trigger", "title"], + filterKeys: ["trigger", "title", "description"], onSelect: handleSlashSelect, }) diff --git a/packages/app/src/components/prompt-input/slash-popover.tsx b/packages/app/src/components/prompt-input/slash-popover.tsx index d8c4bd035c75..e2bb99b272e7 100644 --- a/packages/app/src/components/prompt-input/slash-popover.tsx +++ b/packages/app/src/components/prompt-input/slash-popover.tsx @@ -1,7 +1,8 @@ -import { Component, For, Match, Show, Switch } from "solid-js" +import { Component, For, Match, Show, Switch, createMemo } from "solid-js" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" import { getDirectory, getFilename } from "@opencode-ai/core/util/path" +import { getTopRecentSkills, recordSkillUsage } from "@opencode-ai/core/util/recent-skills" export type AtOption = | { type: "agent"; name: string; display: string } @@ -34,6 +35,8 @@ type PromptPopoverProps = { } export const PromptPopover: Component = (props) => { + const recentNames = createMemo(() => getTopRecentSkills(5)) + return (
= (props) => { when={props.slashFlat.length > 0} fallback={
{props.t("prompt.popover.emptyCommands")}
} > + 0}> +
Recently Used
+ + {(name) => { + const cmd = props.slashFlat.find((c) => c.id === name) + if (!cmd) return null + return ( + + ) + }} + +
+ {(cmd) => (