Documentation
¶
Overview ¶
Package autocomplete provides a fuzzy-matching autocomplete engine for the tmux-webui terminal input bar. It is a 1:1 Go port of the Python autocomplete.py module (Phase 1.7).
Architecture ¶
The package is split into focused files:
- scanner.go — Scanner interface + Item struct
- fuzzy.go — fuzzyScore + rankAndFilter helpers
- path.go — filesystem path completion
- claude.go — optional ~/.claude scanner + minimal YAML frontmatter parser
- cache.go — ResourceCache with mutex + time.Ticker auto-refresh
- complete.go — Complete(query, typeFilter) main entry point (router)
Integration with server.go ¶
Import and wire up in your HTTP server:
import "github.com/operonlab/tmux-webui/internal/autocomplete"
ac := autocomplete.New(autocomplete.Options{
ClaudeDir: cfg.Autocomplete.ClaudeDir,
})
defer ac.Close()
mux.HandleFunc("GET /api/autocomplete", func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q")
t := r.URL.Query().Get("type")
items := ac.Complete(q, t)
if items == nil {
items = []autocomplete.Item{}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(items)
})
mux.HandleFunc("GET /api/autocomplete/refresh", func(w http.ResponseWriter, r *http.Request) {
stats := ac.Refresh()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(stats)
})
mux.HandleFunc("GET /api/autocomplete/stats", func(w http.ResponseWriter, r *http.Request) {
stats := ac.Stats()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(stats)
})
Routing rules ¶
Complete() routes by the first character of query (mirroring Python version):
- "/" prefix → slash items (skills + commands), optional typeFilter "skill"/"command"
- "@" prefix → at items (agents + mcp servers)
- "path" typeFilter or "~"/"." prefix or "/" in query → path completion
- empty query → empty slice
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExpandClaudeDir ¶
func ExpandClaudeDir() string
ExpandClaudeDir returns ~/.claude expanded to an absolute path. Returns empty string if home dir is unavailable.
Types ¶
type BuiltinScanner ¶
type BuiltinScanner struct{}
BuiltinScanner emits the curated list of Claude Code built-in slash commands — the universally available commands a user can type at any session (not loaded from disk, not user-defined). The list is hard-coded because Claude Code does not currently expose a discovery API.
Last verified: claude-code 1.x @ 2026-05-16 against https://code.claude.com/docs/en/commands. When upgrading Claude Code, cross-check that page and bump the date above. Entries marked "[bundled skill]" come from the same docs page (the "Skill" rows in the commands table) — they live inside the CLI rather than in ~/.claude/skills, but they are typed and displayed the same way.
Aliases listed only in a command's description (e.g. /quit for /exit, /bashes for /tasks) are intentionally omitted unless they appear as a standalone row in the docs table — the goal is to mirror the published "/ menu" surface, not every alternative spelling.
func NewBuiltinScanner ¶
func NewBuiltinScanner() *BuiltinScanner
NewBuiltinScanner returns a scanner that always yields the built-in slash command roster.
type ClaudeDirScanner ¶
type ClaudeDirScanner struct {
// contains filtered or unexported fields
}
ClaudeDirScanner scans ~/.claude/{skills,commands,agents} and settings.json for autocomplete items. It implements Scanner.
func NewClaudeDirScanner ¶
func NewClaudeDirScanner(claudeDir string) *ClaudeDirScanner
NewClaudeDirScanner returns a scanner rooted at claudeDir (e.g. "~/.claude"). The path must already be expanded (no "~").
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the main autocomplete engine.
func New ¶
New creates an Engine with the provided options. A background scanner goroutine is started immediately when at least one source is enabled.
func (*Engine) Close ¶
func (e *Engine) Close()
Close stops the background refresh goroutine. Call via defer after New().
func (*Engine) Complete ¶
Complete routes the query and returns up to e.maxResults suggestions (0 → defaultMaxResults, 15).
Routing rules:
- typeFilter == "path" → path completion
- typeFilter == "slash" → slash items (builtin + skill + command)
- typeFilter == "skill" / "command" / "builtin" → slash items, narrowed by type
- typeFilter == "" && query starts with "/" → slash items
- typeFilter == "at" / "agent" / "mcp" / "file" → at items (agents + mcp + files)
- typeFilter == "" && query starts with "@" → at items
- typeFilter == "shortcut" → user-defined shortcuts (bare-word trigger)
- typeFilter == "" && query starts with "~" / "./" → path completion
- typeFilter == "" && query contains "/" → path completion
- empty query → empty slice
func (*Engine) CompleteIn ¶
CompleteIn is Complete with a working-directory context: when cwd is non-empty, the "@" branch additionally offers files under cwd (fzf-style, relative paths) alongside agents/mcp. The web UI passes the focused pane's cwd so "@parti" completes to a path the CLI inside that pane can resolve.
type Item ¶
type Item struct {
Name string `json:"name"`
DisplayName string `json:"display_name,omitempty"`
Description string `json:"description,omitempty"`
Type string `json:"type"` // "skill" | "command" | "agent" | "mcp" | "builtin" | "path" | "file" | "shortcut"
Icon string `json:"icon,omitempty"`
// Source records origin: empty / "user" for ~/.claude scanner,
// "plugin:<marketplace>:<plugin>" for marketplace plugins,
// "builtin" for Claude Code built-in slash commands.
Source string `json:"source,omitempty"`
// Insert, when non-empty, is the exact text a pick places in the input
// buffer instead of Name — shortcuts map a short keyword to a longer
// command/prompt.
Insert string `json:"insert,omitempty"`
}
Item represents a single autocomplete suggestion.
type Options ¶
type Options struct {
// ClaudeDir enables scanning of ~/.claude/{skills,commands,agents}.
// Pass an empty string to disable; path completion is always enabled.
// "~" is NOT expanded here — pass os.UserHomeDir() + "/.claude" explicitly,
// or use ExpandClaudeDir() if you want automatic "~/.claude" expansion.
ClaudeDir string
// RefreshInterval controls how often the background scanner runs.
// Zero defaults to 5 minutes.
RefreshInterval time.Duration
// IncludePlugins enables scanning of
// ~/.claude/plugins/marketplaces/<m>/{external_plugins,plugins}/<p>/{skills,commands,agents}/.
// Requires ClaudeDir to be set; ignored otherwise.
IncludePlugins bool
// IncludeBuiltins enables the curated list of Claude Code built-in slash
// commands (/compact, /model, /clear, etc.).
IncludeBuiltins bool
// MaxResults caps the suggestions returned per query. Zero → defaultMaxResults
// (15), the historical default.
MaxResults int
// SourcePriority orders the slash-item sources for tie-breaking at equal fuzzy
// score. Empty → the canonical builtins/skills/commands order. A source
// omitted from the list keeps its default position after the listed ones
// (reorder, never drop).
SourcePriority []string
// ShortcutsFile, when set, enables user-defined shortcut completion from
// that JSON file (see shortcuts.go for the format). A missing file is
// fine — the source stays empty until the user creates it.
ShortcutsFile string
}
Options configures the autocomplete Engine.
func DefaultOptions ¶
DefaultOptions returns Options with plugin + builtin scanning enabled — the recommended baseline for matching the full Claude Code resource universe. Callers wanting legacy user-only behavior construct Options literally.
type PluginScanner ¶
type PluginScanner struct {
// contains filtered or unexported fields
}
PluginScanner walks plugin marketplaces under <claudeDir>/plugins/.
Two modes:
**Installed-only** (preferred, matches Claude Code behavior). If <claudeDir>/plugins/installed_plugins.json exists, the scanner reads it and only emits items from plugins the user has actually `/plugin install`ed. Each entry's `installPath` is treated as the plugin root (it normally lives under <claudeDir>/plugins/cache/<marketplace>/ <plugin>/<version>/).
**Fallback whole-tree** (legacy). If the manifest is absent or unparseable, the scanner walks every plugin under <claudeDir>/plugins/marketplaces/<marketplace>/{plugins, external_plugins}/<plugin>/. This is what the original scanner did and is kept so OSS users (and tests using bare marketplace fixtures) continue to work without a manifest.
Both "external_plugins/" and "plugins/" subdir layouts coexist in the wild — the fallback scans either.
func NewPluginScanner ¶
func NewPluginScanner(claudeDir string) *PluginScanner
NewPluginScanner returns a scanner rooted at claudeDir (e.g. "~/.claude"). The path must already be expanded (no "~").
type ResourceCache ¶
type ResourceCache struct {
// contains filtered or unexported fields
}
ResourceCache holds a snapshot of scanned Claude resources and refreshes them periodically using a time.Ticker. All fields are protected by a Mutex.
func (*ResourceCache) Close ¶
func (c *ResourceCache) Close()
Close stops the background ticker goroutine.
type Scanner ¶
Scanner is the interface implemented by resource scanners (Claude dir, etc.). Scan performs the full directory walk and returns all discovered items. Implementations must be safe to call concurrently.
type ShortcutScanner ¶
type ShortcutScanner struct {
// contains filtered or unexported fields
}
ShortcutScanner reads user-defined shortcuts from a JSON file.
func NewShortcutScanner ¶
func NewShortcutScanner(path string) *ShortcutScanner
NewShortcutScanner returns a scanner over the given shortcuts file.