Imported from mattrobenolt/appify (
AGENTS.md). Install upstream withnpx skills add mattrobenolt/appify. Copyright stays with the author.
AGENTS.md
This file provides guidance to AI coding agents when working with code in this repository.
Project Overview
appify is a Zig CLI tool that generates macOS .app bundles from terminal commands. It wraps TUI applications (like lazygit, btop, nvim) so they appear as distinct applications in Cmd+Tab, Spotlight, and the Dock.
Architecture: Generated apps are standalone native Swift macOS applications that embed the GhosttyKit terminal emulator library. They are not shell scripts. The CLI tool generates these by unpacking a pre-built template of the native app and injecting configuration.
Development Commands
Building
The build process is multi-stage:
- Builds
GhosttyKit.xcframework(viaghosttydependency). - Builds the Swift template app (
macos/appify) usingxcodebuild. - Embeds the built
.appinto the Zig CLI binary. - Builds the final
appifyCLI.
just build # Build everything (CLI + Template)
just build-release # Build optimized release version
Testing
just test # Run unit tests
Running
just run <command> [options] # Build and run CLI with arguments
./zig-out/bin/appify <command> # Run built binary directly
Code Formatting
just fmt # Format source files
just fmt-check # Check formatting without modifying
Code Architecture
Module Structure
The codebase consists of the Zig CLI (src/) and the Swift Runtime (macos/).
Zig CLI (src/)
main.zig - CLI entry point
- Parses arguments using
std.process.args(). - Validates inputs (existence of files, directories).
- Orchestrates bundle generation via
bundle.zig.
bundle.zig - Bundle Generation
- Unpacks the embedded
appify.apptemplate to the output directory. - Updates
Info.plistwith the user's name and bundle ID. - Writes runtime configuration to
Contents/Resources/appify.json. - Processes and installs the icon.
plist.zig - Info.plist Generation
- Generates
Info.plistXML. - Sets
LSUIElement=false(Dock app). - Configures standard macOS metadata.
icon.zig - Icon Processing
- Converts/copies icons to
Contents/Resources/AppIcon.icns. - Uses
sipsfor PNG-to-ICNS conversion.
Swift Runtime (macos/appify/)
The generated app is a lightweight Swift wrapper around GhosttyKit.
AppifyConfig.swift
- Reads configuration from
Bundle.main.url(forResource: "appify", withExtension: "json"). - Structure:
{ "command": "...", "title": "...", "cwd": "...", "env": {...} }. - Defaults to running
yaziif no config is found.
GhosttyRuntime.swift
- Initializes the
libghostty/GhosttyKitruntime. - Manages the global
ghostty_app_tinstance.
GhosttySurfaceView.swift
- A
NSViewsubclass (wrapped inNSViewRepresentablefor SwiftUI) that hosts the terminal surface. - Configures the surface using the command and environment from
AppifyConfig. - Handles input bridging between macOS events and Ghostty.
Configuration & Data Flow
- User Run:
appify lazygit -n "LazyGit" - CLI Action:
- Unpacks template
appify.app. - Writes
{"command": "lazygit", "title": "LazyGit"}toContents/Resources/appify.json.
- Unpacks template
- App Launch:
- Swift app starts.
AppifyConfigreadsappify.json.GhosttySurfaceViewinitializes a surface executinglazygit.
Zig Style Conventions
This project follows conventions documented in ZIG_STYLE.md.
Allocators:
- Use
ArenaAllocatorfor short-lived CLI tasks. - Pass allocators explicitly.
- Use
deferfor cleanup.
Error Handling:
- Use typed error sets.
- Return errors to
mainfor printing/exit code handling.
Testing Approach
Unit Tests (zig build test)
- Tests logic in
bundle.zig,plist.zig, etc. - Mocks file system operations where possible or uses temp dirs.
Integration Tests
- None currently. Consider adding one later if needed.
Key Implementation Details
Template Embedding:
The build.zig script packages the compiled Swift app into a TAR file and embeds it into the Zig binary as template_tar. This ensures the CLI is a single, self-contained binary that can generate apps without external dependencies at runtime.
GhosttyKit Integration:
The Swift app links against GhosttyKit.xcframework. The build system manages this dependency, ensuring the framework is built and placed correctly in the bundle.