Shared AI assets for webhub GmbH — guidelines, Skills, and slash-commands used across our Laravel and Craft CMS projects, and uploaded to our claude.ai Team workspace.
cd into your dev directory first (~/reps, ~/code, whatever you use), then run:
[ -d ai ] || git clone https://github.com/webhubworks/ai.git && cd ai && git pull && ./bootstrap.shClones webhubworks/ai into the current directory if it isn't already there, pulls the latest, then runs bootstrap.sh to wire commands and guidelines into ~/.claude. Safe to re-run any time — it's the update path too.
ai/
├── bootstrap.sh # One-shot installer that links this repo into ~/.claude
├── hooks/
│ └── session-start.sh # Claude Code SessionStart hook: detects the stack,
│ # injects the matching guideline(s), throttled auto-pull
├── guidelines/ # Plain-markdown guidelines
│ ├── README.md
│ ├── webhub.md # stack-agnostic webhub baseline
│ ├── webhub-laravel.md # Laravel/PHP
│ ├── webhub-craft-cms.md # Craft CMS (scaffold)
│ └── laravel-php-guidelines.md # Spatie's Laravel/PHP standards, mirrored from spatie.be
├── skills/ # Skills — linked into ~/.claude/skills by bootstrap.sh,
│ │ # and each subfolder also zips up into one claude.ai Skill
│ ├── laravel/
│ │ ├── SKILL.md # thin wrapper - the rules live in the guideline
│ │ └── reference.md # → ../../guidelines/webhub-laravel.md (symlink)
│ ├── craft-cms/
│ │ ├── SKILL.md # thin wrapper - the rules live in the guideline
│ │ └── reference.md # → ../../guidelines/webhub-craft-cms.md (symlink)
│ └── craft-upgrade/ # Craft major-version upgrade playbook
│ ├── SKILL.md # orchestrator (only its description loads per session)
│ ├── upgrading-across-multiple-major-versions.md
│ ├── v2-to-v3/ v3-to-v4/ v4-to-v5/ # per-leg docs, read on demand
│ └── v5-to-v6/ # placeholder until the first 5→6 upgrade
└── commands/ # Slash-commands for Claude Code
├── laravel/ # → /webhub:laravel:<name>
│ ├── commit-and-simplify.md
│ └── simplify.md
├── plan-issue.md # → /webhub:plan-issue
└── security-review.md # → /webhub:security-review
Everything is served from your local clone of this repo, so a single git pull updates every project on your machine — no composer update, nothing committed into project repos.
There are two layers, both wired up by bootstrap.sh:
- Stack-agnostic baseline —
guidelines/webhub.mdis@-imported into~/.claude/CLAUDE.md. The import points at the live file in this clone, so it's in context for every session in every project, and updates the moment you pull. - Stack-specific guidelines — a Claude Code SessionStart hook (
hooks/session-start.sh) runs at the start of every session. It:- detects the project type from the working directory (
composer.json→laravel/framework= Laravel,craftcms/cms= Craft; falls back toartisan/craftmarker files), - reads the matching guideline(s) straight from this clone (
webhub-laravel.md+laravel-php-guidelines.mdfor Laravel;webhub-craft-cms.mdfor Craft) and injects them as session context, prefixed with the detected project type, - runs a throttled background
git pullof this clone (at most once/hour, fully detached, offline-safe) so edits you push here reach every developer's next session automatically.
- detects the project type from the working directory (
So Claude opens every session already knowing the stack and with the right rules loaded — in any repo, including Craft and non-PHP repos — and guideline edits propagate on their own.
One-time per developer: the hook can't install itself —
bootstrap.shis what wires it into~/.claude/settings.json. After that singlegit pull && ./bootstrap.sh, it's hands-off: the hook keeps the clone fresh and serves the latest guidelines with no further action. You only need to re-runbootstrap.shif the wiring itself changes (a new globally-imported guideline, or a change to how the hook is registered) — never for ordinary guideline or hook-script edits.
See the one-liner above. What bootstrap.sh does, idempotently:
- Symlinks
commands/into~/.claude/commands/webhub/, so every slash-command in this repo is available as/webhub:<name>(subdirs add namespace segments — e.g./webhub:laravel:simplify). - Symlinks each
skills/<name>/folder into~/.claude/skills/<name>/, so every skill is discoverable in Claude Code — available as/<name>and auto-invoked from itsdescription. Only the description loads each session; a skill's body and any bundled reference docs are read on demand. A pre-existing non-symlink~/.claude/skills/<name>is left untouched (your personal skills are safe). - Appends an
@<path>import for the stack-agnosticwebhub.mdto~/.claude/CLAUDE.md, so the webhub baseline applies to every session regardless of stack. Existing content in~/.claude/CLAUDE.mdis left untouched. - Registers the SessionStart hook (
hooks/session-start.sh) in~/.claude/settings.jsonundermatcher: "*"(fires on startup, resume, clear, compact). Wiring is idempotent and relocation-safe (a moved clone replaces the old entry instead of stacking), and any unrelated SessionStart hooks you have are left intact.
To update: re-run the one-liner from the directory above the clone, or cd into the clone and run git pull && ./bootstrap.sh. In practice the hook's auto-pull keeps the clone fresh on its own; re-running bootstrap.sh is only required when the wiring changes (see the note above).
From inside your clone of this repo, the same effect by hand:
ln -sfn "$PWD/commands" ~/.claude/commands/webhub
echo "@$PWD/guidelines/webhub.md" >> ~/.claude/CLAUDE.md
chmod +x "$PWD/hooks/session-start.sh"
# Then register the SessionStart hook in ~/.claude/settings.json (jq):
jq --arg cmd "$PWD/hooks/session-start.sh" \
'.hooks.SessionStart = ((.hooks.SessionStart // []) + [{"matcher":"*","hooks":[{"type":"command","command":$cmd,"timeout":30}]}])' \
~/.claude/settings.json > /tmp/s.json && mv /tmp/s.json ~/.claude/settings.json(bootstrap.sh does all of this idempotently and is the recommended path.)
Plain markdown files served to Claude from this clone (never copied into project repos):
- Stack-agnostic baseline —
webhub.mdis@-imported into~/.claude/CLAUDE.mdbybootstrap.sh, so it's in context for every session everywhere. - Stack-specific —
webhub-laravel.md(+ the mirroredlaravel-php-guidelines.md) andwebhub-craft-cms.mdare injected by the SessionStart hook only when it detects that project type. Claude sees Laravel rules in Laravel work, Craft rules in Craft work, nothing extra elsewhere. - Because everything resolves to the live files in this clone, a
git pull(the hook auto-pulls hourly) updates every project at once. Nothing is committed into consuming repos anymore.
laravel-php-guidelines.md is mirrored from Spatie (https://spatie.be/laravel-php-ai-guidelines.md). It's vendored here so the hook can serve it locally with no network dependency. Refresh it occasionally with:
curl -fsSL -o guidelines/laravel-php-guidelines.md https://spatie.be/laravel-php-ai-guidelines.mdTo add a new guideline file, drop it into guidelines/ and:
- Add it to the
GUIDELINES=( … )array inbootstrap.shif it should apply user-level to every session (stack-agnostic). - Wire it into
hooks/session-start.sh'scase "$stack"mapping if it should load for a specific stack (and add detection there if it's a new stack).
Each subfolder under skills/ is one Skill, consumed in two places:
- Claude Code (local):
bootstrap.shsymlinks every skill into~/.claude/skills/, so they're discoverable automatically — available as/<name>and auto-invoked from theirdescription. Nothing else to do. - claude.ai org-wide: upload each as a
.zipwithSKILL.md(and any bundled files) at the root of the zip — no wrapper directory.
cd skills/laravel && zip ../laravel.zip SKILL.md reference.md && cd - # zip follows the symlink, so the guideline content lands as reference.md
cd skills/craft-cms && zip ../craft-cms.zip SKILL.md reference.md && cd - # same — keeps the upload self-contained
cd skills/craft-upgrade && zip -r ../craft-upgrade.zip . && cd - # bundles SKILL.md + reference docsThe
laravelandcraft-cmsskills are thin wrappers: theirSKILL.mdjust points to a bundledreference.md, which is a symlink to the correspondingguidelines/webhub-*.md(the single source of truth). Editing the guideline updates the skill everywhere - locally the symlink resolves live; for claude.ai thezipabove dereferences it into the archive. Never duplicate guideline content intoSKILL.md.
Then in claude.ai: Settings → Skills → Organization skills → + Add → pick the .zip. Only the org owner can publish org-wide skills.
When a skill grows extra files (examples, scripts, reference docs — like craft-upgrade's per-leg docs), zip the contents of its folder so siblings of SKILL.md sit at the zip root.
craft-upgrade bundles large per-leg reference docs (Craft's official upgrade docs, plus webhub's pitfalls/protocols). These are not loaded every session: only SKILL.md's description loads at session start. Claude reads the body when an upgrade task begins, and then reads only the version-specific docs matching the from→to versions. That's why a Craft upgrade playbook can live here without bloating every unrelated session.
- Edit the file here.
- Commit + push.
- It propagates automatically:
- Guidelines, the hook script, commands, skills: nothing to do. Each developer's SessionStart hook auto-pulls this clone (≤ 1h), and everything resolves to live files/symlinks — so the next session already has the change. A developer who wants it now can
git -C ~/reps/ai pull. - Wiring changes only (a new globally-imported guideline added to
bootstrap.sh, or a change to how the hook is registered): developers re-rungit pull && ./bootstrap.shonce. - claude.ai Skills: re-zip and re-upload via the admin UI.
- Guidelines, the hook script, commands, skills: nothing to do. Each developer's SessionStart hook auto-pulls this clone (≤ 1h), and everything resolves to live files/symlinks — so the next session already has the change. A developer who wants it now can
Note:
webhubworks/laravel-starterno longer publishes guidelines into project repos. The SessionStart hook is the single source for Claude Code; itswebhub:post-autoloadnow removes any stale.ai/guidelines/copies it finds (and re-runsboost:update) so Boost stops composing outdated rules.