Chops: One Native Mac App to Manage Every AI Agent's Skills

March 26, 2026

|repo-review

by Florian Narr

Chops: One Native Mac App to Manage Every AI Agent's Skills

What it does

Chops is a native macOS app that discovers AI coding agent skills and agents across every major tool — Claude Code, Cursor, Codex, Windsurf, Amp, Copilot, Aider, Hermes, and more. It watches the filesystem for changes, deduplicates symlinked skills, and gives you a single editor to manage them all.

Why I starred it

I have skills scattered across ~/.claude/skills/, ~/.cursor/rules/, ~/.codex/skills/, and a few custom paths. Finding which skill lives where, whether it's duplicated, or when it last changed means poking around dotfile directories manually. Chops replaces that with a three-column native UI that just scans everything on launch.

The multi-tool deduplication is what sold me. If you symlink one skill into three different agent directories (which you should), Chops resolves the symlinks and shows it as a single entry with multiple tool badges. That's a real quality-of-life feature nobody else is building.

How it works

The architecture is SwiftUI + SwiftData, zero web views. The app entry point in Chops/App/ChopsApp.swift sets up a ModelContainer with a versioned schema and a Sparkle updater for auto-updates. The only external dependency besides Sparkle is Highlightr for syntax highlighting and cmark-gfm for markdown rendering.

The real work happens in Chops/Services/SkillScanner.swift. On launch, scanAll() fires a detached Task that calls collectAllSkills() — a pure filesystem I/O function that iterates every ToolSource case, checks isInstalled, and then walks each tool's global paths:

for tool in ToolSource.allCases where tool != .custom {
    guard tool.isInstalled else { continue }
    for path in tool.globalPaths {
        let url = URL(fileURLWithPath: path)
        collectFromDirectory(url, toolSource: tool, isGlobal: true, kind: .skill, into: &results)
    }
}

Each ToolSource enum case in Chops/Models/ToolSource.swift knows its own filesystem paths, display name, icon, color, and how to detect whether it's installed. The isInstalled check is thorough — it looks for app bundles in /Applications, CLI binaries in /usr/local/bin and /opt/homebrew/bin, config files in home directories, and even walks ~/.nvm/versions/node/ to find node-installed CLIs. That's 16 tool sources at last count.

The symlink deduplication is handled via canonicalResolvedPath() in the scanner. For regular tools, it resolves symlinks and uses the resolved path as the unique identity. For Claude plugins, it strips volatile components like session IDs and version numbers to produce a stable canonical key:

// CLI plugins: .../cache/<publisher>/<plugin>/<version>/skills/<skill>/SKILL.md
if toolSource == .claude, let range = path.range(of: ".claude/plugins/cache/") {
    let after = String(path[range.upperBound...])
    let parts = after.components(separatedBy: "/")
    return "claude-plugin:\(parts[0])/\(parts[1])/\(parts[4])"
}

This means a plugin skill survives version bumps without creating duplicates in the database. Smart.

File watching uses DispatchSource.makeFileSystemObjectSource in Chops/Services/FileWatcher.swift — raw FSEvents with a 0.5-second debounce. When a watched directory changes, the scanner re-runs automatically. The debounce prevents rapid-fire rescans when saving a file triggers multiple filesystem events.

Parsing is split between FrontmatterParser (for .md files with YAML frontmatter) and MDCParser (for Cursor's .mdc format). The frontmatter parser in Chops/Utilities/FrontmatterParser.swift is minimal — hand-rolled YAML extraction that looks for --- delimiters and splits on the first colon per line. No YAML library dependency, which keeps the binary lean but means nested YAML values won't parse correctly. For skill metadata (flat key-value pairs), this works fine.

Using it

Install is straightforward if you have Xcode:

git clone https://github.com/Shpigford/chops.git
cd chops
brew install xcodegen
xcodegen generate
open Chops.xcodeproj
# Cmd+R to build and run

Or grab the .dmg from the releases page. The app launches, scans your home directory, and within a second or two you see every skill it found. The three-column layout — tool filter sidebar, skill list, editor — is standard macOS fare. Cmd+S saves edits back to disk.

You can also create new skills directly from the app. It generates the correct boilerplate for whatever tool you're targeting and can make a skill global by moving it to ~/.agents/skills/ and symlinking it into every installed agent's directory.

Rough edges

No test suite. The README acknowledges this directly — "validate manually." For a native app with 54 Swift files and growing remote server support (SSH-based skill discovery), that's a risk. The SwiftData schema versioning was added recently (SchemaV1, migration plan) after what I assume was a data loss incident, given the PR title "prevent silent data loss."

The frontmatter parser can't handle multi-line values or nested structures. If a skill has a description spanning multiple lines, it'll truncate at the first line. Not a blocker for the typical name: My Skill frontmatter, but worth knowing.

The app intentionally disables the macOS sandbox (Chops.entitlements) because it needs to read dotfiles across ~/. That's the right call for this use case, but it means App Store distribution is off the table.

Custom scan paths treat directories as either project parents or skill libraries, and the heuristic for telling them apart (look for .claude/skills/ subpaths vs. SKILL.md files directly) can misfire if your directory structure is unusual.

Bottom line

If you use more than one AI coding agent and your skills are scattered across dotfile directories, Chops is the manager you didn't know you needed. It's native, fast, and the symlink deduplication alone makes it worth the install.

Shpigford/chops on GitHub
Shpigford/chops