diff --git a/.claude/commands/add-sse-event.md b/.claude/commands/add-sse-event.md new file mode 100644 index 00000000..7215a48e --- /dev/null +++ b/.claude/commands/add-sse-event.md @@ -0,0 +1,95 @@ +--- +description: Scaffold a new SSE event end-to-end (Rust backend to web frontend) +allowed-tools: Read, Edit, Write, Glob, Grep, Bash(cargo fmt:*), Bash(cargo clippy:*), Bash(cargo test:*) +argument-hint: [description] +model: opus +--- + +Add a new SSE event called `$ARGUMENTS` to the IronClaw web gateway. This involves changes across 5 files in a specific order. Follow each step exactly. + +## Step 1: Add `StatusUpdate` variant + +**File**: `src/channels/channel.rs` + +Find the `StatusUpdate` enum and add a new variant. Use the event name in PascalCase. Include any fields the event needs as named fields (not a generic String). + +Example for reference (existing variants): +```rust +pub enum StatusUpdate { + Thinking(String), + ToolStarted { name: String }, + ToolCompleted { name: String, success: bool }, + Status(String), + ApprovalNeeded { + request_id: String, + tool_name: String, + description: String, + parameters: serde_json::Value, + }, +} +``` + +## Step 2: Map to `SseEvent` in web channel + +**File**: `src/channels/web/mod.rs` + +Find the `send_status` method in the `Channel` impl for `WebChannel`. Add a match arm for the new `StatusUpdate` variant that maps it to an `SseEvent`. The SSE event name should be snake_case. + +Look at existing match arms for the pattern. The event data is serialized as JSON. + +## Step 3: Add types if needed + +**File**: `src/channels/web/types.rs` + +If the event carries structured data beyond a simple string, add a serializable DTO struct here. Use `#[derive(Debug, Clone, Serialize, Deserialize)]`. Follow the existing patterns in the file. + +## Step 4: Add frontend handler + +**File**: `src/channels/web/static/app.js` + +In the `connectSSE()` function, add a new `eventSource.addEventListener()` for the snake_case event name. Parse the JSON data and call a handler function. + +Create the handler function that updates the DOM. Follow existing patterns: +- `showApproval(data)` for complex card-style UI +- `addMessage(role, content)` for simple text +- `setStatus(text, spinning)` for status bar updates + +## Step 5: Add CSS if needed + +**File**: `src/channels/web/static/style.css` + +If the event needs custom UI (cards, badges, etc.), add styles. Follow the existing naming conventions (`.approval-card`, `.log-entry`, etc.). + +## Step 6: Send the event from Rust + +Identify where in the backend this event should be triggered. Common locations: +- `src/agent/agent_loop.rs` - During message processing or tool execution +- `src/agent/worker.rs` - During job execution +- `src/agent/heartbeat.rs` - During periodic execution + +Use the existing pattern: +```rust +let _ = self.channels.send_status( + &message.channel, + StatusUpdate::YourNewVariant { ... }, + &message.metadata, +).await; +``` + +## Step 7: Quality gate + +Run `cargo fmt` and `cargo clippy --all --benches --tests --examples --all-features` to verify the changes compile cleanly. + +## Checklist + +Before finishing, verify: +- [ ] `StatusUpdate` variant added in `channel.rs` +- [ ] Match arm added in `web/mod.rs` `send_status` +- [ ] DTO added in `types.rs` (if needed) +- [ ] `addEventListener` added in `app.js` +- [ ] Handler function created in `app.js` +- [ ] CSS styles added (if needed) +- [ ] Event sent from appropriate backend location +- [ ] `cargo fmt` clean +- [ ] `cargo clippy` clean +- [ ] Non-web channels unaffected (they ignore unknown StatusUpdate variants) diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md new file mode 100644 index 00000000..675d1507 --- /dev/null +++ b/.claude/commands/ship.md @@ -0,0 +1,25 @@ +--- +description: Run the full Rust quality gate (fmt, clippy, tests) before shipping changes +allowed-tools: Bash(cargo fmt:*), Bash(cargo clippy:*), Bash(cargo test:*) +--- + +Run the IronClaw shipping checklist. This is the mandatory quality gate before any change is considered done. + +## Steps + +1. **Format**: Run `cargo fmt` to normalize formatting. + +2. **Lint**: Run `cargo clippy --all --benches --tests --examples --all-features` and report any warnings or errors. ALL clippy warnings must be resolved before proceeding. + +3. **Test**: Run `cargo test --lib` to execute the full library test suite. Report the total pass/fail count. + +4. **Summary**: Report results for all three steps. If any step failed, list the specific errors and suggest fixes. Do NOT proceed past a failing step. + +If `$ARGUMENTS` is provided, treat it as a specific test filter and run `cargo test --lib -- $ARGUMENTS` instead of the full suite in step 3. + +The expected outcome for a clean ship is: +- `cargo fmt` produces no changes +- `cargo clippy` has zero warnings +- All tests pass + +Note: Integration tests (`--test workspace_integration`) require a PostgreSQL database and are expected to fail locally. Only report `--lib` test failures as blocking. diff --git a/.claude/commands/trace.md b/.claude/commands/trace.md new file mode 100644 index 00000000..8651bd59 --- /dev/null +++ b/.claude/commands/trace.md @@ -0,0 +1,89 @@ +--- +description: Trace a data flow or bug through the IronClaw codebase end-to-end +allowed-tools: Read, Glob, Grep, Bash(cargo test:*) +argument-hint: +model: sonnet +--- + +Trace the flow of `$ARGUMENTS` through the IronClaw codebase. Your job is to map every file and function involved, identify where data transforms or could break, and report the full chain. + +## Architecture Reference + +IronClaw has three main data flow paths. Identify which one(s) are relevant and trace through them: + +### Message Flow (user input to LLM response) +``` +Channel (cli/web/wasm) → IncomingMessage + → Agent::run() message loop (agent_loop.rs) + → handle_message() dispatches by Submission type + → SubmissionParser::parse() (submission.rs) classifies input + → process_user_input() for new turns + → process_approval() for tool approval responses + → handle_command() for /commands + → run_agentic_loop() iterates LLM calls + → Reasoning::respond_with_tools() (reasoning.rs) + → LlmProvider::complete_with_tools() (nearai_chat.rs or nearai.rs) + → Tool execution with approval gating + → Context message accumulation + → Response flows back through Channel::send_response() +``` + +### SSE Event Flow (backend status to web UI) +``` +StatusUpdate variant (channel.rs) + → Channel::send_status() trait method + → WebChannel::send_status() (web/mod.rs) maps to SseEvent + → broadcast via tokio::broadcast channel + → SSE endpoint streams events (web/server.rs) + → Browser EventSource listener (app.js) + → DOM update function + → CSS styling (style.css) +``` + +### Tool Flow (tool definition to execution) +``` +Tool trait impl (tools/builtin/*.rs or tools/mcp/client.rs or tools/wasm/wrapper.rs) + → ToolRegistry::register() (tools/registry.rs) + → tool_definitions() builds Vec for LLM + → ToolDefinition { name, description, parameters } (llm/provider.rs) + → Serialized to ChatCompletionTool (nearai_chat.rs) + → LLM returns ToolCall { id, name, arguments } + → agent_loop.rs executes via execute_chat_tool() + → Safety layer sanitizes output + → Result added as ChatMessage::tool_result() +``` + +## Tracing Instructions + +1. **Read** each file in the relevant flow path, focusing on the functions that handle the data. +2. **Identify transforms**: Where does the data change shape? (e.g., `McpTool.input_schema` → `ToolDefinition.parameters` → `ChatCompletionTool.function.parameters`) +3. **Identify failure points**: Where could the data be lost, malformed, or misrouted? +4. **Report the chain**: List every file:line involved, what happens at each step, and where the issue (if any) is. + +## Key Files Quick Reference + +| Area | File | Key Functions | +|------|------|---------------| +| Message dispatch | `src/agent/agent_loop.rs` | `handle_message`, `process_user_input`, `process_approval`, `run_agentic_loop` | +| Input parsing | `src/agent/submission.rs` | `SubmissionParser::parse` | +| LLM reasoning | `src/llm/reasoning.rs` | `respond_with_tools`, `select_tools`, `plan` | +| Chat completions | `src/llm/nearai_chat.rs` | `complete_with_tools`, `From` | +| Responses API | `src/llm/nearai.rs` | `complete_with_tools`, `split_messages` | +| Channel trait | `src/channels/channel.rs` | `Channel`, `StatusUpdate`, `IncomingMessage` | +| Web gateway | `src/channels/web/mod.rs` | `send_status`, `send_response` | +| Web server | `src/channels/web/server.rs` | Route handlers, SSE endpoints | +| Web frontend | `src/channels/web/static/app.js` | SSE listeners, DOM builders | +| Tool registry | `src/tools/registry.rs` | `tool_definitions`, `get`, `register` | +| MCP tools | `src/tools/mcp/client.rs` | `McpToolWrapper`, `list_tools`, `call_tool` | +| MCP protocol | `src/tools/mcp/protocol.rs` | `McpTool`, `inputSchema` | +| Safety | `src/safety/sanitizer.rs` | `sanitize_tool_output`, `wrap_for_llm` | +| Session state | `src/agent/session.rs` | `ThreadState`, `Turn`, `PendingApproval` | + +## Output Format + +Report your findings as: + +1. **Flow path**: The specific chain of files and functions involved +2. **Data transforms**: How the data changes at each step +3. **Findings**: Any bugs, missing data, or suspicious patterns +4. **Recommendation**: What to fix or investigate further