Standardizes the process of building data-driven report cards for ClaudeBar.

Install

mkdir -p .claude/skills/add-report && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/11941" && unzip -o skill.zip -d .claude/skills/add-report && rm skill.zip

Installs to .claude/skills/add-report

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Guide for adding new report cards to ClaudeBar that analyze local data sources and display metrics with comparison deltas. Use this skill when: (1) Adding a new report/analytics card (e.g., weekly summary, model breakdown, session stats) (2) Creating data analysis features that read local files and display aggregated metrics (3) Adding comparison cards that show "today vs previous" style deltas (4) Building any feature that follows the DailyUsage pattern (parse → aggregate → report → card)
494 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Define report data source
  • Define metrics for aggregation
  • Define comparison periods
  • Create architecture diagrams for reports
  • Implement domain models for stats and reports
  • Implement infrastructure for parsers and analyzers

How it works

The skill guides the creation of new report cards by defining the data flow from source to card view, implementing domain models for stats and reports, and building infrastructure for parsers and analyzers.

Inputs & outputs

You give it
User request for a new report card feature
You get back
A new report card integrated into ClaudeBar displaying aggregated metrics and comparison deltas

When to use add-report

  • Create weekly usage report
  • Build data breakdown card
  • Add comparison dashboard

About this skill

Add Report Card to ClaudeBar

Add new report cards that analyze local data sources, compute metrics with comparison deltas, and display them in the existing card UI style using TDD.

When to Use

This skill covers adding report-style features — cards that:

  • Read and parse local data files (JSONL, JSON, CSV, etc.)
  • Aggregate metrics (cost, tokens, time, counts)
  • Compare periods (today vs yesterday, this week vs last week)
  • Display results in glassmorphism cards matching the existing UI

Architecture Pattern

Every report feature follows this data flow:

Data Source → Parser → Analyzer → Report Model → UsageSnapshot → Card View

Mapped to the codebase layers:

LayerLocationWhat to Create
DomainSources/Domain/{Feature}/Rich models + @Mockable protocol
InfrastructureSources/Infrastructure/{Provider}/Parser + Analyzer implementation
AppSources/App/Views/Card view(s)
IntegrationProvider class + statsGridWire analyzer → snapshot → UI

Reference implementation: See references/daily-usage-pattern.md for the complete DailyUsage feature as a working example of this pattern.

Workflow

Phase 0: Architecture Design (get user approval)
    ↓
Phase 1: Domain Models + Tests (TDD Red→Green)
    ↓
Phase 2: Infrastructure Parser + Analyzer + Tests
    ↓
Phase 3: Card View + Integration
    ↓
Phase 4: Verify all tests pass

Phase 0: Architecture Design (MANDATORY)

Before writing code, design the feature and get user approval.

Step 1: Define the Report

Answer these questions:

  • What data source? (JSONL sessions, API response, local DB, etc.)
  • What metrics? (cost, tokens, time, counts, etc.)
  • What comparison period? (today vs yesterday, this week vs last, etc.)
  • Which provider? (Claude, Codex, or generic across all?)
  • How many cards? (one per metric, or a single combined card?)

Step 2: Create Architecture Diagram

Example: Adding a weekly cost breakdown report

┌─────────────────────────────────────────────────────────┐
│  Data Source        Infrastructure          Domain       │
│                                                          │
│  ~/.claude/    →  WeeklyParser        →  WeeklyStat     │
│  projects/        (reads JSONL)          (per-day cost) │
│  *.jsonl                                                 │
│                    WeeklyAnalyzer     →  WeeklyReport   │
│                    (aggregates by        (this week vs  │
│                     week, implements      last week)    │
│                     protocol)                            │
│                          ↓                    ↓          │
│                    Provider.refresh()  → UsageSnapshot   │
│                                          .weeklyReport   │
│                          ↓                               │
│                    statsGrid() → WeeklyCardView          │
└─────────────────────────────────────────────────────────┘

Step 3: Document Components

ComponentPurposeInputsOutputs
{Name}StatSingle period's dataRaw aggregated valuesFormatted strings, isEmpty
{Name}ReportPeriod comparisonTwo StatsDeltas, percentages, progress
{Name}AnalyzingProtocolDate/configReport
{Name}AnalyzerImplementationFile pathsReport
{Name}CardViewUI cardReport + metricGlassmorphism card

Step 4: Get User Approval

Use AskUserQuestion to confirm the design before proceeding.


Phase 1: Domain Models (TDD)

1a. Create the Stat Model

The stat model represents one period's aggregated data with rich formatting behavior.

Location: Sources/Domain/{Feature}/{Name}Stat.swift

Pattern to follow:

import Foundation

public struct {Name}Stat: Sendable, Equatable {
    public let date: Date
    // Add your metrics here
    public let metricA: Decimal
    public let metricB: Int

    // Formatting — encapsulate in the model, not the view
    public var formattedMetricA: String { /* currency, compact number, duration, etc. */ }
    public var formattedMetricB: String { /* ... */ }

    public var isEmpty: Bool { /* all zeros check */ }
    public static func empty(for date: Date) -> Self { /* zero-valued instance */ }
}

Key rules:

  • Use Decimal for monetary values (not Double — floating point errors)
  • Use TimeInterval for durations
  • Use Locale(identifier: "en_US") for currency formatting (not en_US_POSIX which adds a space)
  • All formatting lives in the model — views just read formatted strings
  • isEmpty uses && (all zeros = empty), not ||

1b. Create the Report Model

The report model compares two periods and computes deltas.

Location: Sources/Domain/{Feature}/{Name}Report.swift

Pattern to follow:

public struct {Name}Report: Sendable, Equatable {
    public let current: {Name}Stat   // e.g., today, this week
    public let previous: {Name}Stat  // e.g., yesterday, last week

    // Delta calculations
    public var metricADelta: Decimal { current.metricA - previous.metricA }
    public var metricAChangePercent: Double? {
        guard previous.metricA > 0 else { return nil }  // nil when previous is zero
        // ...
    }

    // Formatted deltas with sign: "+$5.00", "-1.2M"
    public var formattedMetricADelta: String { /* ... */ }

    // Progress for bar display (0-1 ratio of current vs total)
    public var metricAProgress: Double {
        let total = /* current + previous */
        guard total > 0 else { return 0 }
        return current / total
    }
}

Key rules:

  • Change percent returns nil when previous is zero (avoid division by zero)
  • Formatted deltas always include sign (+ or -)
  • Progress is current / (current + previous), clamped to 0-1
  • Use abs() for formatted values, prepend sign separately

1c. Create the Protocol

Location: Sources/Domain/{Feature}/{Name}Analyzing.swift

import Mockable

@Mockable
public protocol {Name}Analyzing: Sendable {
    func analyze() async throws -> {Name}Report
}

1d. Write Tests First

Location: Tests/DomainTests/{Feature}/

Create two test files following Chicago School TDD (test state, not interactions):

  • {Name}StatTests.swift — Test formatting, isEmpty, edge cases
  • {Name}ReportTests.swift — Test deltas, percentages, nil cases, progress
import Foundation
import Testing
@testable import Domain

@Suite
struct {Name}StatTests {
    @Test func `formats metric as expected`() {
        let stat = {Name}Stat(date: Date(), metricA: 14.26, ...)
        #expect(stat.formattedMetricA == "$14.26")
    }
    // ...
}

After writing tests → implement the models → run tests → all green.


Phase 2: Infrastructure (TDD)

2a. Create the Parser (if reading files)

If the report reads local files (JSONL, JSON, CSV), create a parser.

Location: Sources/Infrastructure/{Provider}/{Name}Parser.swift

Pattern: Parser is a struct (not protocol) since it's a pure data transformation.

struct {Name}Parser {
    func parse(fileURL: URL) throws -> [{Name}Record] { /* ... */ }
    func parse(content: String) -> [{Name}Record] { /* for testing */ }
}

2b. Create the Analyzer

Location: Sources/Infrastructure/{Provider}/{Provider}{Name}Analyzer.swift

The analyzer implements the domain protocol and orchestrates:

  1. Find relevant files (filter by modification date for performance)
  2. Parse files into records
  3. Partition records by time period
  4. Aggregate into stat models
  5. Return report

Performance rule: Only scan files modified within the relevant time window. With 2000+ JSONL files, scanning all of them is too slow.

public struct {Provider}{Name}Analyzer: {Name}Analyzing, Sendable {
    public func analyze() async throws -> {Name}Report {
        let files = findRecentFiles(since: periodStart)  // Performance!
        // parse → partition → aggregate → return
    }
}

2c. Write Infrastructure Tests

Location: Tests/InfrastructureTests/{Provider}/

  • Parser tests: valid input, missing fields, malformed data, timestamps
  • Analyzer tests: use temp directories with JSONL content, test date partitioning

Phase 3: Card View + Integration

3a. Create the Card View

Location: Sources/App/Views/{Name}CardView.swift

The card must match the existing glassmorphism style. Use WrappedStatCard as the reference:

struct {Name}CardView: View {
    let metric: {Name}Metric  // enum for each displayable metric
    let report: {Name}Report
    let delay: Double          // for cascading entrance animation

    @Environment(\.appTheme) private var theme

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            // 1. Header: icon + LABEL (uppercased)
            // 2. Large value (e.g., "$14.26" or "19.5M")
            // 3. Progress bar (animated)
            // 4. Delta comparison line (e.g., "Vs Mar 10 -$27.47 (4.9%)")
        }
        .padding(12)
        .background(/* theme.cardGradient + theme.glassBorder stroke */)
        .scaleEffect(isHovering ? 1.015 : 1.0)
        .onHover { isHovering = $0 }
    }
}

Card styling checklist:

  • .padding(12) on the VStack
  • theme.cardGradient fill + theme.glassBorder stroke (1pt)
  • theme.cardCornerRadius for corners
  • theme.fontDesign on all text
  • theme.textPrimary / theme.textSecondary / theme.textTertiary for text hierarchy
  • theme.progressTrack for bar background
  • Hover scale effect (1.015)
  • Animated progress bar with delay parameter

3b. Add to UsageSnapshot

Add an optional field for the report:

// In Sources/Domain/Provider/UsageSnapshot.swift
public let {name}Report: {Name}Report?
// Add to init with default nil

3c. Wire into Provider


Content truncated.

When not to use it

  • When the feature does not involve analyzing local data sources
  • When the feature does not require comparison deltas
  • When the feature does not display results in glassmorphism cards

Limitations

  • Guide for adding new report cards to ClaudeBar that analyze local data sources and display metrics with comparison deltas.
  • Read and parse local data files (JSONL, JSON, CSV, etc.)
  • Display results in glassmorphism cards matching the existing UI

How it compares

This skill provides a structured, TDD-driven workflow for adding new report cards with specific data analysis and UI requirements, unlike general feature development.

Compared to similar skills

add-report side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
add-report (this skill)05moNo flagsAdvanced
streamlit869moNo flagsIntermediate
openalex-database487moReviewIntermediate
data-storytelling472moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by tddworks

View all by tddworks

fix-bug

tddworks

Guide for fixing bugs in ClaudeBar following Chicago School TDD and rich domain design. Use this skill when: (1) User reports a bug or unexpected behavior (2) Fixing a defect in existing functionality (3) User asks "fix this bug" or "this doesn't work correctly" (4) Correcting behavior that violates the user's mental model

1117

add-provider

tddworks

Guide for adding new AI providers to ClaudeBar using TDD patterns. Use this skill when: (1) Adding a new AI assistant provider (like Antigravity, Cursor, etc.) (2) Creating a usage probe for a CLI tool or local API (3) Following TDD to implement provider integration (4) User asks "how do I add a new provider" or "create a provider for X"

11

implement-feature

tddworks

Guide for implementing features in ClaudeBar following architecture-first design, TDD, rich domain models, and Swift 6.2 patterns. Use this skill when: (1) Adding new functionality to the app (2) Creating domain models that follow user's mental model (3) Building SwiftUI views that consume domain models directly (4) User asks "how do I implement X" or "add feature Y" (5) Implementing any feature that spans Domain, Infrastructure, and App layers

16

improvement

tddworks

Guide for making improvements to existing ClaudeBar functionality using TDD. Use this skill when: (1) Enhancing existing features (not adding new ones) (2) Improving UX, performance, or code quality (3) User asks "improve X", "make Y better", or "enhance Z" (4) Small enhancements that don't require full architecture design For NEW features, use implement-feature skill instead.

14

You might also like

Search skills

Search the agent skills registry