feat: Rich hook forwarding, permission bridge, and toast notifications

Replace hardcoded PowerShell status hooks with stdin-forwarding hooks
that send full Claude Code hook data (tool_input, tool_response, prompt,
session_id, model, etc.) to /api/claude-hook endpoint.

- PowerShell hooks read stdin JSON and POST to /api/claude-hook
- Server derives status for backward-compat FAB animations
- Server extracts assistant_response from transcript on Stop events
- New /api/claude-permission endpoint with Promise-based allow/deny flow
- HookNotifications.vue: toast system showing session, prompt, tool use,
  tool results, notifications, and final assistant response
- WebSocket broadcast for claude-hook and claude-permission message types
This commit is contained in:
2026-02-15 16:16:59 -06:00
parent 4aaeb8844f
commit 816a8d9abe
8 changed files with 897 additions and 30 deletions

View File

@@ -10,9 +10,16 @@ import { handleTables, handleStats, handleTableSchema, handleTableData, handleQu
import { handleWhisperRoutes } from './whisper'
import { handleRecordingsRoutes } from './recordings'
import { handleClaudeStatus } from './claude-status'
import { handleClaudeHook } from './claude-hook'
import { handleClaudePermission, handleClaudePermissionRespond, handleClaudePermissionList } from './claude-permission'
import { handleSnapshots, handleSnapshotById } from './snapshots'
import { handleGitStatus, handleGitDiff, handleGitLog, handleGitLogCommit, handleGitCompare, handleGitBranches, handleGitCurrentBranch, handleGitTree, handleGitFile } from './git'
import { handleAgents, handleAgentsFile } from './agents'
import {
handleAgents, handleAgentsFile,
handleAgentsConfig, handleAgentsKnownTools, handleAgentsSkills,
handleAgentsPlugins, handleAgentsMcpJson,
handleAgentsConfigPermissions, handleAgentsConfigHooks, handleAgentsConfigMcp
} from './agents'
export async function handleRequest(req: Request): Promise<Response> {
const url = new URL(req.url)
@@ -58,6 +65,28 @@ export async function handleRequest(req: Request): Promise<Response> {
if (res) return res
}
// Claude Code hook (rich stdin data forwarding)
if (path === '/api/claude-hook') {
const res = await handleClaudeHook(req)
if (res) return res
}
// Claude Code permission request/respond
if (path === '/api/claude-permission') {
if (req.method === 'GET') {
const res = await handleClaudePermissionList(req)
if (res) return res
} else {
const res = await handleClaudePermission(req)
if (res) return res
}
}
if (path === '/api/claude-permission-respond') {
const res = await handleClaudePermissionRespond(req)
if (res) return res
}
// Components
if (path === '/api/components') {
const res = await handleComponents(req)
@@ -253,6 +282,46 @@ export async function handleRequest(req: Request): Promise<Response> {
return handleAgents(req)
}
if (path === '/api/agents/config' && req.method === 'GET') {
const res = await handleAgentsConfig(req, url)
if (res) return res
}
if (path === '/api/agents/known-tools' && req.method === 'GET') {
const res = await handleAgentsKnownTools(req)
if (res) return res
}
if (path === '/api/agents/skills' && req.method === 'GET') {
const res = await handleAgentsSkills(req, url)
if (res) return res
}
if (path === '/api/agents/plugins' && req.method === 'GET') {
const res = await handleAgentsPlugins(req)
if (res) return res
}
if (path === '/api/agents/mcp-json' && req.method === 'GET') {
const res = await handleAgentsMcpJson(req)
if (res) return res
}
if (path === '/api/agents/config/permissions' && req.method === 'POST') {
const res = await handleAgentsConfigPermissions(req)
if (res) return res
}
if (path === '/api/agents/config/hooks' && req.method === 'POST') {
const res = await handleAgentsConfigHooks(req)
if (res) return res
}
if (path === '/api/agents/config/mcp' && req.method === 'POST') {
const res = await handleAgentsConfigMcp(req)
if (res) return res
}
if (path === '/api/agents/file') {
const res = await handleAgentsFile(req, url)
if (res) return res