feat(tui): vim-style commands and branding updates

- Change command prefix from / to : (vim-style, e.g. :connect, :models)
- Update tips with vim commands (:w, :q, :wq, :help)
- Update branding from OpenCode to agent-core in sidebar
- Update footer hints to use : prefix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Artur Do Lago
2026-01-09 22:36:58 +01:00
parent 2d07e5886c
commit 45fc6a4c2e
7 changed files with 66 additions and 70 deletions
@@ -48,7 +48,7 @@ function extractLineRange(input: string) {
export type AutocompleteRef = {
onInput: (value: string) => void
onKeyDown: (e: KeyEvent) => void
visible: false | "@" | "/"
visible: false | "@" | ":"
}
export type AutocompleteOption = {
@@ -180,7 +180,7 @@ export function Autocomplete(props: {
const [files] = createResource(
() => filter(),
async (query) => {
if (!store.visible || store.visible === "/") return []
if (!store.visible || store.visible === ":") return []
const { lineRange, baseQuery } = extractLineRange(query ?? "")
@@ -253,7 +253,7 @@ export function Autocomplete(props: {
)
const mcpResources = createMemo(() => {
if (!store.visible || store.visible === "/") return []
if (!store.visible || store.visible === ":") return []
const options: AutocompleteOption[] = []
const width = props.anchor().width - 4
@@ -314,10 +314,10 @@ export function Autocomplete(props: {
const s = session()
for (const command of sync.data.command) {
results.push({
display: "/" + command.name + (command.mcp ? " (MCP)" : ""),
display: ":" + command.name + (command.mcp ? " (MCP)" : ""),
description: command.description,
onSelect: () => {
const newText = "/" + command.name + " "
const newText = ":" + command.name + " "
const cursor = props.input().logicalCursor
props.input().deleteRange(0, 0, cursor.row, cursor.col)
props.input().insertText(newText)
@@ -555,7 +555,7 @@ export function Autocomplete(props: {
setStore("selected", 0)
}
function show(mode: "@" | "/") {
function show(mode: "@" | ":") {
command.keybinds(false)
setStore({
visible: mode,
@@ -565,7 +565,7 @@ export function Autocomplete(props: {
function hide() {
const text = props.input().plainText
if (store.visible === "/" && !text.endsWith(" ") && text.startsWith("/")) {
if (store.visible === ":" && !text.endsWith(" ") && text.startsWith(":")) {
const cursor = props.input().logicalCursor
props.input().deleteRange(0, 0, cursor.row, cursor.col)
// Sync the prompt store immediately since onContentChange is async
@@ -590,7 +590,7 @@ export function Autocomplete(props: {
// There is a space between the trigger and the cursor
props.input().getTextRange(store.index, props.input().cursorOffset).match(/\s/) ||
// "/<command>" is not the sole content
(store.visible === "/" && value.match(/^\S+\s+\S+\s*$/))
(store.visible === ":" && value.match(/^\S+\s+\S+\s*$/))
) {
hide()
return
@@ -644,8 +644,8 @@ export function Autocomplete(props: {
if (canTrigger) show("@")
}
if (e.name === "/") {
if (props.input().cursorOffset === 0) show("/")
if (e.name === ":") {
if (props.input().cursorOffset === 0) show(":")
}
}
},
@@ -564,7 +564,7 @@ export function Prompt(props: PromptProps) {
})
setStore("mode", "normal")
} else if (
inputText.startsWith("/") &&
inputText.startsWith(":") &&
iife(() => {
const command = inputText.split(" ")[0].slice(1)
console.log(command)
@@ -1,23 +1,23 @@
export const TIPS = [
"Type {highlight}@{/highlight} followed by a filename to fuzzy search and attach files to your prompt.",
"Start a message with {highlight}!{/highlight} to run shell commands directly (e.g., {highlight}!ls -la{/highlight}).",
"Press {highlight}Tab{/highlight} to cycle between Build (full access) and Plan (read-only) agents.",
"Use {highlight}/undo{/highlight} to revert the last message and any file changes made by OpenCode.",
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes.",
"Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai.",
"Press {highlight}Ctrl+H{/highlight} to toggle between HOLD (research) and RELEASE (edit) modes.",
"Use {highlight}:undo{/highlight} to revert the last message and any file changes made by agent-core.",
"Use {highlight}:redo{/highlight} to restore previously undone messages and file changes.",
"Use {highlight}:share{/highlight} to create a public link to your conversation.",
"Drag and drop images into the terminal to add them as context for your prompts.",
"Press {highlight}Ctrl+V{/highlight} to paste images from your clipboard directly into the prompt.",
"Press {highlight}Ctrl+X E{/highlight} or {highlight}/editor{/highlight} to compose messages in your external editor.",
"Run {highlight}/init{/highlight} to auto-generate project rules based on your codebase structure.",
"Run {highlight}/models{/highlight} or {highlight}Ctrl+X M{/highlight} to see and switch between available AI models.",
"Use {highlight}/theme{/highlight} or {highlight}Ctrl+X T{/highlight} to preview and switch between 50+ built-in themes.",
"Press {highlight}Ctrl+X N{/highlight} or {highlight}/new{/highlight} to start a fresh conversation session.",
"Use {highlight}/sessions{/highlight} or {highlight}Ctrl+X L{/highlight} to list and continue previous conversations.",
"Run {highlight}/compact{/highlight} to summarize long sessions when approaching context limits.",
"Press {highlight}Ctrl+X X{/highlight} or {highlight}/export{/highlight} to save the conversation as Markdown.",
"Press {highlight}Ctrl+X E{/highlight} or {highlight}:editor{/highlight} to compose messages in your external editor.",
"Run {highlight}:init{/highlight} to auto-generate project rules based on your codebase structure.",
"Run {highlight}:models{/highlight} or {highlight}Ctrl+X M{/highlight} to see and switch between available AI models.",
"Use {highlight}:theme{/highlight} or {highlight}Ctrl+X T{/highlight} to preview and switch between 50+ built-in themes.",
"Press {highlight}Ctrl+X N{/highlight} or {highlight}:new{/highlight} to start a fresh conversation session.",
"Use {highlight}:sessions{/highlight} or {highlight}Ctrl+X L{/highlight} to list and continue previous conversations.",
"Run {highlight}:compact{/highlight} to summarize long sessions when approaching context limits.",
"Press {highlight}Ctrl+X X{/highlight} or {highlight}:export{/highlight} to save the conversation as Markdown.",
"Press {highlight}Ctrl+X Y{/highlight} to copy the assistant's last message to clipboard.",
"Press {highlight}Ctrl+P{/highlight} to see all available actions and commands.",
"Run {highlight}/connect{/highlight} to add API keys for 75+ supported LLM providers.",
"Run {highlight}:connect{/highlight} to add API keys for 75+ supported LLM providers.",
"The default leader key is {highlight}Ctrl+X{/highlight}; combine with other keys for quick actions.",
"Press {highlight}F2{/highlight} to quickly switch between recently used models.",
"Press {highlight}Ctrl+X B{/highlight} to show/hide the sidebar panel.",
@@ -27,49 +27,42 @@ export const TIPS = [
"Press {highlight}Shift+Enter{/highlight} or {highlight}Ctrl+J{/highlight} to add newlines in your prompt.",
"Press {highlight}Ctrl+C{/highlight} when typing to clear the input field.",
"Press {highlight}Escape{/highlight} to stop the AI mid-response.",
"Switch to {highlight}Plan{/highlight} agent to get suggestions without making actual changes.",
"Use {highlight}HOLD{/highlight} mode to research without making changes, {highlight}RELEASE{/highlight} to edit files.",
"Use {highlight}@<agent-name>{/highlight} in prompts to invoke specialized subagents.",
"Press {highlight}Ctrl+X Right/Left{/highlight} to cycle through parent and child sessions.",
"Create {highlight}opencode.json{/highlight} in project root for project-specific settings.",
"Place settings in {highlight}~/.config/opencode/opencode.json{/highlight} for global config.",
"Create {highlight}agent-core.jsonc{/highlight} in project root for project-specific settings.",
"Place settings in {highlight}~/.config/agent-core/agent-core.jsonc{/highlight} for global config.",
"Add {highlight}$schema{/highlight} to your config for autocomplete in your editor.",
"Configure {highlight}model{/highlight} in config to set your default model.",
"Override any keybind in config via the {highlight}keybinds{/highlight} section.",
"Set any keybind to {highlight}none{/highlight} to disable it completely.",
"Configure local or remote MCP servers in the {highlight}mcp{/highlight} config section.",
"OpenCode auto-handles OAuth for remote MCP servers requiring auth.",
"Add {highlight}.md{/highlight} files to {highlight}.opencode/command/{/highlight} to define reusable custom prompts.",
"Add {highlight}.md{/highlight} files to {highlight}.agent-core/command/{/highlight} to define reusable custom prompts.",
"Use {highlight}$ARGUMENTS{/highlight}, {highlight}$1{/highlight}, {highlight}$2{/highlight} in custom commands for dynamic input.",
"Use backticks in commands to inject shell output (e.g., {highlight}`git status`{/highlight}).",
"Add {highlight}.md{/highlight} files to {highlight}.opencode/agent/{/highlight} for specialized AI personas.",
"Add {highlight}.md{/highlight} files to {highlight}.agent-core/agent/{/highlight} for specialized AI personas.",
"Configure per-agent permissions for {highlight}edit{/highlight}, {highlight}bash{/highlight}, and {highlight}webfetch{/highlight} tools.",
'Use patterns like {highlight}"git *": "allow"{/highlight} for granular bash permissions.',
'Set {highlight}"rm -rf *": "deny"{/highlight} to block destructive commands.',
'Configure {highlight}"git push": "ask"{/highlight} to require approval before pushing.',
"OpenCode auto-formats files using prettier, gofmt, ruff, and more.",
'Set {highlight}"formatter": false{/highlight} in config to disable all auto-formatting.',
"Define custom formatter commands with file extensions in config.",
"OpenCode uses LSP servers for intelligent code analysis.",
"Create {highlight}.ts{/highlight} files in {highlight}.opencode/tool/{/highlight} to define new LLM tools.",
"Create {highlight}.ts{/highlight} files in {highlight}.agent-core/tool/{/highlight} to define new LLM tools.",
"Tool definitions can invoke scripts written in Python, Go, etc.",
"Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugin/{/highlight} for event hooks.",
"Add {highlight}.ts{/highlight} files to {highlight}.agent-core/plugin/{/highlight} for event hooks.",
"Use plugins to send OS notifications when sessions complete.",
"Create a plugin to prevent OpenCode from reading sensitive files.",
"Use {highlight}opencode run{/highlight} for non-interactive scripting.",
"Use {highlight}opencode run --continue{/highlight} to resume the last session.",
"Use {highlight}opencode run -f file.ts{/highlight} to attach files via CLI.",
"Create a plugin to prevent agent-core from reading sensitive files.",
"Use {highlight}agent-core run{/highlight} for non-interactive scripting.",
"Use {highlight}agent-core run --continue{/highlight} to resume the last session.",
"Use {highlight}agent-core run -f file.ts{/highlight} to attach files via CLI.",
"Use {highlight}--format json{/highlight} for machine-readable output in scripts.",
"Run {highlight}opencode serve{/highlight} for headless API access to OpenCode.",
"Use {highlight}opencode run --attach{/highlight} to connect to a running server for faster runs.",
"Run {highlight}opencode upgrade{/highlight} to update to the latest version.",
"Run {highlight}opencode auth list{/highlight} to see all configured providers.",
"Run {highlight}opencode agent create{/highlight} for guided agent creation.",
"Use {highlight}/opencode{/highlight} in GitHub issues/PRs to trigger AI actions.",
"Run {highlight}opencode github install{/highlight} to set up the GitHub workflow.",
"Comment {highlight}/opencode fix this{/highlight} on issues to auto-create PRs.",
"Comment {highlight}/oc{/highlight} on PR code lines for targeted code reviews.",
"Run {highlight}agent-core serve{/highlight} for headless API access.",
"Use {highlight}agent-core run --attach{/highlight} to connect to a running server for faster runs.",
"Run {highlight}agent-core upgrade{/highlight} to update to the latest version.",
"Run {highlight}agent-core auth list{/highlight} to see all configured providers.",
"Run {highlight}agent-core agent create{/highlight} for guided agent creation.",
'Use {highlight}"theme": "system"{/highlight} to match your terminal\'s colors.',
"Create JSON theme files in {highlight}.opencode/themes/{/highlight} directory.",
"Create JSON theme files in {highlight}.agent-core/themes/{/highlight} directory.",
"Themes support dark/light variants for both modes.",
"Reference ANSI colors 0-255 in custom themes.",
"Use {highlight}{env:VAR_NAME}{/highlight} syntax to reference environment variables in config.",
@@ -82,22 +75,23 @@ export const TIPS = [
"Override global tool settings per agent configuration.",
'Set {highlight}"share": "auto"{/highlight} to automatically share all sessions.',
'Set {highlight}"share": "disabled"{/highlight} to prevent any session sharing.',
"Run {highlight}/unshare{/highlight} to remove a session from public access.",
"Run {highlight}:unshare{/highlight} to remove a session from public access.",
"Permission {highlight}doom_loop{/highlight} prevents infinite tool call loops.",
"Permission {highlight}external_directory{/highlight} protects files outside project.",
"Run {highlight}opencode debug config{/highlight} to troubleshoot configuration.",
"Run {highlight}agent-core debug config{/highlight} to troubleshoot configuration.",
"Use {highlight}--print-logs{/highlight} flag to see detailed logs in stderr.",
"Press {highlight}Ctrl+X G{/highlight} or {highlight}/timeline{/highlight} to jump to specific messages.",
"Press {highlight}Ctrl+X G{/highlight} or {highlight}:timeline{/highlight} to jump to specific messages.",
"Press {highlight}Ctrl+X H{/highlight} to toggle code block visibility in messages.",
"Press {highlight}Ctrl+X S{/highlight} or {highlight}/status{/highlight} to see system status info.",
"Press {highlight}Ctrl+X S{/highlight} or {highlight}:status{/highlight} to see system status info.",
"Enable {highlight}tui.scroll_acceleration{/highlight} for smooth macOS-style scrolling.",
"Toggle username display in chat via command palette ({highlight}Ctrl+P{/highlight}).",
"Run {highlight}docker run -it --rm ghcr.io/anomalyco/opencode{/highlight} for containerized use.",
"Use {highlight}/connect{/highlight} with OpenCode Zen for curated, tested models.",
"Commit your project's {highlight}AGENTS.md{/highlight} file to Git for team sharing.",
"Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs.",
"Run {highlight}/help{/highlight} or {highlight}Ctrl+X H{/highlight} to show the help dialog.",
"Use {highlight}/details{/highlight} to toggle tool execution details visibility.",
"Use {highlight}/rename{/highlight} to rename the current session.",
"Use {highlight}:w{/highlight} to commit changes (vim-style save).",
"Use {highlight}:q{/highlight} to quit, {highlight}:wq{/highlight} to commit and quit.",
"Use {highlight}:help{/highlight} to see available vim-style commands.",
"Use {highlight}:review{/highlight} to review uncommitted changes, branches, or PRs.",
"Run {highlight}:help{/highlight} to show all available commands.",
"Use {highlight}:details{/highlight} to toggle tool execution details visibility.",
"Use {highlight}:rename{/highlight} to rename the current session.",
"Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell.",
"Press {highlight}Tab{/highlight}/{highlight}Shift+Tab{/highlight} to switch between Zee, Stanley, and Johny.",
]
@@ -396,7 +396,7 @@ async function getCustomThemes() {
Global.Path.config,
...(await Array.fromAsync(
Filesystem.up({
targets: [".opencode"],
targets: [".agent-core"],
start: process.cwd(),
}),
)),
@@ -125,7 +125,7 @@ export function Home() {
</Switch>
{connectedMcpCount()} MCP
</text>
<text fg={theme.textMuted}>/status</text>
<text fg={theme.textMuted}>:status</text>
</Show>
</box>
<box flexGrow={1} />
@@ -5,11 +5,13 @@ import { useDirectory } from "../../context/directory"
import { useConnected } from "../../component/dialog-model"
import { createStore } from "solid-js/store"
import { useRoute } from "../../context/route"
import { useLocal } from "../../context/local"
export function Footer() {
const { theme } = useTheme()
const sync = useSync()
const route = useRoute()
const local = useLocal()
const mcp = createMemo(() => Object.values(sync.data.mcp).filter((x) => x.status === "connected").length)
const mcpError = createMemo(() => Object.values(sync.data.mcp).some((x) => x.status === "failed"))
const lsp = createMemo(() => Object.keys(sync.data.lsp))
@@ -53,10 +55,13 @@ export function Footer() {
<Switch>
<Match when={store.welcome}>
<text fg={theme.text}>
Get started <span style={{ fg: theme.textMuted }}>/connect</span>
Get started <span style={{ fg: theme.textMuted }}>:connect</span>
</text>
</Match>
<Match when={connected()}>
<text fg={local.mode.isHold() ? theme.warning : theme.success}>
{local.mode.isHold() ? "HOLD" : "RELEASE"}
</text>
<Show when={permissions().length > 0}>
<text fg={theme.warning}>
<span style={{ fg: theme.warning }}></span> {permissions().length} Permission
@@ -79,7 +84,7 @@ export function Footer() {
{mcp()} MCP
</text>
</Show>
<text fg={theme.textMuted}>/status</text>
<text fg={theme.textMuted}>:status</text>
</Match>
</Switch>
</box>
@@ -290,13 +290,10 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
</text>
</box>
<text fg={theme.textMuted}>OpenCode includes free models so you can start immediately.</text>
<text fg={theme.textMuted}>
Connect from 75+ providers to use other models, including Claude, GPT, Gemini etc
</text>
<text fg={theme.textMuted}>Connect a provider to use Claude, GPT, Gemini, and 75+ other models.</text>
<box flexDirection="row" gap={1} justifyContent="space-between">
<text fg={theme.text}>Connect provider</text>
<text fg={theme.textMuted}>/connect</text>
<text fg={theme.textMuted}>:connect</text>
</box>
</box>
</box>
@@ -306,9 +303,9 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
<span style={{ fg: theme.text }}>{directory().split("/").at(-1)}</span>
</text>
<text fg={theme.textMuted}>
<span style={{ fg: theme.success }}></span> <b>Open</b>
<span style={{ fg: theme.success }}></span> <b>agent</b>
<span style={{ fg: theme.text }}>
<b>Code</b>
<b>-core</b>
</span>{" "}
<span>{Installation.VERSION}</span>
</text>