Packages
Table of contents
- Overview
- Package Structure
- Package Naming
- Commands
- Security: Trusted Packages
- Dependencies
- Build Behavior
- Best Practices
- Versioning
- Troubleshooting
- Examples
Cherri includes a built-in GitHub-based package manager that allows you to easily share and reuse code across projects.
Overview
The package manager:
- Downloads packages from GitHub repositories
- Manages dependencies automatically (including nested dependencies)
- Provides a trust system for security
- Generates deterministic builds
- Stores packages in user-scoped directories to prevent naming conflicts
Package Structure
Every Cherri package must follow this structure:
cherri-{package_name}/
├── main.cherri # Entry point (required)
├── info.plist # Package metadata (required)
└── ... other files
Required Files
main.cherri: The main entry point for your package. This file will be automatically included when your package is installed.
info.plist: A property list file containing package metadata in this format:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Name</key>
<string>package_name</string>
<key>User</key>
<string>github_username</string>
<key>Packages</key>
<array>
<!-- Dependencies go here -->
</array>
</dict>
</plist>
Package Naming
Packages follow the format @{github_username}/{package_name} where:
- Username: Your GitHub username (alphanumeric, underscores, hyphens, and dots allowed)
- Package name: The repository name without the
cherri-prefix (same character restrictions)
GitHub Repository Structure
Your repository must be named: cherri-{package_name}
Example:
- Package signature:
@electrikmilk/example - GitHub repository:
https://github.com/electrikmilk/cherri-example.git
Commands
Initialize a Package
Create a new package in the current directory:
cherri --init=@{github_username}/{package_name}
This creates an info.plist file with the provided details. You can initialize a package for your project without publishing it to GitHub—the username/package format is only required to correspond to a repo if you plan to share it.
Install a Package
Install a package and its dependencies:
cherri --install=@{github_username}/{package_name}
What happens:
- If this is the first time installing this package on your system, you’ll be prompted to trust it
- The package is cloned from GitHub to
./packages/@{github_username}/{package_name}/ - Any dependencies listed in the package’s
info.plistare automatically installed - The package’s
main.cherriis automatically included at compile time
List Package Information
View the current package and its direct dependencies:
cherri --package
List all installed packages:
cherri --packages
Remove a Package
Uninstall a package:
cherri --remove=@{github_username}/{package_name}
This removes the package directory and updates your info.plist.
Tidy Packages
Reinstall all packages (useful for cleaning up or updating):
cherri --tidy
This command:
- Removes all package directories
- Reinstalls all packages listed in your
info.plist - Reinstalls their dependencies
Security: Trusted Packages
The first time you install a package on your system, you’ll see a trust prompt:
Do you trust this package?
This will download this GitHub repository and automatically include it in this project:
https://github.com/{github_username}/cherri-{package_name}.git
If you answer yes, the package signature is saved to ~/.cherri/trusted.plist. Future installations of that package won’t prompt you again.
Important: Review packages before trusting them. Once trusted, packages can add actions in your Shortcut.
Dependencies
Nested Dependencies
Packages can depend on other packages. When you install a package, all of its dependencies are automatically installed recursively.
Example dependency chain:
- You install
@user1/app @user1/appdepends on@user2/utils@user2/utilsdepends on@user3/helpers- All three packages are installed automatically
Dependency Storage
Packages are stored in user-scoped directories:
./packages/
├── @user1/
│ └── app/
│ ├── main.cherri
│ └── info.plist
├── @user2/
│ └── utils/
│ ├── main.cherri
│ └── info.plist
└── @user3/
└── helpers/
├── main.cherri
└── info.plist
This structure prevents naming conflicts—multiple users can have packages with the same name.
Build Behavior
Automatic Includes
When you compile a project with an info.plist, Cherri automatically:
- Installs any missing packages (lazy installation)
- Generates
#includestatements for all packages in your manifest - Sorts includes deterministically (by user, then by package name)
This ensures builds are reproducible across different systems.
Include Order
Packages are included in lexicographic order:
- First by username (e.g.,
@alicebefore@bob) - Then by package name (e.g.,
helpersbeforeutils)
This deterministic ordering ensures consistent builds.
Best Practices
For Package Authors
- Keep packages focused: Each package should do one thing well
- Document dependencies: List all required packages in your
info.plist - Minimize dependencies: Only depend on what you actually need
- Use relative includes: Inside your package, use relative paths for internal files
- Test in isolation: Verify your package works when installed, not just in development
- Avoid name collisions: Choose descriptive, unique package names
- Treat your main branch as production: Don’t remove actions, deprecate them.
For Package Users
- Review before trusting: Check the GitHub repository before trusting a package
- Keep packages updated: Run
cherri --tidyperiodically to refresh packages - Pin critical projects: For production code, consider forking dependencies
- Document dependencies: Comment why you need each package
Versioning
Please maintain your main branch as your production Cherri code. The package repo is copied to the /packages/ directory, and so packages can be easily reset back to a prior commit or tag using the git CLI or a git GUI.
To push fixes or changes over time, a package author should first test those changes on a feature branch or locally, then push to the main branch. The next time someone downloads your package or uses --tidy, they will get that version, so please ensure proper testing before changes are merged or pushed to your main branch.
Troubleshooting
“Package already installed”
The package directory already exists. Options:
- Remove it first:
cherri --remove=@user/package - Reinstall everything:
cherri --tidy
“unable to install: stat …/main.cherri: no such file or directory”
The package repository is missing the required main.cherri file. Contact the package author.
“unable to install: stat …/info.plist: no such file or directory”
The package repository is missing the required info.plist file. The package may not be properly formatted.
Clone Failures
If installation fails with Git errors:
- Verify the repository exists and is public
- Check your internet connection
- Ensure the repository name follows the
cherri-{package_name}format
“Package must follow pattern”
Your package name doesn’t match @{username}/{package_name}. Ensure:
- It starts with
@ - Username and package name contain only letters, numbers, underscores, hyphens, or dots
- There’s exactly one
/separating username and package name
Examples
Creating and Publishing a Package
- Create a GitHub repository:
cherri-math-helpers - Add
main.cherriandinfo.plist - Users install it with:
cherri --install=@yourusername/math-helpers
Using Packages in Your Project
# Initialize your project
cherri --init=@myusername/myapp
# Install dependencies
cherri --install=@electrikmilk/stdlib
cherri --install=@someuser/utilities
# Your main.cherri automatically includes installed packages
# Build as normal
cherri main.cherri -o myapp.shortcut
Package with Dependencies
In your info.plist, list dependencies:
<key>Packages</key>
<array>
<dict>
<key>Name</key>
<string>helpers</string>
<key>User</key>
<string>otheruser</string>
</dict>
</array>
When someone installs your package, @otheruser/helpers is installed automatically.