feature-metrics

feature-metrics is a command for coding agents from MartyBonacci/specswarm. It costs 19 tokens per session (3,135 once invoked), scanned A, original, MIT.

A command-line dashboard for viewing metrics about software features. It accepts options for a project path, recent features, exports, feature or sprint filters, and detailed output, then uses a metrics collector when available.

In plain words
What is it for?
Use it to view recent feature metrics, filter by feature or sprint, show details, choose a project directory, or export results to a CSV file.
Why use it?
It gives developers a repeatable way to inspect feature-level measurements without manually locating the project or collector. It can also save the results for later use.

Command

Part of the ss plugin — 10 skills, 34 commands, 6 agents, 4 hooks shipped together

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add commands/martybonacci/specswarm/metrics
Clone the repo
git clone --depth 1 https://github.com/MartyBonacci/specswarm

Or install ss, the plugin that ships this one along with the rest of its 10 skills, 34 commands, 6 agents, 4 hooks.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for feature-metrics

README.md
[![agentmods](https://agentmods.dev/badge/commands/martybonacci/specswarm/metrics.svg)](https://agentmods.dev/commands/martybonacci/specswarm/metrics)
Your own site
<a href="https://agentmods.dev/commands/martybonacci/specswarm/metrics"><img src="https://agentmods.dev/badge/commands/martybonacci/specswarm/metrics.svg" alt="Measured on agentmods" height="20"></a>
Per session 19 Only the description is in the session, so the agent can decide to use it. The body loads when it is invoked.
When invoked 3,135 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00019 $0.03135
Opus 5 $0.00010 $0.01568
Sonnet 5 $0.00004 $0.00627
Haiku 4.5 $0.00002 $0.00314

Measured 5d ago against content hash 30f4e48d22ea, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

feature-metrics scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 5d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

plugins/ss/commands/metrics.md · 421 lines

How it starts

The opening of the file, as written. The whole thing — 421 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Feature-Level Metrics Dashboard

#!/bin/bash

# Parse arguments
PROJECT_PATH=""
RECENT_COUNT=10
EXPORT_FILE=""
FEATURE_NUMBER=""
SPRINT_FILTER=""
SHOW_DETAILS=false

while [[ $# -gt 0 ]]; do
  case $1 in
    --recent)
      RECENT_COUNT="$2"
      shift 2
      ;;
    --export)
      EXPORT_FILE="${2:-feature-metrics-$(date +%Y%m%d_%H%M%S).csv}"
      shift 2
      ;;
    --feature)
      FEATURE_NUMBER="$2"
      shift 2
      ;;
    --sprint)
      SPRINT_FILTER="$2"
      shift 2
      ;;
    --details)
      SHOW_DETAILS=true
      shift
      ;;
    --path)
      PROJECT_PATH="$2"
      shift 2
      ;;
    *)
      if [ -z "$PROJECT_PATH" ] && [ -d "$1" ]; then
        PROJECT_PATH="$1"
      fi
      shift
      ;;
  esac
done

# Default to current directory if no path specified
PROJECT_PATH="${PROJECT_PATH:-$(pwd)}"

# Source the feature metrics collector library
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
if [ -f "$PLUGIN_ROOT/lib/feature-metrics-collector.sh" ]; then
  source "$PLUGIN_ROOT/lib/feature-metrics-collector.sh"
else
  echo "⚠️  Feature metrics collector not available — using basic metrics"
fi

# Set project root for library
export PROJECT_ROOT="$PROJECT_PATH"

echo "📊 SpecSwarm Feature-Level Metrics Dashboard"
echo "============================================"
echo ""
echo "Project: $PROJECT_PATH"
echo ""

# Collect all feature data
echo "🔍 Scanning for features..."
features_json=$(fm_analyze_all_features "$PROJECT_PATH")

# Check if any features found
total_features=$(echo "$features_json" | jq 'length')

if [ "$total_features" -eq 0 ]; then
  echo ""
  echo "ℹ️  No features found in $PROJECT_PATH"
  echo ""
  echo "Features are detected by the presence of spec.md files."
  echo "Make sure you're in a project directory with SpecSwarm features."
  echo ""
  echo "Searched for:"
  echo "  - */spec.md"
  echo "  - features/*/spec.md"
  echo "  - .features/*/spec.md"
  echo ""
  exit 0
fi

echo "✅ Found $total_features features"
echo ""

# Calculate aggregates
aggregates=$(fm_calculate_aggregates "$features_json")

# Display based on requested view
if [ -n "$FEATURE_NUMBER" ]; then
  #==========================================================================
  # SINGLE FEATURE DETAILS
  #==========================================================================

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Feature $FEATURE_NUMBER Details"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  # Find the feature
  feature=$(echo "$features_json" | jq --arg num "$FEATURE_NUMBER" \
    '.[] | select(.metadata.feature_number == $num)')

  if [ -z "$feature" ] || [ "$feature" = "null" ]; then
    echo "❌ Feature $FEATURE_NUMBER not found"
    exit 1
  fi

  # Display metadata
  echo "📋 Metadata"
  echo "───────────"
  echo "$feature" | jq -r '"  Name: \(.metadata.feature_name)
  Status: \(.metadata.status)
  Parent Branch: \(.metadata.parent_branch)
  Created: \(.metadata.created_at)
  Completed: \(.metadata.completed_at // "N/A")
  Directory: \(.metadata.feature_dir)"'
  echo ""

  # Display task stats
  echo "✅ Tasks"
  echo "────────"
  echo "$feature" | jq -r '"  Total: \(.tasks.total)
  Completed: \(.tasks.completed) (\(.tasks.completion_rate)%)
  Failed: \(.tasks.failed)
  Pending: \(.tasks.pending)"'
  echo ""

  # Display test stats
  if [ "$(echo "$feature" | jq '.tests.total_tests')" -gt 0 ]; then
    echo "🧪 Tests"
    echo "────────"
    echo "$feature" | jq -r '"  Total: \(.tests.total_tests)
  Passing: \(.tests.passing_tests) (\(.tests.pass_rate)%)
  Failing: \(.tests.failing_tests)"'
    echo ""
  fi

  # Display git stats
  echo "🔀 Git History"
  echo "──────────────"
  echo "$feature" | jq -r '"  Branch: \(.git.branch)
  Commits: \(.git.commits)
  Merged: \(.git.merged)
  Merge Date: \(.git.merge_date // "N/A")"'
  echo ""

  # v7.16.0 (AUTO-MAGIC WS8): open DECIDED-BY-DATA deferrals for this feature.
  # A fork deferred to a metric is a decision with a due date — surface it so
  # the review-when condition doesn't rot forgotten in the spec.
  DBD_LIB="${PLUGIN_DIR}/lib/decisions/decided-by-data.sh"
  if [ -f "$DBD_LIB" ]; then
    # shellcheck disable=SC1091
    source "$DBD_LIB"
    DBD_MARKERS=$(ss_dbd_scan_feature "$FEATURE_DIR" 2>/dev/null || true)
    if [ -n "$DBD_MARKERS" ]; then
      echo "📊 Decided-by-Data (open deferrals)"
      echo "───────────────────────────────────"
      while IFS=$'\t' read -r mfile mline metric review; do
        printf "  • %s — review when: %s  (%s:%s)\n" "$metric" "$review" "$(basename "$mfile")" "$mline"
      done <<< "$DBD_MARKERS"
      echo "  Resolve each: gather the metric, make the ruling, replace the marker with the decision (+ distill via ss_taste_add)."
      echo ""
    fi
  fi

elif [ -n "$SPRINT_FILTER" ]; then
  #==========================================================================
  # SPRINT AGGREGATE VIEW
  #==========================================================================

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Sprint: $SPRINT_FILTER"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  # Filter features for this sprint
  sprint_features=$(fm_filter_features "$features_json" "metadata.parent_branch" "$SPRINT_FILTER")
  sprint_count=$(echo "$sprint_features" | jq 'length')

  if [ "$sprint_count" -eq 0 ]; then
    echo "ℹ️  No features found for sprint: $SPRINT_FILTER"
    exit 0
  fi

  # Calculate sprint aggregates
  sprint_aggregates=$(fm_calculate_aggregates "$sprint_features")

  echo "📊 Sprint Statistics"
  echo "────────────────────"
  echo "$sprint_aggregates" | jq -r '"  Total Features: \(.features.total)
  Completed: \(.features.completed)
  In Progress: \(.features.in_progress)

  Total Tasks: \(.tasks.total)
  Completed: \(.tasks.completed)
  Failed: \(.tasks.failed)
  Avg Completion Rate: \(.tasks.avg_completion_rate)%

  Total Tests: \(.tests.total)
  Passing: \(.tests.passing) (\(.tests.avg_pass_rate)%)
  Failing: \(.tests.failing)"'
  echo ""

  echo "📝 Features in $SPRINT_FILTER"
  echo "─────────────────────────────"
  echo "$sprint_features" | jq -r '.[] | "  [\(.metadata.feature_number)] \(.metadata.feature_name)
    Status: \(.metadata.status) | Tasks: \(.tasks.completed)/\(.tasks.total) | Tests: \(.tests.passing_tests)/\(.tests.total_tests)\n"'
  echo ""

else
  #==========================================================================
  # DASHBOARD SUMMARY VIEW
  #==========================================================================

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Overall Statistics"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  echo "📊 Features"
  echo "───────────"
  echo "$aggregates" | jq -r '"  Total: \(.features.total)
  Completed: \(.features.completed)
  In Progress: \(.features.in_progress)"'
  echo ""

  echo "✅ Tasks"
  echo "────────"
  echo "$aggregates" | jq -r '"  Total: \(.tasks.total)
  Completed: \(.tasks.completed)
  Failed: \(.tasks.failed)
  Avg Completion Rate: \(.tasks.avg_completion_rate)%"'
  echo ""

  echo "🧪 Tests"
  echo "────────"
  if [ "$(echo "$aggregates" | jq '.tests.total')" -gt 0 ]; then
    echo "$aggregates" | jq -r '"  Total: \(.tests.total)
  Passing: \(.tests.passing) (\(.tests.avg_pass_rate)%)
  Failing: \(.tests.failing)"'
  else
    echo "  No test data found"
  fi
  echo ""

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Recent Features (Last $RECENT_COUNT)"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  # Get recent features
  recent_features=$(fm_get_recent "$features_json" "$RECENT_COUNT")

  # Display recent features table
  echo "$recent_features" | jq -r '.[] |
    "[\(.metadata.feature_number)] \(.metadata.feature_name)
  Status: \(.metadata.status) | Parent: \(.metadata.parent_branch)
  Tasks: \(.tasks.completed)/\(.tasks.total) (\(.tasks.completion_rate)%)  | Tests: \(.tests.passing_tests)/\(.tests.total_tests) (\(.tests.pass_rate)%)
  Created: \(.metadata.created_at)
  "'

  # Sprint breakdown
  echo ""
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Features by Sprint/Parent Branch"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  # Group by parent branch
  echo "$features_json" | jq -r 'group_by(.metadata.parent_branch) |
    .[] |
    "[\(.[0].metadata.parent_branch)]
  Features: \(length)
  Tasks Completed: \([.[].tasks.completed] | add)/\([.[].tasks.total] | add)
  "'

fi

# Export to CSV if requested
if [ -n "$EXPORT_FILE" ]; then
  echo ""
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Exporting to CSV"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo ""

  exported_file=$(fm_export_csv "$features_json" "$EXPORT_FILE")
  echo "✅ Metrics exported to: $exported_file"
  echo ""
  echo "Total rows: $((total_features + 1))"  # +1 for header
  echo ""
fi

# Help message for next steps
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Available Commands"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "  /ss:metrics                  Dashboard summary"
echo "  /ss:metrics --recent 20      Show last 20 features"
echo "  /ss:metrics --feature 015    Feature 015 details"
echo "  /ss:metrics --sprint sprint-4   Sprint aggregates"
echo "  /ss:metrics --export         Export to CSV"
echo "  /ss:metrics --path /project  Analyze specific project"
echo ""

Read the full file on GitHub · 421 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 5d ago First seen · 421 lines · 19 tokens per session scan A 30f4e48d22ea

Subscribe to this mod's changes

feature-metrics is a command published in the GitHub repository MartyBonacci/specswarm (65 stars, last pushed 1mo ago), licensed MIT. It adds 19 tokens to every session and 3,135 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.