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.