Skip to content

Commit 3e36fdb

Browse files
Lexus2016claude
andcommitted
fix: remove false ask_tool card from AskUserQuestion CLI tool
The onTool handler had an overly broad filter: name.toLowerCase().includes('ask') which caught AskUserQuestion (Claude CLI's internal tool) and displayed it as an interactive question card to the web UI user. AskUserQuestion is handled by the CLI itself (auto-resolved with --dangerously-skip-permissions) — it should never reach the UI. Changes: - Skip AskUserQuestion in onTool (same as ask_user/notify_user) - Remove pendingCliAsks state (no longer needed) - Remove test endpoints /api/test/ask-tool and /api/test/ask-user - Remove ask_tool_response WebSocket handler Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 325dc05 commit 3e36fdb

3 files changed

Lines changed: 9 additions & 110 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "claude-code-studio",
3-
"version": "5.13.6",
3+
"version": "5.13.8",
44
"description": "Full-featured web workspace for Claude Code — chat, Kanban, multi-agent, MCP, skills, projects",
55
"bin": {
66
"claude-code-studio": "./bin/cli.js"

server.js

Lines changed: 6 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,6 @@ const pendingAskUser = new Map();
377377
const ASK_USER_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
378378
const ASK_USER_SECRET = require('crypto').randomBytes(16).toString('hex');
379379

380-
// ─── CLI Ask Tool ──────────────────────────────────────────────────────────
381-
// Pending CLI Ask tool questions: requestId → { sessionId, ws, resolved }
382-
let pendingCliAsks = new Map();
383380

384381
// ─── Notify User (Internal MCP) ──────────────────────────────────────────
385382
const NOTIFY_SECRET = require('crypto').randomBytes(16).toString('hex');
@@ -802,35 +799,13 @@ function runCliSingle(p) {
802799
try { stmts.addMsg.run(sessionId,'assistant','tool',(inp||'').substring(0,10000),name,null,null,null); } catch {}
803800
return;
804801
}
805-
// Debug: log all tool names to find Ask tool
806-
if (name.toLowerCase().includes('ask') || name.toLowerCase().includes('question')) {
807-
log.info('Ask-like tool detected', { name, inputPreview: (inp||'').substring(0, 200) });
808-
}
809-
// Handle Ask tool from Claude Code CLI - render as interactive question card
810-
// Match: "Ask", "mcp__Ask", or tools with "ask" in name (but not ask_user/notify_user which are already filtered)
811-
if (name === 'Ask' || name === 'mcp__Ask' || (name.toLowerCase().includes('ask') && !name.includes('ask_user'))) {
812-
try {
813-
const askData = typeof inp === 'string' ? JSON.parse(inp) : inp;
814-
// Generate a unique request ID for this ask
815-
const askRequestId = `ask-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
816-
// Store pending ask for response handling
817-
if (!pendingCliAsks) pendingCliAsks = new Map();
818-
pendingCliAsks.set(askRequestId, { sessionId, ws, resolved: false });
819-
// Send to client for rendering
820-
ws.send(JSON.stringify({
821-
type: 'ask_tool',
822-
requestId: askRequestId,
823-
question: askData.question || askData.prompt || '',
824-
options: askData.options || [],
825-
tabId
826-
}));
827-
} catch (e) {
828-
// If parsing fails, show as regular tool
829-
ws.send(JSON.stringify({ type:'tool', tool:name, input:(inp||'').substring(0,600), ...(tabId ? { tabId } : {}) }));
830-
}
831-
} else {
832-
ws.send(JSON.stringify({ type:'tool', tool:name, input:(inp||'').substring(0,600), ...(tabId ? { tabId } : {}) }));
802+
// AskUserQuestion is Claude CLI's internal tool — handled by the CLI itself
803+
// (auto-resolved with --dangerously-skip-permissions). Don't show to web UI user.
804+
if (name === 'AskUserQuestion') {
805+
try { stmts.addMsg.run(sessionId,'assistant','tool',(inp||'').substring(0,10000),name,null,null,null); } catch {}
806+
return;
833807
}
808+
ws.send(JSON.stringify({ type:'tool', tool:name, input:(inp||'').substring(0,600), ...(tabId ? { tabId } : {}) }));
834809
try { stmts.addMsg.run(sessionId,'assistant','tool',(inp||'').substring(0,10000),name,null,null,null); } catch {}
835810
})
836811
.onSessionId(sid => { newCid=sid; try { stmts.updateClaudeId.run(sid, sessionId); } catch {} }) // persist early so open-terminal works during active chat
@@ -1108,70 +1083,6 @@ app.get('/api/health', (_, res) => {
11081083
});
11091084

11101085
// Test endpoint to simulate Ask tool (for UI testing)
1111-
app.post('/api/test/ask-tool', express.json(), (req, res) => {
1112-
const { sessionId, question, options } = req.body;
1113-
if (!sessionId) return res.status(400).json({ error: 'sessionId required' });
1114-
1115-
// Find active WebSocket for this session
1116-
const task = activeTasks.get(sessionId);
1117-
const ws = task?.proxy;
1118-
if (!ws) return res.status(404).json({ error: 'No active session' });
1119-
1120-
const askRequestId = `ask-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1121-
1122-
ws.send(JSON.stringify({
1123-
type: 'ask_tool',
1124-
requestId: askRequestId,
1125-
question: question || 'Which option do you prefer?',
1126-
options: options || ['Option A', 'Option B', 'Option C'],
1127-
tabId: sessionId
1128-
}));
1129-
1130-
res.json({ ok: true, requestId: askRequestId });
1131-
});
1132-
1133-
// Test endpoint to simulate MCP ask_user (for UI testing)
1134-
app.post('/api/test/ask-user', express.json(), (req, res) => {
1135-
const { sessionId, question, questions, options, inputType } = req.body;
1136-
if (!sessionId) return res.status(400).json({ error: 'sessionId required' });
1137-
1138-
// Find active WebSocket for this session
1139-
const task = activeTasks.get(sessionId);
1140-
const ws = task?.proxy;
1141-
if (!ws) return res.status(404).json({ error: 'No active session' });
1142-
1143-
const requestId = crypto.randomUUID();
1144-
1145-
// Build payload matching MCP ask_user format
1146-
const payload = {
1147-
type: 'ask_user',
1148-
requestId,
1149-
tabId: sessionId
1150-
};
1151-
1152-
// Single question format
1153-
if (question) {
1154-
payload.question = question;
1155-
if (options) payload.options = options;
1156-
if (inputType) payload.inputType = inputType;
1157-
}
1158-
1159-
// Multiple questions format (paginated)
1160-
if (questions && Array.isArray(questions)) {
1161-
payload.questions = questions;
1162-
}
1163-
1164-
// Default if nothing specified
1165-
if (!question && !questions) {
1166-
payload.question = 'What would you like to do?';
1167-
payload.options = ['Option A', 'Option B', 'Option C'];
1168-
payload.inputType = 'single_choice';
1169-
}
1170-
1171-
ws.send(JSON.stringify(payload));
1172-
res.json({ ok: true, requestId, payload });
1173-
});
1174-
11751086
// Stats
11761087
app.get('/api/stats', (req, res) => {
11771088
const sessionId = req.query.session_id || null;
@@ -2245,18 +2156,6 @@ wss.on('connection', (ws) => {
22452156
return;
22462157
}
22472158

2248-
// ─── CLI Ask Tool responses ───────────────────────────────────────────────
2249-
if (msg.type === 'ask_tool_response') {
2250-
const entry = pendingCliAsks.get(msg.requestId);
2251-
if (entry && !entry.resolved) {
2252-
entry.resolved = true;
2253-
pendingCliAsks.delete(msg.requestId);
2254-
// The answer will be sent as the next user message in the chat
2255-
// This is handled client-side by adding the answer to the input
2256-
}
2257-
return;
2258-
}
2259-
22602159
if (msg.type==='new_session') {
22612160
ws._queue = [];
22622161
if (ws._abort) ws._abort.abort();

0 commit comments

Comments
 (0)