Skip to content

Commit 8dc676c

Browse files
fix: slash command parser handles ❯ format, emit slash_commands from sidecar init
1 parent 367a0bd commit 8dc676c

3 files changed

Lines changed: 37 additions & 17 deletions

File tree

crates/session/src/claude.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ impl ClaudeSession {
269269
}
270270
vec![AgentEvent::SessionReady { claude_session_id: sid, model: confirmed_model }]
271271
}
272+
"slash_commands" => {
273+
// Slash commands from the SDK init — we just log them,
274+
// the frontend loads them separately via list_slash_commands
275+
vec![]
276+
}
272277
"turn_started" => {
273278
vec![AgentEvent::TurnStarted { turn_id: "sidecar".to_string() }]
274279
}

crates/tauri-app/agent-sidecar/index.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ async function handleQuery(cmd) {
231231
}
232232
const confirmedModel = message.model || model || null;
233233
emit({ type: "session_ready", sessionId: message.session_id, model: confirmedModel });
234+
235+
// Emit available slash commands from the init message
236+
const slashCmds = message.slash_commands || message.skills || [];
237+
if (slashCmds.length > 0) {
238+
emit({ type: "slash_commands", commands: slashCmds });
239+
}
234240
}
235241
continue;
236242
}

crates/tauri-app/src/commands/mcp.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -261,31 +261,40 @@ fn parse_mcp_list(output: &str) -> Vec<McpServer> {
261261
/// Parse `claude plugin list` output to extract skill-based slash commands.
262262
fn parse_plugin_skills(output: &str) -> Vec<SlashCommand> {
263263
let mut commands = Vec::new();
264+
let mut current_name = String::new();
265+
let mut current_enabled = true;
264266

265267
for line in output.lines() {
266-
let line = line.trim();
267-
// Look for lines like "- skill-name@marketplace (enabled)"
268-
if line.starts_with('-') || line.starts_with('•') {
269-
let clean = line.trim_start_matches(['-', '•', ' '].as_ref()).trim();
270-
if let Some((name_part, _)) = clean.split_once('@') {
271-
let name = name_part.trim();
268+
let trimmed = line.trim();
269+
270+
// Entry header: "❯ name@source"
271+
if trimmed.starts_with('❯') || trimmed.starts_with('>') {
272+
// Save previous
273+
if !current_name.is_empty() && current_enabled {
274+
// Extract skill name (before @)
275+
let skill_name = current_name.split('@').next().unwrap_or(&current_name);
272276
commands.push(SlashCommand {
273-
name: format!("/{name}"),
274-
description: format!("Plugin skill: {name}"),
277+
name: format!("/{skill_name}"),
278+
description: format!("Plugin: {}", current_name),
275279
source: "plugin".to_string(),
276280
});
277-
} else if let Some((name_part, _)) = clean.split_once(' ') {
278-
let name = name_part.trim();
279-
if !name.is_empty() {
280-
commands.push(SlashCommand {
281-
name: format!("/{name}"),
282-
description: "Plugin skill".to_string(),
283-
source: "plugin".to_string(),
284-
});
285-
}
286281
}
282+
current_name = trimmed.trim_start_matches('❯').trim_start_matches('>').trim().to_string();
283+
current_enabled = true;
284+
} else if trimmed.starts_with("Status:") {
285+
current_enabled = trimmed.contains("enabled") || trimmed.contains("✔");
287286
}
288287
}
289288

289+
// Last entry
290+
if !current_name.is_empty() && current_enabled {
291+
let skill_name = current_name.split('@').next().unwrap_or(&current_name);
292+
commands.push(SlashCommand {
293+
name: format!("/{skill_name}"),
294+
description: format!("Plugin: {}", current_name),
295+
source: "plugin".to_string(),
296+
});
297+
}
298+
290299
commands
291300
}

0 commit comments

Comments
 (0)