feat(tui-go): @ file-reference picker

Typing `@` at a word boundary in the chat input (mid-word `@` stays literal, e.g. emails)
opens a workspace file picker: it requests file.list for the session (cached per session),
filters as you type, and enter inserts `@path ` at the cursor — back to typing. Renders as
a transparent overlay, windowed around the selection. file.list is classified non-rendering
in the render-matrix guard. Tests cover token-boundary detection, ref insertion, and filtering.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 10:24:57 +00:00
parent 65e5d88ed7
commit 04d7e26482
8 changed files with 245 additions and 2 deletions
+10
View File
@@ -55,6 +55,7 @@ const (
TypeConfigSnapshot = "config.snapshot"
TypeSessionStats = "session.stats"
TypeIdeaList = "idea.list"
TypeFileList = "file.list"
)
// ServerMessage is a flat decode of every server->client variant. Field names
@@ -150,6 +151,9 @@ type ServerMessage struct {
// idea.list — the cross-session idea board
Ideas []IdeaDto `json:"ideas"`
// file.list — the session workspace's file paths (for the @ file-ref picker)
Paths []string `json:"paths"`
}
// IdeaDto is one idea on the cross-session board. Mirrors the Kotlin IdeaDto.
@@ -386,6 +390,12 @@ func ListArtifacts(sessionID string) []byte {
return encode("ListArtifacts", map[string]any{"sessionId": sessionID})
}
// ListFiles asks the server for the session workspace's file paths (replied to with file.list),
// used to populate the @ file-reference picker.
func ListFiles(sessionID string) []byte {
return encode("ListFiles", map[string]any{"sessionId": sessionID})
}
// GetSessionStats asks the server for a session's derived metrics (replied to with session.stats).
func GetSessionStats(sessionID string) []byte {
return encode("GetSessionStats", map[string]any{"sessionId": sessionID})