feat: Add conversational AI assistant panel for project codebase Q&A

Implement a slide-in chat panel that allows users to ask questions about
their codebase using Claude Opus 4.5 with read-only access to project files.

Backend changes:
- Add SQLAlchemy models for conversation persistence (assistant_database.py)
- Create AssistantChatSession with read-only Claude SDK client
- Add WebSocket endpoint for real-time chat streaming
- Include read-only MCP tools: feature_get_stats, feature_get_next, etc.

Frontend changes:
- Add floating action button (bottom-right) to toggle panel
- Create slide-in panel component (400px width)
- Implement WebSocket hook with reconnection logic
- Add keyboard shortcut 'A' to toggle assistant

Key features:
- Read-only access: Only Read, Glob, Grep, WebFetch, WebSearch tools
- Persistent history: Conversations saved to SQLite per project
- Real-time streaming: Text chunks streamed as Claude generates response
- Tool visibility: Shows when assistant is using tools to explore code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-04 14:57:58 +02:00
parent 88951e454a
commit 908754302a
13 changed files with 1657 additions and 6 deletions

View File

@@ -14,6 +14,8 @@ import { AddFeatureForm } from './components/AddFeatureForm'
import { FeatureModal } from './components/FeatureModal'
import { DebugLogViewer } from './components/DebugLogViewer'
import { AgentThought } from './components/AgentThought'
import { AssistantFAB } from './components/AssistantFAB'
import { AssistantPanel } from './components/AssistantPanel'
import { Plus, Loader2 } from 'lucide-react'
import type { Feature } from './lib/types'
@@ -31,6 +33,7 @@ function App() {
const [setupComplete, setSetupComplete] = useState(true) // Start optimistic
const [debugOpen, setDebugOpen] = useState(false)
const [debugPanelHeight, setDebugPanelHeight] = useState(288) // Default height
const [assistantOpen, setAssistantOpen] = useState(false)
const { data: projects, isLoading: projectsLoading } = useProjects()
const { data: features } = useFeatures(selectedProject)
@@ -84,9 +87,17 @@ function App() {
setShowAddFeature(true)
}
// A : Toggle assistant panel (when project selected)
if ((e.key === 'a' || e.key === 'A') && selectedProject) {
e.preventDefault()
setAssistantOpen(prev => !prev)
}
// Escape : Close modals
if (e.key === 'Escape') {
if (showAddFeature) {
if (assistantOpen) {
setAssistantOpen(false)
} else if (showAddFeature) {
setShowAddFeature(false)
} else if (selectedFeature) {
setSelectedFeature(null)
@@ -98,7 +109,7 @@ function App() {
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [selectedProject, showAddFeature, selectedFeature, debugOpen])
}, [selectedProject, showAddFeature, selectedFeature, debugOpen, assistantOpen])
// Combine WebSocket progress with feature data
const progress = wsState.progress.total > 0 ? wsState.progress : {
@@ -244,6 +255,21 @@ function App() {
onHeightChange={setDebugPanelHeight}
/>
)}
{/* Assistant FAB and Panel */}
{selectedProject && (
<>
<AssistantFAB
onClick={() => setAssistantOpen(!assistantOpen)}
isOpen={assistantOpen}
/>
<AssistantPanel
projectName={selectedProject}
isOpen={assistantOpen}
onClose={() => setAssistantOpen(false)}
/>
</>
)}
</div>
)
}