RepoPilotOpen in app →

onevcat/FengNiao

A command line tool for cleaning unused resources in Xcode.

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 19 active contributors
  • MIT licensed
Show 3 more →
  • 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.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/onevcat/fengniao)](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:

  1. 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.
  2. 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.
  3. 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 |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>onevcat/FengNiao</code></summary>
#!/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).

</details>

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 here
  • Sources/FengNiao/CLI.swift — Command-line interface entry point that parses arguments and drives the tool's execution; essential for understanding how users interact with FengNiao
  • Sources/FengNiaoKit/FileSearchRule.swift — Defines the pattern-matching rules for detecting resource references across source files; critical for the accuracy of unused-resource detection
  • Sources/FengNiaoKit/ExtensionFindProcess.swift — Handles file discovery and filtering by extension/rules; foundational to the resource enumeration pipeline
  • Package.swift — Swift Package Manager manifest that defines dependencies and build configuration; required context for local development setup
  • Tests/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 InputCLI.swift — Command-line arguments and options (project path, file extensions, exclusions) are parsed
  • CLI.swiftFengNiao.swift — Validated options and project path are passed to the core engine for initialization
  • FengNiao.swiftExtensionFindProcess.swift — Request to enumerate all resource files matching specified extensions and exclusion rules
  • undefinedundefined — undefined

🛠️How to make changes

Add a new resource file type to scan

  1. Define the file extension pattern in your command-line arguments or configuration (Sources/FengNiao/CLI.swift)
  2. Update the FileSearchRule to recognize patterns specific to that file type (Sources/FengNiaoKit/FileSearchRule.swift)
  3. Add test cases to validate detection of references in the new file type (Tests/FengNiaoKitTests/SearchRuleTests.swift)

Modify resource detection rules

  1. Review and update the search rules and patterns in FileSearchRule (Sources/FengNiaoKit/FileSearchRule.swift)
  2. Add or modify test fixtures to cover the new pattern (Tests/Fixtures/FileStringSearcher)
  3. Run the relevant test suite to validate changes (Tests/FengNiaoKitTests/UsedStringSearcherTests.swift)

Add a new output format or action

  1. Extend the FengNiao class to support the new output logic (Sources/FengNiaoKit/FengNiao.swift)
  2. Add CLI command handling in CLI.swift for the new action (Sources/FengNiao/CLI.swift)
  3. Write integration tests for the new functionality (Tests/FengNiaoCLITests/CLIParsingTests.swift)

Improve file enumeration performance

  1. Review the current enumeration logic and identify bottlenecks (Sources/FengNiaoKit/ExtensionFindProcess.swift)
  2. Optimize the filtering and matching algorithm (Sources/FengNiaoKit/FileSearchRule.swift)
  3. 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-reference flag 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
  • realm/SwiftLint — Similar CLI tool for Xcode projects; both parse Swift code and provide configurable rules for code quality/cleanup
  • mac-cain13/R.swift — Complementary tool that generates type-safe resource references for images; FengNiao finds unused ones that R.swift would warn about
  • tuist/tuist — Project generation tool for Xcode that manages dependencies; FengNiao can be used as post-generation cleanup in Tuist workflows
  • onevcat/Kingfisher — Author's image loading library; FengNiao often used in projects that cache Kingfisher-loaded images to clean unused assets
  • krzysztofzablocki/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.swift to cover edge cases in extension matching (currently Tests/FengNiaoCLITests/ exists but specific rule test coverage is unclear)
  • Extend ExtensionFindProcess.swift to support additional reference patterns (e.g., SwiftUI Image() constructors, asset catalog bundle references) with test cases
  • Document the --skip-proj-reference flag more thoroughly in the README with examples of multiproject build scenarios where it prevents .pbxproj corruption

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 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 like swift package show-dependencies and 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.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · onevcat/FengNiao — RepoPilot