alex-kassel / workspace-development-toolkit
Multi-workspace local package development toolkit for Laravel: manage, symlink, and develop isolated packages with ease.
Package info
github.com/alex-kassel/workspace-development-toolkit
pkg:composer/alex-kassel/workspace-development-toolkit
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- symfony/yaml: ^7.0|^8.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Multi-workspace local package development toolkit for Laravel: manage, symlink, and develop isolated packages across git-ready workspaces.
Requirements • Installation • Quick Start • Commands • Testing • Changelog
Requirements
- PHP:
^8.2(PHP 8.2, 8.3, or 8.4) - Laravel:
^11.0,^12.0, or^13.0 - Composer:
^2.2with path repository support
Installation
Install the package via Composer into your Laravel application (typically as a dev dependency):
composer require alex-kassel/workspace-development-toolkit --dev
The package service provider (AlexKassel\WorkspaceDevelopmentToolkit\WorkspaceDevelopmentToolkitServiceProvider) and facade (Workspace) are automatically discovered by Laravel.
Optionally, publish the package configuration:
php artisan vendor:publish --tag=workspace-config
Table of Contents
- Requirements
- Installation
- Introduction & Philosophy
- Key Highlights
- Usage
- Command Reference
- Smart Developer Experience (DX)
- Under the Hood: Architecture & Manifest
- Real-World Recipes & Patterns
- Troubleshooting & Domain Exceptions
- Testing
- License
Introduction & Philosophy
Modern Laravel engineering often demands managing dozens of distinct codebases simultaneously:
- Custom domain packages (DDD / modular monoliths).
- Client-specific micro-applications and customizations.
- Open-source libraries slated for Packagist publication.
Traditional approaches force developers into agonizing compromises: awkward repositories configurations in root composer.json, manual .gitignore edits, git submodules, or constantly jumping between disconnected project windows.
Workspace Development Toolkit bridges this gap by making your host Laravel application the centralized staging ground and mission control center:
- Every package lives in its own directory with its own independent
composer.jsonand can be initialized as a separate Git repository. - Workspaces are registered automatically as Composer
pathrepositories with instant symlinking. - Zero custom logic is placed in the host application — the toolkit remains fully autonomous.
Key Highlights
- 🚀 Zero-Friction Scaffolding: Create production-ready Laravel packages (with
ServiceProvider, PSR-4 autoloading, and manifests) with a single artisan command. - 📦 Dual Workspace Architecture:
- Nested (Multi-Vendor):
packages/{vendor}/{package}/for public libraries and diverse vendors. - Flat (Fixed-Vendor):
labs/{package}/orclients/{package}/without redundant vendor subdirectories.
- Nested (Multi-Vendor):
- 🔗 Automated Symlinking: Optional instant installation into
requireorrequire-devon package creation. - 🛡️ Defensive Developer Experience:
- Auto-sanitizes namespaces and paths (e.g.
Acme\Billing!->acme/billing). - Forgiving input parser accepting Windows backslashes (
\) or standard slashes (/). - Actionable
<comment>How to fix:</comment>blocks on every error. - Context-aware
<comment>Hint:</comment>blocks suggesting your next logical command.
- Auto-sanitizes namespaces and paths (e.g.
- 📋 Interactive CLI Manual: Built-in
workspace:helpcommand providing real-time cheatsheets and workflow guidance.
Usage
The toolkit supports both multi-vendor libraries and dedicated client or internal modules through flexible workspace paradigms.
The Two Workspace Paradigms
1. Multi-Vendor Workspace (Nested Structure)
- Configuration:
"vendor": null - Path Pattern on Disk:
{workspace}/*/* - Directory Structure:
packages/ ├── alex-kassel/ │ └── toolkit/ └── spatie/ └── custom-backup/ - Best Suited For: Public open-source packages, vendor forks, or mixed teams.
- Creating a Package:
php artisan package:make my-vendor/my-package --workspace=packages
2. Fixed-Vendor Workspace (Flat Structure)
- Configuration:
"vendor": "my-vendor" - Path Pattern on Disk:
{workspace}/* - Directory Structure:
labs/ ├── billing/ # composer name: "my-vendor/billing" ├── crm/ # composer name: "my-vendor/crm" └── notifications/ # composer name: "my-vendor/notifications" - Best Suited For: Internal company modules, domain services, or client workspaces where repeating the vendor name in the filesystem is redundant.
- Creating a Package:
php artisan package:make billing --workspace=labs
(Composer package name automatically generated asmy-vendor/billing, namespace:MyVendor\Billing).
Quick Start Tutorial (60 Seconds)
Step 1: Create a Flat Workspace with a Fixed Vendor
php artisan workspace:add labs --vendor=alex-kassel-labs --default
- Adds
labs/*path repository to rootcomposer.json. - Adds
/labsto.gitignore. - Marks
labsas default workspace.
Step 2: Create and Install a Local Module
php artisan package:make ai-assistant --install --dev
- Scaffolds
labs/ai-assistant/src/AiAssistantServiceProvider.php. - Sets package manifest to
alex-kassel-labs/ai-assistant. - Immediately runs
composer require alex-kassel-labs/ai-assistant --dev. - Everything is active in your Laravel app!
Step 3: Inspect Workspaces
php artisan workspace:list
Interactive Help & CLI Guide
Run php artisan workspace:help at any time to view the interactive color-coded guide:
php artisan workspace:help
You can also view standard Laravel help for any specific command:
php artisan help package:make php artisan help workspace:add
Command Reference
workspace:help
Displays a comprehensive, colorized interactive cheatsheet, detailing paradigms and practical workflows.
php artisan workspace:help
workspace:add
Registers a new workspace directory into composer.json (as a path repository), .gitignore, and workspace.json.
# Add a multi-vendor nested workspace: php artisan workspace:add packages # Add a flat workspace with a fixed vendor: php artisan workspace:add labs --vendor=alex-kassel-labs # Add and set as default: php artisan workspace:add modules --vendor=app-core --default
Options:
--vendor=: Fixed vendor name for flat 1-level package structure.--default: Set this workspace as the default workspace forpackage:make.
workspace:list
Lists all registered workspaces, their designated vendor, directory structure mode (Flat vs Nested), package count, and member packages.
php artisan workspace:list
Sample Output:
+-----------+---------+------------------+-----------+----------------+-------------------------------------------+
| Workspace | Default | Vendor | Structure | Packages Count | Packages |
+-----------+---------+------------------+-----------+----------------+-------------------------------------------+
| labs | No | alex-kassel-labs | Flat | 2 | ai-assistant |
| | | | | | telemetry |
| packages | Yes | (none / multi) | Nested | 1 | alex-kassel/workspace-development-toolkit |
+-----------+---------+------------------+-----------+----------------+-------------------------------------------+
workspace:default
Sets the active default workspace. Any subsequent package:make call without --workspace will target this workspace.
php artisan workspace:default labs
workspace:clone
Clones a package repository from Git/GitHub into a target workspace, registers it in workspace.json, and optionally symlinks it via Composer.
# Clone via shorthand (resolved using repository_template, e.g. GitHub SSH/HTTPS): php artisan workspace:clone spatie/laravel-ray --workspace=packages # Clone via full SSH or HTTPS URL: php artisan workspace:clone git@github.com:vendor/package.git # Clone and immediately symlink as development dependency: php artisan workspace:clone vendor/package --install --dev # Recursively clone internal dependencies from trusted organizations: php artisan workspace:clone vendor/package --recursive # Clone the toolkit itself into a local workspace for active contribution: php artisan workspace:clone --self
php workspace restore (Standalone CLI Runner)
Restores and clones missing workspace packages on fresh machines before running composer install — runs with zero framework dependencies:
# Checks workspace.json and git clones any missing packages: php workspace restore # Verify presence of all workspace packages on disk: php workspace status
Tip
This command is automatically registered in your root composer.json under pre-install-cmd and pre-update-cmd, ensuring all local workspace repositories exist before Composer resolves path dependencies!
workspace:remove
Unregisters a workspace from root composer.json and workspace.json.
php artisan workspace:remove labs
Note
Physical files and directories on disk are never deleted by workspace:remove. An actionable hint will remind you how to remove the folder manually if desired.
package:make
Scaffolds a new minimal Laravel package with a ServiceProvider, composer.json, and PSR-4 autoloading.
# In a multi-vendor workspace (requires vendor/package): php artisan package:make my-vendor/my-package --workspace=packages # In a fixed-vendor workspace (single-word allowed): php artisan package:make analytics --workspace=labs # Create and link immediately as dev-dependency: php artisan package:make billing --workspace=labs --install --dev
Options:
--workspace=: The target workspace (defaults to current default workspace).--install: Immediately triggercomposer requirefor the package upon creation.--dev: Install intorequire-devinstead ofrequire(used in conjunction with--install).
package:alias
Assigns a clean, customized directory alias to a package residing in a flat (fixed-vendor) workspace without altering its Composer package name.
# Assign a directory alias using arguments: php artisan package:alias billing MyBilling # Or using the --as option: php artisan package:alias billing --as=MyBilling
Options:
--as=: Alternate option to specify the new directory alias.--alias=: Synonym for--as.
Note
Directory aliasing renames the folder on disk, adjusts workspace.json, and triggers composer dump-autoload automatically to refresh PSR-4 autoload mappings.
package:install
Links an existing workspace package into the root application using Composer.
# By canonical name: php artisan package:install alex-kassel-labs/ai-assistant --dev # Or by short name (if belonging to a fixed-vendor workspace): php artisan package:install ai-assistant --dev
package:uninstall
Removes a package from root composer.json via composer remove.
# By canonical name: php artisan package:uninstall alex-kassel-labs/ai-assistant # Or by short name: php artisan package:uninstall ai-assistant
Tip
package:uninstall automatically detects whether the package is located in require or require-dev and applies --dev automatically.
package:delete
Permanently uninstalls the package from root Composer (if installed) and deletes the package directory from disk.
# Interactive confirmation: php artisan package:delete ai-assistant # Force deletion without prompt: php artisan package:delete ai-assistant --force
Note
package:delete includes built-in Git safety checks (via GitInspector) preventing accidental removal of dirty trees, unpushed commits, or stashed changes unless --force is provided.
package:skills
Discovers and materializes AI agent skills (SKILL.md) from a package's resources/skills into the host application's .agents/skills/ directory (or configured skills destination).
# Materialize published skills into the project (copy mode): php artisan package:skills my-package # Symlink skills for live development (changes sync immediately): php artisan package:skills my-package --symlink # Overwrite skills if already present: php artisan package:skills my-package --force # Remove all installed skills for this package from the project: php artisan package:skills my-package --remove
Options:
--symlink: Create symlinks instead of copying files (ideal for live development within local workspaces).--force: Force overwrite existing skills even if already present.--remove: Remove all materialized skills associated with this package from the project.
Tip
The command strictly honors skill publishing lifecycle rules: skills in draft status (where status != 'published' in the SKILL.md frontmatter) are skipped automatically unless --force is specified.
package:check
Runs automated quality gates across local packages (Composer validation, Pint, PHPStan, PHPUnit/Pest).
# Check a single package (deep tier by default: Composer + Pint + PHPStan + Tests): php artisan package:check my-package # Run quick tier checks only (Composer + Pint in seconds): php artisan package:check my-package --quick # Run specific checks only: php artisan package:check my-package --only=pint,phpstan # Automatically fix code style with Pint: php artisan package:check my-package --fix # Run tests in an isolated temporary Laravel environment: php artisan package:check my-package --isolated # Check all registered packages across workspaces: php artisan package:check --all # Check all registered packages with quick checks: php artisan package:check --all --quick
Options:
--quick: Run quick tier checks only (composervalidation andpintstyle check). Default is deep tier (composer,pint,phpstan,tests).--fix: Automatically format and fix code style issues using Pint.--only=: Comma-separated list of checks to run (composer,pint,phpstan,tests,isolated).--isolated: Install and test an independent temporary copy of the package in isolation.--all: Verify all packages across all registered workspaces.
package:readme
Validates that a package's README.md adheres to compliance standards (required sections, heading, absence of placeholder tokens).
php artisan package:readme my-package
package:release-check
Runs pre-flight verification before releasing or tagging a package (checks git cleanliness, .gitattributes export-ignore, code quality, and README compliance).
# Full release pre-flight (includes isolated environment test): php artisan package:release-check my-package # Fast release pre-flight: php artisan package:release-check my-package --fast
Smart Developer Experience (DX)
1. Robust Input Sanitization
The toolkit forgives unconventional inputs:
- Pasted Class Names / Namespaces:
php artisan package:make "AcmeStudio\SuperWidget"-> safely converted toacmestudio/superwidget. - Capital Letters & Symbols:
php artisan package:make "MyVendor/Special_Package!"-> normalized tomyvendor/special-package. - Slashes:
Supports standard forward slashes (
/), Windows backslashes (\), and repeated slashes (//).
2. Actionable <comment>How to fix:</comment> Guidance
Commands never fail silently with obscure system errors. Whenever validation fails, the terminal outputs clear instructions and exact copy-pasteable commands:
Invalid package name [billing]. Workspace [packages] requires a vendor prefix in 'vendor/package' format.
How to fix: Specify both vendor and package name:
php artisan package:make my-vendor/billing --workspace=packages
3. Contextual Next-Step Hints
After successful operations, commands guide you on what to do next:
Package [acme/billing] created successfully in [packages/acme/billing].
Hint: To link this package into your application via Composer, run:
php artisan package:install acme/billing
Or as a dev-dependency: php artisan package:install acme/billing --dev
Under the Hood: Architecture & Manifest
Manifest Format (workspace.json)
The central registry is stored at the root of your Laravel project:
{
"default": "labs",
"workspaces": {
"labs": {
"vendor": "alex-kassel-labs",
"packages": [
"ai-assistant",
"telemetry"
]
},
"packages": {
"vendor": null,
"packages": [
"alex-kassel/workspace-development-toolkit"
]
}
}
}
Path Repositories in composer.json
- For
packages(vendor: null):{ "name": "workspace-packages", "type": "path", "url": "packages/*/*" } - For
labs(vendor: "alex-kassel-labs"):{ "name": "workspace-labs", "type": "path", "url": "labs/*" }
Real-World Recipes & Patterns
Pattern A: Agency & Multi-Client Management
Create separate workspaces for each client project:
php artisan workspace:add clients/client-alpha --vendor=alpha-corp php artisan workspace:add clients/client-beta --vendor=beta-corp php artisan package:make payment-gateway --workspace=clients/client-alpha php artisan package:make crm-sync --workspace=clients/client-beta
Pattern B: Modular Monolith / DDD
Organize domain modules cleanly in modules/:
php artisan workspace:add modules --vendor=my-app --default php artisan package:make billing php artisan package:make ordering php artisan package:make inventory
Pattern C: Open-Source Library Incubator
Develop public packages ready for GitHub and Packagist:
php artisan workspace:add packages --default php artisan package:make my-handle/laravel-cache-warmer --install --dev
Troubleshooting & Domain Exceptions
All exceptions thrown by the toolkit extend AlexKassel\WorkspaceDevelopmentToolkit\Exceptions\WorkspaceException and include a built-in solution suggestion:
| Exception Class | Cause | Resolution |
|---|---|---|
WorkspaceNotFoundException |
Specified workspace path is not registered. | Run php artisan workspace:add <path> or check workspace:list. |
PackageNotFoundException |
Target package was not found in any workspace. | Check spelling or create it with package:make. |
DefaultWorkspaceNotConfiguredException |
No default workspace is configured. | Run php artisan workspace:default <path>. |
ComposerProcessException |
Composer command failed or timed out. | Inspect Composer error output; check dependency conflicts. |
InvalidJsonException |
Corrupted composer.json or workspace.json. |
Fix syntax errors in the JSON file indicated in the error message. |
Testing
The package includes a comprehensive PHPUnit test suite covering workspace manifest handling, package lifecycle, git safety, skill installation, and command interactions:
# Run tests from the host Laravel application: php artisan test packages/alex-kassel/workspace-development-toolkit/tests --compact # Or run PHPUnit directly: vendor/bin/phpunit packages/alex-kassel/workspace-development-toolkit/tests
License
The MIT License (MIT). Please see License File for more information.