feat: implement task plans feature (#131)
This commit is contained in:
@@ -1,152 +0,0 @@
|
||||
# File Reference Feature Implementation Plan
|
||||
|
||||
## Overview
|
||||
Implement file referencing in chat input with `@filename` autocomplete. When a user types `@`, they should see autocomplete suggestions for files in the workspace. Selected files are passed as **path references** in the prompt - the agent can then read them from its workspace using its own tools.
|
||||
|
||||
**Key insight**: The agent runs in the workspace where files exist. We don't need to embed file contents - just tell the agent which files the user is referring to. The agent can read them itself.
|
||||
|
||||
## Requirements Analysis
|
||||
|
||||
1. **Frontend: `@filename` Autocomplete**
|
||||
- Detect `@` prefix followed by path characters in chat input
|
||||
- Debounce search requests (only search when user stops typing ~300ms)
|
||||
- Call backend to search/filter files in the workspace
|
||||
- Display matching files as autocomplete options
|
||||
- Insert selected file reference into the chat input (keep as `@path/to/file.ts`)
|
||||
|
||||
2. **Backend: File Search Endpoint**
|
||||
- New WebSocket action `workspace.files.search` for searching files
|
||||
- Accept session_id (to route to correct agentctl) and query string
|
||||
- Return matching file paths (fuzzy match)
|
||||
|
||||
3. **Prompt Format**
|
||||
- Keep `@filepath` references in the message as-is
|
||||
- Agent sees: "Look at @src/main.ts and fix the bug"
|
||||
- Agent uses its tools to read the file when needed
|
||||
|
||||
## Architecture Flow
|
||||
|
||||
```
|
||||
User types @main.ts → Frontend debounces 300ms → WS request workspace.files.search
|
||||
↓
|
||||
Backend routes to agentctl
|
||||
↓
|
||||
agentctl searches worktree (git ls-files)
|
||||
↓
|
||||
Returns matching files
|
||||
↓
|
||||
Frontend shows autocomplete
|
||||
↓
|
||||
User selects file → Insert @src/main.ts in input
|
||||
↓
|
||||
User submits → message.add with content "Look at @src/main.ts"
|
||||
↓
|
||||
Agent receives prompt, reads file using its tools
|
||||
```
|
||||
|
||||
## Files to Modify/Create
|
||||
|
||||
### Backend (Go)
|
||||
|
||||
1. **`apps/backend/pkg/websocket/actions.go`**
|
||||
- Add `ActionWorkspaceFilesSearch = "workspace.files.search"`
|
||||
|
||||
2. **`apps/backend/internal/agentctl/server/process/workspace_tracker.go`**
|
||||
- Add `SearchFiles(query string, limit int) ([]string, error)` method
|
||||
- Use fuzzy/substring matching on cached file list from `git ls-files`
|
||||
|
||||
3. **`apps/backend/internal/agentctl/server/api/workspace.go`**
|
||||
- Add `handleFileSearch` HTTP endpoint: `GET /api/v1/workspace/search?q=&limit=`
|
||||
|
||||
4. **`apps/backend/internal/agentctl/client/client.go`**
|
||||
- Add `SearchFiles(ctx, query, limit) ([]string, error)` method
|
||||
|
||||
5. **`apps/backend/internal/agent/handlers/workspace_file_handlers.go`**
|
||||
- Add `wsSearchFiles` handler for `workspace.files.search`
|
||||
|
||||
6. **`apps/backend/internal/agentctl/types/streams/file.go`**
|
||||
- Add `FileSearchRequest` and `FileSearchResponse` types
|
||||
|
||||
### Frontend (TypeScript)
|
||||
|
||||
1. **`apps/web/hooks/use-chat-completions.ts`**
|
||||
- Add file path completion alongside prompt completions
|
||||
- Accept sessionId prop for file search context
|
||||
- Use async completion source with debouncing
|
||||
|
||||
2. **`apps/web/lib/ws/workspace-files.ts`**
|
||||
- Add `searchWorkspaceFiles(client, sessionId, query, limit)` function
|
||||
|
||||
3. **`apps/web/lib/types/backend.ts`**
|
||||
- Add `FileSearchResponse` type
|
||||
|
||||
4. **`apps/web/components/task/task-chat-input.tsx`**
|
||||
- Pass sessionId to useChatCompletions hook
|
||||
|
||||
5. **`apps/web/components/task/task-chat-panel.tsx`**
|
||||
- Pass sessionId down to TaskChatInput
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
### Phase 1: Backend File Search
|
||||
1. Add `FileSearchResponse` type in `types/streams/file.go`
|
||||
2. Add `SearchFiles` method in `workspace_tracker.go` (fuzzy match on git ls-files)
|
||||
3. Add `handleFileSearch` HTTP endpoint in `workspace.go`
|
||||
4. Add `SearchFiles` client method in `client.go`
|
||||
5. Add WS action constant in `actions.go`
|
||||
6. Add `wsSearchFiles` handler in `workspace_file_handlers.go`
|
||||
|
||||
### Phase 2: Frontend Autocomplete
|
||||
1. Add `searchWorkspaceFiles` function in `workspace-files.ts`
|
||||
2. Add `FileSearchResponse` type in `backend.ts`
|
||||
3. Extend `useChatCompletions` to accept sessionId and add file completions
|
||||
4. Update `TaskChatInput` to receive and pass sessionId
|
||||
5. Update `TaskChatPanel` to pass sessionId to input
|
||||
|
||||
## Fuzzy Search Algorithm
|
||||
|
||||
Simple substring matching with scoring:
|
||||
- Exact filename match: highest score
|
||||
- Filename contains query: high score
|
||||
- Path contains query: medium score
|
||||
- Sort by score, return top N results
|
||||
|
||||
```go
|
||||
func (wt *WorkspaceTracker) SearchFiles(query string, limit int) []string {
|
||||
query = strings.ToLower(query)
|
||||
var matches []scoredMatch
|
||||
|
||||
for _, file := range wt.currentFiles.Files {
|
||||
path := strings.ToLower(file.Path)
|
||||
name := filepath.Base(path)
|
||||
|
||||
score := 0
|
||||
if name == query { score = 100 }
|
||||
else if strings.Contains(name, query) { score = 50 }
|
||||
else if strings.Contains(path, query) { score = 25 }
|
||||
|
||||
if score > 0 {
|
||||
matches = append(matches, scoredMatch{path: file.Path, score: score})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(matches, func(i, j int) bool { return matches[i].score > matches[j].score })
|
||||
// Return top `limit` results
|
||||
}
|
||||
```
|
||||
|
||||
## Risks & Considerations
|
||||
|
||||
1. **Performance**: Large repos could have many files. Solutions:
|
||||
- Limit search results (default 20)
|
||||
- Cache file list on workspace tracker (already done via git ls-files)
|
||||
- Client-side debouncing (300ms)
|
||||
|
||||
2. **Session Requirement**: File search requires an active session. Solutions:
|
||||
- Only show file completions when sessionId is available
|
||||
- Gracefully return empty results if no session
|
||||
|
||||
3. **Concurrent Searches**: User typing fast could queue many searches. Solutions:
|
||||
- Debouncing handles this naturally
|
||||
- Each new search supersedes previous results
|
||||
|
||||
Reference in New Issue
Block a user