feat(claude): Add raw hook capture and Claude agent configuration#56
feat(claude): Add raw hook capture and Claude agent configuration#56ivke995 wants to merge 6 commits into
Conversation
53d3ae6 to
a73ccfa
Compare
Implement hidden `sce hooks claude-capture` subcommand for raw Claude hook JSON intake with PostToolUse model enrichment from transcript. Add Pkl-generated Claude project settings with capture hook config, and Claude agent/skill/command definitions for the SCE workflow. - Add ClaudeCapture subcommand to hooks CLI surface (hidden) - Add ClaudeCaptureEvent enum: SessionStart, UserPromptSubmit, PostToolUse, Stop - Implement PostToolUse model enrichment via claude_transcript JSONL transcript model extraction helper - Refactor trace persistence into reusable building blocks (persist_serialized_trace_payload_at, persist_trace_payload_with_retries) - Add RepoPaths::claude_capture_tmp_dir() for canonical path ownership of context/tmp/claude/ - Add Pkl-generated config/.claude/settings.json registering capture hooks for all four event types - Add Claude agent, command, and skill definitions under .claude/ enabling SCE workflow in Claude Code - Update context documentation for new Claude capture surface Co-authored-by: SCE <sce@crocoder.dev>
… intake Remove the `sce hooks claude-capture` hidden CLI route, `ClaudeCaptureEvent` enum variant, `claude_transcript.rs` enrichment module, and `RepoPaths::claude_capture_tmp_dir()` — the raw JSON diagnostic path that wrote Claude hook payloads under `context/tmp/claude/` without DB persistence or cross-editor reuse. Add `sce hooks session-model` STDIN intake for normalized model attribution upsert into the new AgentTraceDb `session_models` table (migration 008). `diff-trace` now accepts optional `model_id` and resolves it from `session_models` when absent, enabling cross-editor reuse: OpenCode sends `model_id` directly, Claude relies on DB resolution. Generate a Claude TypeScript event-adapter runtime at `.claude/plugins/sce-agent-trace.ts` that emits normalized `session-model` and `diff-trace` payloads to the shared Rust intake. Update the generated-config pipeline (Pkl renderers, OpenCode plugin registration, settings) and build fileset to include the new agent-trace-plugin source tree. Update `context/` docs to reflect the removed raw capture route, the new session-model command routing, and the cross-editor shared boundary at `sce hooks diff-trace`. Co-authored-by: SCE <sce@crocoder.dev>
Remove the unused CLI dev-dependency from Cargo.toml and Cargo.lock, leaving the crate without declared dev-dependencies. Update the documented CLI dependency and styling baseline to reflect the current manifest state. Co-authored-by: SCE <sce@crocoder.dev>
Claude sends model attribution via `session-model` at SessionStart rather than including model_id in every diff-trace payload. The parser previously required model_id, rejecting all Claude diff traces. - Change DiffTraceInsert.model_id from &str to Option<&str> - Change DiffTracePayload.model_id from String to Option<String> - Add optional_string_field helper for JSON parsing - Wire resolve_model closure for DB lookup when model_id absent - Thread resolved model_id through persistence functions Co-authored-by: SCE <sce@crocoder.dev>
Add checked-in diff_creation fixtures for Claude Write/Edit PostToolUse payloads and test deriveClaudeDiffTracePayload through the exported derivation seam. Cover create and edit scenarios with exact expected patch comparisons, and update Agent Trace and patch-service context to document the fixture coverage. Co-authored-by: SCE <sce@crocoder.dev>
Delete unused input.json sidecars from the Claude diff-creation fixture suite. The active golden tests consume claude-post-tool-use.json and expected.patch, so the patch-service context now documents that fixture contract directly. Co-authored-by: SCE <sce@crocoder.dev>
d8df734 to
01a6359
Compare
| format!("Invalid session-model payload from STDIN: {detail}.") | ||
| } | ||
|
|
||
| fn sm_non_empty(payload: &serde_json::Map<String, Value>, field: &str) -> Result<String> { |
There was a problem hiding this comment.
We already have helper to check for non empty string field (required_non_empty_string_field).
| clippy::cast_possible_truncation, | ||
| clippy::cast_sign_loss | ||
| )] | ||
| fn sm_u64(payload: &serde_json::Map<String, Value>, field: &str) -> Result<u64> { |
There was a problem hiding this comment.
This is duplicate of required_u64_millisecond_field
| ))) | ||
| } | ||
|
|
||
| fn sm_nullable_or_non_empty( |
There was a problem hiding this comment.
I believe this is duplicate of optional_string_field
| return; | ||
| } | ||
|
|
||
| // For PostToolUse, attempt best-effort diff-trace forwarding |
There was a problem hiding this comment.
I think comment is misleading.
| try { | ||
| const parseResult = parseClaudeHookPayloadJson(rawJson); | ||
| if (parseResult.status !== "ok") { | ||
| return; |
There was a problem hiding this comment.
We could handle this parsing failure better.
| ["hooks", "session-model"], | ||
| `${JSON.stringify({ | ||
| sessionID: sessionId, | ||
| time: currentTimeMs(context.now), |
There was a problem hiding this comment.
Why not just Date.now()?
| payload.tool_version, | ||
| payload.claude_version, | ||
| payload.version, |
There was a problem hiding this comment.
We expect claude to have different properties for version?
| } | ||
|
|
||
| try { | ||
| const parseResult = parseClaudeHookPayloadJson(rawJson); |
There was a problem hiding this comment.
You could parse hook payload at the top of the function and send parsed result to handlers.
| if (value === undefined) { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (value === null) { | ||
| return null; | ||
| } | ||
|
|
||
| if (typeof value !== "string") { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Function could be simplified
function normalizeOptionalVersion(value: unknown): string | null {
if (!value || typeof value !== "string") {
return null;
}
const normalized = value.trim();
return normalized.length === 0 ? null : normalized;
}
| "originalFile", | ||
| "original_file", |
There was a problem hiding this comment.
Do we expect this property in different formats?
No description provided.