Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Daten und Forschung · SKILL.md

add-report

add-report

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) Showing usage history in a new way, or reading another tool's usage logs (3) Adding comparison cards that show "today…

Independent GitHub snapshot · Automated classification · Original documentation governs

Ttddworks / claudebarApache-2.0

Skill-Dokumentation

GitHub

name: add-report description: > 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) Showing usage history in a new way, or reading another tool's usage logs (3) Adding comparison cards that show "today vs previous" style deltas (4) Building any feature that follows the DailyUsage pattern (days → report value → card)

Add Report Card to ClaudeBar

Add report cards that turn a login's usage history into metrics with comparison deltas, shown in the existing card style, test first.

When to Use

This skill covers report-style features — cards that:

  • Aggregate a login's days of usage (cost, tokens, time, sessions)
  • Compare periods (today vs yesterday, this week vs last week)
  • Read another tool's usage logs (as data in its definition)
  • Display results in cards matching the existing UI

First, which kind of report is it?

Usage history is data now (daily-usage design). A report is almost always a new view over days the login already reads, not new reading code. Place it before anything else:

The person wants…It isYou write
a new look at usage this app already reads — this week vs last, cost per model, a 30-day charta report over days: the page asks account.usageHistory?.days(in: range) and a value in Quotas turns the days into the reporta Quotas value + its tests, a card. No reading code
the same history for another tool (its own log files)a usageHistory block in that provider's definitionJSON (paths, where, fields, prices), its golden tests — no Swift
history from a log format no reader understands yeta new reader, named for the format, in Modules/DataSources/Sources/Internal/Logs/the reader, test-first in DataSourcesTests, then the JSON; a row in ENGINE_DESIGN §1
an answer to a different question than "how much did I use?"a capability (CANONICAL §2.1): declared in the definition, a handle on Account that is nil when not declareddesign first; a runner outside the modules is supplied by the Engine (TARGET_ARCHITECTURE §10)

Never: a field on UsageSnapshot (the kernel is shrinking toward Usage, CANONICAL_MODEL §6), an XxxAnalyzer / XxxAnalyzing protocol, a parser in Sources/Infrastructure, or a vendor-named type in a module.

Reference implementation: references/daily-usage-pattern.md — TODAY'S USAGE: Claude's logs and prices as data, UsageLog in DataSources, account.usageHistory, DailyUsageReport in Quotas, DailyUsageCardView. The Leaderboard reads 30 days the same way (login.history.days(in:) → DailyTokens.summed).

Check the design first — the docs are the source of truth (AGENTS.md): USER_JOURNEYS, CANONICAL_MODEL, TARGET_ARCHITECTURE, then the feature's design.md.

How a report over days flows

 <id>.json "usageHistory"  ──►  UsageLog (DataSources)        one per login, built by
                                  reader · PriceList ·          ProviderFactory from the definition
                                  DayAggregator
                                       │
 popover opens (never in the background, #204)
        │                              ▼
 account.usageHistory?.days(in: .last(14))   ──►  [DailyUsageStat]   (closed days from the ledger)
        │
        ▼
 WeeklyReport(days:)            a rich value in Modules/Quotas: deltas, percentages, formatting
        │
        ▼
 WeeklyCardView                 renders what the report says — never compares or counts

Workflow

Phase 0: Place it and design it (get user approval)
    ↓
Phase 1: The report value + tests (TDD Red→Green)
    ↓
Phase 2: Only if needed — the definition's usageHistory, a new reader, or a capability
    ↓
Phase 3: Mockup, card view, reading it on popover open
    ↓
Phase 4: Verify, screenshots on mock data, docs

Phase 0: Place it and design it (MANDATORY)

Step 1: Define the report

  • What does the person want to know? In their words (USER_JOURNEYS).
  • Which kind is it? The table above.
  • Which days? DateRange (.last(n), a week, today vs yesterday).
  • Which logins? A report is per login — two logins are two reports, never summed.
  • How many cards? One per metric, or one combined card.

Step 2: Diagram it

Example: this week vs last week, per login

┌────────────────────────────────────────────────────────────────────┐
│  Providers (module)           Quotas (module)        App           │
│                                                                    │
│  account.usageHistory   ─►  WeeklyReport(days:)  ─►  WeeklyCardView │
│   .days(in: .last(14))       thisWeek / lastWeek      × 3 metrics   │
│   → [DailyUsageStat]         deltas, %, formatted                   │
│                                                                    │
│  No reading code: claude.json's usageHistory already reads the logs│
└────────────────────────────────────────────────────────────────────┘

Step 3: The pieces

PieceModuleOne jobTest
WeeklyReportQuotasturns days into the comparison the card showsTests/DomainTests/<Feature>/
WeeklyCardViewApprenders itAppTests / screenshots
(only if needed) usageHistory block, a reader, a capabilitydefinition / DataSources / Providers—golden tests / DataSourcesTests / ProvidersTests

Step 4: Mockup and approval

A UI change gets a mockup in design-concept/<feature>/ on mock data first (AGENTS.md). Present the doc change, the diagram, the pieces and the mockup; use AskUserQuestion and wait for approval.


Phase 1: The report value (TDD)

Name each test should <outcome> [when <situation>], in the person's words, never a method, type or mechanism verb → Naming tests.

Location: Modules/Quotas/Sources/{Name}Report.swift (beside DailyUsageReport), tests in Tests/DomainTests/{Feature}/{Name}ReportTests.swift (beside DailyUsageReportTests). It is built from days — [DailyUsageStat] — so it never reads a file:

public struct {Name}Report: Sendable, Equatable {
    public let current: [DailyUsageStat]    // e.g. this week
    public let previous: [DailyUsageStat]   // e.g. last week

    /// The days split at `boundary`, as the page asks for them.
    public init(days: [DailyUsageStat], splitAt boundary: Date) { … }

    public var cost: Decimal { current.reduce(0) { $0 + $1.totalCost } }
    public var costChangePercent: Double? {
        let before = previous.reduce(Decimal(0)) { $0 + $1.totalCost }
        guard before > 0 else { return nil }          // nil when there was nothing before
        …
    }
    public var formattedCostDelta: String { … }       // "+$5.00", "-$1.20": the sign always shown
    public var costProgress: Double { … }             // current / (current + previous), 0…1
}

Rules:

  • Decimal for money, TimeInterval for durations; Locale(identifier: "en_US") for currency.
  • Every formatted string and every comparison lives in the value — the view only reads them.
  • A change percent is nil when the previous period is zero; deltas always carry a sign.
  • isEmpty means all zero (&&, not ||).

Write the tests first (state and return values, Chicago school), then the value.


Phase 2: Only if the report needs reading it doesn't have

2a. Another tool's history → its definition

Add a usageHistory block to <id>.json — files, format, where, fields, prices, sessionGap — the shape is in the daily-usage design §2. Golden tests in Modules/Providers/Tests/ over fixture logs (see ClaudeUsageHistoryTests). No Swift.

2b. A log format no reader knows → one reader in DataSources

A reader named for the format (JSONLinesReader, never AcmeLogReader) in Modules/DataSources/Sources/Internal/Logs/, test-first in Modules/DataSources/Tests/Logs/ with neutral fixtures, yielding the one LogRecord every reader yields. Then the JSON uses it. Add its row to ENGINE_DESIGN §1. Only scan files changed within the range (LogFileFinder).

2c. A different question → a capability

Design it in the docs first (CANONICAL §2.1, TARGET_ARCHITECTURE): declared in the definition, its own types in Modules/Providers, an @Observable handle on Account that is nil when not declared — like usageHistory and guestPasses. ProviderFactory.make builds it from the definition; a runner that lives outside the modules (a CLI the App runs, the Keychain) is supplied by the Engine the App builds once. The App never passes anything per provider.


Phase 3: The card, read on popover open

3a. The card view

Location: Sources/App/Views/{Name}CardView.swift. Match the existing cards; DailyUsageCardView is the reference:

struct {Name}CardView: View {
    let metric: {Name}Metric   // one case per card
    let report: {Name}Report
    let delay: Double          // cascading entrance

    @Environment(\.appTheme) private var theme

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            // header: icon + LABEL · the large value · the progress bar · the delta line
        }
        .padding(12)
        .background(/* theme.cardGradient fill + theme.glassBorder stroke */)
    }
}

Card styling checklist: .padding(12) · theme.cardGradient + theme.glassBorder (1pt) · theme.cardCornerRadius · theme.fontDesign on text · theme.textPrimary/Secondary/Tertiary · theme.progressTrack · hover scale 1.015 · progress bar animated with delay.

3b. Read the days when the popover opens

Usage history is read when the popover opens, never in the background (#204), and per login — the page asks the selected login, as MenuContentView does:

.task(id: login.id) {
    guard let history = login.usageHistory else { return }   // nil: this login offers none
    let days = await history.days(in: .last(14))
    report = {Name}Report(days: days, splitAt: weekStart)
}

The view renders report; it never compares, counts or decides from the days itself.

3c. Nothing to register

ProviderFactory.make builds usageHistory from the definition, and every provider is found by ProviderCatalog.detect() (TARGET_ARCHITECTURE §10). The App passes nothing per provider.


Phase 4: Verify

  1. tuist generate
  2. xcodebuild test -workspace ClaudeBar.xcworkspace -scheme ClaudeBar-Workspace -destination 'platform=macOS,arch=arm64' — the full run; tuist test skips cached targets
  3. Run the real popover on mock data (scripts/demo-screenshots.sh) — screenshots, never real names, emails or usage
  4. Docs in the same change: the feature's README.md / design.md, one CHANGELOG line (≤300 chars), python3 scripts/gen-docs.py && python3 scripts/check-docs.py --strict

Checklist

Phase 0

  • Placed: report over days · definition usageHistory · new reader · capability
  • Design in the docs, diagram, pieces table, mockup in design-concept/<feature>/
  • User approved

Phase 1

  • {Name}ReportTests written first (deltas, nil percent, signs, progress, empty)
  • {Name}Report in Modules/Quotas, built from [DailyUsageStat] — green

Phase 2 (only if needed)

  • usageHistory block + golden tests, or a format-named reader in DataSources + its ENGINE_DESIGN row, or a declared capability
  • No UsageSnapshot field, no analyzer protocol, no vendor-named Swift

Phase 3

  • {Name}CardView matching the card style
  • Days read on popover open, per login; nothing passed in ClaudeBarApp

Phase 4

  • Full xcodebuild test green
  • Screenshots on mock data
  • Docs and CHANGELOG line; docs check passes
Gespeicherte Skills