Mod is pre-1.0. Minor releases may change the API until 1.0.
Skip to content

Commands ​

Every Artisan command mod registers, the options each one takes, and the folder each one writes to in each built-in layout. php artisan mod:list shows the file types, templates and scaffolds your layout has.

Placement Options ​

Every generator takes these, in addition to its own options:

OptionTakesExample
--inevery value of the layout's placeholders, in order, separated by /; a value spanning folders separates them with .--in=Knowledge/IndexDocument
--<placeholder>one placeholder's value, one option per placeholder the command's folder uses--module=Knowledge, --feature=Knowledge --slice=IndexDocument, --domain=Knowledge.Search
<values>: prefix on the namethe same values as --in, before a colonKnowledge:Document, Knowledge/IndexDocument:Handler

The laravel layout takes no placement. A placeholder option is left out when the Laravel command already has an option of that name; see When an Option Name Is Already Taken.

A value that differs from an existing group only by case, such as knowledge for Knowledge, uses that group and says so. A near miss, such as Knowledg, asks which you meant in a terminal; with --no-interaction it creates the new group. A new value creates its folder and says so. New and Misspelled Modules shows the output.

Before writing, a generator checks every file it is about to write, related files included. When one exists, it prints an error and writes nothing. --force overwrites, on the commands that list it below.

MCP Generator Commands ​

Since 0.5 (unreleased). These file types are added to modules and ddd when laravel/mcp is installed. Each command runs the corresponding Laravel MCP generator and inherits that installed package version’s arguments and options, plus mod’s placement and preview options. See MCP File Types for executable examples and custom placement.

CommandRunsmodules folderddd application folder
mod:mcp-servermake:mcp-server@module/Mcp/Serversapp/Modules/{domain}/Mcp/Servers
mod:mcp-toolmake:mcp-tool@module/Mcp/Toolsapp/Modules/{domain}/Mcp/Tools
mod:mcp-resourcemake:mcp-resource@module/Mcp/Resourcesapp/Modules/{domain}/Mcp/Resources
mod:mcp-promptmake:mcp-prompt@module/Mcp/Promptsapp/Modules/{domain}/Mcp/Prompts

The native make:mcp-* commands keep writing to their usual folders. mod:mcp-app-resource is not added: its native generator also writes a companion view. General delegation to arbitrary package generators is tracked separately in mod#87.

Generator Commands ​

Each command runs the Laravel command in the second column and takes that command's options, listed in the last column. The options come from Laravel, so they follow your Laravel version; these are Laravel 13's. Commands marked "none" have no Laravel generator: they write an empty class, or the file type's stub.

CommandRunsLayoutsOptions
mod:actionnonemodules, ddd--force
mod:castmake:castall--inbound, --force
mod:channelmake:channelall--force
mod:classmake:classall--invokable, --force
mod:commandmake:commandall--command, --test, --pest, --phpunit, --force
mod:configmake:configlaravel, type-first--force
mod:controllermake:controllerall--api, --type, --invokable, --model, --parent, --resource, --requests, --singleton, --creatable, --test, --pest, --phpunit, --force
mod:dtononemodules, ddd--force
mod:enummake:enumall--string, --int, --force
mod:eventmake:eventall--force
mod:exceptionmake:exceptionall--render, --report, --force
mod:factorymake:factoryall--model
mod:handlernoneslices--force
mod:interfacemake:interfaceall--force
mod:jobmake:joball--sync, --batched, --test, --pest, --phpunit, --force
mod:job-middlewaremake:job-middlewareall--test, --pest, --phpunit, --force
mod:listenermake:listenerall--event, --queued, --test, --pest, --phpunit, --force
mod:mailmake:mailall--markdown, --view, --test, --pest, --phpunit, --force
mod:messagenoneslices--force
mod:middlewaremake:middlewareall--test, --pest, --phpunit
mod:migrationmake:migrationall--create, --table, --fullpath
mod:modelmake:modelall--all, --controller, --factory, --migration, --morph-pivot, --policy, --seed, --pivot, --resource, --api, --requests, --test, --pest, --phpunit, --force
mod:notificationmake:notificationall--markdown, --test, --pest, --phpunit, --force
mod:observermake:observerall--model, --force
mod:policymake:policyall--model, --guard, --force
mod:providermake:providerall--force. In ddd, the provider is named as given, with no ServiceProvider suffix
mod:querynonemodules, features, slices, type-first--force
mod:requestmake:requestall--force
mod:resourcemake:resourceall--json-api, --collection, --force
mod:rulemake:ruleall--implicit, --force
mod:scopemake:scopeall--force
mod:seedermake:seederallnone
mod:testmake:testall--unit, --pest, --phpunit, --force
mod:traitmake:traitall--force
mod:validatornonefeatures, slices--force
mod:value-objectnonemodules, ddd--force
mod:view-modelnonemodules, ddd--force
  • mod:migration places migrations from the layout, so it exits with an error for --path and --realpath. Use --in instead.
  • mod:dto, mod:view-model, mod:value-object and mod:action start from starter stubs, and the first mod:dto or mod:view-model writes its base class.
  • mod:model's related-file options (--factory, --migration, --seed, --policy, --controller, --requests, --all) write each file in the same group.

Aliases ​

CommandAliases
mod:dtomod:data in modules and ddd; mod:data-transfer-object in ddd
mod:value-objectmod:value

Every hyphenated command or alias also works without the dash: mod:viewmodel, mod:valueobject, mod:jobmiddleware, mod:datatransferobject. When that name is already a command or alias, the existing one keeps it. php artisan list mod shows each command's aliases in brackets:

text
  mod:value-object    [mod:value|mod:valueobject] Create a new value object class

Commands for Your Own File Types ​

A file type you add with generates() gets mod:<id>, or the name given in command:, plus any aliases:, each also without the dash: generates('api-resource', ...) answers to mod:api-resource and mod:apiresource. Without a Laravel generator, it takes --force and the placement options.

Commands From Another Layout ​

A built-in command your layout doesn't have exits with an error naming the layouts that have it:

bash
php artisan mod:handler Knowledge:Thing
text
   ERROR  mod:handler is not a command of the modules layout. The slices layout has it.

  To add it, declare the file type in a service provider: Mod::layout('modules')->generates('handler', in: '<folder>'). Or switch layouts in config/mod.php.

Customizing a built-in layout shows the generates() line.

Where Each Command Writes ​

Folders below each layout's group folder:

LayoutGroup folder
modulesapp/Modules/<Module>
featuresapp/Features/<Feature>
slicesapp/<Feature>, and app/<Feature>/<Slice> for a slice's classes
dddsrc/Domain/<Domain>
Commandmodulesfeaturesslicesddd
mod:actionActionsActions
mod:castCastsCastsCastsCasts
mod:channelChannelsBroadcastingBroadcastingChannels
mod:class, mod:interface, mod:traitthe group folderthe group folderthe group folderthe group folder
mod:commandConsoleConsole/CommandsConsole/CommandsCommands
mod:controllerHttp/ControllersHttp/ControllersHttp/Controllersapp/Modules/<Domain>/Controllers
mod:dtoDataData
mod:enumEnumsEnumsEnumsEnums
mod:eventEventsEventsEventsEvents
mod:exceptionExceptionsExceptionsExceptionsExceptions
mod:factoryDatabase/FactoriesDatabase/FactoriesDatabase/FactoriesDatabase/Factories
mod:handler<Slice>/Handler.php
mod:jobJobsJobsJobsJobs
mod:job-middlewareJobs/MiddlewareJobs/MiddlewareJobs/MiddlewareJobs/Middleware
mod:listenerListenersListenersListenersListeners
mod:mailMailMailMailMail
mod:message<Slice>/Command.php
mod:middlewareHttp/MiddlewareHttp/MiddlewareHttp/Middlewareapp/Modules/<Domain>/Middleware
mod:migrationDatabase/MigrationsDatabase/MigrationsDatabase/MigrationsDatabase/Migrations
mod:modelModelsModelsModelsModels
mod:notificationNotificationsNotificationsNotificationsNotifications
mod:observerObserversObserversObserversObservers
mod:policyPoliciesPoliciesPoliciesPolicies
mod:providerProvidersProvidersProvidersProviders
mod:queryQueriesQueries<Slice>/Query.php
mod:requestHttp/RequestsHttp/Requests<Slice>/Http/Requests/Request.phpapp/Modules/<Domain>/Requests
mod:resourceHttp/ResourcesHttp/ResourcesHttp/ResourcesResources
mod:ruleRulesRulesRulesRules
mod:scopeScopesScopesScopesScopes
mod:seederDatabase/SeedersDatabase/SeedersDatabase/SeedersDatabase/Seeders
mod:testtests/Feature/Modules/<Module>tests/Feature/<Feature>tests/Feature/<Feature>/<Slice>tests/Feature/<Domain>
mod:validatorValidation<Slice>/Validator.php
mod:value-objectValueObjectsValueObjects
mod:view-modelViewModelsViewModels
  • The laravel layout writes every file where the matching make:* command does. type-first uses the same folders with an optional sub-folder for the feature, such as app/Models/Knowledge or database/factories/Knowledge, and adds mod:query, which writes to app/Queries/<Feature>.
  • A slice's classes have fixed names, so the name can be left out: mod:handler --in=Knowledge/IndexDocument writes app/Knowledge/IndexDocument/Handler.php. A different name is not used, and the command says so. In slices, mod:test takes the slice as an optional second value.
  • mod:test --unit writes to tests/Unit instead of tests/Feature.
  • In features and slices, mod:command without a feature writes to app/Console/Commands.
  • Class names take the file type's suffix: Controller, Request, Policy, Factory, Seeder, and ServiceProvider for providers in every layout but ddd. In ddd, a provider is named as given: mod:provider Knowledge:Knowledge writes src/Domain/Knowledge/Providers/Knowledge.php, and Knowledge:KnowledgeServiceProvider writes KnowledgeServiceProvider.php.

Writing Base Classes ​

CommandDoes
mod:basesWrites every generated base class the layout can use that is missing, whether or not a class extends it yet. It never overwrites one, and takes no options
bash
php artisan mod:bases
text
   INFO  Created base class App\Support\Data\DataTransferObject [app/Support/Data/DataTransferObject.php].
   INFO  Created base class App\Support\ViewModels\ViewModel [app/Support/ViewModels/ViewModel.php].

When every base exists, it prints Every base class already exists. In a layout whose file types extend no generated base, such as laravel, it prints No file type in this layout extends a generated base class. A configured base or an installed package means there is no base to write. It is registered with the mod:* commands.

Discovery Commands ​

CommandDoesAlso run by
mod:cacheScans the layout and caches the discovered providers, commands, listeners, subscribers and migration foldersphp artisan optimize
mod:clearRemoves the discovery cache filephp artisan optimize:clear
bash
php artisan mod:cache
text
   INFO  Discovery cached in [bootstrap/cache/mod-discovery.php]: 1 providers, 0 commands, 1 listeners, 0 subscribers, 2 directories, 5 rejected.

  Rejected files were found but not registered: 5 placed by no file type (helpers and plain classes; nothing to do). Run with -v to list them.

The second line counts the files it found but didn't register, grouped by reason. php artisan mod:cache -v lists each rejected file with its reason; Caching Discovery in Production explains each reason.

Both are registered only while the mod:* commands and discovery are both on. Caching Discovery in Production covers when the cache is used.

Creating Templates ​

mod:template [type] [path] creates a generator template:

bash
php artisan mod:template tool
php artisan mod:template dto @module/Data/links

With no arguments, choose a starting type and name in prompts. One argument names a class template; two arguments are the starting type and the name/path. A type alone therefore names a class template: mod:template job can collide with the built-in command. Custom generators covers starting types, paths, extraction and prompts.

OptionDoes
--from[=class-or-file]Extracts namespace and declared-name tokens without loading source PHP; a bare option opens search
--into=pathSupplies the extraction destination
--forceOverwrites a template

Without interaction, extraction without --into uses the source-folder suggestion and kebab-case name. Other missing answers name the flag or command form that resolves them.

Inspecting the Layout ​

bash
php artisan mod:list
php artisan mod:list -v
php artisan mod:list --type=model
php artisan mod:list --json

mod:list counts discovered factories and policies alongside providers, commands, listeners, subscribers and directories. -v adds aliases, stubs, bases, relations and discovered classes, including factory and policy targets. --type=<id> shows every detail for one file type. JSON root keys, in output order, are layout, extends, path, token, groups, types, templates, scaffolds, discovery, views, translations, package_pages, frontend_owners, assets, frontend, stack, wiring, frontend_notices, frontend_wiring, routes, connections, rename. The inventory schema describes required fields and additive keys. Scaffold tree nodes expose children and reference metadata. Invalid config exits 1; --json prints an error object. An unknown --type also exits 1 with guidance.

Inventory Fields Added in 0.6 ​

package_pages lists registered package namespaces and directories; frontend_owners records resolved paths, page names, view namespaces and aliases for layout and mounted-root owners. assets describes static patterns, expanded executable entries, their existence and wiring status. frontend_wiring records explicit package wiring, and frontend_notices lists package frontend declarations that were skipped, each with code, path and message. connections rows contain group, connection and declared. stack adds Svelte and Livewire detection. Package route rows use kind: "package" and loaded_by: "Mod::routes" after loading.

Scaffold items expose vendor, name, id, command, provider source, Composer package when known, overrides and structured notices. mod:list --vendor=acme filters scaffold items and problems in text and JSON. Consumers should accept additive fields; the schema gives their complete shapes.

Registering Autoload Mappings ​

mod:autoload adds missing Composer PSR-4 entries for the active layout and reloads Composer. Existing exact or parent mappings, including autoload-dev, count as coverage:

bash
php artisan mod:autoload --dry-run
php artisan mod:autoload --no-dump
php artisan mod:autoload
OptionDoes
--dry-runPreviews missing mappings without writing or running Composer
--no-dumpWrites mappings without running Composer
--namespace=<namespace>Supplies the namespace for a moved group outside the mounted roots

A moved path outside the roots prompts for a namespace, defaulting to its folder name (Areas\). Without a terminal, it uses and announces that default. A conflicting mapping exits 1 without writing; fix the mapping in composer.json and run again. When every mapping is configured, the command says so and suggests composer dump-autoload if classes do not load. A failed Composer run, including a missing executable, exits 1, keeps the written entries and names composer dump-autoload to run manually.

Scaffold Commands ​

A registered recipe gets mod:<scaffold>, with placement and question options, --skip-existing and --force. A child gets mod:<root>.<part> and takes the root's name and a child value. Scaffolds covers planning, collision choices and growing clusters.

Frontend Commands ​

CommandBehaviourOptions
mod:page <group>:<name>Generates a minimal detected-stack Inertia page--force, placement, preview
mod:view <group>:<dotted-name>Generates a namespaced Blade view--extension, --test, --pest, --phpunit, --force, placement, preview
mod:component <group>:<name>Generates a Blade class and view--view for anonymous, --inline, --path, test/style flags, --force, placement, preview

Vue, React and Svelte pages follow the detected stack; components use generator templates. Grouped mod:mail --markdown / --view and mod:notification --markdown place and qualify companion views. All planned collisions are checked before a class is written.

Route Commands ​

CommandBehaviourOptions
mod:routes <group>Generates web route files--api, --console, --force, preview
mod:route-registrar <group>Generates a RegistersRoutes class--force, preview

Load the files through Mod::routes(). Route generators prompt about replacement in a terminal; without one, the error names --force.

Installing Inertia ​

bash
php artisan mod:install inertia --dry-run --json
php artisan mod:install inertia --no-interaction

--dry-run previews edits without prompting; --json requires --dry-run. --no-interaction applies recognized wiring. A custom resolver or configuration prints manual instructions and exits 0 without edits. Declining the preview changes nothing. A repeated installation prints Already wired.

Installing Assets ​

bash
php artisan mod:install assets --dry-run --json
php artisan mod:install assets --no-interaction

This synchronises declared static patterns and executable entries without requiring Inertia. See Static assets and executable entries for Laravel Vite plugin requirements and loading examples.

Previewing Generator Plans ​

Commands that write files support --dry-run; add --json for the machine plan. Required questions use flags/defaults, with missing answers reported in warnings. Read would_write rather than the exit code alone. Cache maintenance has no file preview. Agents describes the plan and inventory schemas.

Renaming and recovery ​

mod:rename <old> <new> renames an explicitly selected recipe-owned cluster through its compiled layout. See Renaming and moving before execution.

OptionBehaviour
--scaffold=<recipe>Selects the effective recipe; mandatory for ordinary planning, or selected interactively
Recipe question/part flagsSupply the complete historical answers; repeated list flags include grown parts
--answer=<qualified-key=JSON>Supplies nested/reserved answers using original part paths
--dry-runRead-only preview; no prompts, writes, staging or journal
--jsonMachine plan; requires --dry-run
--table-migrationSelects complete reversible migration candidates when exact inference/planning is available; never executes them
--yesAccepts final confirmation after the fresh plan
--no-interactionRequires explicit decisions; use with --yes for approved execution
--recoverRecovers the worktree journal without cluster arguments or recipe/table flags

--recover --dry-run --json inspects without restoring. --recover --yes --no-interaction restores after review and refuses conflicting outside changes. Journals live in the actual worktree Git directory under mod-rename/. Ordinary execution requires a clean Git tree/index and a configured target. The staged result is never committed. There is no --force, overwrite, automatic stash or --apply-plan.

Structured Plan Warnings ​

Every --dry-run --json warning keeps file, line and message, and adds code and an object data. missing_placement provides file_type, dimension, accepted options and available group choices. generation_refused reports another blocking refusal; advisory is non-blocking; routes_not_loaded reminds you to wire routes. Codes and data are additive: consumers should tolerate new codes and fields, and display message for people. Missing placement messages name the running command's option, --in and the Group:Name short form.

The plan schema defines the warning envelope. Vendor reconciliation notices use vendor_scaffold_overridden, vendor_prefix_shadowed, vendor_scaffold_conflict and vendor_required; see Shipping scaffolds.

Released under the MIT License. Created by Jasper Tey.