feat: add slash command autocomplete to HappyComposer

Implements full-stack slash command autocomplete with agent-specific built-in commands and user-defined command discovery. Includes React Strict Mode fix for suggestion handling.
This commit is contained in:
weishu
2025-12-28 15:19:08 +08:00
parent 20c05a06ec
commit fbc0d601f8
12 changed files with 322 additions and 2 deletions
+16
View File
@@ -133,6 +133,18 @@ export type RpcReadFileResponse = {
error?: string
}
export type SlashCommand = {
name: string
description?: string
source: 'builtin' | 'user'
}
export type RpcSlashCommandsResponse = {
success: boolean
commands?: SlashCommand[]
error?: string
}
export type SyncEventType =
| 'session-added'
| 'session-updated'
@@ -705,6 +717,10 @@ export class SyncEngine {
return await this.sessionRpc(sessionId, 'ripgrep', { args, cwd }) as RpcCommandResponse
}
async listSlashCommands(sessionId: string, agent: string): Promise<RpcSlashCommandsResponse> {
return await this.sessionRpc(sessionId, 'listSlashCommands', { agent }) as RpcSlashCommandsResponse
}
private async sessionRpc(sessionId: string, method: string, params: unknown): Promise<unknown> {
return await this.rpcCall(`${sessionId}:${method}`, params)
}
+26
View File
@@ -182,5 +182,31 @@ export function createSessionsRoutes(getSyncEngine: () => SyncEngine | null): Ho
return c.json({ ok: true })
})
app.get('/sessions/:id/slash-commands', async (c) => {
const engine = requireSyncEngine(c, getSyncEngine)
if (engine instanceof Response) {
return engine
}
// Session must exist but doesn't need to be active
const sessionResult = requireSessionFromParam(c, engine)
if (sessionResult instanceof Response) {
return sessionResult
}
// Get agent type from session metadata, default to 'claude'
const agent = sessionResult.session.metadata?.flavor ?? 'claude'
try {
const result = await engine.listSlashCommands(sessionResult.sessionId, agent)
return c.json(result)
} catch (error) {
return c.json({
success: false,
error: error instanceof Error ? error.message : 'Failed to list slash commands'
})
}
})
return app
}