1 · Create the project & bring in the code
Objective — stand up the workspace the rest of the AI System setup builds inside: create the project folder, open it in your IDE and Claude Code, and bring the CodeCanyon vendor ZIP in so the agent can inspect the real code. Everything after this — the constitution, rules, config — is written into this folder, about this codebase.
Background
Section titled “Background”The AI System lives inside the project — AGENTS.md, CLAUDE.md, .claude/rules/, .claude/skills/, IDE wiring. All of that is written into the project folder and describes this app. So before any of it, you need two things in place: the folder (open in your tools) and the vendor code (so the agent can read composer.json, routes, and migrations when it writes the constitution in the next step).
The full git import — GitHub remote, branch model, storage subtree ignores, push — continues in Code & repository setup. This page gets the code onto disk, starts a local repo safely, and lands the most valuable commit in a CodeCanyon project: pristine vendor at root + bootstrap .gitignore only — the diff anchor for _CUSTOMIZATIONS.md, ZAJ:BEGIN/END, and every future vendor update.
flowchart LR IG["bootstrap<br/>.gitignore"] --> R["vendor at root<br/>(rsync promote)"] R --> C["commit<br/>pristine only"] C --> B["branch<br/>author/vX.X.X"] C --> T["tag<br/>author-vX.X.X"] B --> D["develop<br/>(same commit)"] D --> AI["steps 02–06<br/>C2–C6 commits"]1. Create the project folder and open it
Section titled “1. Create the project folder and open it”Open your terminal, make an empty folder for the app, then open that folder in your editor and Claude Code — so the agent and every command operate on this project.
-
Open a terminal, then make the folder and move into it.
Terminal window mkdir -p ~/Sites/my-app && cd ~/Sites/my-app# Expected: an empty folder is created and your terminal is inside it- ✅
pwdprints your new project path.
- ✅
-
Open it in your IDE and start Claude Code inside it.
Terminal window code . # or: cursor .claude # starts a Claude Code session in this folder# Expected: the editor opens the folder as its root; the AI session's cwd is the project- ✅ The editor shows the (empty) project as its root, and Claude Code is running inside it.
2. Stage the CodeCanyon download in _source/
Section titled “2. Stage the CodeCanyon download in _source/”Download from CodeCanyon, extract locally (usually in Downloads), then move the ZIP, extracted folder, and license files into _source/ — nothing at the project root yet. Everything under _source/ stays on disk only; §4 adds /_source/ to .gitignore before git init so nothing here ever enters git (not the ZIP, not the tree, not license .txt files).
mkdir -p _source# Adjust paths to match your Downloads folder and item namemv ~/Downloads/<item>.zip ~/Downloads/<extracted-folder> _source/mv ~/Downloads/*license*.txt _source/ 2>/dev/null || truels _source/# Expected: ZIP + extracted tree + license text visible under _source/- ✅
_source/contains the ZIP, the extracted folder, and license files — nothing promoted to the project root yet.
3. Verify the Laravel app path in _source/
Section titled “3. Verify the Laravel app path in _source/”Find the folder that actually contains the Laravel app (artisan + composer.json may sit one level down inside a CodeCanyon wrapper).
find _source -maxdepth 4 \( -name artisan -o -name composer.json \) | head -20# Expected: both files appear under the same directory inside _source/- ✅ The Laravel app directory inside
_source/is identified (same folder holdsartisanandcomposer.json).
4. Write an anchored bootstrap .gitignore and initialize git
Section titled “4. Write an anchored bootstrap .gitignore and initialize git”Write .gitignore before git init and before promoting the app. /_source/ is fully ignored — the ZIP, extracted tree, and license files stay on disk for rollback and provenance (you handle license retention outside git). Do not partially track _source/; committing the extracted duplicate is never desirable.
You may copy the kit template verbatim: dot-gitignore.bootstrap.example in the AI System kit tarball → project root .gitignore. The kit seed.sh does not write this file — bootstrap git is always §4 on this page.
# Bootstrap .gitignore — project setup (minimal C1 baseline; expand after the author anchor)# Dependencies (root only — leading slash = root-only)/vendor//node_modules/
# Archive — NEVER in git (ZIP + extracted tree + license on disk only)/_source/
# Secrets (refine after C1; keep templates trackable).env.env.*!.env.example!.env.tplgit initgit check-ignore -v _source/ _source/. _source/license.txt 2>/dev/null | head -5# Expected: each path matches rule /_source/ (or _source/ under that rule)git status# Expected: repo exists; _source/ absent from untracked; vendor/, node_modules/, .env/.env.* not listed- ✅
.gitignoreexists at the project root before anygitcommand runs. - ✅
git check-ignorereports_source/, nested paths, and license files under_source/as ignored. - ✅
git initcompleted;git statusdoes not showvendor/,node_modules/,.env,.env.*, or anything under_source/.
5. Promote the Laravel app to the project root — and prove nothing was missed
Section titled “5. Promote the Laravel app to the project root — and prove nothing was missed”Copy (don’t move) the identified app folder’s contents so artisan, composer.json, and the usual Laravel directories sit at the root — not inside a CodeCanyon wrapper. Keep the original folder, ZIP, and license files in _source/: that pristine copy is your manifest to verify against (and the one you may want to explore to confirm nothing dropped). Writing .vscode/ after the baseline — not a delete — handles the duplicate index, so promotion stays non-destructive and re-runnable.
-
Copy the app to the root. A trailing slash +
-abrings dotfiles (.env,.htaccess,.editorconfig) too — but never let a vendor root.gitignorereplace the bootstrap you wrote earlier (stock Laravel and many CodeCanyon items ship one). Use a leading slash on the rsync exclude — same anchor rule as the bootstrap block: bare.gitignorewould skip every nested Laravel ignore (storage/,bootstrap/cache/,database/, …).Terminal window APP_DIR="_source/<folder-with-artisan>" # from the layout check aboversync -a --exclude='/.gitignore' "$APP_DIR"/ ./# Expected: ./artisan and ./composer.json exist at the project roottest -f artisan && test -f composer.json && echo "smoke: root OK"grep -q 'Bootstrap .gitignore' .gitignore && grep -q '/_source/' .gitignore && echo "bootstrap .gitignore intact"test -f bootstrap/cache/.gitignore && test -f storage/app/.gitignore && echo "nested Laravel .gitignore files copied"test -f "$APP_DIR/.gitignore" && echo "note: vendor .gitignore at $APP_DIR/.gitignore — merge useful lines in Code & repository setup, not via rsync"- ✅
artisanandcomposer.jsonare at the project root (smoke check, not the gate). - ✅ Bootstrap header +
/_source/rule still present at the root (vendor root.gitignoredid not clobber the bootstrap block). - ✅ Nested Laravel
.gitignorefiles (e.g.bootstrap/cache/,storage/app/) were copied — the exclude is root-only (/.gitignore).
- ✅
-
Run the completeness gate. A filesystem dry-run that prints zero lines when every source file is present and identical at root — the real “nothing missed” check the two-file smoke can’t give you. Exclude only the root
.gitignore(/.gitignore): root keeps the bootstrap; a diff on that one file is expected when the vendor shipped their own. Nested.gitignorefiles must appear in the gate — a bare.gitignoreexclude would hide their absence.Terminal window # any '>f' line = a file missing or different at root. Output MUST be empty.DIFF=$(rsync -ain --exclude='/.gitignore' "$APP_DIR"/ ./ | awk '$1 ~ /^>f/')[ -z "$DIFF" ] && echo "PASS: no file missed" || { echo "FAIL — missing/changed:"; echo "$DIFF"; }- ✅ Prints
PASS: no file missed. Never excludevendor/ornode_modules/from this gate — a vendor-named folder going missing is the exact failure it guards against.
- ✅ Prints
-
Confirm the dotfiles landed (these are what historically went missing).
Terminal window for f in .env .env.example .htaccess .editorconfig; dotest -f "$f" && echo "$f ✅" || echo "$f MISSING ⚠"done- ✅
.env(and the rest, where the vendor shipped them) are present at the root.
- ✅
Staging has a second blind spot: user-level ignore rules outside the repo. Handle that at C1, not after the anchor is already wrong.
The dependency install stays later even with the safer staging command.
6. Commit the pristine vendor baseline — author/v* + tag
Section titled “6. Commit the pristine vendor baseline — author/v* + tag”This is the single most valuable commit in a CodeCanyon repo: vendor source + bootstrap .gitignore, nothing else. Every customization you ever make is git diff author-vX.X.X..HEAD — the anchor the _CUSTOMIZATIONS.md / ZAJ:BEGIN/END ownership model depends on.
Commit & freeze builds on this (push, working branch) — it does not recreate the baseline.
-
Set the vendor version for branch + tag names.
Most Laravel apps have no top-level
"version"incomposer.json(stocklaravel/laravelskeleton). Workdo / many CodeCanyon Laravel items store the vendor semver inconfig/verification.phpassystem_version— try that before asking the user. Other vendors: CodeCanyon item page, README, or changelog.Terminal window # Heuristic 1 — composer (often empty)VERSION="$(grep -E '"version"\s*:' composer.json 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -1)"# Heuristic 2 — Workdo family (config/verification.php → system_version)[ -n "$VERSION" ] || VERSION="$(grep -E "['\"]system_version['\"]" config/verification.php 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -1)": "${VERSION:?export VERSION=x.y.z — both heuristics missed; set from CodeCanyon item / README / changelog}"echo "Vendor version: $VERSION"# Expected: VERSION is set — Workdo often fills via verification.php; else export VERSION=7.6 (your item's semver)- ✅
VERSIONis the Workdo / vendor semver you will freeze (export VERSION=…only when both greps return nothing).
- ✅
-
Safety-check — prove
_source/is fully ignored (not partial),.vscode/stays out, and global excludes cannot corrupt C1, before staging.Terminal window git check-ignore -q _source/ && echo "_source/ ignored OK" || { echo "STOP: add /_source/ to .gitignore"; exit 1; }git check-ignore -q _source/license.txt 2>/dev/null && echo "license under _source ignored OK" || { echo "STOP: full /_source/ ignore only — no ! negation for license"; exit 1; }git check-ignore -q .vscode/settings.json 2>/dev/null && echo ".vscode ignored OK" || echo "note: .vscode not written yet — OK"git status --short _source/ 2>/dev/null | grep . && { echo "STOP: _source/ visible to git"; exit 1; } || echo "_source/ absent from status OK"GLOBAL="$(git config --get core.excludesFile || true)"GLOBAL_SHADOW=0if [ -n "$GLOBAL" ] && [ -f "$GLOBAL" ]; thenecho "Global excludesFile: $GLOBAL"for f in phpunit.xml composer.json .env.example artisan; dotest -f "$f" || continueif git check-ignore -v "$f" 2>/dev/null | grep -Fq "$GLOBAL"; thenecho "FAIL: $f shadowed by global excludesFile ($GLOBAL)"GLOBAL_SHADOW=1fidoneif [ "$GLOBAL_SHADOW" -eq 1 ]; thenecho "FAIL: global excludesFile shadows app-critical files (listed above)"echo "STOP: plain git add is forbidden for C1 — continue ONLY at step 3 with git -c core.excludesFile=/dev/null add -A"elseecho "global excludesFile present — no shadow on app-critical files OK"fifi# Expected: all _source / .vscode ignore checks OK; global shadow → FAIL + mandatory neutralized staging in step 3- ✅
_source/is fully gitignored (including license paths). - ✅ If
core.excludesFileshadows app-critical files, the step prints FAIL/STOP (not a warning) and C1 must use neutralized staging in step 3 — never plaingit add.
- ✅
-
Stage, verify the index, commit pristine vendor only, then freeze branch + tag and point
developat the same commit.Use
git diff --cached --name-onlywith root-anchored paths — immune tostatus.showUntrackedFiles=alllistingresources/views/vendor/…as untracked noise.Use an annotated author tag — lightweight
git tag author-v…fails withfatal: no tag message?whentag.gpgSign=trueortag.forceSignAnnotated=true(common on signed-tag setups).Terminal window git config --get core.excludesFile && echo "note: global excludes file exists — neutralized for C1 staging" || truegit -c core.excludesFile=/dev/null add -Agit -c core.excludesFile=/dev/null status --porcelain | head -40for f in phpunit.xml composer.json .env.example artisan; dotest -f "$f" || continuegit check-ignore -q "$f" 2>/dev/null && continuegit diff --cached --name-only -- "$f" | grep -qx "$f" \|| { echo "STOP! $f on disk but missing from C1 index — re-run with git -c core.excludesFile=/dev/null add -A"; exit 1; }doneecho "app-critical index cross-check OK"git diff --cached --name-only | grep -E '^(vendor/|node_modules/)' && { echo "STOP! /vendor/ or /node_modules/ not ignored — fix bootstrap .gitignore"; exit 1; } || echo "deps not in index OK"git diff --cached --name-only | grep -xE '^\.env$' && { echo "STOP! .env staged"; exit 1; } || echo ".env not in index OK"git diff --cached --name-only | grep -E '^(_source/|\.vscode/)' && { echo "STOP! archive/editor staged"; exit 1; } || echo "archive/editor not in index OK"git status --short | head -30 # review: vendor tree + .gitignore only — no _source/, .vscode/, .claude/git commit -m "📦 Import v${VERSION} from CodeCanyon (pristine author baseline)"git branch "author/v${VERSION}" # frozen — never commit here againgit tag -a "author-v${VERSION}" -m "Import v${VERSION} from CodeCanyon (pristine author baseline)" # permanent diff anchorgit branch -M develop # working branch starts here; AI commits advance develop onlygit log -1 --onelinegit branch --list 'author/*' && git tag --list 'author-*'# Expected: one commit; author/vX.X.X branch + annotated author-vX.X.X tag; current branch develop- ✅ C1 was staged with
core.excludesFile=/dev/null, so global ignores cannot shadow files likephpunit.xml. - ✅ Every on-disk app-critical file (
phpunit.xml,composer.json,.env.example,artisan) that is not repo-ignored appears in the C1 index — hard-stop if any is missing. - ✅ Cached paths include no
vendor/,node_modules/,.env,_source/, or.vscode/;git log -1is the pristine import;author/v${VERSION}and annotatedauthor-v${VERSION}exist;git branch --show-currentprintsdevelop.
- ✅ C1 was staged with
7. Write editor config for this repo — one clean PHP language server
Section titled “7. Write editor config for this repo — one clean PHP language server”Write .vscode/ after the baseline commit so it never enters the pristine snapshot. It stays on disk, uncommitted until C2 at machine setup. Machine-wide extension installs are global — same page §2.
-
.vscode/settings.json— exclude_source/from indexing.intelephense.files.excludeis the load-bearing line: Intelephense indexes via its own exclude and does not reliably honor VS Code’sfiles.exclude, so without it the LSP re-indexes the whole_source/duplicate. Use search/watcher excludes — notfiles.exclude, which would hide_source/from the Explorer and fight the “explore it to verify” workflow.{"intelephense.files.exclude": ["**/_source/**", "**/node_modules/**", "**/vendor/**/{Tests,tests}/**"],"search.exclude": { "_source/**": true },"files.watcherExclude": { "**/_source/**": true },"php.suggest.basic": false,"php.validate.enable": false,"editor.formatOnSave": true,"[blade]": {"editor.defaultFormatter": "shufo.vscode-blade-formatter"}}- ✅
vendor/stays indexed (framework autocomplete); only_source/is excluded, and it’s still visible in the Explorer. - ✅
php.suggest.basic/php.validate.enableare off — the built-in PHP language features step aside so only Intelephense runs (one LSP; pairs with the competitor-uninstall in machine setup).
- ✅
-
.vscode/extensions.json— the canonical six (identical to machine setup; do not extend one list without updating the other).Extension Marketplace ID Intelephense bmewburn.vscode-intelephense-clientLaravel Blade onecentlin.laravel-bladeBlade formatter shufo.vscode-blade-formatterLaravel 5 Snippets onecentlin.laravel5-snippetsLaravel Extra IntelliSense amiralizadeh9480.laravel-extra-intellisenseEditorConfig editorconfig.editorconfig{"recommendations": ["bmewburn.vscode-intelephense-client","onecentlin.laravel-blade","shufo.vscode-blade-formatter","onecentlin.laravel5-snippets","amiralizadeh9480.laravel-extra-intellisense","editorconfig.editorconfig"]}Terminal window git check-ignore -q .vscode/settings.json && echo "vscode gitignored OK (until C2)" || echo "note: already un-ignored"ls .vscode/settings.json .vscode/extensions.json# Expected: both files exist on disk; not in the pristine baseline; committed at C2- ✅
.vscode/*.jsonfiles exist on disk; they are not in the pristine baseline commit. Blade files now have an actual formatter instead offormatOnSavesilently doing nothing for.blade.php.
- ✅
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 👤 Folder open — the project folder is the workspace root in your IDE, with Claude Code running inside it.
- 👤 Staged in
_source/— CodeCanyon ZIP, extracted folder, and license.txtfiles are in_source/. - 🤖 App path verified — the folder inside
_source/that containsartisan+composer.jsonis identified. - 🤖
_source/fully gitignored —/_source/in bootstrap.gitignore;git check-ignore -v _source/ _source/license.txtmatches; no!_source/…negation;git statusshows nothing under_source/. - 🤖 Anchored bootstrap git — leading-slash anchors +
/_source/; thengit init;git statusshows novendor/,node_modules/,.env, or.env.*. - 🤖 App copied to root —
artisan+composer.jsonsit at the project root; the ZIP + extracted tree + license files remain in_source/on disk (copied, not moved; never committed). - 🤖 Completeness gate passes —
rsync -ain --exclude='/.gitignore' "$APP_DIR"/ ./prints zero>flines; bootstrap.gitignoreheader still present; nested Laravel.gitignorefiles at root;.env/dotfiles at root. Verified by filesystem, notgit add. - 🤖 Pristine baseline frozen — one commit with vendor +
.gitignoreonly; global-excludes hard-stop passed (or nocore.excludesFile); staged withgit -c core.excludesFile=/dev/null add -A; app-critical index cross-check OK (phpunit.xmletc. when on disk);author/vX.X.Xbranch +author-vX.X.Xtag exist;developis current;git log --oneline -1is the import message. - 🤖 Editor config on disk —
.vscode/settings.json+.vscode/extensions.jsonwritten (uncommitted until C2 at machine setup), including the Blade formatter extension +[blade]formatter setting. - 🤖 No
composer installyet — dependency install waits for local development.