[{"data":1,"prerenderedAt":1886},["ShallowReactive",2],{"article:automating-claude-code-worktree-setup-env-node-modules-and-ports":3,"\u002Fblog\u002Fautomating-claude-code-worktree-setup-env-node-modules-and-ports-surround":1875},{"id":4,"title":5,"author":6,"body":7,"date":1864,"description":1865,"extension":1866,"image":1867,"meta":1868,"minRead":168,"navigation":219,"path":1869,"published":219,"seo":1870,"stem":1871,"tags":1872,"__hash__":1874},"blog\u002Fblog\u002Fautomating-claude-code-worktree-setup-env-node-modules-and-ports.md","Automating Claude Code Worktree Setup — ENV Files, Node Modules, and Ports","Baljeet Singh",{"type":8,"value":9,"toc":1852},"minimark",[10,14,40,43,48,55,84,87,91,94,128,132,141,178,185,189,195,862,865,881,884,925,929,936,1156,1159,1172,1189,1193,1199,1206,1409,1415,1516,1534,1581,1587,1669,1673,1678,1687,1691,1694,1726,1731,1735,1738,1806,1813,1817,1820,1842,1845,1848],[11,12,13],"p",{},"If you're using Claude Code's worktree feature for parallel development, you've probably run into the same annoyances I did. Every time a new worktree spins up, you have to:",[15,16,17,26,37],"ol",{},[18,19,20,21,25],"li",{},"Manually copy all your ",[22,23,24],"code",{},".env"," files",[18,27,28,29,32,33,36],{},"Run ",[22,30,31],{},"npm install"," or ",[22,34,35],{},"pnpm install"," (and wait)",[18,38,39],{},"Figure out which ports are free if you want to run dev servers alongside your main repo",[11,41,42],{},"It gets old fast, especially when you're spinning up worktrees frequently. Here's how I automated all of it with a simple setup that runs automatically when Claude Code enters a worktree.",[44,45,47],"h2",{"id":46},"the-problem","The Problem",[11,49,50,51,54],{},"Claude Code's worktree feature creates a fresh copy of your repo on a separate git branch. That's great for isolation — but it also means your new worktree is missing everything that's in ",[22,52,53],{},".gitignore",":",[56,57,58,66,74],"ul",{},[18,59,60,65],{},[61,62,63,25],"strong",{},[22,64,24],{}," — your API keys, database URLs, and service configs",[18,67,68,73],{},[61,69,70],{},[22,71,72],{},"node_modules\u002F"," — your entire dependency tree",[18,75,76,79,80,83],{},[61,77,78],{},"Port assignments"," — if you run ",[22,81,82],{},"pnpm dev"," in both the main repo and the worktree, they'll fight over the same port",[11,85,86],{},"You end up doing the same manual setup every single time. Let's fix that.",[44,88,90],{"id":89},"the-solution","The Solution",[11,92,93],{},"We're going to use three things:",[15,95,96,106,112],{},[18,97,98,105],{},[61,99,100,101,104],{},"A ",[22,102,103],{},".worktreeinclude"," file"," that lists which files to symlink from the main repo",[18,107,108,111],{},[61,109,110],{},"A setup script"," that symlinks those files and generates unique port offsets",[18,113,114,120,121,124,125],{},[61,115,116,117],{},"Claude Code's ",[22,118,119],{},"settings.json"," to run the script automatically on ",[22,122,123],{},"SessionStart"," and symlink ",[22,126,127],{},"node_modules",[44,129,131],{"id":130},"step-1-list-files-to-symlink","Step 1: List Files to Symlink",[11,133,134,135,137,138,140],{},"Create a ",[22,136,103],{}," file in your project root. List every file you want available in worktrees — one per line. These are typically your ",[22,139,24],{}," files:",[142,143,148],"pre",{"className":144,"code":145,"filename":103,"language":146,"meta":147,"style":147},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Environment files\napps\u002Fweb\u002F.env\napps\u002Fapi\u002F.env\n.env\n","bash","",[22,149,150,159,166,172],{"__ignoreMap":147},[151,152,155],"span",{"class":153,"line":154},"line",1,[151,156,158],{"class":157},"sHwdD","# Environment files\n",[151,160,162],{"class":153,"line":161},2,[151,163,165],{"class":164},"sBMFI","apps\u002Fweb\u002F.env\n",[151,167,169],{"class":153,"line":168},3,[151,170,171],{"class":164},"apps\u002Fapi\u002F.env\n",[151,173,175],{"class":153,"line":174},4,[151,176,177],{"class":164},".env\n",[11,179,180,181,184],{},"Add whatever your project needs. Comments (lines starting with ",[22,182,183],{},"#",") and blank lines are ignored.",[44,186,188],{"id":187},"step-2-the-setup-script","Step 2: The Setup Script",[11,190,191,192,54],{},"Create ",[22,193,194],{},"scripts\u002Fworktree\u002Fsetup.sh",[142,196,198],{"className":144,"code":197,"filename":194,"language":146,"meta":147,"style":147},"#!\u002Fbin\u002Fbash\n# Symlinks files from main repo and generates a port offset.\n# Called automatically by Claude Code's SessionStart hook.\n\nset -e\n\nMAIN_REPO=$(git worktree list --porcelain | head -1 | cut -d' ' -f2)\nWORKTREE_DIR=$(git rev-parse --show-toplevel)\nWORKTREES_DIR=\"$MAIN_REPO\u002F.claude\u002Fworktrees\"\n\n# Skip if already set up (idempotent)\nif [ -f \"$WORKTREE_DIR\u002F.worktree-port-offset\" ]; then\n  exit 0\nfi\n\necho \"Setting up worktree: $(basename \"$WORKTREE_DIR\")\" >&2\n\n# 1. Symlink files listed in .worktreeinclude\nif [ -f \"$MAIN_REPO\u002F.worktreeinclude\" ]; then\n  while IFS= read -r file || [ -n \"$file\" ]; do\n    [[ -z \"$file\" || \"$file\" == \\#* ]] && continue\n    if [ -f \"$MAIN_REPO\u002F$file\" ]; then\n      mkdir -p \"$WORKTREE_DIR\u002F$(dirname \"$file\")\"\n      ln -sf \"$MAIN_REPO\u002F$file\" \"$WORKTREE_DIR\u002F$file\"\n      echo \"  Linked $file\" >&2\n    fi\n  done \u003C \"$MAIN_REPO\u002F.worktreeinclude\"\nfi\n\n# 2. Generate a unique port offset so dev servers don't collide\nUSED_OFFSETS=\"\"\nfor f in \"$WORKTREES_DIR\"\u002F*\u002F.worktree-port-offset; do\n  [ -f \"$f\" ] && USED_OFFSETS=\"$USED_OFFSETS $(cat \"$f\")\"\ndone\nPORT_OFFSET=10\nwhile echo \"$USED_OFFSETS\" | grep -qw \"$PORT_OFFSET\"; do\n  PORT_OFFSET=$(( PORT_OFFSET + 10 ))\ndone\necho \"$PORT_OFFSET\" > \"$WORKTREE_DIR\u002F.worktree-port-offset\"\necho \"  Port offset: +$PORT_OFFSET\" >&2\n",[22,199,200,205,210,215,221,232,237,289,307,328,333,339,369,379,385,390,417,422,428,450,489,530,555,582,611,628,634,651,656,661,667,678,705,747,753,764,798,819,824,846],{"__ignoreMap":147},[151,201,202],{"class":153,"line":154},[151,203,204],{"class":157},"#!\u002Fbin\u002Fbash\n",[151,206,207],{"class":153,"line":161},[151,208,209],{"class":157},"# Symlinks files from main repo and generates a port offset.\n",[151,211,212],{"class":153,"line":168},[151,213,214],{"class":157},"# Called automatically by Claude Code's SessionStart hook.\n",[151,216,217],{"class":153,"line":174},[151,218,220],{"emptyLinePlaceholder":219},true,"\n",[151,222,224,228],{"class":153,"line":223},5,[151,225,227],{"class":226},"s2Zo4","set",[151,229,231],{"class":230},"sfazB"," -e\n",[151,233,235],{"class":153,"line":234},6,[151,236,220],{"emptyLinePlaceholder":219},[151,238,240,244,248,251,254,257,260,263,266,269,271,274,277,280,283,286],{"class":153,"line":239},7,[151,241,243],{"class":242},"sTEyZ","MAIN_REPO",[151,245,247],{"class":246},"sMK4o","=$(",[151,249,250],{"class":164},"git",[151,252,253],{"class":230}," worktree",[151,255,256],{"class":230}," list",[151,258,259],{"class":230}," --porcelain",[151,261,262],{"class":246}," |",[151,264,265],{"class":164}," head",[151,267,268],{"class":230}," -1",[151,270,262],{"class":246},[151,272,273],{"class":164}," cut",[151,275,276],{"class":230}," -d",[151,278,279],{"class":246},"'",[151,281,282],{"class":246}," '",[151,284,285],{"class":230}," -f2",[151,287,288],{"class":246},")\n",[151,290,292,295,297,299,302,305],{"class":153,"line":291},8,[151,293,294],{"class":242},"WORKTREE_DIR",[151,296,247],{"class":246},[151,298,250],{"class":164},[151,300,301],{"class":230}," rev-parse",[151,303,304],{"class":230}," --show-toplevel",[151,306,288],{"class":246},[151,308,310,313,316,319,322,325],{"class":153,"line":309},9,[151,311,312],{"class":242},"WORKTREES_DIR",[151,314,315],{"class":246},"=",[151,317,318],{"class":246},"\"",[151,320,321],{"class":242},"$MAIN_REPO",[151,323,324],{"class":230},"\u002F.claude\u002Fworktrees",[151,326,327],{"class":246},"\"\n",[151,329,331],{"class":153,"line":330},10,[151,332,220],{"emptyLinePlaceholder":219},[151,334,336],{"class":153,"line":335},11,[151,337,338],{"class":157},"# Skip if already set up (idempotent)\n",[151,340,342,346,349,352,355,358,361,363,366],{"class":153,"line":341},12,[151,343,345],{"class":344},"s7zQu","if",[151,347,348],{"class":246}," [",[151,350,351],{"class":246}," -f",[151,353,354],{"class":246}," \"",[151,356,357],{"class":242},"$WORKTREE_DIR",[151,359,360],{"class":230},"\u002F.worktree-port-offset",[151,362,318],{"class":246},[151,364,365],{"class":246}," ];",[151,367,368],{"class":344}," then\n",[151,370,372,375],{"class":153,"line":371},13,[151,373,374],{"class":226},"  exit",[151,376,378],{"class":377},"sbssI"," 0\n",[151,380,382],{"class":153,"line":381},14,[151,383,384],{"class":344},"fi\n",[151,386,388],{"class":153,"line":387},15,[151,389,220],{"emptyLinePlaceholder":219},[151,391,393,396,398,401,404,407,409,411,414],{"class":153,"line":392},16,[151,394,395],{"class":226},"echo",[151,397,354],{"class":246},[151,399,400],{"class":230},"Setting up worktree: ",[151,402,403],{"class":246},"$(",[151,405,406],{"class":164},"basename",[151,408,354],{"class":246},[151,410,357],{"class":242},[151,412,413],{"class":246},"\")\"",[151,415,416],{"class":246}," >&2\n",[151,418,420],{"class":153,"line":419},17,[151,421,220],{"emptyLinePlaceholder":219},[151,423,425],{"class":153,"line":424},18,[151,426,427],{"class":157},"# 1. Symlink files listed in .worktreeinclude\n",[151,429,431,433,435,437,439,441,444,446,448],{"class":153,"line":430},19,[151,432,345],{"class":344},[151,434,348],{"class":246},[151,436,351],{"class":246},[151,438,354],{"class":246},[151,440,321],{"class":242},[151,442,443],{"class":230},"\u002F.worktreeinclude",[151,445,318],{"class":246},[151,447,365],{"class":246},[151,449,368],{"class":344},[151,451,453,456,459,461,464,467,469,472,474,477,479,482,484,486],{"class":153,"line":452},20,[151,454,455],{"class":344},"  while",[151,457,458],{"class":242}," IFS",[151,460,315],{"class":246},[151,462,463],{"class":226}," read",[151,465,466],{"class":230}," -r",[151,468,104],{"class":230},[151,470,471],{"class":246}," ||",[151,473,348],{"class":246},[151,475,476],{"class":246}," -n",[151,478,354],{"class":246},[151,480,481],{"class":242},"$file",[151,483,318],{"class":246},[151,485,365],{"class":246},[151,487,488],{"class":344}," do\n",[151,490,492,495,498,500,502,504,506,508,510,512,515,518,521,524,527],{"class":153,"line":491},21,[151,493,494],{"class":246},"    [[",[151,496,497],{"class":246}," -z",[151,499,354],{"class":246},[151,501,481],{"class":242},[151,503,318],{"class":246},[151,505,471],{"class":246},[151,507,354],{"class":246},[151,509,481],{"class":242},[151,511,318],{"class":246},[151,513,514],{"class":246}," ==",[151,516,517],{"class":242}," \\#",[151,519,520],{"class":246},"*",[151,522,523],{"class":246}," ]]",[151,525,526],{"class":246}," &&",[151,528,529],{"class":344}," continue\n",[151,531,533,536,538,540,542,544,547,549,551,553],{"class":153,"line":532},22,[151,534,535],{"class":344},"    if",[151,537,348],{"class":246},[151,539,351],{"class":246},[151,541,354],{"class":246},[151,543,321],{"class":242},[151,545,546],{"class":230},"\u002F",[151,548,481],{"class":242},[151,550,318],{"class":246},[151,552,365],{"class":246},[151,554,368],{"class":344},[151,556,558,561,564,566,568,570,572,575,577,579],{"class":153,"line":557},23,[151,559,560],{"class":164},"      mkdir",[151,562,563],{"class":230}," -p",[151,565,354],{"class":246},[151,567,357],{"class":242},[151,569,546],{"class":230},[151,571,403],{"class":246},[151,573,574],{"class":164},"dirname",[151,576,354],{"class":246},[151,578,481],{"class":242},[151,580,581],{"class":246},"\")\"\n",[151,583,585,588,591,593,595,597,599,601,603,605,607,609],{"class":153,"line":584},24,[151,586,587],{"class":164},"      ln",[151,589,590],{"class":230}," -sf",[151,592,354],{"class":246},[151,594,321],{"class":242},[151,596,546],{"class":230},[151,598,481],{"class":242},[151,600,318],{"class":246},[151,602,354],{"class":246},[151,604,357],{"class":242},[151,606,546],{"class":230},[151,608,481],{"class":242},[151,610,327],{"class":246},[151,612,614,617,619,622,624,626],{"class":153,"line":613},25,[151,615,616],{"class":226},"      echo",[151,618,354],{"class":246},[151,620,621],{"class":230},"  Linked ",[151,623,481],{"class":242},[151,625,318],{"class":246},[151,627,416],{"class":246},[151,629,631],{"class":153,"line":630},26,[151,632,633],{"class":344},"    fi\n",[151,635,637,640,643,645,647,649],{"class":153,"line":636},27,[151,638,639],{"class":344},"  done",[151,641,642],{"class":246}," \u003C",[151,644,354],{"class":246},[151,646,321],{"class":242},[151,648,443],{"class":230},[151,650,327],{"class":246},[151,652,654],{"class":153,"line":653},28,[151,655,384],{"class":344},[151,657,659],{"class":153,"line":658},29,[151,660,220],{"emptyLinePlaceholder":219},[151,662,664],{"class":153,"line":663},30,[151,665,666],{"class":157},"# 2. Generate a unique port offset so dev servers don't collide\n",[151,668,670,673,675],{"class":153,"line":669},31,[151,671,672],{"class":242},"USED_OFFSETS",[151,674,315],{"class":246},[151,676,677],{"class":246},"\"\"\n",[151,679,681,684,687,690,692,695,697,700,703],{"class":153,"line":680},32,[151,682,683],{"class":344},"for",[151,685,686],{"class":242}," f ",[151,688,689],{"class":344},"in",[151,691,354],{"class":246},[151,693,694],{"class":242},"$WORKTREES_DIR",[151,696,318],{"class":246},[151,698,699],{"class":230},"\u002F*\u002F.worktree-port-offset",[151,701,702],{"class":246},";",[151,704,488],{"class":344},[151,706,708,711,713,715,718,720,723,725,728,730,732,735,738,741,743,745],{"class":153,"line":707},33,[151,709,710],{"class":246},"  [",[151,712,351],{"class":246},[151,714,354],{"class":246},[151,716,717],{"class":242},"$f",[151,719,318],{"class":246},[151,721,722],{"class":246}," ]",[151,724,526],{"class":246},[151,726,727],{"class":242}," USED_OFFSETS",[151,729,315],{"class":246},[151,731,318],{"class":246},[151,733,734],{"class":242},"$USED_OFFSETS",[151,736,737],{"class":246}," $(",[151,739,740],{"class":164},"cat",[151,742,354],{"class":246},[151,744,717],{"class":242},[151,746,581],{"class":246},[151,748,750],{"class":153,"line":749},34,[151,751,752],{"class":344},"done\n",[151,754,756,759,761],{"class":153,"line":755},35,[151,757,758],{"class":242},"PORT_OFFSET",[151,760,315],{"class":246},[151,762,763],{"class":230},"10\n",[151,765,767,770,773,775,777,779,781,784,787,789,792,794,796],{"class":153,"line":766},36,[151,768,769],{"class":344},"while",[151,771,772],{"class":226}," echo",[151,774,354],{"class":246},[151,776,734],{"class":242},[151,778,318],{"class":246},[151,780,262],{"class":246},[151,782,783],{"class":164}," grep",[151,785,786],{"class":230}," -qw",[151,788,354],{"class":246},[151,790,791],{"class":242},"$PORT_OFFSET",[151,793,318],{"class":246},[151,795,702],{"class":246},[151,797,488],{"class":344},[151,799,801,804,807,810,813,816],{"class":153,"line":800},37,[151,802,803],{"class":242},"  PORT_OFFSET",[151,805,806],{"class":246},"=$((",[151,808,809],{"class":164}," PORT_OFFSET",[151,811,812],{"class":230}," +",[151,814,815],{"class":377}," 10",[151,817,818],{"class":246}," ))\n",[151,820,822],{"class":153,"line":821},38,[151,823,752],{"class":344},[151,825,827,829,831,833,835,838,840,842,844],{"class":153,"line":826},39,[151,828,395],{"class":226},[151,830,354],{"class":246},[151,832,791],{"class":242},[151,834,318],{"class":246},[151,836,837],{"class":246}," >",[151,839,354],{"class":246},[151,841,357],{"class":242},[151,843,360],{"class":230},[151,845,327],{"class":246},[151,847,849,851,853,856,858,860],{"class":153,"line":848},40,[151,850,395],{"class":226},[151,852,354],{"class":246},[151,854,855],{"class":230},"  Port offset: +",[151,857,791],{"class":242},[151,859,318],{"class":246},[151,861,416],{"class":246},[11,863,864],{},"Make it executable:",[142,866,868],{"className":144,"code":867,"language":146,"meta":147,"style":147},"chmod +x scripts\u002Fworktree\u002Fsetup.sh\n",[22,869,870],{"__ignoreMap":147},[151,871,872,875,878],{"class":153,"line":154},[151,873,874],{"class":164},"chmod",[151,876,877],{"class":230}," +x",[151,879,880],{"class":230}," scripts\u002Fworktree\u002Fsetup.sh\n",[11,882,883],{},"Here's what the script does:",[56,885,886,895,907,916],{},[18,887,888,891,892],{},[61,889,890],{},"Finds the main repo"," using ",[22,893,894],{},"git worktree list",[18,896,897,900,901,903,904,906],{},[61,898,899],{},"Symlinks each file"," from ",[22,902,103],{}," — the worktree gets a link to the original, so changes to ",[22,905,24],{}," in your main repo are reflected everywhere",[18,908,909,912,913],{},[61,910,911],{},"Generates a port offset"," (10, 20, 30, ...) by checking what offsets are already in use by other worktrees. Writes it to ",[22,914,915],{},".worktree-port-offset",[18,917,918,921,922,924],{},[61,919,920],{},"Is idempotent"," — if ",[22,923,915],{}," already exists, it exits immediately",[44,926,928],{"id":927},"step-3-wire-it-into-claude-code","Step 3: Wire It Into Claude Code",[11,930,931,932,935],{},"This is where the magic happens. In your ",[22,933,934],{},".claude\u002Fsettings.json",", add:",[142,937,941],{"className":938,"code":939,"filename":934,"language":940,"meta":147,"style":147},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"hooks\": {\n    \"SessionStart\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"MAIN_REPO=$(git worktree list --porcelain | head -1 | cut -d' ' -f2) && WORKTREE_DIR=$(git rev-parse --show-toplevel) && [ \\\"$MAIN_REPO\\\" != \\\"$WORKTREE_DIR\\\" ] && bash \\\"$MAIN_REPO\u002Fscripts\u002Fworktree\u002Fsetup.sh\\\" || true\",\n            \"timeout\": 30\n          }\n        ]\n      }\n    ]\n  },\n  \"worktree\": {\n    \"symlinkDirectories\": [\"node_modules\"]\n  }\n}\n","json",[22,942,943,948,964,978,983,996,1001,1024,1072,1086,1091,1096,1101,1106,1111,1124,1146,1151],{"__ignoreMap":147},[151,944,945],{"class":153,"line":154},[151,946,947],{"class":246},"{\n",[151,949,950,953,957,959,961],{"class":153,"line":161},[151,951,952],{"class":246},"  \"",[151,954,956],{"class":955},"spNyl","hooks",[151,958,318],{"class":246},[151,960,54],{"class":246},[151,962,963],{"class":246}," {\n",[151,965,966,969,971,973,975],{"class":153,"line":168},[151,967,968],{"class":246},"    \"",[151,970,123],{"class":164},[151,972,318],{"class":246},[151,974,54],{"class":246},[151,976,977],{"class":246}," [\n",[151,979,980],{"class":153,"line":174},[151,981,982],{"class":246},"      {\n",[151,984,985,988,990,992,994],{"class":153,"line":223},[151,986,987],{"class":246},"        \"",[151,989,956],{"class":377},[151,991,318],{"class":246},[151,993,54],{"class":246},[151,995,977],{"class":246},[151,997,998],{"class":153,"line":234},[151,999,1000],{"class":246},"          {\n",[151,1002,1003,1006,1010,1012,1014,1016,1019,1021],{"class":153,"line":239},[151,1004,1005],{"class":246},"            \"",[151,1007,1009],{"class":1008},"swJcz","type",[151,1011,318],{"class":246},[151,1013,54],{"class":246},[151,1015,354],{"class":246},[151,1017,1018],{"class":230},"command",[151,1020,318],{"class":246},[151,1022,1023],{"class":246},",\n",[151,1025,1026,1028,1030,1032,1034,1036,1039,1042,1044,1046,1049,1051,1053,1055,1058,1060,1063,1065,1068,1070],{"class":153,"line":291},[151,1027,1005],{"class":246},[151,1029,1018],{"class":1008},[151,1031,318],{"class":246},[151,1033,54],{"class":246},[151,1035,354],{"class":246},[151,1037,1038],{"class":230},"MAIN_REPO=$(git worktree list --porcelain | head -1 | cut -d' ' -f2) && WORKTREE_DIR=$(git rev-parse --show-toplevel) && [ ",[151,1040,1041],{"class":242},"\\\"",[151,1043,321],{"class":230},[151,1045,1041],{"class":242},[151,1047,1048],{"class":230}," != ",[151,1050,1041],{"class":242},[151,1052,357],{"class":230},[151,1054,1041],{"class":242},[151,1056,1057],{"class":230}," ] && bash ",[151,1059,1041],{"class":242},[151,1061,1062],{"class":230},"$MAIN_REPO\u002Fscripts\u002Fworktree\u002Fsetup.sh",[151,1064,1041],{"class":242},[151,1066,1067],{"class":230}," || true",[151,1069,318],{"class":246},[151,1071,1023],{"class":246},[151,1073,1074,1076,1079,1081,1083],{"class":153,"line":309},[151,1075,1005],{"class":246},[151,1077,1078],{"class":1008},"timeout",[151,1080,318],{"class":246},[151,1082,54],{"class":246},[151,1084,1085],{"class":377}," 30\n",[151,1087,1088],{"class":153,"line":330},[151,1089,1090],{"class":246},"          }\n",[151,1092,1093],{"class":153,"line":335},[151,1094,1095],{"class":246},"        ]\n",[151,1097,1098],{"class":153,"line":341},[151,1099,1100],{"class":246},"      }\n",[151,1102,1103],{"class":153,"line":371},[151,1104,1105],{"class":246},"    ]\n",[151,1107,1108],{"class":153,"line":381},[151,1109,1110],{"class":246},"  },\n",[151,1112,1113,1115,1118,1120,1122],{"class":153,"line":387},[151,1114,952],{"class":246},[151,1116,1117],{"class":955},"worktree",[151,1119,318],{"class":246},[151,1121,54],{"class":246},[151,1123,963],{"class":246},[151,1125,1126,1128,1131,1133,1135,1137,1139,1141,1143],{"class":153,"line":392},[151,1127,968],{"class":246},[151,1129,1130],{"class":164},"symlinkDirectories",[151,1132,318],{"class":246},[151,1134,54],{"class":246},[151,1136,348],{"class":246},[151,1138,318],{"class":246},[151,1140,127],{"class":230},[151,1142,318],{"class":246},[151,1144,1145],{"class":246},"]\n",[151,1147,1148],{"class":153,"line":419},[151,1149,1150],{"class":246},"  }\n",[151,1152,1153],{"class":153,"line":424},[151,1154,1155],{"class":246},"}\n",[11,1157,1158],{},"Two things happening here:",[11,1160,1161,1167,1168,1171],{},[61,1162,1163,1164,1166],{},"The ",[22,1165,123],{}," hook"," fires every time Claude Code starts a session. The command checks if we're inside a worktree (by comparing the main repo path with the current directory). If we are, it runs our setup script. The ",[22,1169,1170],{},"|| true"," at the end prevents errors if we're in the main repo.",[11,1173,1174,1179,1180,1182,1183,1185,1186,1188],{},[61,1175,1163,1176],{},[22,1177,1178],{},"worktree.symlinkDirectories"," setting tells Claude Code to symlink ",[22,1181,127],{}," instead of copying it. This is huge — no more waiting for ",[22,1184,31],{}," in every worktree. The worktree just points to the same ",[22,1187,127],{}," from your main repo.",[44,1190,1192],{"id":1191},"step-4-use-the-port-offset-in-your-dev-server","Step 4: Use the Port Offset in Your Dev Server",[11,1194,1195,1196,1198],{},"The setup script writes a number (10, 20, 30, etc.) to ",[22,1197,915],{},". You need to read it in your dev server config.",[11,1200,1201,1202,1205],{},"For a ",[61,1203,1204],{},"Nuxt"," project, create a small utility:",[142,1207,1212],{"className":1208,"code":1209,"filename":1210,"language":1211,"meta":147,"style":147},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nexport function getPortOffset(configDir: string): number {\n  try {\n    return parseInt(\n      readFileSync(\n        resolve(configDir, '..\u002F..\u002F.worktree-port-offset'),\n        'utf-8'\n      ).trim()\n    );\n  } catch {\n    return 0; \u002F\u002F Main repo — no offset\n  }\n}\n","scripts\u002Fworktree\u002Fport-offset.ts","typescript",[22,1213,1214,1241,1263,1267,1298,1305,1316,1323,1347,1358,1372,1379,1389,1401,1405],{"__ignoreMap":147},[151,1215,1216,1219,1222,1225,1228,1231,1233,1236,1238],{"class":153,"line":154},[151,1217,1218],{"class":344},"import",[151,1220,1221],{"class":246}," {",[151,1223,1224],{"class":242}," readFileSync",[151,1226,1227],{"class":246}," }",[151,1229,1230],{"class":344}," from",[151,1232,282],{"class":246},[151,1234,1235],{"class":230},"node:fs",[151,1237,279],{"class":246},[151,1239,1240],{"class":246},";\n",[151,1242,1243,1245,1247,1250,1252,1254,1256,1259,1261],{"class":153,"line":161},[151,1244,1218],{"class":344},[151,1246,1221],{"class":246},[151,1248,1249],{"class":242}," resolve",[151,1251,1227],{"class":246},[151,1253,1230],{"class":344},[151,1255,282],{"class":246},[151,1257,1258],{"class":230},"node:path",[151,1260,279],{"class":246},[151,1262,1240],{"class":246},[151,1264,1265],{"class":153,"line":168},[151,1266,220],{"emptyLinePlaceholder":219},[151,1268,1269,1272,1275,1278,1281,1285,1287,1290,1293,1296],{"class":153,"line":174},[151,1270,1271],{"class":344},"export",[151,1273,1274],{"class":955}," function",[151,1276,1277],{"class":226}," getPortOffset",[151,1279,1280],{"class":246},"(",[151,1282,1284],{"class":1283},"sHdIc","configDir",[151,1286,54],{"class":246},[151,1288,1289],{"class":164}," string",[151,1291,1292],{"class":246},"):",[151,1294,1295],{"class":164}," number",[151,1297,963],{"class":246},[151,1299,1300,1303],{"class":153,"line":223},[151,1301,1302],{"class":344},"  try",[151,1304,963],{"class":246},[151,1306,1307,1310,1313],{"class":153,"line":234},[151,1308,1309],{"class":344},"    return",[151,1311,1312],{"class":226}," parseInt",[151,1314,1315],{"class":1008},"(\n",[151,1317,1318,1321],{"class":153,"line":239},[151,1319,1320],{"class":226},"      readFileSync",[151,1322,1315],{"class":1008},[151,1324,1325,1328,1330,1332,1335,1337,1340,1342,1345],{"class":153,"line":291},[151,1326,1327],{"class":226},"        resolve",[151,1329,1280],{"class":1008},[151,1331,1284],{"class":242},[151,1333,1334],{"class":246},",",[151,1336,282],{"class":246},[151,1338,1339],{"class":230},"..\u002F..\u002F.worktree-port-offset",[151,1341,279],{"class":246},[151,1343,1344],{"class":1008},")",[151,1346,1023],{"class":246},[151,1348,1349,1352,1355],{"class":153,"line":309},[151,1350,1351],{"class":246},"        '",[151,1353,1354],{"class":230},"utf-8",[151,1356,1357],{"class":246},"'\n",[151,1359,1360,1363,1366,1369],{"class":153,"line":330},[151,1361,1362],{"class":1008},"      )",[151,1364,1365],{"class":246},".",[151,1367,1368],{"class":226},"trim",[151,1370,1371],{"class":1008},"()\n",[151,1373,1374,1377],{"class":153,"line":335},[151,1375,1376],{"class":1008},"    )",[151,1378,1240],{"class":246},[151,1380,1381,1384,1387],{"class":153,"line":341},[151,1382,1383],{"class":246},"  }",[151,1385,1386],{"class":344}," catch",[151,1388,963],{"class":246},[151,1390,1391,1393,1396,1398],{"class":153,"line":371},[151,1392,1309],{"class":344},[151,1394,1395],{"class":377}," 0",[151,1397,702],{"class":246},[151,1399,1400],{"class":157}," \u002F\u002F Main repo — no offset\n",[151,1402,1403],{"class":153,"line":381},[151,1404,1150],{"class":246},[151,1406,1407],{"class":153,"line":387},[151,1408,1155],{"class":246},[11,1410,1411,1412,54],{},"Then in your ",[22,1413,1414],{},"nuxt.config.ts",[142,1416,1418],{"className":1208,"code":1417,"filename":1414,"language":1211,"meta":147,"style":147},"import { getPortOffset } from '.\u002Fscripts\u002Fworktree\u002Fport-offset';\n\nexport default defineNuxtConfig({\n  devServer: {\n    port: 3000 + getPortOffset(import.meta.dirname),\n  },\n  \u002F\u002F ... rest of your config\n});\n",[22,1419,1420,1441,1445,1459,1468,1498,1502,1507],{"__ignoreMap":147},[151,1421,1422,1424,1426,1428,1430,1432,1434,1437,1439],{"class":153,"line":154},[151,1423,1218],{"class":344},[151,1425,1221],{"class":246},[151,1427,1277],{"class":242},[151,1429,1227],{"class":246},[151,1431,1230],{"class":344},[151,1433,282],{"class":246},[151,1435,1436],{"class":230},".\u002Fscripts\u002Fworktree\u002Fport-offset",[151,1438,279],{"class":246},[151,1440,1240],{"class":246},[151,1442,1443],{"class":153,"line":161},[151,1444,220],{"emptyLinePlaceholder":219},[151,1446,1447,1449,1452,1455,1457],{"class":153,"line":168},[151,1448,1271],{"class":344},[151,1450,1451],{"class":344}," default",[151,1453,1454],{"class":226}," defineNuxtConfig",[151,1456,1280],{"class":242},[151,1458,947],{"class":246},[151,1460,1461,1464,1466],{"class":153,"line":174},[151,1462,1463],{"class":1008},"  devServer",[151,1465,54],{"class":246},[151,1467,963],{"class":246},[151,1469,1470,1473,1475,1478,1480,1482,1484,1486,1488,1491,1493,1496],{"class":153,"line":223},[151,1471,1472],{"class":1008},"    port",[151,1474,54],{"class":246},[151,1476,1477],{"class":377}," 3000",[151,1479,812],{"class":246},[151,1481,1277],{"class":226},[151,1483,1280],{"class":242},[151,1485,1218],{"class":344},[151,1487,1365],{"class":246},[151,1489,1490],{"class":242},"meta",[151,1492,1365],{"class":246},[151,1494,1495],{"class":242},"dirname)",[151,1497,1023],{"class":246},[151,1499,1500],{"class":153,"line":234},[151,1501,1110],{"class":246},[151,1503,1504],{"class":153,"line":239},[151,1505,1506],{"class":157},"  \u002F\u002F ... rest of your config\n",[151,1508,1509,1512,1514],{"class":153,"line":291},[151,1510,1511],{"class":246},"}",[151,1513,1344],{"class":242},[151,1515,1240],{"class":246},[11,1517,1518,1519,1522,1523,1526,1527,1530,1531,54],{},"For ",[61,1520,1521],{},"Next.js",", you can do the same thing in your ",[22,1524,1525],{},"next.config.js"," or just read the file in a ",[22,1528,1529],{},"dev"," script in ",[22,1532,1533],{},"package.json",[142,1535,1537],{"className":938,"code":1536,"language":940,"meta":147,"style":147},"{\n  \"scripts\": {\n    \"dev\": \"OFFSET=$(cat .worktree-port-offset 2>\u002Fdev\u002Fnull || echo 0) && next dev -p $((3000 + OFFSET))\"\n  }\n}\n",[22,1538,1539,1543,1556,1573,1577],{"__ignoreMap":147},[151,1540,1541],{"class":153,"line":154},[151,1542,947],{"class":246},[151,1544,1545,1547,1550,1552,1554],{"class":153,"line":161},[151,1546,952],{"class":246},[151,1548,1549],{"class":955},"scripts",[151,1551,318],{"class":246},[151,1553,54],{"class":246},[151,1555,963],{"class":246},[151,1557,1558,1560,1562,1564,1566,1568,1571],{"class":153,"line":168},[151,1559,968],{"class":246},[151,1561,1529],{"class":164},[151,1563,318],{"class":246},[151,1565,54],{"class":246},[151,1567,354],{"class":246},[151,1569,1570],{"class":230},"OFFSET=$(cat .worktree-port-offset 2>\u002Fdev\u002Fnull || echo 0) && next dev -p $((3000 + OFFSET))",[151,1572,327],{"class":246},[151,1574,1575],{"class":153,"line":174},[151,1576,1150],{"class":246},[151,1578,1579],{"class":153,"line":223},[151,1580,1155],{"class":246},[11,1582,1518,1583,1586],{},[61,1584,1585],{},"Vite"," projects:",[142,1588,1591],{"className":1208,"code":1589,"filename":1590,"language":1211,"meta":147,"style":147},"import { getPortOffset } from '.\u002Fscripts\u002Fworktree\u002Fport-offset';\n\nexport default defineConfig({\n  server: {\n    port: 5173 + getPortOffset(__dirname),\n  },\n});\n","vite.config.ts",[22,1592,1593,1613,1617,1630,1639,1657,1661],{"__ignoreMap":147},[151,1594,1595,1597,1599,1601,1603,1605,1607,1609,1611],{"class":153,"line":154},[151,1596,1218],{"class":344},[151,1598,1221],{"class":246},[151,1600,1277],{"class":242},[151,1602,1227],{"class":246},[151,1604,1230],{"class":344},[151,1606,282],{"class":246},[151,1608,1436],{"class":230},[151,1610,279],{"class":246},[151,1612,1240],{"class":246},[151,1614,1615],{"class":153,"line":161},[151,1616,220],{"emptyLinePlaceholder":219},[151,1618,1619,1621,1623,1626,1628],{"class":153,"line":168},[151,1620,1271],{"class":344},[151,1622,1451],{"class":344},[151,1624,1625],{"class":226}," defineConfig",[151,1627,1280],{"class":242},[151,1629,947],{"class":246},[151,1631,1632,1635,1637],{"class":153,"line":174},[151,1633,1634],{"class":1008},"  server",[151,1636,54],{"class":246},[151,1638,963],{"class":246},[151,1640,1641,1643,1645,1648,1650,1652,1655],{"class":153,"line":223},[151,1642,1472],{"class":1008},[151,1644,54],{"class":246},[151,1646,1647],{"class":377}," 5173",[151,1649,812],{"class":246},[151,1651,1277],{"class":226},[151,1653,1654],{"class":242},"(__dirname)",[151,1656,1023],{"class":246},[151,1658,1659],{"class":153,"line":234},[151,1660,1110],{"class":246},[151,1662,1663,1665,1667],{"class":153,"line":239},[151,1664,1511],{"class":246},[151,1666,1344],{"class":242},[151,1668,1240],{"class":246},[44,1670,1672],{"id":1671},"step-5-update-gitignore","Step 5: Update .gitignore",[11,1674,1675,1676,54],{},"Add the generated port offset file to ",[22,1677,53],{},[142,1679,1681],{"className":144,"code":1680,"filename":53,"language":146,"meta":147,"style":147},".worktree-port-offset\n",[22,1682,1683],{"__ignoreMap":147},[151,1684,1685],{"class":153,"line":154},[151,1686,1680],{"class":164},[44,1688,1690],{"id":1689},"how-it-all-works-together","How It All Works Together",[11,1692,1693],{},"Here's the flow when Claude Code creates a worktree:",[15,1695,1696,1699,1706,1711,1720],{},[18,1697,1698],{},"Claude Code creates a new worktree directory with a fresh branch",[18,1700,1701,1703,1704,1344],{},[22,1702,127],{}," is automatically symlinked (from ",[22,1705,1178],{},[18,1707,1163,1708,1710],{},[22,1709,123],{}," hook fires and detects we're in a worktree",[18,1712,1713,1716,1717,1719],{},[22,1714,1715],{},"setup.sh"," runs — symlinks all ",[22,1718,24],{}," files and generates a port offset",[18,1721,1722,1723,1725],{},"When you run ",[22,1724,82],{},", the dev server starts on a unique port (e.g., 3010 instead of 3000)",[11,1727,1728,1729,1365],{},"No manual steps. No port collisions. No missing environment variables. No waiting for ",[22,1730,31],{},[44,1732,1734],{"id":1733},"multiple-apps-in-a-monorepo","Multiple Apps in a Monorepo",[11,1736,1737],{},"If your project has multiple apps (say, a frontend, an API, and an admin panel), you can assign each a base port and they all shift by the same offset:",[1739,1740,1741,1760],"table",{},[1742,1743,1744],"thead",{},[1745,1746,1747,1751,1754,1757],"tr",{},[1748,1749,1750],"th",{},"App",[1748,1752,1753],{},"Main Repo",[1748,1755,1756],{},"Worktree (+10)",[1748,1758,1759],{},"Worktree (+20)",[1761,1762,1763,1778,1792],"tbody",{},[1745,1764,1765,1769,1772,1775],{},[1766,1767,1768],"td",{},"Frontend",[1766,1770,1771],{},"3000",[1766,1773,1774],{},"3010",[1766,1776,1777],{},"3020",[1745,1779,1780,1783,1786,1789],{},[1766,1781,1782],{},"API",[1766,1784,1785],{},"3001",[1766,1787,1788],{},"3011",[1766,1790,1791],{},"3021",[1745,1793,1794,1797,1800,1803],{},[1766,1795,1796],{},"Admin",[1766,1798,1799],{},"3002",[1766,1801,1802],{},"3012",[1766,1804,1805],{},"3022",[11,1807,1808,1809,1812],{},"Just use the same ",[22,1810,1811],{},"getPortOffset()"," function in each app's config with a different base port.",[44,1814,1816],{"id":1815},"summary","Summary",[11,1818,1819],{},"The full setup is just four files:",[56,1821,1822,1827,1832,1837],{},[18,1823,1824,1826],{},[22,1825,103],{}," — list of files to symlink",[18,1828,1829,1831],{},[22,1830,194],{}," — symlink + port offset script",[18,1833,1834,1836],{},[22,1835,1210],{}," — utility to read the offset",[18,1838,1839,1841],{},[22,1840,934],{}," — hooks and worktree config",[11,1843,1844],{},"Once it's in place, every worktree Claude Code creates is ready to go immediately. You never have to think about env files, node_modules, or ports again.",[11,1846,1847],{},"Let me know if you run into any issues ✌️",[1849,1850,1851],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":147,"searchDepth":161,"depth":161,"links":1853},[1854,1855,1856,1857,1858,1859,1860,1861,1862,1863],{"id":46,"depth":161,"text":47},{"id":89,"depth":161,"text":90},{"id":130,"depth":161,"text":131},{"id":187,"depth":161,"text":188},{"id":927,"depth":161,"text":928},{"id":1191,"depth":161,"text":1192},{"id":1671,"depth":161,"text":1672},{"id":1689,"depth":161,"text":1690},{"id":1733,"depth":161,"text":1734},{"id":1815,"depth":161,"text":1816},"2026-04-02","How to automate your Claude Code worktree setup so that environment files are symlinked, node_modules are shared, and dev server ports don't collide — all without manual intervention.","md","\u002Fimg\u002Fclaude-code-worktree-setup.jpg",{},"\u002Fblog\u002Fautomating-claude-code-worktree-setup-env-node-modules-and-ports",{"title":5,"description":1865},"blog\u002Fautomating-claude-code-worktree-setup-env-node-modules-and-ports",[1873],"Web Development","-A72O94EpGFs2VshrpSioIOFJDZImUzYV-n8OnY9ICE",[1876,1881],{"title":1877,"path":1878,"stem":1879,"description":1880,"children":-1},"AngularJS POST request with PHP (Creating login form)","\u002Fblog\u002Fangularjs-post-request-php-creating-login-form-309","blog\u002Fangularjs-post-request-php-creating-login-form-309","In this article we will see how we can make a POST request inside AngularJS using PHP while creating a Login Form",{"title":1882,"path":1883,"stem":1884,"description":1885,"children":-1},"Best free wordpress plugins for wordpress theme developers & How to use them","\u002Fblog\u002Fbest-free-wordpress-plugins-wordpress-theme-developers-339","blog\u002Fbest-free-wordpress-plugins-wordpress-theme-developers-339","In this article we will see take a look at some of the best free wordpress plugins for theme developers",1775568345648]