A completely vibe-coded Swift implementation of the plist-yaml-plist conversion tool. Converts between Apple plist, YAML, and JSON formats with special optimizations for AutoPkg recipes.
- Bidirectional conversion: plist ↔ YAML, JSON → plist
- AutoPkg recipe optimization: Automatic formatting and key ordering for AutoPkg recipes
- Batch processing: Convert entire directories with glob patterns
- Smart path handling: Recognizes YAML/_YAML and JSON/_JSON folder conventions
- Native performance: Leverages Swift and Foundation for fast, native macOS performance
- macOS 15.0 or later
- Swift 6.0 or later (for building from source)
Download the latest .pkg installer from the Releases page and run it. The binary will be installed to /usr/local/bin/plistyamlplist.
Note that the neither the package nor the binary are signed. So you'll have to remove the quarantine bit or use the bypass mechanism in Privacy Settings to install and run the binary.
git clone https://github.com/grahampugh/plist-yaml-plist-swift.git
cd plist-yaml-plist-swift
swift build -c release
sudo cp .build/release/plistyamlplist /usr/local/bin/# Convert plist to YAML
plistyamlplist file.plist [output.yaml]
# Convert YAML to plist
plistyamlplist file.yaml [output.plist]
# Convert JSON to plist
plistyamlplist file.json [output.plist]If the output file is omitted:
.plistfiles → replaces.plistextension with.yaml(e.g.foo.recipe.plist→foo.recipe.yaml).recipefiles (and other plist files without a.plistextension) → appends.yaml(e.g.foo.recipe→foo.recipe.yaml).yamlfiles → removes.yamlextension.jsonfiles → removes.jsonextension
# Tidy a YAML recipe in-place
plistyamlplist recipe.yaml --tidy
# Tidy all recipes in a folder
plistyamlplist /path/to/YAML/ --tidy# Convert all YAML files matching a pattern (note the quotes)
plistyamlplist '/path/to/*.yaml'
# Convert all plist-based AutoPkg recipes in a folder to YAML
plistyamlplist '/path/to/recipes/*.recipe'
# Convert entire folder (processes plist, YAML, and JSON files)
plistyamlplist /path/to/recipes/
# Convert entire folder with output to a different directory
plistyamlplist /path/to/recipes/ /path/to/output/
# Tidy all recipes in a folder recursively
plistyamlplist /path/to/YAML/ --tidyNote: When using glob patterns from the command line, wrap the pattern in quotes to prevent shell expansion.
If your path contains YAML or _YAML (or JSON/_JSON), the tool automatically maps output paths to the parent folder:
- Input:
/project/YAML/subfolder/file.yaml - Output:
/project/subfolder/file.plist(if/project/subfolderexists)
When converting AutoPkg recipes (files ending in .recipe, .recipe.plist, or .recipe.yaml), the tool automatically:
- Orders top-level keys: Comment → Description → Identifier → ParentRecipe → MinimumVersion → Input → Process → ParentRecipeTrustInfo
- Orders Process entries: Processor key first, then Comment, then Arguments
- Orders Input dictionary: NAME key always first
- Adds blank lines: Before Input, Process, each Processor, and ParentRecipeTrustInfo for readability
This Swift implementation provides:
- Full CLI compatibility with the Python version
- All AutoPkg recipe optimizations
- Native binary plist support (no
plutilcommand needed) - Faster performance for batch operations
- Same folder convention handling (YAML/_YAML, JSON/_JSON)
- Glob pattern support for batch conversion
- Requires macOS 15+ (Python version supports older macOS)
- Single file conversion (plist ↔ YAML, JSON → plist)
- Batch conversion with glob patterns
- Directory recursion with structure replication
- AutoPkg recipe detection and optimization
- YAML/_YAML and JSON/_JSON folder conventions
--tidyflag for recipe reformatting- Binary and XML plist support
- Complex AutoPkg recipe formatting edge cases
- Large-scale batch operations
# Simple conversion
$ plistyamlplist com.example.plist
plist-yaml-plist version 1.0.2
Processing plist file...
Wrote to: com.example.plist.yaml
# Reverse conversion
$ plistyamlplist com.example.plist.yaml
plist-yaml-plist version 1.0.2
Processing yaml file...
Written to com.example.plist# Input file in YAML subfolder
$ plistyamlplist /Users/admin/recipes/YAML/Chrome.recipe.yaml
YAML folder exists: /Users/admin/recipes/YAML/Chrome.recipe.yaml
Path exists: /Users/admin/recipes
Wrote to: /Users/admin/recipes/Chrome.recipe
# The tool automatically maps YAML/subfolder → subfolder# Convert all plist files in a directory to YAML
$ plistyamlplist '/tmp/plists/*.plist'
plist-yaml-plist version 1.0.2
Processing 15 file(s)...
Wrote to: /tmp/plists/file1.yaml
Wrote to: /tmp/plists/file2.yaml
...
# Convert all plist-based AutoPkg recipes in a directory to YAML
$ plistyamlplist '/tmp/recipes/*.recipe'
plist-yaml-plist version 1.0.2
Processing 3 file(s)...
Wrote to: /tmp/recipes/MyApp.recipe.yaml
Wrote to: /tmp/recipes/OtherApp.recipe.yaml
...# Tidy a single recipe
$ plistyamlplist MyRecipe.recipe.yaml --tidy
Wrote to: MyRecipe.recipe.yaml
# Tidy all recipes in a folder
$ plistyamlplist /path/to/YAML/ --tidy
WARNING! Processing all subfolders...
Processed 47 file(s)Swift port by Graham Pugh, based on the original Python implementation:
This project is completely vibe-coded using GitHub CoPilot CLI and the Claude Sonnet 4.5 model.
Apache License 2.0 - See LICENSE file for details
# Build and create installer package
make package
# Clean build artifacts
make clean
# Create GitHub pre-release (requires gh CLI)
make release
# Show all available targets
make helpThe Makefile creates a universal macOS installer package:
- Binary: Universal (arm64 + x86_64)
- Install Location:
/usr/local/bin/plistyamlplist - Package Size: ~1.8MB
- Identifier:
com.github.grahampugh.plistyamlplist - Output:
.build/pkg/plistyamlplist-{version}.pkg
The make release target automatically:
- Builds the package
- Creates a GitHub pre-release
- Uploads the .pkg file
- Generates release notes
Requirements:
- GitHub CLI (
brew install gh) - Authenticated GitHub session (
gh auth login)
After running make release, visit GitHub to publish the pre-release as a full release.
0 comments
log in to comment.