feat: add full chat slash commands support (/help, /status, /sessions, /stats)
This commit is contained in:
@@ -34,6 +34,8 @@ This document tracks the completed features and the pending backlog for the Herm
|
|||||||
* **System View (`SystemScreen`)**: Diagnostics doctor reports (`GET /api/ops/doctor`) and backups trigger (`POST /api/ops/backup`).
|
* **System View (`SystemScreen`)**: Diagnostics doctor reports (`GET /api/ops/doctor`) and backups trigger (`POST /api/ops/backup`).
|
||||||
* **Kanban View (`KanbanScreen`)**: Full task boards, task columns (To Do, In Progress, Done), new tasks dialog, and task movement with optimistic updates.
|
* **Kanban View (`KanbanScreen`)**: Full task boards, task columns (To Do, In Progress, Done), new tasks dialog, and task movement with optimistic updates.
|
||||||
* **Navigation Drawer**: Material3 navigation drawer using Navigation3 keys to switch between all screens.
|
* **Navigation Drawer**: Material3 navigation drawer using Navigation3 keys to switch between all screens.
|
||||||
|
* **Chat Slash Commands**: Local interception and execution of custom commands (`/help`, `/status`, `/sessions`, `/stats` / `/system`, `/new`, `/stop` / `/interrupt`) displaying results inline.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -310,6 +310,12 @@ class ChatViewModel : ViewModel() {
|
|||||||
if (text.isBlank()) return
|
if (text.isBlank()) return
|
||||||
val sessionId = _uiState.value.currentSessionId ?: return
|
val sessionId = _uiState.value.currentSessionId ?: return
|
||||||
|
|
||||||
|
val trimmed = text.trim()
|
||||||
|
if (trimmed.startsWith("/", ignoreCase = true)) {
|
||||||
|
handleSlashCommand(trimmed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Add user message to UI
|
// Add user message to UI
|
||||||
val userMessage =
|
val userMessage =
|
||||||
ChatMessage(
|
ChatMessage(
|
||||||
@@ -329,6 +335,125 @@ class ChatViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSlashCommand(command: String) {
|
||||||
|
val userMsg = ChatMessage(role = MessageRole.USER, content = command)
|
||||||
|
_uiState.update { it.copy(messages = it.messages + userMsg) }
|
||||||
|
|
||||||
|
val parts = command.split(" ")
|
||||||
|
val cmd = parts[0].lowercase()
|
||||||
|
|
||||||
|
when (cmd) {
|
||||||
|
"/stop", "/interrupt" -> {
|
||||||
|
interruptSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
"/new" -> {
|
||||||
|
createNewSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
"/help" -> {
|
||||||
|
val helpText =
|
||||||
|
"""
|
||||||
|
**Available Commands:**
|
||||||
|
• `/help` - Show this help menu
|
||||||
|
• `/status` - Check gateway and platform status
|
||||||
|
• `/sessions` - List all chat sessions
|
||||||
|
• `/stats` or `/system` - Check system resource usage
|
||||||
|
• `/new` - Create a new chat session
|
||||||
|
• `/stop` or `/interrupt` - Interrupt the active run
|
||||||
|
""".trimIndent()
|
||||||
|
addAssistantMessage(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
"/status" -> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val response = withContext(Dispatchers.IO) { ApiClient.hermesApi.getStatus() }
|
||||||
|
if (response.isSuccessful && response.body() != null) {
|
||||||
|
val body = response.body()!!
|
||||||
|
val platformsStr =
|
||||||
|
body.gateway_platforms?.entries?.joinToString("\n") { (k, v) ->
|
||||||
|
" • **$k**: ${v.state ?: "Unknown"}${if (v.error_code != null) " (Error: ${v.error_code})" else ""}"
|
||||||
|
} ?: "No active platforms"
|
||||||
|
|
||||||
|
val statusText =
|
||||||
|
"""
|
||||||
|
**Hermes Gateway Status:**
|
||||||
|
• **Version:** ${body.version ?: "Unknown"}
|
||||||
|
• **Gateway Running:** ${body.gateway_running ?: false}
|
||||||
|
• **Active Sessions:** ${body.active_sessions ?: 0}
|
||||||
|
• **Auth Required:** ${body.auth_required ?: false}
|
||||||
|
|
||||||
|
**Platforms:**
|
||||||
|
$platformsStr
|
||||||
|
""".trimIndent()
|
||||||
|
addAssistantMessage(statusText)
|
||||||
|
} else {
|
||||||
|
addAssistantMessage("Failed to retrieve status: HTTP ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
addAssistantMessage("Failed to retrieve status: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"/sessions" -> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val response = withContext(Dispatchers.IO) { ApiClient.hermesApi.getSessions() }
|
||||||
|
if (response.isSuccessful && response.body() != null) {
|
||||||
|
val body = response.body()!!
|
||||||
|
val sessionsStr =
|
||||||
|
body.sessions.joinToString("\n") { s ->
|
||||||
|
"• **${s.title ?: "Untitled"}** (ID: `${s.id}`, Messages: ${s.message_count ?: 0})"
|
||||||
|
}
|
||||||
|
addAssistantMessage("**Sessions List:**\n$sessionsStr")
|
||||||
|
} else {
|
||||||
|
addAssistantMessage("Failed to list sessions: HTTP ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
addAssistantMessage("Failed to list sessions: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"/stats", "/system" -> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val response = withContext(Dispatchers.IO) { ApiClient.hermesApi.getSystemStats() }
|
||||||
|
if (response.isSuccessful && response.body() != null) {
|
||||||
|
val body = response.body()!!
|
||||||
|
val cpuPct = body.cpuPercent?.let { String.format("%.1f%%", it) } ?: "N/A"
|
||||||
|
val memPct = body.memoryPercent?.let { String.format("%.1f%%", it) } ?: "N/A"
|
||||||
|
val uptimeVal = body.uptime ?: "N/A"
|
||||||
|
val statsText =
|
||||||
|
"""
|
||||||
|
**System Resource Stats:**
|
||||||
|
• **CPU Usage:** $cpuPct
|
||||||
|
• **Memory Usage:** $memPct
|
||||||
|
• **Uptime:** $uptimeVal
|
||||||
|
""".trimIndent()
|
||||||
|
addAssistantMessage(statsText)
|
||||||
|
} else {
|
||||||
|
addAssistantMessage("Failed to retrieve system stats: HTTP ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
addAssistantMessage("Failed to retrieve system stats: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
addAssistantMessage("Unknown command: `$cmd`. Type `/help` to view a list of available commands.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAssistantMessage(text: String) {
|
||||||
|
val msg = ChatMessage(role = MessageRole.ASSISTANT, content = text)
|
||||||
|
_uiState.update { it.copy(messages = it.messages + msg) }
|
||||||
|
}
|
||||||
|
|
||||||
fun interruptSession() {
|
fun interruptSession() {
|
||||||
val sessionId = _uiState.value.currentSessionId ?: return
|
val sessionId = _uiState.value.currentSessionId ?: return
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|||||||
Reference in New Issue
Block a user