From 875ba62fb2030c762b7cd06f6d6a48ea12708106 Mon Sep 17 00:00:00 2001 From: zerafachris Date: Wed, 15 Jul 2026 11:38:37 +0200 Subject: [PATCH] feat(hook): custom trace tags and session grouping via commands Adds two opt-in, related config options for attributing traces to external work items, both empty by default (no behavior change for existing users): - CC_LANGFUSE_TAG_COMMAND: run once per hook run; each non-empty stdout line is merged into the trace tags (de-duped, order-preserving). - CC_LANGFUSE_SESSION_LABEL_COMMAND + CC_LANGFUSE_SESSION_LABEL_MODE: use the first stdout line as a label to group the session's traces (prefix / collapse / off). One script can drive both -- tags use every line, the label uses the first. Session labeling changes only the trace grouping id; the incremental-read state key stays on the raw session id, so relabeling never re-emits past turns and needs no state migration. Both paths are fail-open and bounded (timeout, tag count/length, 199-char session id cap). Adds tests/unit/test_custom_tags.py (15 cases). Full suite green. Co-Authored-By: Claude Opus 4.8 --- .claude-plugin/plugin.json | 22 ++++++ README.md | 44 ++++++++++++ hooks/langfuse_hook.py | 123 +++++++++++++++++++++++++++++++- tests/unit/test_custom_tags.py | 125 +++++++++++++++++++++++++++++++++ 4 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_custom_tags.py diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 35c551c..43d0d6c 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -61,6 +61,28 @@ "type": "string", "title": "Trace ID seed", "description": "Optional. Derive deterministic trace ids as Langfuse.create_trace_id(seed=f\"{seed}:{turn_number}\") so external systems can precompute them. Use a unique seed per session (e.g. the --session-id UUID). Leave empty for auto-generated ids." + }, + "CC_LANGFUSE_TAG_COMMAND": { + "type": "string", + "title": "Custom tag command", + "description": "Optional shell command run once per hook run; each non-empty line of its stdout is added to the trace tags. Use it to attribute traces to a git branch, ticket id, CI run, or cost center without editing the hook. Fail-open and bounded (see CC_LANGFUSE_TAG_TIMEOUT; max 20 tags of 64 chars). Example: a script that echoes 'sc-1234' derived from the current branch." + }, + "CC_LANGFUSE_TAG_TIMEOUT": { + "type": "number", + "title": "Custom tag command timeout (seconds)", + "description": "Timeout for CC_LANGFUSE_TAG_COMMAND and CC_LANGFUSE_SESSION_LABEL_COMMAND. Default 2. If a command runs longer it is killed and contributes nothing.", + "default": 2 + }, + "CC_LANGFUSE_SESSION_LABEL_COMMAND": { + "type": "string", + "title": "Session label command", + "description": "Optional shell command whose first non-empty stdout line becomes a label used to group this session's traces in Langfuse (e.g. a ticket id from the branch). The same script can serve CC_LANGFUSE_TAG_COMMAND (tags use every line, the label uses the first). Fail-open and bounded." + }, + "CC_LANGFUSE_SESSION_LABEL_MODE": { + "type": "string", + "title": "Session label mode", + "description": "How the label groups sessions. 'prefix' (default): session id becomes '