onevcat/FengNiao
A command line tool for cleaning unused resources in Xcode.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓19 active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 68% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/onevcat/fengniao)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/onevcat/fengniao on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: onevcat/FengNiao
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/onevcat/FengNiao shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 19 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 68% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live onevcat/FengNiao
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/onevcat/FengNiao.
What it runs against: a local clone of onevcat/FengNiao — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in onevcat/FengNiao | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of onevcat/FengNiao. If you don't
# have one yet, run these first:
#
# git clone https://github.com/onevcat/FengNiao.git
# cd FengNiao
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of onevcat/FengNiao and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "onevcat/FengNiao(\\.git)?\\b" \\
&& ok "origin remote is onevcat/FengNiao" \\
|| miss "origin remote is not onevcat/FengNiao (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "Sources/FengNiaoKit/FengNiao.swift" \\
&& ok "Sources/FengNiaoKit/FengNiao.swift" \\
|| miss "missing critical file: Sources/FengNiaoKit/FengNiao.swift"
test -f "Sources/FengNiao/CLI.swift" \\
&& ok "Sources/FengNiao/CLI.swift" \\
|| miss "missing critical file: Sources/FengNiao/CLI.swift"
test -f "Sources/FengNiaoKit/FileSearchRule.swift" \\
&& ok "Sources/FengNiaoKit/FileSearchRule.swift" \\
|| miss "missing critical file: Sources/FengNiaoKit/FileSearchRule.swift"
test -f "Sources/FengNiaoKit/ExtensionFindProcess.swift" \\
&& ok "Sources/FengNiaoKit/ExtensionFindProcess.swift" \\
|| miss "missing critical file: Sources/FengNiaoKit/ExtensionFindProcess.swift"
test -f "Package.swift" \\
&& ok "Package.swift" \\
|| miss "missing critical file: Package.swift"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/onevcat/FengNiao"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
FengNiao is a macOS/Linux command-line utility that scans Xcode projects to identify and delete unused image assets (PNG, JPG, GIF, PDF, imageset files). It parses Swift, Objective-C, XIB, and Storyboard files to build a usage map, then reports and optionally removes images with zero references in the codebase. Dual architecture: Sources/FengNiaoKit/ contains the core scanning engine (FengNiao.swift, FileSearchRule.swift, ExtensionFindProcess.swift), while Sources/FengNiao/CLI.swift wraps it as a command-line tool. A separate macOS GUI app in /App/macOS/ provides a SwiftUI interface (AppViewModel.swift, DeleteViewModel.swift) wrapping the same FengNiaoKit library.
👥Who it's for
iOS and macOS developers who maintain Xcode projects and want to clean up accumulated unused image resources without manual audit. Project leads concerned with reducing app bundle size and technical debt.
🌱Maturity & risk
Actively maintained and production-ready. The project has CI/CD via GitHub Actions (.github/workflows/ci.yml), Swift Package Manager integration, and a mature command-line interface with comprehensive flags. Last activity appears recent based on the structured test suite and macOS GUI app in /App/macOS/, though this appears to be a stable utility rather than actively feature-adding.
Low risk for a mature utility. Single maintainer (onevcat) is a known Swift community figure. No external dependencies listed beyond Swift stdlib. Primary risk: destructive operation (file deletion) means mistakes are unrecoverable—users must have version control. No visible open issues in file list, though testing coverage unknown.
Active areas of work
The repo maintains dual interfaces: active CLI tool via Swift Package Manager and a modern SwiftUI-based macOS application. The App/ directory suggests recent work on GUI usability (DeleteView.swift, ExportView.swift). Build configuration shows .pbxproj reference cleaning as an ongoing concern (--skip-proj-reference flag).
🚀Get running
git clone https://github.com/onevcat/FengNiao.git
cd FengNiao
swift build -c release
sudo cp .build/release/FengNiao /usr/local/bin/fengniao
fengniao --help
Daily commands:
CLI: fengniao (or fengniao --project /path --force). GUI: Open App/fengniao.xcodeproj in Xcode and run the macOS scheme. Build: swift build -c release produces executable at .build/release/FengNiao.
🗺️Map of the codebase
Sources/FengNiaoKit/FengNiao.swift— Core orchestration engine that coordinates resource finding, filtering, and deletion—every contributor must understand the main workflow hereSources/FengNiao/CLI.swift— Command-line interface entry point that parses arguments and drives the tool's execution; essential for understanding how users interact with FengNiaoSources/FengNiaoKit/FileSearchRule.swift— Defines the pattern-matching rules for detecting resource references across source files; critical for the accuracy of unused-resource detectionSources/FengNiaoKit/ExtensionFindProcess.swift— Handles file discovery and filtering by extension/rules; foundational to the resource enumeration pipelinePackage.swift— Swift Package Manager manifest that defines dependencies and build configuration; required context for local development setupTests/FengNiaoKitTests/ResourceSearcherTests.swift— Comprehensive test suite for resource searching logic; demonstrates expected behavior and helps validate changes to core algorithms
🧩Components & responsibilities
- FengNiao (Core Engine) (Swift, File I/O, Pattern matching) — Orchestrates the entire workflow: resource enumeration, reference searching, deletion coordination, and result reporting
- Failure mode: Crashes or hangs if project path is invalid, symlinks are circular, or file permissions are insufficient
- ExtensionFindProcess (File Enumeration) (FileManager, URL APIs) — Recursively walks the file system, filters by extension and exclusion rules, and returns candidate resource files
- Failure mode: May miss files if exclusion patterns are too broad; silent failure if directory access is denied
- FileSearchRule (Pattern Matching) (String operations, Regular expressions) — Defines and applies regex-like patterns to detect references to resources in source files (Swift, Obj-C, XML, etc.)
- Failure mode: False positives if patterns over-match; false negatives if code uses indirect/dynamic references
- CLI Handler (Swift CLI libraries, Standard I/O) — Parses command-line arguments, formats output, manages user interaction, and calls the core engine
- Failure mode: Incorrect argument parsing leads to unexpected behavior; missing error messages if engine fails silently
- macOS UI (SwiftUI) (SwiftUI, AppKit integration) — Provides a graphical interface for selecting projects, previewing results, and confirming deletion with visual feedback
- Failure mode: UI freezes during long file enumeration; crashes if KVC bindings are mismatched
🔀Data flow
User CLI Input→CLI.swift— Command-line arguments and options (project path, file extensions, exclusions) are parsedCLI.swift→FengNiao.swift— Validated options and project path are passed to the core engine for initializationFengNiao.swift→ExtensionFindProcess.swift— Request to enumerate all resource files matching specified extensions and exclusion rulesundefined→undefined— undefined
🛠️How to make changes
Add a new resource file type to scan
- Define the file extension pattern in your command-line arguments or configuration (
Sources/FengNiao/CLI.swift) - Update the FileSearchRule to recognize patterns specific to that file type (
Sources/FengNiaoKit/FileSearchRule.swift) - Add test cases to validate detection of references in the new file type (
Tests/FengNiaoKitTests/SearchRuleTests.swift)
Modify resource detection rules
- Review and update the search rules and patterns in FileSearchRule (
Sources/FengNiaoKit/FileSearchRule.swift) - Add or modify test fixtures to cover the new pattern (
Tests/Fixtures/FileStringSearcher) - Run the relevant test suite to validate changes (
Tests/FengNiaoKitTests/UsedStringSearcherTests.swift)
Add a new output format or action
- Extend the FengNiao class to support the new output logic (
Sources/FengNiaoKit/FengNiao.swift) - Add CLI command handling in CLI.swift for the new action (
Sources/FengNiao/CLI.swift) - Write integration tests for the new functionality (
Tests/FengNiaoCLITests/CLIParsingTests.swift)
Improve file enumeration performance
- Review the current enumeration logic and identify bottlenecks (
Sources/FengNiaoKit/ExtensionFindProcess.swift) - Optimize the filtering and matching algorithm (
Sources/FengNiaoKit/FileSearchRule.swift) - Add performance benchmarks to validate improvements (
Tests/FengNiaoKitTests/FindProcessTests.swift)
🔧Why these technologies
- Swift 4.0+ — Type-safe, modern language suitable for cross-platform CLI and macOS app development; excellent for regex-based file pattern matching
- Swift Package Manager (SPM) — Enables reproducible builds, dependency management, and easy distribution; supports both CLI and macOS app targets from a single manifest
- SwiftUI (macOS app) — Modern declarative UI framework for native macOS desktop application; allows parallel development of GUI alongside CLI
⚖️Trade-offs already made
-
Single-threaded file enumeration and pattern matching
- Why: Simpler, more predictable behavior; easier to reason about and debug resource discovery
- Consequence: Slower performance on very large projects; could be optimized with concurrent file operations in future
-
CLI-first design with macOS app as secondary UI
- Why: CLI tool is composable, scriptable, and integrates well into build pipelines; app serves power users needing visual feedback
- Consequence: Feature parity between CLI and app requires dual maintenance; CLI remains primary focus
-
Pattern-matching based reference detection instead of full AST parsing
- Why: Simpler implementation, works across multiple languages (Obj-C, Swift, Storyboard, etc.); fast and low memory overhead
- Consequence: Higher false-positive/negative risk; may miss complex references or misclassify refactored code
🚫Non-goals (don't propose these)
- Does not support real-time project monitoring or continuous integration hooks
- Does not handle intelligent code refactoring beyond simple file deletion
- Does not parse or understand full Xcode project semantics or build settings
- Does not support Windows or Linux (macOS and command-line only)
🪤Traps & gotchas
Destructive by design: Running without --list-only or --force will prompt deletion; files are permanently removed. Project file mutation: --skip-proj-reference flag is critical if you're building multiple dependent projects—omitting it will modify .pbxproj which can break multiproject builds. Pattern limitations: Resource references in dynamically loaded code, build-phase scripts, or plist files may not be detected. Xcode version coupling: Relies on parsing .pbxproj format which can vary across Xcode versions.
🏗️Architecture
💡Concepts to learn
- AST-free regex-based pattern matching — FengNiao doesn't parse full ASTs; it uses regex to find image references in source files, making it fast but potentially incomplete—understanding this trade-off is key to knowing when results are reliable
- Pbxproj file format mutation — FengNiao modifies Xcode's binary .pbxproj project file when deleting references; understanding this format is critical to the
--skip-proj-referenceflag and why multiproject builds need special handling - Resource localization and asset catalogs — iOS assets can be language/device-specific in Asset.xcassets; FengNiao must track variations (1x, 2x, 3x, idiom variants) as separate candidates to avoid over-deletion
- Static vs. dynamic reference detection — FengNiao only finds static string references (e.g., UIImage(named: "foo")) and misses dynamic loading (e.g., NSBundle.load(stringVariable)); this limitation defines the tool's scope
- Cross-file dependency graph construction — FengNiao must recursively scan all Swift/ObjC/XIB files and build a usage graph before identifying isolated nodes; this is the core algorithmic problem
- Idiomatic macOS CLI via SPM — FengNiao is distributed as a Swift Package Manager binary, not a Homebrew formula or pre-compiled binary; understanding SPM's executable product target is how you'd extend it
🔗Related repos
realm/SwiftLint— Similar CLI tool for Xcode projects; both parse Swift code and provide configurable rules for code quality/cleanupmac-cain13/R.swift— Complementary tool that generates type-safe resource references for images; FengNiao finds unused ones that R.swift would warn abouttuist/tuist— Project generation tool for Xcode that manages dependencies; FengNiao can be used as post-generation cleanup in Tuist workflowsonevcat/Kingfisher— Author's image loading library; FengNiao often used in projects that cache Kingfisher-loaded images to clean unused assetskrzysztofzablocki/Sourcery— Code generation tool; shares similar AST/pattern-matching approach to finding unused code artifacts in Xcode projects
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for ExtensionFindProcess.swift resource scanning
The file Sources/FengNiaoKit/ExtensionFindProcess.swift appears to be a critical component for finding unused resources, but Tests/FengNiaoKitTests/FindProcessTests.swift may not have comprehensive coverage for edge cases like nested resource structures, symlinks, and special file extensions. Adding detailed integration tests would improve reliability of the core scanning functionality.
- [ ] Review existing FindProcessTests.swift to identify gaps in extension handling
- [ ] Add test cases in Tests/FengNiaoKitTests/FindProcessTests.swift for: nested .xcassets folders, localized resources (.lproj), .strings files, and custom extension types
- [ ] Create fixture files in Tests/Fixtures/FindProcess/ to test realistic Xcode project structures
- [ ] Add tests for handling of symlinked resources and permission-denied scenarios
Add GitHub Actions workflow to replace Travis CI for faster feedback
.travis.yml exists but the repo has a .github/workflows/ci.yml structure in place. Modernizing to GitHub Actions native CI would provide faster feedback, better integration with GitHub, and reduce reliance on external services. The workflow file appears incomplete or needs enhancement.
- [ ] Review .github/workflows/ci.yml to ensure it runs all test targets (FengNiaoCLITests and FengNiaoKitTests)
- [ ] Add separate workflow steps for: Swift version validation (4.0+), SPM dependency resolution, unit tests, and code coverage reporting
- [ ] Add a workflow_dispatch trigger to allow manual test runs and a scheduled nightly run for comprehensive testing
- [ ] Update README.md badges to point to GitHub Actions status instead of Travis CI
Add comprehensive CLI argument documentation and validation tests
Sources/FengNiao/CLI.swift handles command-line parsing, but Tests/FengNiaoCLITests/CLIParsingTests.swift appears minimal. The tool likely supports multiple flags (path, exclude patterns, delete modes, etc.) that need thorough validation tests to prevent user errors and unexpected behavior.
- [ ] Expand Tests/FengNiaoCLITests/CLIParsingTests.swift with test cases for all CLI flags documented in the tool (project paths, exclude patterns, output formats, etc.)
- [ ] Add negative test cases for invalid argument combinations (e.g., conflicting flags, missing required arguments)
- [ ] Add tests for path handling: relative paths, absolute paths, paths with spaces, and symlinked directories
- [ ] Consider adding validation tests for the help/version output formatting
🌿Good first issues
- Add unit tests for
Sources/FengNiaoKit/FileSearchRule.swiftto cover edge cases in extension matching (currentlyTests/FengNiaoCLITests/exists but specific rule test coverage is unclear) - Extend
ExtensionFindProcess.swiftto support additional reference patterns (e.g., SwiftUI Image() constructors, asset catalog bundle references) with test cases - Document the
--skip-proj-referenceflag more thoroughly in the README with examples of multiproject build scenarios where it prevents .pbxproj corruption
⭐Top contributors
Click to expand
Top contributors
- @onevcat — 68 commits
- @ldakhoa — 8 commits
- @voidless — 4 commits
- @Frank-728 — 2 commits
- @yogeshkhandelwal — 2 commits
📝Recent commits
Click to expand
Recent commits
180af9c— Bump version to 0.13.0 (onevcat)b3192e1— Merge pull request #98 from onevcat/codex/fix-generated-asset-catalog-symbol-detection (onevcat)8b5ef26— Catch nested asset symbols used via type inference (onevcat)018f293— Fix generated asset catalog symbol detection (onevcat)af80dd3— Merge pull request #97 from onevcat/codex/pr93-followup-generated-asset-symbols (onevcat)981ffd6— refactor: clarify generated Objective-C symbol scanning (onevcat)4c1e757— test: cover generated Objective-C asset symbols (onevcat)81da1e6— Bump version to 0.12.0 (onevcat)ee4b531— feat: support ImageResource prefix (Frank-728)5e9ee68— Merge pull request #95 from onevcat/fix/remove-stale-app-package-resolved (onevcat)
🔒Security observations
FengNiao is a command-line utility with relatively low security risk due to its nature as a local file management tool. No critical vulnerabilities identified from the visible file structure. Main concerns are: (1) inability to assess dependencies without Package.swift content, (2) potential sensitive data in test fixtures, and (3) need to verify CI/CD security practices. The codebase appears well-structured with proper separation of concerns (CLI, Kit, App components). Recommendations focus on dependency management auditing and test fixture sanitization.
- Low · Missing dependency information in Package.swift —
Package.swift. The Package.swift file content was not provided for analysis. Dependencies and their versions cannot be verified for known security vulnerabilities. Fix: Provide the Package.swift file for dependency vulnerability scanning. Regularly audit Swift package dependencies using tools likeswift package show-dependenciesand check against security advisories. - Low · Test fixtures may contain sensitive project data —
Tests/Fixtures/DeleteReference/FengNiao.xcodeproj/. The repository contains test fixtures with actual Xcode project files (.pbxproj, plist files) in the Tests/Fixtures directory. While these are test files, they may inadvertently contain sensitive configuration data if copied from real projects. Fix: Ensure test fixtures do not contain real credentials, API keys, or sensitive project identifiers. Use sanitized/anonymized fixture data. Review .pbxproj files for any embedded secrets. - Low · CI/CD configuration should be reviewed for security —
.travis.yml, .github/workflows/ci.yml. The repository contains CI configuration files (.travis.yml and .github/workflows/ci.yml) whose content was not provided. These should be reviewed to ensure secure handling of secrets and proper permission scoping. Fix: Review CI/CD configuration for: 1) Use of GitHub encrypted secrets instead of plaintext, 2) Proper credential rotation, 3) Limited permission scopes, 4) No secrets in logs or artifacts.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.