nowo-tech/claude-php-setup
A setup helper for PHP and Laravel projects that bootstraps Claude Code with practical configuration, project guidance, and reusable commands. Designed for quick installation and consistent AI-assisted development workflows across teams.
What it is: a Composer dev-tool — a Symfony Console–style interactive generator that writes Claude Code context files (CLAUDE.md, slash commands, subagent definitions) into your project. It is not a runtime library; nothing in your Laravel app depends on it after it runs.
Minimal setup
composer require --dev nowo-tech/claude-php-setup
vendor/bin/. List it before guessing the name:
ls vendor/bin | grep -i claude
git add -A && git commit), so you can inspect and revert the generator's output with git status / git diff.Where to look first
CLAUDE.md — the project contract Claude reads every session. Read this line by line; it is the only file that shapes every interaction..claude/commands/*.md — reusable slash commands (test runner, review, formatting)..claude/agents/*.md — subagent definitions with their own tool scopes.CLAUDE.md the tool scatters into subdirectories (Claude Code resolves the nearest file up the tree).First use case
A fresh Laravel 11/12 app with Pest and Pint. Generate the scaffold, then immediately trim it to: how to run the test suite (php artisan test / ./vendor/bin/pest), how to format (./vendor/bin/pint), static analysis command, and your directory conventions (app/Actions, app/Data, etc.). Delete every generated stub that references a tool you don't use. That edited file — not the generator output — is the artifact you keep.
After generation: composer remove --dev nowo-tech/claude-php-setup. The markdown is plain text; the package has served its purpose and the project is abandoned. Keeping a dev dependency you no longer invoke is a supply-chain liability with zero upside.
Treat it as a one-shot scaffolder, not a dependency. Generate once, commit the markdown, drop the package. Re-running it later produces diffs against files you've heavily hand-edited, which is worse than editing markup by hand.
Layer, don't replace. Keep the generated file's heading structure intact and append a Laravel-specific section below it:
## Testing
- Feature tests: `tests/Feature`, use `RefreshDatabase`.
- Run one file: `php artisan test tests/Feature/OrderTest.php`.
## Conventions
- Formatters: `./vendor/bin/pint` before every commit.
- Static analysis: `./vendor/bin/phpstan analyse` (level 6, `phpstan.neon`).
Preserving the skeleton makes future regeneration diffs readable if you ever fork and update.
Split knowledge by scope, not by topic. Root CLAUDE.md holds the global contract (stack, commands, invariants). Put module-local rules in nested CLAUDE.md files — packages/billing/CLAUDE.md, app/Domain/Pricing/CLAUDE.md — because Claude Code reads the nearest file in the tree. An 800-line root file is an anti-pattern; a 120-line root file plus scoped leaves is not.
Turn runbooks into slash commands. The generator gives you command files; replace the placeholder bodies with your team's actual repeated workflows:
/review — run Pint dry-run + PHPStan, then summarize findings./test-file — take the current file, run its matching test, report failures./migrate-check — inspect a new migration for missing indexes and destructive down().Each command should fit on one screen. A command that needs scrolling is a command nobody invokes.
Subagents for read-only analysis. Generated agent files are a starting point. Constrain them deliberately:
tests/ plus read access to app/.Give every agent an explicit, minimal tool list. Default permissive agents are how a "review my controller" task becomes a php artisan migrate:fresh on a shared database.
Regenerate into a scratch directory and diff. If you do want the generator's newer output, run it in a throwaway clone of the repo, git diff --no-index the result against your committed files, and cherry-pick. Never run it on a dirty working tree.
Fork the templates instead of pinning the package. Because the project is abandoned, the durable move is to copy the template markdown into tools/claude-setup/templates/ in your repo (or an internal Composer path repo) and edit freely. That preserves the useful part — the template structure — without the dead dependency.
Keep one source of truth across agent tools. If you already maintain .cursor/rules/ or AGENTS.md, don't let CLAUDE.md drift into a contradictory second copy. Either symlink, or make CLAUDE.md a short file that says "see AGENTS.md" plus Claude-specific deltas.
CI guard against rot. CLAUDE.md references commands and paths; those rot silently. Add a cheap check:
# fail if CLAUDE.md mentions a binary that no longer exists
grep -oE '\./vendor/bin/[a-z0-9-]+' CLAUDE.md | sort -u | while read -r b; do
[ -x "$b" ] || { echo "stale: $b"; exit 1; }
done
This catches the common failure after a dependency swap (PHPStan → Larastan, Pest → PHPUnit) where the instructions confidently name a tool that isn't installed.
Onboarding flow: new developers run git pull and get the curated committed files. They do not re-run the generator. Document that explicitly, or you'll get a PR that reformats the entire .claude/ tree.
It is abandoned — budget for drift. No PHP 8.4 / Laravel 12 idiom updates, no security patches, no fixes. Pin the exact version in composer.json if you keep it at all, and assume every generated line is a starting draft rather than advice. The repo's own agent rules are written for a Symfony bundle ("Assume this repository is a reusable Symfony bundle"), not a PHP-project CLI — a clear signal of maintenance drift between what the tool advertises and what it maintains.
Back up before the first run. Interactive generators commonly overwrite an existing CLAUDE.md or clobber .claude/ entries without merging. That's why step 4 above is a clean commit: git checkout -- CLAUDE.md is your undo.
Interactive prompts don't go in CI. If the CLI has no non-interactive flag, it needs a TTY. In Docker use docker exec -it …; never wire it into a build step or composer install post-install hook.
Token budget is the real constraint. CLAUDE.md is injected into every session. A bloated file crowds out the actual code context and makes Claude worse, not better. Target under ~200 lines. Push detail outward: commands, scoped CLAUDE.md files, or prose docs Claude can read on demand by path.
Never put secrets or environment specifics in these files. No DSNs, API keys, .env values, absolute home paths, or internal hostnames. The file is committed and transmitted to the model on every session. The generator can't know this — you must enforce it during review.
Filename and frontmatter errors fail silently. If a slash command doesn't appear in Claude Code, check three things in order: the file lives in .claude/commands/, the filename matches the command name, and the frontmatter is valid YAML. Commands don't error — they just don't exist.
Line endings and encoding. Generated markdown on Windows/WSL can land with CRLF, which occasionally breaks frontmatter parsing and always creates noisy diffs. Normalize with a .gitattributes rule for .claude/** and CLAUDE.md.
Check .gitignore both ways. .claude/ is sometimes ignored by templates written for local-only settings — but you want this content versioned. Conversely, make sure local artifacts (settings, caches, credentials) inside .claude/ are excluded.
The extension point is the templates, not the CLI. There's no plugin API worth relying on in an unmaintained package. If you need different behavior, edit the generated files or fork the templates. Post-processing the output with sed is a maintenance trap.
Watch generated agents with shell access. Any agent definition granting shell or unrestricted Bash should get a manual audit before it enters the repo — especially ones that could run migrate, db:wipe, or queue:clear.
Migration path when you outgrow it: by roughly the third hand-edit, the generator is a net negative. Move to a hand-written CLAUDE.md + AGENTS.md pair, keep every piece of content you've accumulated, and delete the package. There's no lock-in here — the output is plain markdown, which is the one genuinely good property of this tool.
How can I help you explore Laravel packages today?