
Peekie
- 31 repo stars
- Updated August 4, 2026
- dodobrands/Peekie
Parse Xcode .xcresult bundles from CI — extract fully-qualified test names, warnings, errors, coverage, and attachments as JSON for jq pipelines.
About
Peekie teaches the agent when and how to use the peekie CLI to read Xcode .xcresult bundles. It surfaces test results, warnings, errors, code coverage, and attachments, emitting JSON suited for jq pipelines and SonarQube reports. Useful for inspecting Swift/iOS CI output and turning raw result bundles into actionable QA data.
- xcresult bundle parsing via peekie CLI
- Fully-qualified test names + coverage
- JSON output for jq pipelines
- SonarQube report export
- Per-subcommand sub-skills
Peekie by the numbers
- Data as of Aug 5, 2026 (Skillselion catalog sync)
/plugin marketplace add dodobrands/Peekie/plugin install peekie@peekieAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| repo stars | ★ 31 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | dodobrands/Peekie ↗ |
What it does
Parse Xcode .xcresult bundles from CI — extract fully-qualified test names, warnings, errors, coverage, and attachments as JSON for jq pipelines.
README.md
Peekie
Peekie is a CLI for Xcode .xcresult bundles. One subcommand per data type — tests, warnings, errors, coverage — each emitting machine-readable JSON (or a human-readable list, or SonarQube XML). Designed to be reached from CI scripts and LLM coding agents (Claude Code, Cursor) without re-explaining how xcresulttool works every time.
Upgrading from 4.x? Read MIGRATION.md.
Table of Contents
Install
The fastest way to get the peekie binary on your PATH is via mise:
mise use github:dodobrands/peekie
Other options: Homebrew tap (coming soon), or download a pre-built binary from the Releases page.
CLI
Five data-axis subcommands. Each runs only the xcrun calls it needs.
peekie tests <xcresult> [--format json|list|sonar] default: list
peekie warnings <xcresult> [--format json|list] default: json
peekie errors <xcresult> [--format json|list] default: json
peekie coverage <xcresult> [--format json|list] default: json
peekie attachments <xcresult> --output-dir <dir> [--format json|list] default: json
peekie tests
Test results, with status filtering and SonarQube export.
# Human-readable, all statuses
peekie tests Tests.xcresult
# Only failures and flaky tests
peekie tests Tests.xcresult --include failure,mixed
# JSON for a dashboard
peekie tests Tests.xcresult --format json > tests.json
# SonarQube generic test execution XML
peekie tests Tests.xcresult --format sonar --tests-path Tests > sonar-tests.xml
Options: --include (comma-separated statuses), --include-device-details, --tests-path (required with --format sonar), --attachments skip|export + --attachments-to <dir> (embeds attachment metadata into each test in the JSON output; required together when exporting).
peekie warnings
Flat JSON array of build warnings. Pipe to jq for filtering.
peekie warnings Tests.xcresult
# [
# {"file":"Foo.swift","line":42,"column":8,"type":"DeprecatedDeclaration","message":"'oldFoo()' is deprecated: use bar"},
# ...
# ]
# Group by type
peekie warnings Tests.xcresult | jq 'group_by(.type) | map({type: .[0].type, count: length})'
# Human-readable
peekie warnings Tests.xcresult --format list
# Foo.swift:42:8 [DeprecatedDeclaration] 'oldFoo()' is deprecated
peekie errors
Same shape as warnings, for the errors[] array. Use as a build-failed gate:
errors=$(peekie errors Tests.xcresult | jq 'length')
[ "$errors" -gt 0 ] && exit 1
peekie coverage
# JSON for a dashboard
peekie coverage Tests.xcresult
# {"coverage": 0.6234, "modules": [{"name":"Calculator","coverage":0.71,"files":[…]}, …]}
# Total in one line
peekie coverage Tests.xcresult | jq '.coverage'
# 0.6234
# Padded table
peekie coverage Tests.xcresult --format list
# Calculator 71.6% (167/233)
# StringUtils 84.0% (42/50)
# total 62.3%
peekie attachments
Exports XCTAttachment / Swift Testing Attachment.record(...) payloads to a directory and emits a flat JSON array describing each one (which test it came from, suggested filename, MIME type, whether it's tied to a failure).
# Export every attachment in the bundle
peekie attachments Tests.xcresult --output-dir ./attachments
# [
# {
# "qualifiedName": "ExamplesTests / ExampleSUITests / withAttachment()",
# "name": "Calculation Result_0_<uuid>.txt",
# "exportedFileName": "<uuid>.txt",
# "path": "./attachments/<uuid>.txt",
# "contentType": "text/plain",
# "isAssociatedWithFailure": false,
# "repetitionNumber": 1
# },
# ...
# ]
# Only attachments from failing tests
peekie attachments Tests.xcresult --output-dir ./attachments --include failure
# A single test by identifier
peekie attachments Tests.xcresult --output-dir ./attachments --test-id "ExamplesTests/ExampleSUITests/withAttachment()"
# Human-readable
peekie attachments Tests.xcresult --output-dir ./attachments --format list
Options: --output-dir (required), --test-id, --include (comma-separated statuses), --format json|list.
Need the attachments embedded into each test (instead of a flat list)? Use peekie tests --attachments export --attachments-to <dir> --format json — each test gains an attachments[] array in its JSON node.
Agent integration
Peekie is intentionally shaped to be reachable by LLM coding agents. The four data-axis subcommands plus --format json are orthogonal so an agent can compose calls without inventing arguments.
This repo also ships as a plugin marketplace, so you can install the skill in one command.
Claude Code
/plugin marketplace add dodobrands/Peekie
/plugin install peekie
Cursor
Teams / Enterprise: add as a team marketplace in settings.
Individual: clone and symlink the rule into your project:
git clone https://github.com/dodobrands/Peekie.git ~/.peekie-plugin
mkdir -p .cursor/rules
ln -s ~/.peekie-plugin/agent/rules/peekie.mdc .cursor/rules/peekie.mdc
The skill lives in agent/; the marketplace catalog is at .claude-plugin/marketplace.json.
Library (PeekieSDK)
If you need to embed parsing in your own Swift code (instead of shelling out to the CLI), depend on PeekieSDK:
.package(url: "https://github.com/dodobrands/Peekie.git", from: "5.0.0")
The model is File-primary: Report.files is the source of truth; Report.modules is a projection grouping files by target name.
import PeekieSDK
let report = try await Report(xcresultPath: URL(fileURLWithPath: "/path/to.xcresult"))
// All warnings, anywhere in the bundle
for issue in report.warnings {
print("\(issue.type.rawValue): \(issue.message)")
}
// Files in a specific target
let bonusesFiles = report.modules.first { $0.name == "Bonuses" }?.files ?? []
// Files without a known target (test-less targets, project-level issues)
let untargeted = report.files.filter { $0.module == nil }
Report.init accepts includeCoverage, includeWarnings, includeTests flags (all default true). Disable what you don't need to skip the corresponding xcrun calls.
For the full model — File, Module, Suite, Issue.Location, IssueType — see the source under Sources/PeekieSDK/Models/ and the tests / warnings / coverage formatter implementations under Sources/PeekieSDK/Formatters/.
Test fixtures
.xcresult bundles under Tests/PeekieTests/Resources/ are generated by the swift-tests-example repo. To regenerate after Xcode updates, follow that repo's instructions.
License
This code is released under the Apache License. See LICENSE for more information.