mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-04-28 11:30:03 +00:00
127 lines
4.7 KiB
Python
127 lines
4.7 KiB
Python
"""CLI event parser for Claude Code CLI output."""
|
|
|
|
import logging
|
|
from typing import Dict, List, Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CLIParser:
|
|
"""Helper to structure raw CLI events."""
|
|
|
|
@staticmethod
|
|
def parse_event(event: Any) -> List[Dict]:
|
|
"""
|
|
Parse a CLI event and return a structured result.
|
|
|
|
Args:
|
|
event: Raw event dictionary from CLI
|
|
|
|
Returns:
|
|
List of parsed event dicts. Empty list if not recognized.
|
|
"""
|
|
if not isinstance(event, dict):
|
|
return []
|
|
|
|
etype = event.get("type")
|
|
results = []
|
|
|
|
# 1. Handle full messages (assistant or result)
|
|
msg_obj = None
|
|
if etype == "assistant":
|
|
msg_obj = event.get("message")
|
|
elif etype == "result":
|
|
res = event.get("result")
|
|
if isinstance(res, dict):
|
|
msg_obj = res.get("message")
|
|
if not msg_obj:
|
|
msg_obj = event.get("message")
|
|
|
|
if msg_obj and isinstance(msg_obj, dict):
|
|
content = msg_obj.get("content", [])
|
|
if isinstance(content, list):
|
|
parts = []
|
|
thinking_parts = []
|
|
tool_calls = []
|
|
for c in content:
|
|
if not isinstance(c, dict):
|
|
continue
|
|
ctype = c.get("type")
|
|
if ctype == "text":
|
|
parts.append(c.get("text", ""))
|
|
elif ctype == "thinking":
|
|
thinking_parts.append(c.get("thinking", ""))
|
|
elif ctype == "tool_use":
|
|
tool_calls.append(c)
|
|
|
|
# Prioritize thinking first
|
|
if thinking_parts:
|
|
results.append(
|
|
{"type": "thinking", "text": "\n".join(thinking_parts)}
|
|
)
|
|
|
|
# Then tools or subagents
|
|
if tool_calls:
|
|
# Check for subagents (Task tool)
|
|
subagents = [
|
|
t.get("input", {}).get("description", "Subagent")
|
|
for t in tool_calls
|
|
if t.get("name") == "Task"
|
|
]
|
|
if subagents:
|
|
results.append({"type": "subagent_start", "tasks": subagents})
|
|
else:
|
|
results.append({"type": "tool_start", "tools": tool_calls})
|
|
|
|
# Then text content if any
|
|
if parts:
|
|
results.append({"type": "content", "text": "".join(parts)})
|
|
|
|
if results:
|
|
return results
|
|
|
|
# 2. Handle streaming deltas
|
|
if etype == "content_block_delta":
|
|
delta = event.get("delta", {})
|
|
if isinstance(delta, dict):
|
|
if delta.get("type") == "text_delta":
|
|
return [{"type": "content", "text": delta.get("text", "")}]
|
|
if delta.get("type") == "thinking_delta":
|
|
return [{"type": "thinking", "text": delta.get("thinking", "")}]
|
|
|
|
# 3. Handle tool usage start
|
|
if etype == "content_block_start":
|
|
block = event.get("content_block", {})
|
|
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
if block.get("name") == "Task":
|
|
desc = block.get("input", {}).get("description", "Subagent")
|
|
return [{"type": "subagent_start", "tasks": [desc]}]
|
|
return [{"type": "tool_start", "tools": [block]}]
|
|
|
|
# 4. Handle errors and exit
|
|
if etype == "error":
|
|
err = event.get("error")
|
|
msg = err.get("message") if isinstance(err, dict) else str(err)
|
|
logger.info(f"CLI_PARSER: Parsed error event: {msg[:100]}")
|
|
return [{"type": "error", "message": msg}]
|
|
elif etype == "exit":
|
|
code = event.get("code", 0)
|
|
stderr = event.get("stderr")
|
|
if code == 0:
|
|
logger.debug(f"CLI_PARSER: Successful exit (code={code})")
|
|
return [{"type": "complete", "status": "success"}]
|
|
else:
|
|
# Non-zero exit is an error
|
|
error_msg = stderr if stderr else f"Process exited with code {code}"
|
|
logger.warning(
|
|
f"CLI_PARSER: Error exit (code={code}): {error_msg[:100]}"
|
|
)
|
|
return [
|
|
{"type": "error", "message": error_msg},
|
|
{"type": "complete", "status": "failed"},
|
|
]
|
|
|
|
# Log unrecognized events for debugging
|
|
if etype:
|
|
logger.debug(f"CLI_PARSER: Unrecognized event type: {etype}")
|
|
return []
|