<< All versions
Skill v1.0.0
currentAutomated scan100/100gwittebolle/claude-carbon/carbon-report
──Details
PublishedSeptember 27, 2026 at 03:24 AM
Content Hashsha256:9ef12472f3d50ef6...
Git SHA0061d7efc97c
──Files
Files (1 file, 7.4 KB)
SKILL.md7.4 KBactive
SKILL.md · 166 lines · 7.4 KB
version: "1.0.0" name: carbon-report description: Display CO2 emissions report for Claude Code sessions
Run the following bash script exactly as written and present the output to the user. Do not paraphrase or reformat the results.
bash
#!/usr/bin/env bash# Locate the install (equivalence factors + shared locale lib live in the repo),# mirroring /carbon-card: status line wiring first, then plugin root, then default.# Windows (Git Bash): CLAUDE_* variables and the paths stored in settings.json can# arrive in the native "C:\Users\..." spelling, which bash cannot open. cygpath# ships with Git for Windows; everywhere else this is the identity function.ccp() {case "${OSTYPE:-}" in msys*|cygwin*) ;; *) printf '%s' "$1"; return 0 ;; esaccase "$1" in[A-Za-z]:[\\/]*|*\\*) cygpath -u "$1" 2>/dev/null || printf '%s' "$1" ;;*) printf '%s' "$1" ;;esac}CFG="$(ccp "${CLAUDE_CONFIG_DIR:-$HOME/.claude}")"REPO_DIR=""if command -v jq >/dev/null 2>&1 && [ -f "$CFG/settings.json" ]; thenSL_CMD="$(jq -r '.statusLine.command // empty' "$CFG/settings.json" 2>/dev/null)"# statusLine.command stores a shell-escaped path: expand ~ and unescape spacesSL_CMD="${SL_CMD//\\ / }"; SL_CMD="${SL_CMD/#\~/$HOME}"; SL_CMD="$(ccp "$SL_CMD")"[ -n "$SL_CMD" ] && [ -f "$SL_CMD" ] && REPO_DIR="$(cd "$(dirname "$SL_CMD")/.." 2>/dev/null && pwd)"fi[ -z "$REPO_DIR" ] && [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && REPO_DIR="$(ccp "$CLAUDE_PLUGIN_ROOT")"[ -z "$REPO_DIR" ] && [ -n "${CLAUDE_CARBON_DIR:-}" ] && REPO_DIR="$(ccp "$CLAUDE_CARBON_DIR")"if [ -z "$REPO_DIR" ]; then# Pure marketplace installs reach here: settings.json carries no statusLine.command# (the status line is wired through plugin.json) and CLAUDE_PLUGIN_ROOT is not in the# env of a skill's bash. Pick the newest cached plugin copy, the same scan as# statusline.sh CACHE_LATEST, keeping only candidates that ship the equivalence lib.CACHE_LATEST=""for D in "$CFG/plugins/cache"/*/claude-carbon/*/; do[ -f "${D}scripts/equiv-lib.sh" ] || continueif [ -z "$CACHE_LATEST" ] || [ "$(printf '%s\n%s\n' "$CACHE_LATEST" "$D" | sort -V | tail -1)" = "$D" ]; thenCACHE_LATEST="$D"fidone[ -n "$CACHE_LATEST" ] && REPO_DIR="${CACHE_LATEST%/}"fi[ -z "$REPO_DIR" ] && REPO_DIR="$HOME/code/claude-carbon"FACTORS_FILE="$REPO_DIR/data/factors.json"if ! command -v jq >/dev/null 2>&1 || [ ! -f "$FACTORS_FILE" ] || [ ! -f "$REPO_DIR/scripts/equiv-lib.sh" ]; thenecho "Error: jq and $REPO_DIR/data/factors.json are required (equivalence factors)." >&2exit 1fi# Pick the equivalence set from the user locale BEFORE LC_ALL=C below masks it:# the factors are country-specific (a French car and a US car differ by ~1.7x).. "$REPO_DIR/scripts/equiv-lib.sh"EQUIV_SET="$(detect_equiv_set)"# Force C locale: comma-decimal locales (de_DE, fr_FR) make awk mis-parse# "431.7045" as 431 and print "431,0" instead of "431.7"export LC_ALL=CDB_PATH="$(ccp "${CLAUDE_CARBON_DB:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}/claude-carbon/carbon.db}")"if [ ! -f "$DB_PATH" ]; thenecho "Database not found. Run setup.sh first:"echo " bash ${CLAUDE_CARBON_DIR:-$HOME/code/claude-carbon}/scripts/setup.sh"exit 1fiCURRENT_YEAR="$(date +%Y)"TODAY="$(date +%Y-%m-%d)"# Ensure the excluded column exists on pre-existing DBs (idempotent).# Excluded sessions (non-Anthropic models, e.g. local models) are left out of all aggregates.sqlite3 "$DB_PATH" "ALTER TABLE sessions ADD COLUMN excluded INTEGER DEFAULT 0;" 2>/dev/null || trueNOT_EXCLUDED="COALESCE(excluded, 0) = 0"# --- Aggregates ---TODAY_CO2="$(sqlite3 "$DB_PATH" "SELECT COALESCE(SUM(co2_grams), 0) FROM sessions WHERE ${NOT_EXCLUDED} AND started_at LIKE '${TODAY}%';" | awk '{printf "%.1f", $1}')"TODAY_SESSIONS="$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM sessions WHERE ${NOT_EXCLUDED} AND started_at LIKE '${TODAY}%';")"YEAR_CO2="$(sqlite3 "$DB_PATH" "SELECT COALESCE(SUM(co2_grams), 0) FROM sessions WHERE ${NOT_EXCLUDED} AND started_at LIKE '${CURRENT_YEAR}%';" | awk '{printf "%.1f", $1}')"YEAR_SESSIONS="$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM sessions WHERE ${NOT_EXCLUDED} AND started_at LIKE '${CURRENT_YEAR}%';")"ALL_CO2="$(sqlite3 "$DB_PATH" "SELECT COALESCE(SUM(co2_grams), 0) FROM sessions WHERE ${NOT_EXCLUDED};" | awk '{printf "%.1f", $1}')"ALL_SESSIONS="$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM sessions WHERE ${NOT_EXCLUDED};")"ALL_COST="$(sqlite3 "$DB_PATH" "SELECT COALESCE(SUM(cost_usd), 0) FROM sessions WHERE ${NOT_EXCLUDED};" | awk '{printf "%.2f", $1}')"# --- Equivalences (all-time total) ---# Factors: data/factors.json "equivalences" (single source, shared with the cards);# derivations and sources: METHODOLOGY.md "Equivalences used in reports". France and# undetected locales get the ADEME/SNCF set, a US locale the EPA one, everyone else# the world average: a car, a kWh and a kilo of beef each differ by 2x+ between# countries, so a single set would be wrong for most readers.EQUIV_ROWS="$(jq -r --arg set "$EQUIV_SET" \'.equivalences[$set][] | "\(.divisor)|\(.decimals)|\(.label)|\(.source)"' "$FACTORS_FILE" \| while IFS='|' read -r divisor decimals label source; docount="$(echo "$ALL_CO2" | awk -v d="$divisor" -v p="$decimals" '{ fmt = "%." p "f"; printf fmt, $1 / d }')"printf '%s|%s|%s\n' "$count" "$label" "$source"done)"# A malformed or hand-edited factors.json must fail loudly, not print an empty sectionif [ -z "$EQUIV_ROWS" ]; thenecho "Error: could not read the '$EQUIV_SET' equivalence set from $FACTORS_FILE" >&2exit 1fi# --- Top 5 sessions by CO2 ---TOP5="$(sqlite3 -separator '|' "$DB_PATH" \"SELECT DATE(started_at), project, ROUND(co2_grams, 2), model, ROUND(cost_usd, 4)FROM sessionsWHERE ${NOT_EXCLUDED}ORDER BY co2_grams DESCLIMIT 5;")"# --- By project ---BY_PROJECT="$(sqlite3 -separator '|' "$DB_PATH" \"SELECT project, ROUND(SUM(co2_grams), 2), COUNT(*), ROUND(SUM(cost_usd), 4)FROM sessionsWHERE ${NOT_EXCLUDED}GROUP BY projectORDER BY SUM(co2_grams) DESC;")"echo "==============================="echo " claude-carbon report"echo "==============================="echo ""echo "Today (${TODAY})"echo " CO2 : ${TODAY_CO2}g"echo " Sessions : ${TODAY_SESSIONS}"echo ""echo "${CURRENT_YEAR}"echo " CO2 : ${YEAR_CO2}g"echo " Sessions : ${YEAR_SESSIONS}"echo ""echo "All time"echo " CO2 : ${ALL_CO2}g"echo " Sessions : ${ALL_SESSIONS}"echo " Cost : \$${ALL_COST}"echo ""echo "--- Equivalences (all-time) ---"while IFS='|' read -r count label source; do[ -z "$count" ] && continueprintf " %10s %-20s (%s)\n" "$count" "$label" "$source"done <<< "$EQUIV_ROWS"echo ""echo "--- Top 5 sessions by CO2 ---"echo "Date | Project | CO2 (g) | Model | Cost"echo "------------|-------------------------|---------|--------------------------------|--------"while IFS='|' read -r date project co2 model cost; doprintf "%-11s | %-23s | %-7s | %-30s | \$%s\n" "$date" "$project" "$co2" "$model" "$cost"done <<< "$TOP5"echo ""echo "--- By project ---"echo "Project | CO2 (g) | Sessions | Cost"echo "-------------------------|----------|----------|--------"while IFS='|' read -r project co2 sessions cost; doprintf "%-25s | %-8s | %-8s | \$%s\n" "$project" "$co2" "$sessions" "$cost"done <<< "$BY_PROJECT"echo ""echo "Team view (same methodology): tokenclimate.com"echo ""