PageSourceSearch

https://helgesver.re/_next/static/chunks/pages/articles/conductor-…-laravel-herd-worktrees-11927135270e8304.js

js helgesver.re collected 2026-10-03 23:56:32 UTC 62,043 bytes, 180 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8004],{5045:(e,n,s)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/articles/conductor-dev-laravel-herd-worktrees",function(){return s(67473)}])},26090:(e,n,s)=>{"use strict";s.d(n,{T:()=>x});var a=s(37876),t=s(33660),r=s(48230),o=s.n(r),c=s(34824),i=s(91668),l=s(14232);function d({id:e,host:n,repo:t,repoId:r,category:o,categoryId:c,mapping:i,term:h,strict:p,reactionsEnabled:m,emitMetadata:u,inputPosition:x,theme:j,lang:k,loading:N}){let[g,v]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{g||s.e(5485).then(s.bind(s,5485)).then(()=>v(!0))},[]),g?(0,a.jsx)("giscus-widget",{id:e,host:n,repo:t,repoid:r,category:o,categoryid:c,mapping:i,term:h,strict:p,reactionsenabled:m,emitmetadata:u,inputposition:x,theme:j,lang:k,loading:N}):null}let h=new Set(["theme-kde","theme-win95","theme-aqua","theme-y2k","theme-nrk","theme-rema1000","theme-steria","theme-construction"]);function p(){let e=h.has(document.body.className)?"light":"dark",n=`@import url("https://giscus.app/themes/${e}.css");.gsc-header{display:none!important;}`;return`data:text/css;base64,${btoa(n)}`}function m(){let[e,n]=(0,l.useState)("dark");return(0,l.useEffect)(()=>{n(p());let e=new MutationObserver(()=>{n(p())});return e.observe(document.body,{attributes:!0,attributeFilter:["class"]}),()=>e.disconnect()},[]),(0,a.jsx)(d,{repo:"HelgeSverre/blog-comments",repoId:"R_kgDOSERevw",category:"Announcements",categoryId:"DIC_kwDOSERev84C6-o_",mapping:"pathname",reactionsEnabled:"0",emitMetadata:"0",inputPosition:"top",theme:e,lang:"en",loading:"lazy"})}let u={pre:function({children:e,...n}){let s=(0,l.useRef)(null),[t,r]=(0,l.useState)(!1),[o,c]=(0,l.useState)(!0),i=async()=>{let e=s.current?.innerText??"";try{if(!navigator.clipboard)return void c(!1);await navigator.clipboard.writeText(e),r(!0),setTimeout(()=>r(!1),1500)}catch{c(!1)}};return(0,a.jsxs)("div",{className:"code-block",children:[(0,a.jsx)("pre",{ref:s,...n,children:e}),o&&(0,a.jsx)("button",{type:"button",className:"code-block__copy",onClick:i,"aria-label":t?"Copied":"Copy code",children:t?"Copied!":"Copy"})]})}};function x({children:e,meta:n,isRssFeed:s=!1}){let r=(0,c.useRouter)();if(s)return e;let l=n?.slug??function(e){let n=(e?.asPath||"").split("?")[0].split("#")[0],s="/articles/";if(n.startsWith(s))return n.slice(s.length).replace(/\/$/,"")}(r),d=new Date(n.date)<=new Date(2022,1,1);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.G,{title:`${n.title} - Helge Sverre`,headline:n.title,description:n.description,path:l?`/articles/${l}`:void 0,image:l?`/og/article-${l}.png`:void 0,imageAlt:n.title,kind:"BlogPosting",date:n.date,modified:n.dateModified,author:n.author}),(0,a.jsx)("div",{style:{marginBottom:"8px"},children:(0,a.jsx)(o(),{href:"/articles",style:{color:"#667788",fontSize:"10px"},children:"\xab Back to articles"})}),d&&(0,a.jsxs)("div",{className:"old-post-disclaimer",children:[(0,a.jsx)("b",{children:"Content Notice!"})," This post is really old, and no longer reflects my skill level, views or opinions. It is made available here for archival purposes (it was originally on my old WordPress blog). Keep that in mind when you read the contents within."]}),(0,a.jsx)("div",{className:"content-header",children:n.title}),(0,a.jsx)("div",{style:{color:"#666688",fontSize:"10px",marginTop:"4px",marginBottom:"12px"},children:function(e){if(!e)return"No date";let n=new Date(`${e}T00:00:00Z`);return isNaN(n.getTime())?"Invalid date":n.toLocaleDateString("en-US",{day:"numeric",month:"long",year:"numeric",timeZone:"UTC"})}(n.date)}),(0,a.jsx)("div",{className:"article-content",children:n.disableCodeCopy?e:(0,a.jsx)(i.x,{components:u,children:e})}),!1!==n.comments&&(0,a.jsx)("div",{id:"comments",style:{marginTop:"24px"},children:(0,a.jsx)(m,{})}),(0,a.jsx)("br",{}),(0,a.jsx)("hr",{}),(0,a.jsx)("div",{style:{marginTop:"8px"},children:(0,a.jsx)(o(),{href:"/articles",style:{color:"#667788",fontSize:"10px"},children:"\xab Back to articles"})}),(0,a.jsx)("br",{})]})}},67473:(e,n,s)=>{"use strict";s.r(n),s.d(n,{default:()=>g,meta:()=>j});var a=s(37876),t=s(91668),r=s(26090),o=s(14232);let c=`# Conductor Environment Variables:
2# CONDUCTOR_WORKSPACE_NAME - Workspace name
3# CONDUCTOR_WORKSPACE_PATH - Workspace path
4# CONDUCTOR_ROOT_PATH      - Path to the main repo root
5# CONDUCTOR_DEFAULT_BRANCH - Default branch name
6# CONDUCTOR_PORT           - First of 10 reserved ports`,i={"laravel-herd":{label:"Laravel + Herd",setup:`#!/bin/zsh
7
8${c}
9
10# Register this worktree as a Herd site (creates http://WORKSPACE.test)
11herd link \${CONDUCTOR_WORKSPACE_NAME}
12
13# Pin PHP version so it doesn't depend on your global Herd setting
14herd isolate 8.3 --site="\${CONDUCTOR_WORKSPACE_NAME}"
15
16# Symlink .env from the main repo — every worktree shares the same config.
17# ln -sf won't fail if the target doesn't exist yet (creates a dangling symlink).
18ln -sf "\${CONDUCTOR_ROOT_PATH}/.env" .env
19
20# Install dependencies
21export NVM_DIR="$HOME/.nvm"
22[ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh"
23nvm use
24herd composer install
25{{PM}} install`,run:`#!/bin/zsh
26
27${c}
28
29# Open the site in the default browser
30herd open
31
32# Run Vite dev server and queue worker side by side.
33# Ctrl+C stops both.
34npx concurrently "{{PM}} run dev" "herd php artisan queue:work"`,archive:`#!/bin/zsh
35
36${c}
37
38# Unregister the Herd site
39herd unlink
40
41# Free disk space
42rm -rf node_modules`,conductorJson:{scripts:{setup:".conductor/setup.sh",run:".conductor/run.sh",archive:".conductor/archive.sh"},runScriptMode:"concurrent"}},vitepress:{label:"VitePress",setup:`#!/bin/zsh
43
44${c}
45
46# Install dependencies
47{{PM}} install`,run:`#!/bin/zsh
48
49${c}
50
51# Start VitePress dev server on the conductor-assigned port
52{{PM}} run docs:dev --port \${CONDUCTOR_PORT}`,archive:`#!/bin/zsh
53
54${c}
55
56# Clean up build artifacts and dependencies
57rm -rf node_modules .vitepress/cache .vitepress/dist`,conductorJson:{scripts:{setup:".conductor/setup.sh",run:".conductor/run.sh",archive:".conductor/archive.sh"},runScriptMode:"concurrent"}},docker:{label:"Docker Compose",setup:`#!/bin/zsh
58
59${c}
60
61# Copy .env into worktree (Docker needs a real file, not a symlink)
62cp "\${CONDUCTOR_ROOT_PATH}/.env" .env
63
64# Override app name and database for this worktree so containers don't collide.
65# Install dotenvx: brew install dotenvx/brew/dotenvx
66dotenvx set APP_NAME "\${CONDUCTOR_WORKSPACE_NAME}" -f .env --plain
67dotenvx set DB_DATABASE "\${CONDUCTOR_WORKSPACE_NAME}" -f .env --plain
68
69# Set unique container prefix — each worktree gets its own docker stack
70export COMPOSE_PROJECT_NAME="\${CONDUCTOR_WORKSPACE_NAME}"
71
72# Build images with worktree-specific build args
73docker compose build \\
74  --build-arg APP_NAME="\${CONDUCTOR_WORKSPACE_NAME}"
75
76# Boot the stack and run migrations
77docker compose up -d
78docker compose exec app php artisan migrate --seed`,run:`#!/bin/zsh
79
80${c}
81
82# Ensure containers use the right prefix
83export COMPOSE_PROJECT_NAME="\${CONDUCTOR_WORKSPACE_NAME}"
84
85# Follow logs from all containers
86docker compose up`,archive:`#!/bin/zsh
87
88${c}
89
90export COMPOSE_PROJECT_NAME="\${CONDUCTOR_WORKSPACE_NAME}"
91
92# Tear down containers, volumes, and orphaned networks
93docker compose down -v --remove-orphans
94
95# Clean up the copied .env
96rm -f .env`,conductorJson:{scripts:{setup:".conductor/setup.sh",run:".conductor/run.sh",archive:".conductor/archive.sh"},runScriptMode:"concurrent"}},"dart-cli":{label:"Dart CLI",setup:`#!/bin/zsh
97
98${c}
99
100# Install dependencies
101dart pub get
102
103# Run code generation if the project uses build_runner
104# (freezed, json_serializable, etc.)
105if grep -q "build_runner" pubspec.yaml 2>/dev/null; then
106  dart run build_runner build --delete-conflicting-outputs
107fi`,run:`#!/bin/zsh
108
109${c}
110
111# Run the CLI entry point
112dart run`,archive:`#!/bin/zsh
113
114${c}
115
116# Clean up generated files and caches
117rm -rf .dart_tool build`,conductorJson:{scripts:{setup:".conductor/setup.sh",run:".conductor/run.sh",archive:".conductor/archive.sh"},runScriptMode:"concurrent"}},"flutter-fvm":{label:"Flutter + FVM",setup:`#!/bin/zsh
118
119${c}
120
121# Pin Flutter SDK version via FVM (change to your project's version)
122fvm use 3.27.0
123
124# Install dependencies
125fvm flutter pub get
126
127# Run code generation (freezed, json_serializable, etc.)
128if grep -q "build_runner" pubspec.yaml 2>/dev/null; then
129  fvm dart run build_runner build \\
130    --delete-conflicting-outputs
131fi`,run:`#!/bin/zsh
132
133${c}
134
135# Boot iOS simulator (comment out for Android)
136open -a Simulator
137
138# Run with dev flavor.
139# Each flavor can point to different API endpoints, Firebase projects, etc.
140fvm flutter run \\
141  --flavor development \\
142  --dart-define=ENV=development \\
143  --target lib/main_development.dart`,archive:`#!/bin/zsh
144
145${c}
146
147# Nuke all build artifacts
148fvm flutter clean
149rm -rf .dart_tool build .fvm`,conductorJson:{scripts:{setup:".conductor/setup.sh",run:".conductor/run.sh",archive:".conductor/archive.sh"},runScriptMode:"concurrent"}}},l=[{value:"bun",label:"bun"},{value:"npm",label:"npm"},{value:"pnpm",label:"pnpm"},{value:"yarn",label:"yarn"}],d={backgroundColor:"var(--bg-input)",border:"1px solid var(--border)",color:"var(--text-body)",fontFamily:"Verdana, sans-serif",fontSize:"12px",padding:"4px 6px"},h={backgroundColor:"var(--bg-input)",border:"1px solid var(--border)",color:"var(--text-body)",fontFamily:"'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', monospace",fontSize:"12px",lineHeight:"1.6",padding:"8px 10px",width:"100%",resize:"vertical",boxSizing:"border-box",tabSize:2},p={backgroundColor:"var(--bg-btn)",border:"1px solid var(--border-btn)",color:"var(--btn-text)",fontFamily:"Verdana, sans-serif",fontSize:"12px",padding:"5px 14px",cursor:"pointer"},m={display:"block",fontWeight:"bold",fontSize:"11px",marginBottom:"3px",color:"var(--text-body)",fontFamily:"Verdana, sans-serif"};function u(e){return(e||"").split("\n").length}function x(){let[e,n]=(0,o.useState)("laravel-herd"),[s,t]=(0,o.useState)("bun"),[r,c]=(0,o.useState)(""),[x,j]=(0,o.useState)(""),[k,N]=(0,o.useState)(""),[g,v]=(0,o.useState)(null),[f,b]=(0,o.useState)(!1);(0,o.useEffect)(()=>{let n=i[e];c(n.setup.replace(/\{\{PM\}\}/g,s)),j(n.run.replace(/\{\{PM\}\}/g,s)),N(n.archive.replace(/\{\{PM\}\}/g,s)),v(null),b(!1)},[e,s]);let y=(0,o.useCallback)(()=>{let n=JSON.stringify(i[e].conductorJson,null,2),s=btoa(unescape(encodeURIComponent(`#!/bin/bash
150set -e
151
152mkdir -p .conductor
153
154cat > conductor.json << 'CONDUCTOR_EOF'
155${n}
156CONDUCTOR_EOF
157
158cat > .conductor/setup.sh << 'SETUP_EOF'
159${r}
160SETUP_EOF
161
162cat > .conductor/run.sh << 'RUN_EOF'
163${x}
164RUN_EOF
165
166cat > .conductor/archive.sh << 'ARCHIVE_EOF'
167${k}
168ARCHIVE_EOF
169
170chmod +x .conductor/setup.sh .conductor/run.sh .conductor/archive.sh
171
172echo ""
173echo "Created:"
174echo "  conductor.json"
175echo "  .conductor/setup.sh"
176echo "  .conductor/run.sh"
177echo "  .conductor/archive.sh"
178echo ""
179echo "Next: open conductor.dev and create a workspace for this project."
180`)));v(`echo "${s}" | base64 -d | bash`),b(!1)},[r,x,k,e]),C=(0,o.useCallback)(()=>{g&&navigator.clipboard.writeText(g).then(()=>{b(!0),setTimeout(()=>b(!1),2e3)}).catch(()=>{})},[g]),O=(0,o.useMemo)(()=>Math.max(14,u(r)+1),[r]),w=(0,o.useMemo)(()=>Math.max(10,u(x)+1),[x]),_=(0,o.useMemo)(()=>Math.max(8,u(k)+1),[k]);return(0,a.jsxs)("div",{style:{backgroundColor:"var(--bg-sidebar)",border:"1px solid var(--border)",padding:"14px",marginTop:"16px",marginBottom:"16px"},children:[(0,a.jsxs)("div",{style:{display:"flex",gap:"14px",marginBottom:"14px",flexWrap:"wrap",alignItems:"flex-end"},children:[(0,a.jsxs)("div",{style:{flex:"1 1 180px",minWidth:"140px"},children:[(0,a.jsx)("label",{style:m,children:"Template"}),(0,a.jsx)("select",{value:e,onChange:e=>n(e.target.value),style:{...d,width:"100%"},children:Object.entries(i).map(([e,n])=>(0,a.jsx)("option",{value:e,children:n.label},e))})]}),(0,a.jsxs)("div",{style:{flex:"0 1 120px",minWidth:"100px"},children:[(0,a.jsx)("label",{style:m,children:"Package Manager"}),(0,a.jsx)("select",{value:s,onChange:e=>t(e.target.value),style:{...d,width:"100%"},children:l.map(e=>(0,a.jsx)("option",{value:e.value,children:e.label},e.value))})]})]}),(0,a.jsxs)("div",{style:{marginBottom:"12px"},children:[(0,a.jsx)("label",{style:m,children:"setup.sh"}),(0,a.jsx)("textarea",{value:r,onChange:e=>
180c(e.target.value),rows:O,spellCheck:!1,style:h})]}),(0,a.jsxs)("div",{style:{marginBottom:"12px"},children:[(0,a.jsx)("label",{style:m,children:"run.sh"}),(0,a.jsx)("textarea",{value:x,onChange:e=>j(e.target.value),rows:w,spellCheck:!1,style:h})]}),(0,a.jsxs)("div",{style:{marginBottom:"14px"},children:[(0,a.jsx)("label",{style:m,children:"archive.sh"}),(0,a.jsx)("textarea",{value:k,onChange:e=>N(e.target.value),rows:_,spellCheck:!1,style:h})]}),(0,a.jsx)("button",{onClick:y,style:p,children:"Generate Install Script"}),g&&(0,a.jsxs)("div",{style:{marginTop:"12px"},children:[(0,a.jsx)("label",{style:m,children:"One-liner — paste this in your project root and hit enter:"}),(0,a.jsxs)("div",{style:{position:"relative"},children:[(0,a.jsx)("textarea",{readOnly:!0,value:g,rows:4,spellCheck:!1,style:{...h,fontSize:"11px",backgroundColor:"var(--bg-body)"}}),(0,a.jsx)("button",{onClick:C,style:{...p,position:"absolute",top:"6px",right:"6px",fontSize:"11px",padding:"3px 10px"},children:f?"Copied!":"Copy"})]})]})]})}let j={author:"Helge Sverre",date:"2026-03-01",title:"Conductor.dev + Laravel Herd: Worktrees That Actually Work",description:"How I use Conductor.dev with Laravel Herd to manage worktree-based dev environments. Symlinked .env, SQLite cloning, and a script generator to get you started."},k=e=>(0,a.jsx)(r.T,{meta:j,...e});function N(e){let n={a:"a",code:"code",h2:"h2",h3:"h3",hr:"hr",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["I use ",(0,a.jsx)(n.a,{href:"https://conductor.dev",children:"Conductor"})," to manage git worktrees. It's great — you get isolated branches, each with\ntheir own working directory, and Conductor handles creating and tearing them down. But every time it spun up a new\nworkspace for a Laravel project, I'd hit the same annoying wall: no ",(0,a.jsx)(n.code,{children:".env"}),", no ",(0,a.jsx)(n.code,{children:"node_modules"}),", site not linked in Herd,\nwrong PHP version. Five minutes of mechanical setup before I could even look at the code."]}),"\n",(0,a.jsxs)(n.p,{children:["Turns out Conductor has a ",(0,a.jsx)(n.code,{children:"conductor.json"})," config with a scripts feature that solved this in a pretty clean way. You\ndefine setup, run, and archive scripts, and Conductor runs them at each stage of the worktree lifecycle. One command,\nfully working Laravel app, every time."]}),"\n",(0,a.jsxs)(n.p,{children:["Here's how I set it up with ",(0,a.jsx)(n.a,{href:"https://herd.laravel.com",children:"Laravel Herd"}),", and the tricks I've picked up along the way."]}),"\n",(0,a.jsxs)(n.h2,{id:"what-conductor-does",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#what-conductor-does",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"What Conductor Does"]}),"\n",(0,a.jsx)(n.p,{children:"Conductor is a desktop app that sits on top of git worktrees. You point it at a repo, it creates worktrees for you, and\nit runs your scripts at each stage of the worktree lifecycle:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Setup"})," runs once when the workspace is created — install dependencies, link the site, configure the environment."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Run"})," boots your dev environment — starts the dev server, queue workers, whatever you need."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Archive"})," tears everything down when you're done with the branch — unlinks the site, removes ",(0,a.jsx)(n.code,{children:"node_modules"}),", frees\ndisk space."]}),"\n"]}),"\n",(0,a.jsxs)(n.p,{children:["You define these scripts in a ",(0,a.jsx)(n.code,{children:".conductor/"})," folder in your project root, and point to them from a ",(0,a.jsx)(n.code,{children:"conductor.json"})," file.\nCommit both to your repo and every developer on your team gets the same setup experience."]}),"\n",(0,a.jsxs)(n.h2,{id:"how-worktrees-are-organized",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#how-worktrees-are-organized",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"How Worktrees Are Organized"]}),"\n",(0,a.jsxs)(n.p,{children:["Conductor keeps everything under ",(0,a.jsx)(n.code,{children:"~/conductor/workspaces
180/"}),". Each project gets a folder, and each worktree inside it gets\na city name (Conductor picks these automatically):"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{children:"~/conductor/workspaces/\n├── my-project/\n│   ├── nagoya/\n│   ├── montreal/\n│   └── salvador/\n├── another-app/\n│   └── khartoum/\n├── client-site/\n│   ├── bordeaux/\n│   ├── london/\n│   ├── minsk/\n│   ├── quito-v1/\n│   └── vilnius-v1/\n├── sema-lisp/\n├── sql-splitter/\n└── token-editor/\n    └── abu-dhabi/\n"})}),"\n",(0,a.jsxs)(n.p,{children:["Each of these is a full git worktree. ",(0,a.jsx)(n.code,{children:"nagoya"})," might be a feature branch, ",(0,a.jsx)(n.code,{children:"montreal"})," a bugfix, ",(0,a.jsx)(n.code,{children:"salvador"})," a spike — all\nrunning simultaneously without stepping on each other."]}),"\n",(0,a.jsxs)(n.h2,{id:"the-config",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#the-config",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"The Config"]}),"\n",(0,a.jsxs)(n.p,{children:["This goes in your project root as ",(0,a.jsx)(n.code,{children:"conductor.json"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-json",children:(0,a.jsxs)(n.code,{className:"language-json",children:[(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"\n  ",(0,a.jsx)(n.span,{className:"token property",children:'"scripts"'}),(0,a.jsx)(n.span,{className:"token operator",children:":"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"\n    ",(0,a.jsx)(n.span,{className:"token property",children:'"setup"'}),(0,a.jsx)(n.span,{className:"token operator",children:":"})," ",(0,a.jsx)(n.span,{className:"token string",children:'".conductor/setup.sh"'}),(0,a.jsx)(n.span,{className:"token punctuation",children:","}),"\n    ",(0,a.jsx)(n.span,{className:"token property",children:'"run"'}),(0,a.jsx)(n.span,{className:"token operator",children:":"})," ",(0,a.jsx)(n.span,{className:"token string",children:'".conductor/run.sh"'}),(0,a.jsx)(n.span,{className:"token punctuation",children:","}),"\n    ",(0,a.jsx)(n.span,{className:"token property",children:'"archive"'}),(0,a.jsx)(n.span,{className:"token operator",children:":"})," ",(0,a.jsx)(n.span,{className:"token string",children:'".conductor/archive.sh"'}),"\n  ",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),(0,a.jsx)(n.span,{className:"token punctuation",children:","}),"\n  ",(0,a.jsx)(n.span,{className:"token property",children:'"runScriptMode"'}),(0,a.jsx)(n.span,{className:"token operator",children:":"})," ",(0,a.jsx)(n.span,{className:"token string",children:'"concurrent"'}),"\n",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),"\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["Three scripts, three lifecycle hooks. ",(0,a.jsx)(n.code,{children:'runScriptMode: "concurrent"'})," means Conductor runs the ",(0,a.jsx)(n.code,{children:"run"})," script in a way that\nsupports concurrent processes (like a Vite dev server and a queue worker running side by side)."]}),"\n",(0,a.jsxs)(n.p,{children:["One thing I wish ",(0,a.jsx)(n.code,{children:"conductor.json"})," supported: arrays for the script values, so you could inline multiple commands without\ncramming everything into one unreadable string (the way ",(0,a.jsx)(n.code,{children:"composer.json"})," scripts do it). It doesn't, so just bypass the\nwhole problem by pointing each hook at its own ",(0,a.jsx)(n.code,{children:".sh"})," file. You get proper syntax highlighting, comments, multi-line\ncommands — all the things you lose when you try to stuff shell logic into a JSON string. Later in this article there's a\nzsh function you can paste into your ",(0,a.jsx)(n.code,{children:"~/.zshrc"})," to scaffold the whole thing out in any project."]}),"\n",(0,a.jsxs)(n.h2,{id:"environment-variables",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#environment-variables",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Environment Variables"]}),"\n",(0,a.jsx)(n.p,{children:"Conductor injects these into every script it runs. You'll use them throughout your setup and teardown logic:"}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# Available in every .conductor/ script:"}),"\nCONDUCTOR_WORKSPACE_NAME     ",(0,a.jsx)(n.span,{className:"token comment",children:'# e.g. "nagoya"'}),"\nCONDUCTOR_WORKSPACE_PATH     ",(0,a.jsx)(n.span,{className:"token comment",children:'# e.g. "~/conductor/workspaces
180/my-project/nagoya"'}),"\nCONDUCTOR_ROOT_PATH          ",(0,a.jsx)(n.span,{className:"token comment",children:'# e.g. "~/code/my-project"'}),"\nCONDUCTOR_DEFAULT_BRANCH     ",(0,a.jsx)(n.span,{className:"token comment",children:'# e.g. "main"'}),"\nCONDUCTOR_PORT               ",(0,a.jsx)(n.span,{className:"token comment",children:'# e.g. "55100" (first of 10 ports: PORT+0 through PORT+9)'}),"\n"]})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"CONDUCTOR_ROOT_PATH"})," is the important one. It points to your actual repo directory — not the worktree. This is how you\nshare files like ",(0,a.jsx)(n.code,{children:".env"})," without copying them."]}),"\n",(0,a.jsxs)(n.h2,{id:"the-scripts",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#the-scripts",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"The Scripts"]}),"\n",(0,a.jsx)(n.p,{children:"These are the actual scripts I use for a Laravel + Herd project. I'm showing them verbatim — this is exactly what's\nrunning in production on my machine."}),"\n",(0,a.jsxs)(n.h3,{id:"setupsh",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#setupsh",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"setup.sh"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token shebang important",children:"#!/bin/zsh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Conductor Environment Variables:"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_WORKSPACE_NAME - Workspace name (e.g. "nagoya")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_WORKSPACE_PATH - Workspace path"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_ROOT_PATH      - Path to the main repo root"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_DEFAULT_BRANCH - Default branch (e.g. "main")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_PORT           - First of 10 ports, PORT+0 through PORT+9"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Link folder"}),"\nherd ",(0,a.jsx)(n.span,{className:"token function",children:"link"})," ",(0,a.jsx)(n.span,{className:"token variable",children:"$CONDUCTOR_WORKSPACE_NAME"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Set php version"}),"\nherd isolate ",(0,a.jsx)(n.span,{className:"token number",children:"8.3"})," --site",(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Symlink .env from project root into worktree"}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"ln"})," -sf ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/.env"']})," .env\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Install deps"}),"\n",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"export"})," ",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"NVM_DIR"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token environment constant",children:"$HOME"}),'/.nvm"']}),"\n",(0,a.jsx)(n.span,{className:"token punctuation",children:"["})," -s ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"$NVM_DIR"}),'/nvm.sh"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"]"})," ",(0,a.jsx)(n.span,{className:"token operator",children:"&&"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),". ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"$NVM_DIR"}),'/nvm.sh"']}),"\nnvm use\nherd ",(0,a.jsx)(n.span,{className:"token function",children:"composer"})," i\n",(0,a.jsx)(n.span,{className:"token function",children:"pnpm"})," ",(0,a.jsx)(n.span,{className:"token function",children:"install"}),"\n"]})}),"\n",(0,a.jsx)(n.p,{children:"Let me walk through what each piece does."}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:(0,a.jsx)(n.code,{children:"herd link"})})," registers this worktree directory as a Herd site. After this, ",(0,a.jsx)(n.code,{children:"http://nagoya.test"})," resolves to this\nworktree. Each worktree gets its own ",(0,a.jsx)(n.code,{children:".test"})," domain automatically based on the workspace name."]}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:(0,a.jsx)(n.code,{children:"herd isolate"})})," pins PHP 8.3 for this specific site. Without it, the worktree uses whatever PHP version Herd is\nglobally set to — which might be wrong if you've been switching between projects. Isolating per-site means it doesn't\nmatter."]}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:(0,a.jsx)(n.code,{children:"ln -sf"})})," creates a symlink from the worktree's ",(0,a.jsx)(n.code,{children:".env"})," to the main repo's ",(0,a.jsx)(n.code,{children:".env"}),". This is the single most important\nline. Every worktree shares the same database credentials, API keys, and service config. Change your ",(0,a.jsx)(n.code,{children:".env"})," once and\nevery worktree picks it up immediately."]}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"ln -sf"})," won't fail if the target file doesn't exist yet — it creates a dangling symlink, which resolves the moment the\nfile appears. So the order doesn't matter."]}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"The rest"})," is standard: switch to the right Node version, install Composer and npm dependencies."]}),"\n",(0,a.jsxs)(n.h3,{id:"runsh",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#runsh",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"run.sh"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token shebang important",children:"#!/bin/zsh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Conductor Environment Variables:"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_WORKSPACE_NAME - Workspace name (e.g. "n
180agoya")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_WORKSPACE_PATH - Workspace path"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_ROOT_PATH      - Path to the main repo root"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_DEFAULT_BRANCH - Default branch (e.g. "main")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_PORT           - First of 10 ports, PORT+0 through PORT+9"}),"\n\nherd ",(0,a.jsx)(n.span,{className:"token function",children:"open"}),"\nnpx concurrently ",(0,a.jsx)(n.span,{className:"token string",children:'"pnpm run start"'})," ",(0,a.jsx)(n.span,{className:"token string",children:'"herd php artisan queue:work"'}),"\n"]})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"herd open"})," launches ",(0,a.jsx)(n.code,{children:"http://nagoya.test"})," in your default browser. Then ",(0,a.jsx)(n.code,{children:"concurrently"})," runs the Vite dev server and the\nLaravel queue worker side by side. When you hit ",(0,a.jsx)(n.code,{children:"Ctrl+C"}),", both stop."]}),"\n",(0,a.jsxs)(n.h3,{id:"archivesh",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#archivesh",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"archive.sh"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token shebang important",children:"#!/bin/zsh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Conductor Environment Variables:"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_WORKSPACE_NAME - Workspace name (e.g. "nagoya")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_WORKSPACE_PATH - Workspace path"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_ROOT_PATH      - Path to the main repo root"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:'# CONDUCTOR_DEFAULT_BRANCH - Default branch (e.g. "main")'}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# CONDUCTOR_PORT           - First of 10 ports, PORT+0 through PORT+9"}),"\n\nherd unlink\n",(0,a.jsx)(n.span,{className:"token function",children:"rm"})," -rf node_modules\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["Unlink the Herd site and delete ",(0,a.jsx)(n.code,{children:"node_modules"})," to reclaim disk space. Conductor handles deleting the worktree directory\nitself — archive is just for your cleanup logic."]}),"\n",(0,a.jsxs)(n.h2,{id:"pain-points-this-solves",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#pain-points-this-solves",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Pain Points This Solves"]}),"\n",(0,a.jsxs)(n.h3,{id:"the-env-problem",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#the-env-problem",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"The .env problem"]}),"\n",(0,a.jsxs)(n.p,{children:["Without this setup, every worktree needs its own ",(0,a.jsx)(n.code,{children:".env"}),". You either copy it manually (and forget, every time), or you\nwrite a wrapper script that does it for you (and then maintain that script forever)."]}),"\n",(0,a.jsxs)(n.p,{children:["The symlink approach sidesteps all of this. There is exactly one ",(0,a.jsx)(n.code,{children:".env"})," file, in your main repo directory. Every\nworktree reads from it. Update your database password once and you're done."]}),"\n",(0,a.jsxs)(n.p,{children:["One caveat: if you need per-worktree database isolation (different DB per worktree), you'll want to ",(0,a.jsx)(n.strong,{children:"copy"})," the ",(0,a.jsx)(n.code,{children:".env"}),"\ninstead of symlinking it. I cover this in the advanced section below."]}),"\n",(0,a.jsxs)(n.h3,{id:"sqlite-database-cloning",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#sqlite-database-cloning",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"SQLite database cloning"]}),"\n",(0,a.jsxs)(n.p,{children:["If your project uses SQLite, you might want each worktree to start with a copy of your current dev database. Add this to\n",(0,a.jsx)(n.code,{children:"setup.sh"})," after the symlink line:"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/setup.sh — add after the ln -sf line:"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Clone the SQLite database so this worktree starts with real data."}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Use cp, not ln — each w
180orktree needs its own copy because"}),"\n",(0,a.jsx)(n.span,{className:"token comment",children:"# they'll diverge as you make changes."}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"cp"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/database/database.sqlite"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n   database/database.sqlite\n"]})}),"\n",(0,a.jsx)(n.p,{children:"This gives you the full schema and all your seed data instantly without running migrations from scratch. It's a copy,\nnot a symlink, because each worktree will make its own changes and you don't want them stomping on each other."}),"\n",(0,a.jsxs)(n.h3,{id:"worktree-subdirectories-and-gitignore",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#worktree-subdirectories-and-gitignore",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Worktree subdirectories and .gitignore"]}),"\n",(0,a.jsxs)(n.p,{children:["Some tools create worktrees inside your project directory instead of in ",(0,a.jsx)(n.code,{children:"~/conductor/"}),". Claude Code puts its worktrees\nin ",(0,a.jsx)(n.code,{children:".claude/worktrees/"}),". If you're using any tool that does this, add the directory to ",(0,a.jsx)(n.code,{children:".gitignore"})," so you don't\naccidentally commit a worktree:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{children:"# AI tool worktrees\n.claude/worktrees/\n"})}),"\n",(0,a.jsxs)(n.h3,{id:"commit-the-conductor-config",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#commit-the-conductor-config",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Commit the conductor config"]}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsxs)(n.strong,{children:["Do commit ",(0,a.jsx)(n.code,{children:"conductor.json"})," and ",(0,a.jsx)(n.code,{children:".conductor/"})," to your repo."]})," That's the whole point — every developer on your team\ngets the same setup, run, and teardown scripts. The scripts use Conductor's environment variables, so they're portable.\nIt doesn't matter where Conductor puts the worktree or what the workspace is called."]}),"\n",(0,a.jsxs)(n.h2,{id:"quick-setup-zsh-functions",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#quick-setup-zsh-functions",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Quick Setup: ZSH Functions"]}),"\n",(0,a.jsxs)(n.p,{children:["If you set up Conductor config in multiple projects, add one of these to your ",(0,a.jsx)(n.code,{children:"~/.zshrc"})," so you can run\n",(0,a.jsx)(n.code,{children:"setup-conductor"})," from any project root."]}),"\n",(0,a.jsxs)(n.h3,{id:"template-version",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#template-version",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Template version"]}),"\n",(0,a.jsx)(n.p,{children:"Keep your default scripts in a template folder and copy them in:"}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token function-name function",children:"setup-conductor"}),(0,a.jsx)(n.span,{className:"token punctuation",children:"("}),(0,a.jsx)(n.span,{className:"token punctuation",children:")"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"\n  ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"local"})," ",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"tpl"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token environment constant",children:"$HOME"}),'/.templates/conductor-workflow"']}),"\n\n  ",(0,a.jsx)(n.span,{className:"token keyword",children:"if"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"["})," ",(0,a.jsx)(n.span,{className:"token operator",children:"!"})," -d ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"$tpl"}),'"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"]"}),(0,a.jsx)(n.span,{className:"token punctuation",children:";"})," ",(0,a.jsx)(n.span,{className:"token keyword",children:"then"}),"\n    ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"Template not found: ',(0,a.jsx)(n.span,{className:"token variable",children:"$tpl"}),'"']}),"\n    ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsx)(n.span,{className:"token string",children:'"Create it with conductor.json and .conductor/*.sh"'}),"\n    ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"return"})," ",(0,a.jsx)(n.span,{className:"token number",children:"1"}),"\n  ",(0,a.jsx)(n.span,{className:"token keyword",children:"fi"}),"\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cp"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"$tpl"}),'/conductor.json"']})," ./conductor.json\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cp"})," -r ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"$tpl"}),'/.conductor"']})," ./.conductor\n  ",(0,a.jsx)(n.span,{className:"token function",children:"chmod"})," +x .conductor/*.sh\n\n  ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsx)(n.span,{className:"token string",children:'"Conductor config copied. Edit .conductor/*.sh for this project."'}),"\n",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),"\n"]})}),"\n",(0,a.jsxs)(n.h3,{id:"inline-version",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#inline-version",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Inline version"]}),"\n",(0,a.jsxs)(n.p,{children:["No template directory needed — this creates everything directly. Copy the whole thing and paste it into your ",(0,a.jsx)(n.code,{children:"~/.zshrc"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token function-name function",children:"setup-conductor"}),(0,a.jsx)(n.span,{className:"token punctuation",children:"("}),(0,a.jsx)(n.span,{className:"token punctuation",children:")"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"\n  ",(0,a.jsx)(n.span,{className:"token function",children:"mkdir"})," -p .conductor\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cat"})," ",(0,a.jsx)(n.span,{className:"token operator",children:">"})," conductor.json ",(0,a.jsx)(n.span,{className:"token operator",children:"<<"})," ",(0,a.jsx)(n.span,{className:"token string",children:'\'EOF\'\n{\n  "scripts": {\n    "setup": ".conductor/setup.sh",\n    "run": ".conductor/run.sh",\n    "archive": ".conductor/archive.sh"\n  },\n  "runScriptMode": "concurrent"\n}\nEOF'}),"\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cat"})," ",(0,a.jsx)(n.span,{className:"token operator",children:">"})," .conductor/setup.sh ",(0,a.jsx)(n.span,{className:"token operator",children:"<<"})," ",(0,a.jsx)(n.span,{className:"token string",children:"'EOF'\n#!/bin/zsh\n\n# Conductor Environment Variables:\n#   CONDUCTOR_WORKSPACE_NAME    - Workspace name\n#   CONDUCTOR_WORKSPACE_PATH    - Workspace path\n#   CONDUCTOR_ROOT_PATH         - Path to the main repo root\n#   CONDUCTOR_DEFAULT_BRA
180NCH    - Default branch name\n#   CONDUCTOR_PORT              - First of 10 ports (PORT+0 through PORT+9)\n\n# --- Customize below for your project ---\n\n# Symlink .env from the main repo\nln -sf \"${CONDUCTOR_ROOT_PATH}/.env\" .env\n\n# Install dependencies (change to your package manager)\nnpm install\nEOF"}),"\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cat"})," ",(0,a.jsx)(n.span,{className:"token operator",children:">"})," .conductor/run.sh ",(0,a.jsx)(n.span,{className:"token operator",children:"<<"})," ",(0,a.jsx)(n.span,{className:"token string",children:"'EOF'\n#!/bin/zsh\n\n# Start the dev server (change to your start command)\nnpm run dev\nEOF"}),"\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"cat"})," ",(0,a.jsx)(n.span,{className:"token operator",children:">"})," .conductor/archive.sh ",(0,a.jsx)(n.span,{className:"token operator",children:"<<"})," ",(0,a.jsx)(n.span,{className:"token string",children:"'EOF'\n#!/bin/zsh\n\n# Clean up\nrm -rf node_modules\nEOF"}),"\n\n  ",(0,a.jsx)(n.span,{className:"token function",children:"chmod"})," +x .conductor/*.sh\n  ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsx)(n.span,{className:"token string",children:'"Created conductor.json and .conductor/ scripts."'}),"\n  ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsx)(n.span,{className:"token string",children:'"Edit the scripts in .conductor/ for your project."'}),"\n",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),"\n"]})}),"\n",(0,a.jsxs)(n.h2,{id:"generate-your-own",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#generate-your-own",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Generate Your Own"]}),"\n",(0,a.jsxs)(n.p,{children:["Pick a template, customize the scripts, and hit generate. You'll get a one-liner you can paste into your terminal at the\nproject root — it creates ",(0,a.jsx)(n.code,{children:"conductor.json"})," and all three ",(0,a.jsx)(n.code,{children:".conductor/"})," scripts in one go."]}),"\n",(0,a.jsx)(x,{}),"\n",(0,a.jsxs)(n.h2,{id:"advanced-per-worktree-isolation",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#advanced-per-worktree-isolation",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Advanced: Per-Worktree Isolation"]}),"\n",(0,a.jsxs)(n.p,{children:["The setup above shares a single ",(0,a.jsx)(n.code,{children:".env"})," across all worktrees. That's the right default — it means zero config drift\nbetween worktrees and zero maintenance burden."]}),"\n",(0,a.jsx)(n.p,{children:"But sometimes you need actual isolation: a separate database per worktree, different cache prefixes, worktree-specific\nmail routing. Here are the patterns I've found useful."}),"\n",(0,a.jsxs)(n.h3,{id:"sharing-your-site-via-herd",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#sharing-your-site-via-herd",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Sharing your site via Herd"]}),"\n",(0,a.jsxs)(n.p,{children:["Herd has built-in tunnel support via ",(0,a.jsx)(n.a,{href:"https://expose.dev",children:"Expose"}),". If you need to share a running worktree with someone\n(demo for a client, testing a webhook, pair debugging), add this to your ",(0,a.jsx)(n.code,{children:".conductor/run.sh"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/run.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Share this worktree publicly via Herd's Expose tunnel"}),"\nherd share ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Grab the public URL (useful for logging or passing to other tools)"}),"\n",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"SHARE_URL"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token variable",children:[(0,a.jsx)(n.span,{className:"token variable",children:"$("}),"herd fetch-share-url",(0,a.jsx)(n.span,{className:"token variable",children:")"})]}),"\n",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"echo"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"Public URL: ',(0,a.jsx)(n.span,{className:"token variable",children:"${SHARE_URL}"}),'"']}),"\n"]})}),"\n",(0,a.jsx)(n.p,{children:"Each worktree gets its own tunnel URL. This is particularly useful when you're running multiple feature branches and\nneed a client to test a specific one."}),"\n",(0,a.jsxs)(n.h3,{id:"per-worktree-mysql-databases",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#per-worktree-mysql-databases",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Per-worktree MySQL databases"]}),"\n",(0,a.jsx)(n.p,{children:"Instead of sharing one database, create a fresh one per worktree. This is essential if you're working on migrations —\nyou don't want one branch's migration to mess up another branch's sc
180hema."}),"\n",(0,a.jsxs)(n.p,{children:["Several of the patterns below need to override specific ",(0,a.jsx)(n.code,{children:".env"})," values per worktree. ",(0,a.jsx)(n.a,{href:"https://dotenvx.com",children:"dotenvx"})," is a\nCLI tool by the original author of dotenv that lets you properly get and set values in ",(0,a.jsx)(n.code,{children:".env"})," files. It's basically a\nbetter dotenv CLI — you give it a key, a value, and a file, and it does the right thing. Much cleaner than writing ",(0,a.jsx)(n.code,{children:"sed"}),"\nsubstitutions that nobody can read and everyone gets wrong. Install it with ",(0,a.jsx)(n.code,{children:"brew install dotenvx/brew/dotenvx"}),"."]}),"\n",(0,a.jsxs)(n.p,{children:["Add to ",(0,a.jsx)(n.code,{children:".conductor/setup.sh"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/setup.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Create a database named after this worktree"}),"\n",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"DB_NAME"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\nmysql -u root -e ",(0,a.jsxs)(n.span,{className:"token string",children:['"CREATE DATABASE IF NOT EXISTS \\',(0,a.jsxs)(n.span,{className:"token variable",children:[(0,a.jsx)(n.span,{className:"token variable",children:"`"}),"$",(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"DB_NAME",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),(0,a.jsx)(n.span,{className:"token variable",children:"`"})]}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Optionally import a dump from the main repo"}),"\n",(0,a.jsx)(n.span,{className:"token keyword",children:"if"})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"["})," -f ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/database/dump.sql"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"]"}),(0,a.jsx)(n.span,{className:"token punctuation",children:";"})," ",(0,a.jsx)(n.span,{className:"token keyword",children:"then"}),"\n  mysql -u root ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${DB_NAME}"}),'"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n    ",(0,a.jsx)(n.span,{className:"token operator",children:"<"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/database/dump.sql"']}),"\n",(0,a.jsx)(n.span,{className:"token keyword",children:"fi"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Copy .env (not symlink) because we need a different DB_DATABASE"}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"cp"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/.env"']})," .env\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Point this worktree at its own database"}),"\ndotenvx ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"set"})," DB_DATABASE ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${DB_NAME}"}),'"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  -f .env --plain\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["And clean up in ",(0,a.jsx)(n.code,{children:".conductor/archive.sh"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/archive.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Drop the worktree-specific database"}),"\nmysql -u root ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  -e ",(0,a.jsxs)(n.span,{className:"token string",children:['"DROP DATABASE IF EXISTS \\',(0,a.jsxs)(n.span,{className:"token variable",children:[(0,a.jsx)(n.span,{className:"token variable",children:"`"}),"$",(0,a.jsx)(n.span,{className:"token punctuation",children:"{"}),"CONDUCTOR_WORKSPACE_NAME",(0,a.jsx)(n.span,{className:"token punctuation",children:"}"}),(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),(0,a.jsx)(n.span,{className:"token variable",children:"`"})]}),'"']}),"\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["Important: when you need per-worktree ",(0,a.jsx)(n.code,{children:".env"})," values, you ",(0,a.jsx)(n.strong,{children:"copy"})," the ",(0,a.jsx)(n.code,{children:".env"})," instead of symlinking it. The symlink\napproach is for shared config; the copy approach is for isolated config."]}),"\n",(0,a.jsxs)(n.h3,{id:"docker-containers-per-worktree",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#docker-containers-per-worktree",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Docker containers per worktree"]}),"\n",(0,a.jsxs)(n.p,{children:["If your project uses Docker, ",(0,a.jsx)(n.code,{children:"COMPOSE_PROJECT_NAME"})," is your friend. It prefixes all container and network names, so each\nworktree gets a completely isolated Docker stack:"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/setup.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"export"})," ",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"COMPOSE_PROJECT_NAME"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Copy .env (Docker needs a real file)"}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"cp"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'/.env"']})," .env\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Override the app name for this worktree"}),"\ndotenvx ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"set"})," APP_NAME ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  -f .env --plain\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Build images with worktree-specific args"}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"docker"})," compose build ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  --build-arg ",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"APP_NAME"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token function",children:"docker"})," compose up -d\n",(0,a.jsx)(n.span,{className:"token function",children:"docker"})," compose ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"exec"})," app php artisan migrate --seed\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["In your ",(0,a.jsx)(n.code,{children:"Dockerfile"}),", use the build arg:"]}),"\n",(0,a.jsx)(n.pre,{className:"language-dockerfile",children:(0,a.jsxs)(n.code,{className:"language-dockerfile",children:[(0,a.jsxs)(n.span,{className:"token instruction",children:[(0,a.jsx)(n.span,{className:"token keyword",children:"ARG"})," APP_NAME=app"]}),"\n",(0,a.jsxs)(n.span,{className:"token instruction",children:[(0,a.jsx)(n.span,{className:"token keyword",children:"ENV"})," APP_NAME=",(0,a.jsx)(n.span,{className:"token variable",children:"${APP_NAME}"})]}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Label for easy identification and cleanup"}),"\n",(0,a.jsxs)(n.span,{className:"token instruction",children:[(0,a.jsx)(n.span,{className:"token keyword",children:"LABEL"})," conductor.workspace=",(0,a.jsx)(n.span,{className:"token string",children:'"${APP_NAME}"'})]}),"\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["And in ",(0,a.jsx)(n.code,{children:".conductor/archive.sh"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/archive.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"export"})," ",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"COMPOSE_PROJECT_NAME"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'"']}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Tear down everything — containers, volumes, networks"}),"\n",(0,a.jsx)(n.span,{className:"token function",children:"docker"})," compose down -v --remove-orphans\n",(0,a.jsx)(n.span,{className:"token function",children:"rm"})," -f .env\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["With this setup, ",(0,a.jsx)(n.code,{children:"nagoya"})," and ",(0,a.jsx)(n.code,{children:"montreal"}
180)," run completely independent Docker stacks. Different containers, different\nvolumes, different networks. Run ",(0,a.jsx)(n.code,{children:"docker compose ls"})," and you can see exactly what's running:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{children:"$ docker compose ls\nNAME        STATUS              CONFIG FILES\nnagoya      running(3)          /Users/you/conductor/workspaces/my-project/nagoya/docker-compose.yml\nmontreal    running(3)          /Users/you/conductor/workspaces/my-project/montreal/docker-compose.yml\nsalvador    exited(3)           /Users/you/conductor/workspaces/my-project/salvador/docker-compose.yml\n"})}),"\n",(0,a.jsx)(n.p,{children:"Each worktree is its own compose project. No name collisions, no port conflicts, no accidentally nuking the wrong stack."}),"\n",(0,a.jsxs)(n.h3,{id:"redis-cache-prefix-isolation",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#redis-cache-prefix-isolation",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Redis cache prefix isolation"]}),"\n",(0,a.jsxs)(n.p,{children:["If all your worktrees hit the same Redis server, their cache keys will collide. ",(0,a.jsx)(n.code,{children:"nagoya"})," flushes its cache and\n",(0,a.jsx)(n.code,{children:"montreal"})," loses its cached data too. Fix this by prefixing cache keys per worktree."]}),"\n",(0,a.jsxs)(n.p,{children:["In ",(0,a.jsx)(n.code,{children:".conductor/setup.sh"})," (with a copied ",(0,a.jsx)(n.code,{children:".env"}),", not symlinked):"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/setup.sh"}),"\n\ndotenvx ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"set"})," REDIS_PREFIX ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'_"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  -f .env --plain\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["Now ",(0,a.jsx)(n.code,{children:"nagoya"})," writes to ",(0,a.jsx)(n.code,{children:"nagoya_cache:users:1"})," and ",(0,a.jsx)(n.code,{children:"montreal"})," writes to ",(0,a.jsx)(n.code,{children:"montreal_cache:users:1"}),". No collisions, no\naccidental flushes, no mysterious cache misses."]}),"\n",(0,a.jsxs)(n.h3,{id:"per-worktree-mail-routing",children:[(0,a.jsx)(n.a,{className:"heading-anchor","aria-hidden":!0,tabIndex:"-1",href:"#per-worktree-mail-routing",children:(0,a.jsx)(n.span,{className:"icon icon-link"})}),"Per-worktree mail routing"]}),"\n",(0,a.jsxs)(n.p,{children:["Route outbound mail to worktree-specific addresses so you can trace which worktree sent what. This is useful if you're\nusing a mail trap like ",(0,a.jsx)(n.a,{href:"https://mailpit.axllent.org",children:"Mailpit"})," or ",(0,a.jsx)(n.a,{href:"https://mailtrap.io",children:"Mailtrap"})," and need to debug email\nissues across branches."]}),"\n",(0,a.jsxs)(n.p,{children:["In ",(0,a.jsx)(n.code,{children:".conductor/setup.sh"}),":"]}),"\n",(0,a.jsx)(n.pre,{className:"language-shell",children:(0,a.jsxs)(n.code,{className:"language-shell",children:[(0,a.jsx)(n.span,{className:"token comment",children:"# .conductor/setup.sh"}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Extract the project name from the root path"}),"\n",(0,a.jsx)(n.span,{className:"token assign-left variable",children:"PROJECT"}),(0,a.jsx)(n.span,{className:"token operator",children:"="}),(0,a.jsxs)(n.span,{className:"token variable",children:[(0,a.jsx)(n.span,{className:"token variable",children:"$("}),(0,a.jsx)(n.span,{className:"token function",children:"basename"})," ",(0,a.jsxs)(n.span,{className:"token string",children:['"',(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_ROOT_PATH}"}),'"']}),(0,a.jsx)(n.span,{className:"token variable",children:")"})]}),"\n\n",(0,a.jsx)(n.span,{className:"token comment",children:"# Route mail so each worktree has a unique sender address"}),"\ndotenvx ",(0,a.jsx)(n.span,{className:"token builtin class-name",children:"set"})," MAIL_FROM_ADDRESS ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  ",(0,a.jsxs)(n.span,{className:"token string",children:['"noreply+',(0,a.jsx)(n.span,{className:"token variable",children:"${PROJECT}"}),"+",(0,a.jsx)(n.span,{className:"token variable",children:"${CONDUCTOR_WORKSPACE_NAME}"}),'@herdsite.test"']})," ",(0,a.jsx)(n.span,{className:"token punctuation",children:"\\"}),"\n  -f .env --plain\n"]})}),"\n",(0,a.jsxs)(n.p,{children:["Emails from ",(0,a.jsx)(n.code,{children:"nagoya"})," show up as ",(0,a.jsx)(n.code,{children:"[email protected]"}),". When you're staring at a list of test emails\nin Mailpit, you can immediately see which worktree and which project generated each one."]}),"\n",(0,a.jsx)(n.hr,{}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Links:"})}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://conductor.dev",children:"Conductor.dev"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://docs.conductor.build",children:"Conductor Docs"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://herd.laravel.com",children:"Laravel Herd"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://herd.laravel.com/docs/macos/advanced-usage/herd-cli",children:"Herd CLI Reference"})}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"https://dotenvx.com",children:"dotenvx"})," — CLI for editing ",(0,a.jsx)(n.code,{children:".env"})," files properly"]}),"\n"]})]})}function g(e={}){return(0,a.jsx)(k,{...e,children:(0,a.jsx)(N,{...e})})}},91668:(e,n,s)=>{"use strict";s.d(n,{R:()=>o,x:()=>c});var a=s(14232);let t={},r=a.createContext(t);function o(e){let n=a.useContext(r);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),a.createElement(r.Provider,{value:n},e.children)}}},e=>{e.O(0,[636,6593,8792],()=>e(e.s=5045)),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.