Add a Claude Code session-transcript adapter#8
Conversation
Adds skillloop/adapters/claude_code.py and wires `ingest claude-code`, so SkillLoop can ingest Claude Code sessions alongside the generic and hermes adapters. Claude Code stores sessions as JSONL under ~/.claude/projects/<slug>/*.jsonl with a nested envelope and block-array content, so the generic adapter cannot read them. The adapter normalizes that into AgentTrace: - two-pass parse matching tool_use blocks to their tool_result across turns, populating ToolCall name/arguments/result/status/started_at/ended_at/duration_ms - flattens text blocks into content; tolerates a partial trailing line on a live session; drops meta lines and pure tool_result turns - --no-sidechains to exclude subagent sidechain turns - forward-compatible extended-thinking capture (Claude Code persists thinking blocks with empty text + a signature, so the reasoning text is stripped) 6 tests added; full suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR introduces a Claude Code session adapter for SkillLoop that parses JSONL session transcripts into structured ChangesClaude Code Session Adapter
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/test_claude_code.py (1)
55-63: ⚡ Quick winAdd a regression test for malformed non-trailing JSON lines.
Line 55 onward covers empty sessions, but there’s no assertion that a malformed middle JSONL line raises an error instead of being silently dropped. This protects the parsing contract and prevents future silent data loss regressions.
🧪 Suggested test
+import pytest + def test_claude_code_adapter_rejects_empty_session(tmp_path): @@ raise AssertionError("expected ValueError for a session with no usable messages") + + +def test_claude_code_adapter_rejects_malformed_non_trailing_line(tmp_path): + session = tmp_path / "malformed_mid.jsonl" + session.write_text( + "\n".join( + [ + json.dumps({"message": {"role": "user", "content": [{"type": "text", "text": "hi"}]}}), + '{"message": ', # malformed non-trailing line + json.dumps({"message": {"role": "assistant", "content": [{"type": "text", "text": "bye"}]}}), + ] + ) + + "\n", + encoding="utf-8", + ) + with pytest.raises(ValueError): + load_claude_code_session(session)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_claude_code.py` around lines 55 - 63, Add a regression test that ensures load_claude_code_session raises on a malformed non-trailing JSONL line: create a temp session file via _write_session containing a valid message, then a malformed JSON line (e.g., truncated or invalid JSON), then another valid message, and assert that calling load_claude_code_session(session) raises ValueError (similar to test_claude_code_adapter_rejects_empty_session but specifically exercising a malformed middle line); reference the test name test_claude_code_adapter_rejects_empty_session and the loader function load_claude_code_session when locating where to add the new test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@skillloop/adapters/claude_code.py`:
- Around line 69-76: The loop currently swallows every JSONDecodeError for any
line (iterating raw_text -> raw), but we should only tolerate a malformed JSON
on the final non-empty line; change the loop to detect whether the current `raw`
is the last non-empty line (e.g., pre-split `raw_text.splitlines()` into a list
and use index/enumeration or compute remaining non-empty lines) and in the
except json.JSONDecodeError block only `continue` when there are no further
non-empty lines; otherwise re-raise (or propagate) the error so corrupted middle
lines are not silently dropped. Reference the variables and loop using
`raw_text`, `raw`, `parsed` and the JSON parse try/except surrounding
`json.loads(raw)`.
---
Nitpick comments:
In `@tests/test_claude_code.py`:
- Around line 55-63: Add a regression test that ensures load_claude_code_session
raises on a malformed non-trailing JSONL line: create a temp session file via
_write_session containing a valid message, then a malformed JSON line (e.g.,
truncated or invalid JSON), then another valid message, and assert that calling
load_claude_code_session(session) raises ValueError (similar to
test_claude_code_adapter_rejects_empty_session but specifically exercising a
malformed middle line); reference the test name
test_claude_code_adapter_rejects_empty_session and the loader function
load_claude_code_session when locating where to add the new test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 774ee182-7695-4351-af9b-b5977d7a6f3f
📒 Files selected for processing (3)
skillloop/adapters/claude_code.pyskillloop/cli.pytests/test_claude_code.py
|
Nice adapter. One preservation detail I would keep even if The most useful fields are It also helps render subagent sidechains later without losing the original transcript topology. Generated with ax. |
Claude Code session-transcript adapter
Adds an adapter so SkillLoop can ingest Claude Code sessions, alongside the existing
genericandhermesadapters.Claude Code persists sessions as JSONL under
~/.claude/projects/<slug>/*.jsonlwith a nested envelope ({message: {role, content: [blocks]}}), block-array content (text/tool_use/tool_result), and interleaved non-message meta lines, so thegenericadapter cannot read it. This normalizes that intoAgentTrace:tool_resultblocks and matches them back to the originatingtool_useacross turns, populatingToolCallname / arguments / result / status /started_at/ended_at/duration_ms.textblocks intocontent; tolerates a partial trailing JSON line on a live session; drops meta lines and pure-tool_resultturns.--no-sidechainsflag to exclude subagent sidechain turns.thinkingfield plus asignature, so the reasoning text is stripped from the transcript and there is nothing to recover from a Claude Code session (documented in code).Usage
Tests
6 new tests in
tests/test_claude_code.py, verified end to end against a real ~860-message session.Heads-up (separate from this PR): on Windows,
test_store.py::test_store_preserves_raw_trace_and_hashesfails becauseraw_artifact_refis stored withos.sep(backslashes) while the test asserts forward slashes. Happy to send a one-line fix as a follow-up.🤖 Generated with Claude Code
Summary by CodeRabbit
ingestcommand to load either the latest session (scoped by project) or a provided input file, with options to control sidechain inclusion.