The Package Skeleton CLI is a command-line interface that allows you to initialize a PHP package skeleton by replacing placeholders in files with the values you provide.
⚠️ The CLI currently works only on macOS with ARM architecture.
Using cURL:
curl -L "https://github.com/coyotito-mx/package-skeleton-cli/releases/latest/download/skeleton.tar.gz" -o skeleton.tar.gz
tar -xzf skeleton.tar.gz && chmod +x skeletonUsing wget:
wget "https://github.com/coyotito-mx/package-skeleton-cli/releases/latest/download/skeleton.tar.gz" -O skeleton.tar.gz
tar -xzf skeleton.tar.gz && chmod +x skeletonThen you can move the binary to your PATH:
mv skeleton /usr/local/bin/✅ The CLI initializes packages in the current working directory or a specified
--path. You can initialize from existing files or bootstrap from a template using--bootstrap.
The placeholders must follow the format: {{placeholder}}. You can apply modifiers to format values before replacement:
{{placeholder|modifier[,modifier]}}Initialize a package with vendor, package name, and author details:
skeleton init \
acme \
blog \
"Acme\\Blog" \
"John Doe" \
"john@doe.com" \
"A blogging package for Laravel" \
--path="$HOME/projects/my-package"Or use prompts:
skeleton init
# Prompts: vendor, package, namespace (optional), author, email (fetched from git config, if available), descriptionNote: The testing framework prompt appears only when dependencies are installed (i.e., without
--no-install).
To skip confirmation:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--proceedTo skip Composer dependency installation:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--no-installTo skip creating a LICENSE file when it does not exist:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--skip-licenseTo exclude specific files/directories from processing:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--exclude="composer.json" \
--exclude="package.json"To bootstrap from a skeleton template (vanilla or laravel):
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--bootstrap=vanillaTo force bootstrapping into a non-empty directory:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--bootstrap=laravel \
--forcevendor- Package vendor (e.g.,acme)package- Package name (e.g.,blog)namespace- Package namespace (auto-generated from vendor\package, or custom)description- Package descriptionauthor- Package author nameemail- Author's email addresslicense- License name (defaults toMIT)version- Package version (defaults to0.0.1)year- Current yearclass- Class name (defaults to package name in PascalCase). Used primarily in filenames
Namespace Format Requirements
The
namespaceargument must follow this pattern:Vendor\Package
- Must have exactly two parts separated by a backslash
- Each part must start with an uppercase letter
- Each part can contain alphanumeric characters (A-Z, a-z, 0-9)
- Invalid example:
acme\blog,Acme_Blog,Acme/Blog❌- Valid example:
Acme\Blog,MyVendor\MyPackage✅If not provided, the namespace will be auto-generated as
Vendor\Packagebased on the vendor and package arguments.
upper- Converts to UPPERCASElower- Converts to lowercasetitle- Converts to Title Casesnake- Converts to snake_casekebab- Converts to kebab-casecamel- Converts to camelCasepascal- Converts to PascalCase (StudlyCase)slug- Converts to slug-formatucfirst- Converts first character to uppercaseacronym- Generates acronym (e.g., "John Doe" → "JD")
⚠️ Modifier Order Matters! The order of chained modifiers affects the output.John Doe → JOHN-DOE {{author|upper,slug}} → john-doe (incorrect) {{author|slug,upper}} → JOHN-DOE (correct)
escape- Escapes\to\\(e.g.,Acme\Blog→Acme\\Blog)reverse- Reverses\to/(e.g.,Acme\Blog→Acme/Blog)
Note: Modifiers are applied to each part of the namespace separately (vendor and package).
major- Extracts major version (e.g.,2from2.5.3)minor- Extracts minor version (e.g.,5from2.5.3)patch- Extracts patch version (e.g.,3from2.5.3)pre- Extracts pre-release (e.g.,alphafrom1.0.0-alpha)meta- Extracts build metadata (e.g.,abc123from1.0.0+abc123)prefix- Addsvprefix if not present (e.g.,1.0.0→v1.0.0)
upper- Converts email to uppercase while preserving@and.
- Used only in filename contexts
- Converts to kebab-case in filenames (e.g.,
{{class}}→my-class.php) - Default value is transformed from package name (e.g.,
blog→Blog→blogin filename) - Supports all global modifiers when used in content
SYNOPSIS
skeleton init [options] [--] <vendor> <package> <namespace> <author> <email> <description>
Arguments
vendor The name of the package vendor (prompted if not provided)
package The name of the package (prompted if not provided)
namespace The package namespace (auto-generated as Vendor\Package if not provided)
author The package author (fetched from `git config user.name` or prompted)
email The package author email (fetched from `git config user.email` or prompted)
description The package description (prompted if not provided)
Options
--bootstrap[=BOOTSTRAP] Initialize a new package from skeleton template (options: laravel, vanilla)
--class[=CLASS] The class name to use in replacements (defaults to package name)
--force Force bootstrapping even if target directory is not empty (use with --bootstrap)
--proceed Accept the configuration and proceed without confirmation
--no-install Skip installing composer dependencies
--skip-license Skip creating a LICENSE file if one does not exist
--path[=PATH] The path to initialize the package in (defaults to current working directory)
--exclude[=EXCLUDE] Paths to exclude when processing files (multiple values allowed)
-h, --help Display help for the command
--silent Do not output any messages
-q, --quiet Only errors are displayed. All other output is suppressed
-V, --version Display this application version
--ansi|--no-ansi Force (or disable --no-ansi) ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output, and 3 for debug outputBy default, the following paths are excluded from placeholder replacement:
.git.DS_Storevendornode_modules
Add custom exclusions with --exclude:
skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "Description" \
--exclude="dist" \
--exclude="build" \
--exclude=".env"The CLI automatically fetches author information from git configuration:
git config user.name "John Doe"
git config user.email "john@doe.com"If git config is not available, you'll be prompted interactively.
When dependencies are enabled (without --no-install), you'll be prompted to choose your testing framework:
- Pest (default) - Modern, elegant testing for PHP
- PHPUnit - Industry-standard PHP testing framework
The selected framework's dev dependencies will be automatically installed.
If --no-install is used, this prompt is skipped and no testing framework dependencies are installed.
At the end of a successful init run, the CLI always asks:
Do you want to remove this CLI now?
If you answer yes, the CLI removes the invoked executable file. If it cannot resolve or delete that executable, it shows a warning and continues.
To remove content you can use a tag named <remove></remove>, and all the content between this tag would be removed, also the spaces after the close tag will be removed.
# Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sem libero, rutrum ut congue quis, cursus quis lectus.
<remove>Etiam tempor ac lacus in congue.</remove>
Ut lobortis eros a ipsum varius, eget tristique risus laoreet. Vestibulum ultricies augue ligula, vitae imperdiet urna tempus non.and the result should be something like:
# Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sem libero, rutrum ut congue quis, cursus quis lectus.
Ut lobortis eros a ipsum varius, eget tristique risus laoreet. Vestibulum ultricies augue ligula, vitae imperdiet urna tempus non.$ skeleton init --path="$HOME/projects"
Enter the package vendor name: acme
Enter the package name: blog
Enter the package namespace (Optional, press Enter to auto-generate): Acme\Blog
Enter the package description: A blogging package
(Author and email fetched from git config, if available)
...
Do you want to proceed with this configuration?: Yes
...
Which testing framework do you want to use? [pest/phpunit]: pest
...
Package [Acme\Blog] initialized successfully!
Do you want to remove this CLI now?: Noskeleton init acme blog "Acme\\Blog" "Jane Doe" "jane@example.com" "A blogging package" \
--proceed \
--path="$HOME/projects"skeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "A blogging package" \
--no-install \
--proceedskeleton init acme blog "Acme\\Blog" "John Doe" "john@doe.com" "A blogging package" \
--class="BlogManager" \
--proceedIf you would like to contribute to the Skeleton CLI, please follow these steps:
- Fork the repository.
- Create a new branch for your feature or bugfix.
- Make your changes and add tests.
- Ensure all tests pass:
composer test - Run code standards:
composer lint:with-style - Submit a pull request.
Run tests with:
composer test # Run all tests
composer test:ci # Run tests in CI modeCheck code standards with:
composer lint # Run PHPStan (level 5)
composer style # Check code formatting with PintFix code formatting:
composer fix # Auto-fix code formattingThe Skeleton CLI is open-sourced software licensed under the MIT license.