PHPackages                             laramint/laravel-zonda - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. laramint/laravel-zonda

ActiveProject[CLI &amp; Console](/categories/cli)

laramint/laravel-zonda
======================

Zonda is a command-line tool for building standalone Laravel packages without needing a host Laravel application. It lets you work inside a package directory as if you were inside a regular Laravel project — running migrations, artisan commands, scaffolding files — while the package itself stays clean and portable.

v0.1.2(4w ago)47MITPHPPHP ^8.2

Since May 12Pushed 4w agoCompare

[ Source](https://github.com/laramint/laravel-zonda)[ Packagist](https://packagist.org/packages/laramint/laravel-zonda)[ RSS](/packages/laramint-laravel-zonda/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (4)Versions (4)Used By (0)

[![Zonda](art/logo.png)](art/logo.png)

A CLI that lets you build Laravel packages without a host Laravel app. Run `zonda` commands from inside a package directory and Zonda transparently boots a Laravel sandbox, links your package into it, and runs `artisan` against it — so package work feels like working inside a regular Laravel project.

Built on [Laravel Zero](https://laravel-zero.com/).

How it works
------------

[](#how-it-works)

1. `zonda new vendor/name` scaffolds a package skeleton (composer.json, ServiceProvider, Pest tests, etc.) and pins it to one **or several** Laravel majors (9–13).
2. Inside that package, `zonda make:*` writes files directly into `src/`, `database/`, `resources/`, `tests/` — no sandbox round-trip, fast and offline.
3. `zonda artisan ` lazily creates a sandbox at `~/.zonda/sandboxes/laravel-{N}/` (one per Laravel major), wires the package in via a Composer path repository + symlink, and runs `php artisan` there. A package pinned to multiple majors uses the highest by default; `--laravel=N` switches to any other pinned version.
4. `zonda test` runs Pest/PHPUnit inside the package itself against its own `vendor/`.

A package is identified by `extra.zonda.package: true` in its `composer.json`. Supported Laravel majors live at `extra.zonda.laravel` as an array (e.g. `[10, 11, 12]`); a single int is also accepted for backward compatibility. Sandboxes are version-keyed, so a Laravel 10 package and a Laravel 13 package coexist with their own installs and don't fight over composer state.

Requirements
------------

[](#requirements)

- PHP `^8.2`
- Composer on `PATH` (used to build sandboxes)

Install
-------

[](#install)

### Composer global

[](#composer-global)

```
composer global require laramint/laravel-zonda -W
```

The `-W` (`--with-all-dependencies`) flag is recommended: Composer's global namespace often has other CLIs installed that pin `illuminate/*` to older majors, and without `-W` you'll see errors like *"fixed to v11.x (lock file version) by a partial update"*. `-W` lets Composer upgrade those locked transitive deps to satisfy Zonda's requirements.

Make sure Composer's global `bin` is on your `PATH` (usually `~/.composer/vendor/bin` or `~/.config/composer/vendor/bin`), then:

```
zonda --help
```

If you'd rather not touch your global Composer state at all, use the [PHAR install](#phar) below — it's self-contained and won't conflict with anything.

### PHAR

[](#phar)

Download the latest release directly:

```
curl -L https://github.com/laramint/laravel-zonda/releases/latest/download/zonda -o zonda
chmod +x zonda
sudo mv zonda /usr/local/bin/zonda
zonda --version
```

To pin a specific version, replace `latest/download` with `download/vX.Y.Z`:

```
curl -L https://github.com/laramint/laravel-zonda/releases/download/v0.1.0/zonda -o zonda
```

Or browse the [GitHub releases](https://github.com/laramint/laravel-zonda/releases) page and grab the `zonda` asset manually.

Quickstart
----------

[](#quickstart)

```
zonda new acme/widget --laravel=12     # scaffold a package pinned to Laravel 12
cd widget
zonda make:command SayHello             # writes src/Console/Commands/SayHello.php
zonda make:model Post                   # writes src/Models/Post.php
zonda make:migration create_posts_table # writes database/migrations/_create_posts_table.php
zonda artisan migrate                   # boots the L12 sandbox, links the package, runs artisan migrate
zonda test                              # runs Pest in the package
```

Commands
--------

[](#commands)

### Top-level

[](#top-level)

CommandPurpose`zonda new  [--laravel=N[,N,...]] [--path=...]`Scaffold a new package. Shows a multi-select prompt of Laravel 9–13 if `--laravel` is omitted; pass `--laravel=12` for a single version or `--laravel=10,11,12` for a multi-version package.`zonda artisan  [--laravel=N]`Run `php artisan` inside the version-matched sandbox with the current package linked. When the package pins multiple majors, the highest is used by default; `--laravel=N` picks a different pinned major. The sandbox is created on first use.`zonda test `Run the package's own test suite (Pest preferred, PHPUnit fallback). Runs `composer install` in the package on first use.`zonda update [--check] [--force]`Replace the running Zonda PHAR with the latest GitHub release. Aliased to `zonda self-update`. Composer-installed users should use `composer global update laramint/laravel-zonda -W` instead — the command detects that and tells you.`zonda resync [--laravel=N] [--all] [--reset]`Re-link the current package into its sandbox(es). Useful after moving the package directory or when the sandbox got into a weird state. `--all` covers every pinned Laravel major; `--reset` deletes the sandbox first and rebuilds it.### Generators (`make:*`)

[](#generators-make)

All generators are run from inside a package directory and write into the package itself. Most accept a single `{name}` argument and a `--force` flag to overwrite an existing file. Names like `Blog/Post` or `Blog\Post` create subfolders/sub-namespaces.

CommandTargetNotes`make:command ``src/Console/Commands/.php`Signature defaults to the kebab-cased class name.`make:controller ``src/Http/Controllers/.php``make:model ``src/Models/.php``make:provider ``src/Providers/.php``make:migration ``database/migrations/_.php`Timestamped. Guesses the table name from `create__table`.`make:request ``src/Http/Requests/.php`FormRequest with `authorize` + `rules`.`make:resource ``src/Http/Resources/.php`JsonResource.`make:factory  [--model=]``database/factories/Factory.php``--model` controls the FQN in `use` / `$model`.`make:seeder ``database/seeders/Seeder.php``make:middleware ``src/Http/Middleware/.php``make:job ``src/Jobs/.php`Implements `ShouldQueue`.`make:event ``src/Events/.php`Uses `Dispatchable` + `SerializesModels`.`make:listener  [--event=]``src/Listeners/.php``--event=Pinged` resolves to `{Namespace}\Events\Pinged`; pass a FQN to point elsewhere.`make:mail ``src/Mail/.php`Mailable with `envelope`/`content`/`attachments`.`make:notification ``src/Notifications/.php``make:policy  [--model=]``src/Policies/.php``make:test  [--unit]``tests/Feature/Test.php` (or `tests/Unit/...`)Pest by default.`make:view ``resources/views/.blade.php`Both `admin.users.index` and `admin/users/index` work.`make:config [name]``config/.php``name` defaults to the package's short name, matching what the generated ServiceProvider auto-merges.The generated package
---------------------

[](#the-generated-package)

`zonda new` produces a skeleton with:

```
acme/widget/
├── composer.json
├── README.md
├── phpunit.xml.dist
├── src/
│   └── WidgetServiceProvider.php
└── tests/
    ├── ExampleTest.php
    ├── Pest.php
    └── TestCase.php

```

`composer.json` carries the markers Zonda needs:

```
{
    "extra": {
        "zonda": { "package": true, "laravel": [10, 11, 12] },
        "laravel": { "providers": ["Acme\\Widget\\WidgetServiceProvider"] }
    }
}
```

The pinned majors drive:

- which sandbox `zonda artisan` runs against (highest by default, overridable with `--laravel=N`)
- the `illuminate/support` constraint — `^10.0|^11.0|^12.0` for the example above
- the matching `orchestra/testbench` constraint (union from the version matrix)

A package pinned to a single major still works exactly the same — `extra.zonda.laravel: 12` and `extra.zonda.laravel: [12]` are both accepted, and resolve to a one-element major set.

#### Testing against every pinned version

[](#testing-against-every-pinned-version)

Because each pinned major has its own sandbox, you can sanity-check your package end-to-end on the whole matrix:

```
zonda artisan --laravel=10 list
zonda artisan --laravel=11 list
zonda artisan --laravel=12 list
```

Each run uses its dedicated `~/.zonda/sandboxes/laravel-{N}/` install with your package linked in.

### Auto-loading ServiceProvider

[](#auto-loading-serviceprovider)

The generated `ServiceProvider` is conventions-aware: it auto-registers anything it finds on disk, so you can just run `make:view`, `make:config`, `make:migration`, etc. and the loader picks them up without you editing the provider. What gets wired up if it exists:

PathWhat happens`config/.php``mergeConfigFrom(..., '')` in `register()`; publishable as tag `-config`.`database/migrations/``loadMigrationsFrom(...)`; publishable as tag `-migrations`.`resources/views/``loadViewsFrom(..., '')`; publishable as tag `-views`.`resources/lang/` (or `lang/`)`loadTranslationsFrom(..., '')`; publishable as tag `-lang`.`routes/web.php` / `routes/api.php` / `routes/console.php``loadRoutesFrom(...)` for each that exists.`` is the package's kebab name (e.g. `acme/widget` → `widget`). The provider uses `dirname(__DIR__)` to find the package root, which is correct because it lives at `src/.php`.

The sandbox
-----------

[](#the-sandbox)

State lives under `~/.zonda/sandboxes/laravel-/`:

```
~/.zonda/sandboxes/
├── laravel-10/         # full Laravel 10 install (only created on first L10 zonda artisan)
│   ├── composer.json   # has a path repo + require pointing at your package
│   └── state.json      # { "linked": "/path/to/the/currently-linked/package" }
├── laravel-12/
└── laravel-13/

```

`zonda artisan ...` is the only command that needs the sandbox; `make:*` and `new` are offline. Linking is cached in `state.json`, so repeated artisan calls from the same package don't re-run `composer update`.

To rebuild from scratch you can use the dedicated command, which is safer than `rm -rf` because it also re-links the package immediately:

```
zonda resync --reset              # nuke + rebuild the default-pinned sandbox
zonda resync --reset --all        # do that for every pinned Laravel major
```

Or, equivalently, the manual route:

```
rm -rf ~/.zonda/sandboxes/laravel-12   # forces a fresh L12 sandbox on next zonda artisan
```

### What if I move the package directory?

[](#what-if-i-move-the-package-directory)

You usually don't need to do anything — `zonda artisan ...` detects that `state.json`'s cached path no longer matches the current package root, drops the stale path-repo entry from the sandbox's `composer.json`, writes the new one, runs `composer update `, and updates `state.json`. Next call is back to the fast path.

If that auto-heal doesn't kick in (e.g. you have two clones of the same package and the state happens to match the wrong one, or the sandbox's vendor tree got corrupted), force it explicitly:

```
cd /new/location/of/the/package
zonda resync                # forces a fresh link from this directory
zonda resync --all          # do it for every pinned Laravel major
zonda resync --reset        # nuke the sandbox first, then re-link
```

Adding your own top-level commands
----------------------------------

[](#adding-your-own-top-level-commands)

Drop a class under `app/Commands/` extending `LaravelZero\Framework\Commands\Command`. Laravel Zero auto-discovers it.

```
namespace App\Commands;

use LaravelZero\Framework\Commands\Command;

class DoThingCommand extends Command
{
    protected $signature = 'do:thing {name} {--force}';
    protected $description = 'Do a thing.';

    public function handle(): int
    {
        $this->info("Doing {$this->argument('name')}");
        return self::SUCCESS;
    }
}
```

For new `make:*` generators, extend `App\Support\AbstractMakeCommand` and implement `stubName()`, `relativeTargetPath()`, and `replacements()`. Use `$this->parseName($name)` for free `Blog/Post` subfolder support, and `$this->targetNamespace(...)` / `$this->targetPath(...)` to compose the namespace and file path.

Updating
--------

[](#updating)

### PHAR install

[](#phar-install)

```
zonda update          # download and replace the running PHAR
zonda update --check  # just compare current vs. latest, don't download
zonda update --force  # re-download even if already on the latest version
```

How it works: the command fetches `releases/latest` from the GitHub API to learn the newest tag, compares it to the version baked into the PHAR (`zonda --version`), then downloads the `zonda` asset for that tag into `.new`, sanity-checks that it's a real PHAR, sets it executable, and renames it over the running binary. The rename is atomic on Unix — the in-memory PHP process keeps running off the old inode, and the next invocation picks up the new file.

If the binary lives somewhere root-owned (`/usr/local/bin/zonda` is typical), you'll need:

```
sudo zonda update
```

### Composer global install

[](#composer-global-install)

```
composer global update laramint/laravel-zonda -W
```

Running `zonda update` on a Composer install will detect that case and print this command for you — it won't try to overwrite Composer-managed files.

Build the PHAR locally
----------------------

[](#build-the-phar-locally)

```
php zonda app:build zonda --build-version=0.1.0
# → builds/zonda
```

If you get `Failed to compile the application.`, your PHP has `phar.readonly=On` (the default on most distros and Homebrew). Two ways to fix it:

```
# Quick: permanently flip it in your loaded php.ini
php -r 'echo php_ini_loaded_file().PHP_EOL;'
# edit that file → set:  phar.readonly = Off

# Or build via box directly (no wrapper subprocess to worry about):
php -d phar.readonly=0 vendor/laravel-zero/framework/bin/box compile --config=box.json
```

CI doesn't hit this — the release workflow sets `phar.readonly=Off` explicitly via `shivammathur/setup-php`.

Development
-----------

[](#development)

```
git clone https://github.com/laramint/laravel-zonda.git
cd laravel-zonda
composer install
./vendor/bin/pest
```

License
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance94

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

28d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/23707114?v=4)[Abdelrahman Muhammed](/maintainers/mrmarchone)[@mrmarchone](https://github.com/mrmarchone)

---

Top Contributors

[![mrmarchone](https://avatars.githubusercontent.com/u/23707114?v=4)](https://github.com/mrmarchone "mrmarchone (13 commits)")

---

Tags

laravellaravel-packageopen-sourcepackagephpphp-packagecliconsolelaravel-zerozonda

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/laramint-laravel-zonda/health.svg)

```
[![Health](https://phpackages.com/badges/laramint-laravel-zonda/health.svg)](https://phpackages.com/packages/laramint-laravel-zonda)
```

###  Alternatives

[nunomaduro/collision

Cli error handling for console/command-line PHP applications.

4.6k348.7M10.3k](/packages/nunomaduro-collision)[mehrancodes/laravel-harbor

A CLI tool to Quickly create On-Demand preview environment for your apps.

10095.6k](/packages/mehrancodes-laravel-harbor)[nunomaduro/laravel-console-menu

Laravel Console Menu is an output method for your Laravel/Laravel Zero commands.

816424.6k51](/packages/nunomaduro-laravel-console-menu)[nunomaduro/laravel-console-task

Laravel Console Task is a output method for your Laravel/Laravel Zero commands.

2582.3M13](/packages/nunomaduro-laravel-console-task)[nunomaduro/laravel-console-summary

A Beautiful Laravel Console Summary for your Laravel/Laravel Zero commands.

672.2M4](/packages/nunomaduro-laravel-console-summary)[nunomaduro/laravel-console-dusk

Laravel Console Dusk allows the usage of Laravel Dusk in Laravel/Laravel Zero artisan commands.

16357.3k8](/packages/nunomaduro-laravel-console-dusk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
