From d7a4d5aab6c5373d5b86e691d0628fe0eff28e78 Mon Sep 17 00:00:00 2001 From: hvsk004 Date: Thu, 9 Apr 2026 19:03:44 +0530 Subject: [PATCH 1/5] Add Claude plugin files for ctrlb-decompose including marketplace, plugin, README, hooks, and skills documentation --- .claude-plugin/marketplace.json | 27 +++++ plugin/.claude-plugin/plugin.json | 12 +++ plugin/README.md | 49 +++++++++ plugin/bin/check-log-paste | 71 ++++++++++++ plugin/hooks/hooks.json | 14 +++ plugin/skills/analyze-logs/SKILL.md | 161 ++++++++++++++++++++++++++++ 6 files changed, 334 insertions(+) create mode 100644 .claude-plugin/marketplace.json create mode 100644 plugin/.claude-plugin/plugin.json create mode 100644 plugin/README.md create mode 100755 plugin/bin/check-log-paste create mode 100644 plugin/hooks/hooks.json create mode 100644 plugin/skills/analyze-logs/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..c4479b4 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,27 @@ +{ + "name": "ctrlb-hq", + "owner": { + "name": "CtrlB", + "email": "engineering@ctrlb.ai" + }, + "metadata": { + "description": "Official CtrlB plugins for Claude Code" + }, + "plugins": [ + { + "name": "ctrlb-decompose", + "source": { + "source": "git-subdir", + "url": "https://github.com/ctrlb-hq/ctrlb-decompose.git", + "path": "plugin" + }, + "description": "Analyze log files with ctrlb-decompose — pattern clustering, anomaly detection, and severity scoring", + "version": "1.0.0", + "author": { "name": "CtrlB", "email": "engineering@ctrlb.ai" }, + "homepage": "https://github.com/ctrlb-hq/ctrlb-decompose", + "repository": "https://github.com/ctrlb-hq/ctrlb-decompose", + "license": "MIT", + "keywords": ["logs", "log-analysis", "observability", "patterns", "anomaly-detection"] + } + ] +} diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json new file mode 100644 index 0000000..bf84c21 --- /dev/null +++ b/plugin/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "ctrlb-decompose", + "description": "Analyze log files with ctrlb-decompose — pattern clustering, anomaly detection, and severity scoring", + "version": "1.0.0", + "author": { + "name": "CtrlB", + "email": "engineering@ctrlb.ai" + }, + "homepage": "https://github.com/ctrlb-hq/ctrlb-decompose", + "repository": "https://github.com/ctrlb-hq/ctrlb-decompose", + "license": "MIT" +} diff --git a/plugin/README.md b/plugin/README.md new file mode 100644 index 0000000..ba3b681 --- /dev/null +++ b/plugin/README.md @@ -0,0 +1,49 @@ +# ctrlb-decompose Claude Code Plugin + +Adds log analysis to Claude Code via [ctrlb-decompose](https://github.com/ctrlb-hq/ctrlb-decompose) — compress millions of log lines into a handful of actionable patterns with typed variable statistics, anomaly detection, and severity scoring. + +## Install + +In Claude Code: + +``` +/plugin marketplace add ctrlb-hq/ctrlb-decompose +/plugin install ctrlb-decompose@ctrlb-hq +``` + +## Usage + +Just ask Claude to analyze logs — the skill triggers automatically: + +- "Analyze the errors in `/var/log/app.log`" +- "What are the most common patterns in this log file?" +- "Summarize these logs:" followed by pasted log lines + +If `ctrlb-decompose` is not installed on your system, Claude will detect your OS +and walk you through installation (Homebrew, apt/rpm package, or binary download). +If those fail, it can build from source using the Rust toolchain. + +## What the skill does + +1. Checks if `ctrlb-decompose` is installed; installs it if not +2. Runs `ctrlb-decompose --llm` on the log file or pasted text +3. Interprets the output — surfaces errors first, explains anomalies, summarizes variable patterns + +## Manual install of ctrlb-decompose + +**macOS:** +```bash +brew tap ctrlb-hq/tap && brew install ctrlb-decompose +``` + +**Debian / Ubuntu:** +```bash +curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.deb +sudo dpkg -i ctrlb-decompose_amd64.deb +``` + +**Build from source:** +```bash +git clone https://github.com/ctrlb-hq/ctrlb-decompose.git +cd ctrlb-decompose && cargo build --release +``` diff --git a/plugin/bin/check-log-paste b/plugin/bin/check-log-paste new file mode 100755 index 0000000..cd749e1 --- /dev/null +++ b/plugin/bin/check-log-paste @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +""" +UserPromptSubmit hook for ctrlb-decompose plugin. + +Detects when the user pastes a large volume of log-like content and injects +a warning into Claude's context, asking it to redirect to a file-based workflow +rather than processing raw pasted logs inline. + +Input (stdin): JSON {"prompt": "..."} +Output (stdout): warning text injected as context feedback for Claude +Exit 0: always allow the prompt through +""" + +import sys +import json +import re + +def main(): + try: + data = json.load(sys.stdin) + except (json.JSONDecodeError, ValueError): + sys.exit(0) + + prompt = data.get("prompt", "") + if not prompt: + sys.exit(0) + + lines = prompt.splitlines() + total_lines = len(lines) + + # Heuristic: count lines that look like structured log entries + # Matches: ISO timestamps, syslog timestamps, Apache log format, + # lines with log levels (ERROR/WARN/INFO/DEBUG/FATAL etc.) + log_pattern = re.compile( + r"(" + r"\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # ISO 8601 / datetime + r"|\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}" # syslog: Jan 5 12:00:00 + r"|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*\[.*\]" # Apache: IP [date] + r"|\b(ERROR|WARN(?:ING)?|INFO|DEBUG|FATAL|CRITICAL|TRACE|NOTICE)\b" + r")", + re.IGNORECASE, + ) + + log_line_count = sum(1 for line in lines if log_pattern.search(line)) + + # Trigger if at least 50 lines look like logs + THRESHOLD = 50 + if log_line_count < THRESHOLD: + sys.exit(0) + + # Estimate character volume + char_count = len(prompt) + + print( + f"[ctrlb-decompose hook] The user's message contains approximately " + f"{log_line_count} log lines ({total_lines} total lines, ~{char_count:,} characters). " + f"This is a large paste that will consume significant context.\n\n" + f"Before proceeding:\n" + f"1. Ask the user if they can save the logs to a file and share the path instead — " + f"this is always preferred for large volumes.\n" + f"2. If they insist on using the pasted content, use the Write tool to save it to " + f"/tmp/ctrlb_input.log, then run ctrlb-decompose on that file.\n" + f"3. After analysis, do NOT quote or echo any raw log lines in your response. " + f"Work only from the ctrlb-decompose output." + ) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/plugin/hooks/hooks.json b/plugin/hooks/hooks.json new file mode 100644 index 0000000..4de030c --- /dev/null +++ b/plugin/hooks/hooks.json @@ -0,0 +1,14 @@ +{ + "hooks": { + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/bin/check-log-paste" + } + ] + } + ] + } +} diff --git a/plugin/skills/analyze-logs/SKILL.md b/plugin/skills/analyze-logs/SKILL.md new file mode 100644 index 0000000..3962d83 --- /dev/null +++ b/plugin/skills/analyze-logs/SKILL.md @@ -0,0 +1,161 @@ +--- +name: analyze-logs +description: > + Analyze log files or raw log text using ctrlb-decompose. Use when the user + asks to: analyze logs, investigate errors in log files, understand log patterns, + summarize a log file, detect anomalies, or identify the most common log events. +--- + +# ctrlb-decompose Log Analysis + +## Before you begin — large log volumes + +**If the user signals they are about to paste log content** (e.g. "here are my +logs", "let me paste some logs"), ask first: + +> About how many lines are you sharing? If it's more than ~100 lines, it's much +> better to save them to a file and share the path — it keeps the conversation +> context clean and ctrlb-decompose handles the file directly. +> +> ```bash +> # Save your logs to a file, then share the path: +> ctrlb-decompose --llm --top 20 /path/to/your.log +> ``` + +Only proceed with pasted content if: +- The user insists on pasting, or +- The volume is clearly small (a handful of lines) + +**After processing any pasted content:** do not quote, repeat, or reference +individual raw log lines in your response. ctrlb-decompose already compressed +them — treat the raw input as consumed and work only from the tool output. + +--- + +## Step 1 — Check installation + +Run: +```bash +which ctrlb-decompose +``` + +If found, skip to Step 2. + +If not found, detect the OS and offer to install: + +```bash +uname -s +``` + +### macOS (Darwin) + +Check if Homebrew is available: +```bash +which brew +``` + +If Homebrew is available, ask the user: +> ctrlb-decompose is not installed. I can install it via Homebrew. May I run: +> ``` +> brew tap ctrlb-hq/tap && brew install ctrlb-decompose +> ``` + +If the user approves, run it, then verify with `which ctrlb-decompose`. + +If Homebrew is not installed, offer to install it first: +> Homebrew is not installed either. I can install Homebrew and then ctrlb-decompose. +> This will require your password. May I proceed? + +If approved, run the official Homebrew installer, then install ctrlb-decompose. + +### Linux + +Check the distro: +```bash +cat /etc/os-release +``` + +**Debian / Ubuntu** (`ID=debian`, `ID=ubuntu`, or `ID_LIKE` contains `debian`): +> ctrlb-decompose is not installed. May I run the following to install it? +> ``` +> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.deb && sudo dpkg -i ctrlb-decompose_amd64.deb +> ``` + +**RHEL / Fedora / CentOS** (`ID` is `rhel`, `fedora`, or `centos`): +> ctrlb-decompose is not installed. May I run: +> ``` +> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.rpm && sudo rpm -i ctrlb-decompose_amd64.rpm +> ``` + +**Other Linux** (generic binary): +> May I download the ctrlb-decompose binary and place it in /usr/local/bin? +> ``` +> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose-linux-x86_64 && chmod +x ctrlb-decompose-linux-x86_64 && sudo mv ctrlb-decompose-linux-x86_64 /usr/local/bin/ctrlb-decompose +> ``` + +### Fallback — build from source + +If any installation method fails (wrong architecture, missing binary for the +platform, package manager errors, download failures), offer to build from source: + +> Installation did not succeed. As a fallback, I can clone the repository and +> build ctrlb-decompose from source. This requires `git` and the Rust toolchain. +> May I proceed? + +Check prerequisites: +```bash +which git && which cargo +``` + +If `cargo` is missing, offer to install Rust first: +> The Rust toolchain (`cargo`) is not installed. May I install it via rustup? +> ``` +> curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source "$HOME/.cargo/env" +> ``` + +Once prerequisites are confirmed and approved, clone and build: +```bash +git clone https://github.com/ctrlb-hq/ctrlb-decompose.git /tmp/ctrlb-decompose-build \ + && cd /tmp/ctrlb-decompose-build \ + && cargo build --release \ + && sudo cp target/release/ctrlb-decompose /usr/local/bin/ctrlb-decompose \ + && cd / && rm -rf /tmp/ctrlb-decompose-build +``` + +Ask permission before each step if the user has not already given blanket approval. + +### After installation + +Run `which ctrlb-decompose` to confirm success, then continue to Step 2. + +--- + +## Step 2 — Run analysis + +Run ctrlb-decompose in LLM-optimized output mode. + +**When the user provides a file path:** +```bash +ctrlb-decompose --llm --top 20 "$FILE_PATH" +``` + +**When the user pastes log text directly in the conversation:** + +Use the Write tool to save the pasted content to `/tmp/ctrlb_input.log` (this +handles multi-line text, special characters, and quotes correctly), then run: +```bash +ctrlb-decompose --llm --top 20 /tmp/ctrlb_input.log && rm /tmp/ctrlb_input.log +``` + +Use `--top 20` by default. If the user wants to see more patterns, use `--top 50`. + +--- + +## Step 3 — Interpret results + +After running, summarize the output for the user: + +1. **Lead with problems** — highlight ERROR, FATAL, and WARN severity patterns first +2. **Call out anomalies** — frequency spikes, high error rates, low-cardinality variables +3. **Summarize key variables** — most common IPs, slowest durations (p99), notable enum values +4. **Suggest next steps** — which patterns warrant further investigation and why From 9bb7c1587a10033cbdc33e698d4982f1b8d8ade4 Mon Sep 17 00:00:00 2001 From: hvsk004 Date: Fri, 10 Apr 2026 13:29:27 +0530 Subject: [PATCH 2/5] Refine analyze-logs skill description and workflow for clarity and user guidance --- plugin/skills/analyze-logs/SKILL.md | 152 ++++++++++++---------------- 1 file changed, 66 insertions(+), 86 deletions(-) diff --git a/plugin/skills/analyze-logs/SKILL.md b/plugin/skills/analyze-logs/SKILL.md index 3962d83..5f67878 100644 --- a/plugin/skills/analyze-logs/SKILL.md +++ b/plugin/skills/analyze-logs/SKILL.md @@ -1,47 +1,53 @@ --- name: analyze-logs description: > - Analyze log files or raw log text using ctrlb-decompose. Use when the user - asks to: analyze logs, investigate errors in log files, understand log patterns, - summarize a log file, detect anomalies, or identify the most common log events. + Use this skill when the user asks to: "analyze logs", "analyze this log file", + "what's in my logs", "investigate errors in logs", "find patterns in log file", + "summarize logs", "detect anomalies in logs", "parse log file", "understand log + output", "what are the most common errors", or shares a log file path or pastes + log content for review. +version: 1.0.0 --- # ctrlb-decompose Log Analysis -## Before you begin — large log volumes +## Overview -**If the user signals they are about to paste log content** (e.g. "here are my -logs", "let me paste some logs"), ask first: +Analyze log files or raw log text using [ctrlb-decompose](https://github.com/ctrlb-hq/ctrlb-decompose) — a CLI that compresses millions of log lines into a handful of typed patterns with variable statistics, anomaly detection, and severity scoring. Output is optimized for LLM consumption via `--llm`. -> About how many lines are you sharing? If it's more than ~100 lines, it's much -> better to save them to a file and share the path — it keeps the conversation -> context clean and ctrlb-decompose handles the file directly. -> +## Workflow + +1. Gate large pastes — redirect to file path if volume is high +2. Check if `ctrlb-decompose` is installed; install if not +3. Run analysis on the file or pasted content +4. Interpret results — lead with problems, surface anomalies, suggest next steps + +--- + +## Large Paste Policy + +If the user signals they are about to paste log content (e.g. "here are my logs", "let me paste some logs"), ask first: + +> About how many lines are you sharing? For more than ~100 lines, save to a file and share the path — it keeps context clean and ctrlb-decompose handles files directly: > ```bash -> # Save your logs to a file, then share the path: > ctrlb-decompose --llm --top 20 /path/to/your.log > ``` -Only proceed with pasted content if: -- The user insists on pasting, or -- The volume is clearly small (a handful of lines) +Proceed with pasted content only if the user insists or the volume is clearly small (a handful of lines). -**After processing any pasted content:** do not quote, repeat, or reference -individual raw log lines in your response. ctrlb-decompose already compressed -them — treat the raw input as consumed and work only from the tool output. +**After processing pasted content:** do not quote, repeat, or reference individual raw log lines. Work only from ctrlb-decompose output — the raw lines are consumed. --- -## Step 1 — Check installation +## Step 1 — Verify Installation -Run: ```bash which ctrlb-decompose ``` -If found, skip to Step 2. +If found → skip to Step 2. -If not found, detect the OS and offer to install: +If not found → detect the OS: ```bash uname -s @@ -49,71 +55,50 @@ uname -s ### macOS (Darwin) -Check if Homebrew is available: ```bash which brew ``` -If Homebrew is available, ask the user: -> ctrlb-decompose is not installed. I can install it via Homebrew. May I run: -> ``` -> brew tap ctrlb-hq/tap && brew install ctrlb-decompose -> ``` - -If the user approves, run it, then verify with `which ctrlb-decompose`. - -If Homebrew is not installed, offer to install it first: -> Homebrew is not installed either. I can install Homebrew and then ctrlb-decompose. -> This will require your password. May I proceed? +**Homebrew found** — install ctrlb-decompose: +```bash +brew tap ctrlb-hq/tap && brew install ctrlb-decompose +``` -If approved, run the official Homebrew installer, then install ctrlb-decompose. +**Homebrew not found** — install Homebrew first: +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` +Then install ctrlb-decompose: +```bash +brew tap ctrlb-hq/tap && brew install ctrlb-decompose +``` ### Linux -Check the distro: ```bash cat /etc/os-release ``` -**Debian / Ubuntu** (`ID=debian`, `ID=ubuntu`, or `ID_LIKE` contains `debian`): -> ctrlb-decompose is not installed. May I run the following to install it? -> ``` -> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.deb && sudo dpkg -i ctrlb-decompose_amd64.deb -> ``` - -**RHEL / Fedora / CentOS** (`ID` is `rhel`, `fedora`, or `centos`): -> ctrlb-decompose is not installed. May I run: -> ``` -> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.rpm && sudo rpm -i ctrlb-decompose_amd64.rpm -> ``` +| Distro | Install command | +|--------|----------------| +| Debian / Ubuntu (`ID=debian`, `ID=ubuntu`, or `ID_LIKE` contains `debian`) | `curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.deb && sudo dpkg -i ctrlb-decompose_amd64.deb` | +| RHEL / Fedora / CentOS (`ID` is `rhel`, `fedora`, or `centos`) | `curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose_amd64.rpm && sudo rpm -i ctrlb-decompose_amd64.rpm` | +| Other Linux | `curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose-linux-x86_64 && chmod +x ctrlb-decompose-linux-x86_64 && sudo mv ctrlb-decompose-linux-x86_64 /usr/local/bin/ctrlb-decompose` | -**Other Linux** (generic binary): -> May I download the ctrlb-decompose binary and place it in /usr/local/bin? -> ``` -> curl -LO https://github.com/ctrlb-hq/ctrlb-decompose/releases/latest/download/ctrlb-decompose-linux-x86_64 && chmod +x ctrlb-decompose-linux-x86_64 && sudo mv ctrlb-decompose-linux-x86_64 /usr/local/bin/ctrlb-decompose -> ``` - -### Fallback — build from source +### Fallback — Build from Source -If any installation method fails (wrong architecture, missing binary for the -platform, package manager errors, download failures), offer to build from source: +If any installation method fails (wrong architecture, missing binary, package manager error): -> Installation did not succeed. As a fallback, I can clone the repository and -> build ctrlb-decompose from source. This requires `git` and the Rust toolchain. -> May I proceed? - -Check prerequisites: ```bash which git && which cargo ``` -If `cargo` is missing, offer to install Rust first: -> The Rust toolchain (`cargo`) is not installed. May I install it via rustup? -> ``` -> curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source "$HOME/.cargo/env" -> ``` +If `cargo` is missing, install Rust: +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source "$HOME/.cargo/env" +``` -Once prerequisites are confirmed and approved, clone and build: +Clone and build: ```bash git clone https://github.com/ctrlb-hq/ctrlb-decompose.git /tmp/ctrlb-decompose-build \ && cd /tmp/ctrlb-decompose-build \ @@ -122,40 +107,35 @@ git clone https://github.com/ctrlb-hq/ctrlb-decompose.git /tmp/ctrlb-decompose-b && cd / && rm -rf /tmp/ctrlb-decompose-build ``` -Ask permission before each step if the user has not already given blanket approval. - -### After installation - -Run `which ctrlb-decompose` to confirm success, then continue to Step 2. +Confirm with `which ctrlb-decompose`, then continue to Step 2. --- -## Step 2 — Run analysis - -Run ctrlb-decompose in LLM-optimized output mode. +## Step 2 — Run Analysis -**When the user provides a file path:** +**File path provided:** ```bash ctrlb-decompose --llm --top 20 "$FILE_PATH" ``` -**When the user pastes log text directly in the conversation:** - -Use the Write tool to save the pasted content to `/tmp/ctrlb_input.log` (this -handles multi-line text, special characters, and quotes correctly), then run: +**Pasted log text:** Use the Write tool to save content to `/tmp/ctrlb_input.log` (handles multi-line text, special characters, and quotes correctly), then: ```bash ctrlb-decompose --llm --top 20 /tmp/ctrlb_input.log && rm /tmp/ctrlb_input.log ``` -Use `--top 20` by default. If the user wants to see more patterns, use `--top 50`. +Use `--top 20` by default. Increase to `--top 50` if the user wants more patterns. For all available flags and options, run `ctrlb-decompose --help`. --- -## Step 3 — Interpret results +## Step 3 — Interpret Results + +Present findings in this priority order: -After running, summarize the output for the user: +| Priority | Focus | What to cover | +|----------|-------|---------------| +| 1st | **Problems** | ERROR, FATAL, WARN patterns — frequency, rate, associated variables | +| 2nd | **Anomalies** | Frequency spikes, high error rates, unexpectedly low-cardinality variables | +| 3rd | **Variable summaries** | Most common IPs, slowest durations (p99), notable enum distributions | +| 4th | **Next steps** | Which patterns to drill into, what filters or time ranges to investigate | -1. **Lead with problems** — highlight ERROR, FATAL, and WARN severity patterns first -2. **Call out anomalies** — frequency spikes, high error rates, low-cardinality variables -3. **Summarize key variables** — most common IPs, slowest durations (p99), notable enum values -4. **Suggest next steps** — which patterns warrant further investigation and why +Keep the response grounded in ctrlb-decompose output. Do not speculate about root causes beyond what the pattern data supports. From 9336a2ed5207d55525dca2f55d7230661e0439a3 Mon Sep 17 00:00:00 2001 From: hvsk004 Date: Fri, 10 Apr 2026 13:40:08 +0530 Subject: [PATCH 3/5] Add Claude Code plugin section to README with installation and usage instructions --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 8b7d4a0..3044602 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,31 @@ Options: --- +## Claude Code Plugin + +Use ctrlb-decompose directly from [Claude Code](https://claude.ai/code) — no CLI knowledge needed. The plugin installs ctrlb-decompose automatically and lets you analyze logs just by asking. + +### Install + +``` +/plugin marketplace add ctrlb-hq/ctrlb-decompose +/plugin install ctrlb-decompose@ctrlb-hq +``` + +### Usage + +Just describe what you want in plain language: + +- "Analyze the errors in `/var/log/app.log`" +- "What are the most common patterns in this log file?" +- "Summarize these logs and highlight anomalies" + +Claude will check if ctrlb-decompose is installed (and walk you through installation if not), run the analysis, and explain the results — surfacing errors first, calling out anomalies, and suggesting what to investigate next. + +See [`plugin/README.md`](plugin/README.md) for full details. + +--- + ## License [MIT](LICENSE) From 357e2c9dd1a9b1876b9846cbe3501d690cf2dd00 Mon Sep 17 00:00:00 2001 From: hvsk004 Date: Fri, 10 Apr 2026 13:46:23 +0530 Subject: [PATCH 4/5] Update repository URLs in marketplace.json to reflect correct GitHub path --- .claude-plugin/marketplace.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c4479b4..1298f41 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,14 +12,14 @@ "name": "ctrlb-decompose", "source": { "source": "git-subdir", - "url": "https://github.com/ctrlb-hq/ctrlb-decompose.git", + "url": "https://github.com/hvsk004/ctrlb-decompose.git", "path": "plugin" }, "description": "Analyze log files with ctrlb-decompose — pattern clustering, anomaly detection, and severity scoring", "version": "1.0.0", "author": { "name": "CtrlB", "email": "engineering@ctrlb.ai" }, - "homepage": "https://github.com/ctrlb-hq/ctrlb-decompose", - "repository": "https://github.com/ctrlb-hq/ctrlb-decompose", + "homepage": "https://github.com/hvsk004/ctrlb-decompose", + "repository": "https://github.com/hvsk004/ctrlb-decompose", "license": "MIT", "keywords": ["logs", "log-analysis", "observability", "patterns", "anomaly-detection"] } From aeb26651656007d999c2beff6d59b052eaac0047 Mon Sep 17 00:00:00 2001 From: hvsk004 Date: Fri, 10 Apr 2026 15:26:51 +0530 Subject: [PATCH 5/5] Enhance UserPromptSubmit hook to process log content and provide context feedback --- plugin/bin/check-log-paste | 205 ++++++++++++++++++++++++++++++------- 1 file changed, 168 insertions(+), 37 deletions(-) diff --git a/plugin/bin/check-log-paste b/plugin/bin/check-log-paste index cd749e1..20ae2ac 100755 --- a/plugin/bin/check-log-paste +++ b/plugin/bin/check-log-paste @@ -2,18 +2,92 @@ """ UserPromptSubmit hook for ctrlb-decompose plugin. -Detects when the user pastes a large volume of log-like content and injects -a warning into Claude's context, asking it to redirect to a file-based workflow -rather than processing raw pasted logs inline. +When the user pastes a large volume of log-like content: + 1. Extracts the log block (first..last log-matching line, preserving continuations) + 2. Runs ctrlb-decompose --llm on the log block + 3. Blocks the original prompt (raw lines never enter Claude's context) + 4. Injects only the compressed ctrlb-decompose output as additionalContext -Input (stdin): JSON {"prompt": "..."} -Output (stdout): warning text injected as context feedback for Claude -Exit 0: always allow the prompt through +Falls back to a warning-only inject (no block) if ctrlb-decompose is not +installed or fails, so the hook never breaks the conversation. + +Input (stdin): JSON {"prompt": "...", ...} +Output (stdout): JSON with decision/hookSpecificOutput +Exit code: always 0 """ -import sys import json +import os import re +import shutil +import subprocess +import sys +import tempfile + +# Trigger threshold: minimum log-matching lines to activate the hook +THRESHOLD = 50 + +# Set to False to disable blocking (inject-only fallback mode) +BLOCK_ENABLED = True + +LOG_PATTERN = re.compile( + r"(" + r"\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # ISO 8601 / datetime + r"|\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}" # syslog: Jan 5 12:00:00 + r"|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*\[.*\]" # Apache: IP [date] + r"|\b(ERROR|WARN(?:ING)?|INFO|DEBUG|FATAL|CRITICAL|TRACE|NOTICE)\b" + r")", + re.IGNORECASE, +) + + +def block_with_context(reason_msg, context): + """Block the prompt and inject context for Claude.""" + print(json.dumps({ + "decision": "block", + "reason": reason_msg, + "hookSpecificOutput": { + "hookEventName": "UserPromptSubmit", + "additionalContext": context, + }, + })) + sys.exit(0) + + +def not_installed_fallback(log_line_count): + """ctrlb-decompose is not installed — block and ask Claude to install it first.""" + block_with_context( + reason_msg=( + f"Your message contained {log_line_count} log lines but ctrlb-decompose " + f"is not installed yet. Let me install it first — then paste your logs again." + ), + context=( + f"[ctrlb-decompose hook] The user pasted ~{log_line_count} log lines but " + f"ctrlb-decompose is not installed on this machine.\n\n" + f"Install ctrlb-decompose now using the analyze-logs skill installation flow " + f"(Step 1), then tell the user: " + f"'ctrlb-decompose is now installed — please paste your logs again and I'll " + f"analyze them automatically.'" + ), + ) + + +def process_failed_fallback(log_line_count): + """ctrlb-decompose ran but produced no output — block and redirect to file.""" + block_with_context( + reason_msg=( + f"Your message contained {log_line_count} log lines but ctrlb-decompose " + f"could not process the pasted content. Please save the logs to a file and " + f"share the path instead." + ), + context=( + f"[ctrlb-decompose hook] The user pasted ~{log_line_count} log lines but " + f"ctrlb-decompose failed to produce output (possible format issue or timeout).\n\n" + f"Ask the user to save their logs to a file and share the path. " + f"Then run: ctrlb-decompose --llm --top 20 /path/to/their.log" + ), + ) + def main(): try: @@ -28,41 +102,98 @@ def main(): lines = prompt.splitlines() total_lines = len(lines) - # Heuristic: count lines that look like structured log entries - # Matches: ISO timestamps, syslog timestamps, Apache log format, - # lines with log levels (ERROR/WARN/INFO/DEBUG/FATAL etc.) - log_pattern = re.compile( - r"(" - r"\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # ISO 8601 / datetime - r"|\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}" # syslog: Jan 5 12:00:00 - r"|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*\[.*\]" # Apache: IP [date] - r"|\b(ERROR|WARN(?:ING)?|INFO|DEBUG|FATAL|CRITICAL|TRACE|NOTICE)\b" - r")", - re.IGNORECASE, - ) - - log_line_count = sum(1 for line in lines if log_pattern.search(line)) + # Find which lines match the log pattern + matches = [bool(LOG_PATTERN.search(line)) for line in lines] + log_line_count = sum(matches) - # Trigger if at least 50 lines look like logs - THRESHOLD = 50 if log_line_count < THRESHOLD: sys.exit(0) - # Estimate character volume - char_count = len(prompt) - - print( - f"[ctrlb-decompose hook] The user's message contains approximately " - f"{log_line_count} log lines ({total_lines} total lines, ~{char_count:,} characters). " - f"This is a large paste that will consume significant context.\n\n" - f"Before proceeding:\n" - f"1. Ask the user if they can save the logs to a file and share the path instead — " - f"this is always preferred for large volumes.\n" - f"2. If they insist on using the pasted content, use the Write tool to save it to " - f"/tmp/ctrlb_input.log, then run ctrlb-decompose on that file.\n" - f"3. After analysis, do NOT quote or echo any raw log lines in your response. " - f"Work only from the ctrlb-decompose output." + # --- Separate log block from user question --- + # Log block: everything from the first to last matching line (inclusive). + # This preserves continuation lines (stack traces, JSON payloads) that sit + # between log-matching lines but don't match the pattern themselves. + first_log_idx = next(i for i, m in enumerate(matches) if m) + last_log_idx = len(matches) - 1 - next(i for i, m in enumerate(reversed(matches)) if m) + + log_block_lines = lines[first_log_idx:last_log_idx + 1] + + # User's question: non-empty lines before the first log line and after the last + question_lines = ( + [l.strip() for l in lines[:first_log_idx] if l.strip()] + + [l.strip() for l in lines[last_log_idx + 1:] if l.strip()] ) + user_question = " ".join(question_lines) if question_lines else None + + # --- Check ctrlb-decompose is available --- + if not shutil.which("ctrlb-decompose"): + not_installed_fallback(log_line_count) + + # --- Run ctrlb-decompose on the extracted log block --- + tmp_path = None + try: + with tempfile.NamedTemporaryFile( + mode="w", suffix=".log", prefix="ctrlb_hook_", delete=False + ) as tmp: + tmp.write("\n".join(log_block_lines)) + tmp_path = tmp.name + + result = subprocess.run( + ["ctrlb-decompose", "--llm", "--top", "20", tmp_path], + capture_output=True, + text=True, + timeout=30, + ) + ctrlb_output = result.stdout.strip() + except Exception: + ctrlb_output = "" + finally: + if tmp_path and os.path.exists(tmp_path): + os.unlink(tmp_path) + + if not ctrlb_output: + process_failed_fallback(log_line_count) + + # --- Build context to inject --- + question_note = ( + f"User's question: {user_question}\n\n" if user_question + else "" + ) + additional_context = ( + f"{question_note}" + f"ctrlb-decompose compressed {log_line_count} log lines " + f"({len(log_block_lines)} in log block) into patterns:\n\n" + f"{ctrlb_output}\n\n" + f"Answer the user's question based solely on this output. " + f"Do not reference or quote any raw log lines." + ) + + if BLOCK_ENABLED: + question_ack = f'Your question ("{user_question}") was noted.' if user_question else "" + reason = ( + f"Your message contained {log_line_count} log lines. " + f"They were compressed to patterns by ctrlb-decompose. " + f"{question_ack} " + f"Continue the conversation to get the analysis." + ).strip() + + print(json.dumps({ + "decision": "block", + "reason": reason, + "hookSpecificOutput": { + "hookEventName": "UserPromptSubmit", + "additionalContext": additional_context, + }, + })) + else: + # Inject-only fallback: raw lines still in context but Claude is + # told to use only the compressed output + print(json.dumps({ + "hookSpecificOutput": { + "hookEventName": "UserPromptSubmit", + "additionalContext": additional_context, + }, + })) sys.exit(0)