[{"data":1,"prerenderedAt":3440},["ShallowReactive",2],{"footer-articles":3,"/blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js":1451,"related-/blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js":1471},[4,51,85,116],{"id":5,"title":6,"body":7,"category":40,"date":41,"description":13,"draft":42,"extension":43,"image":44,"meta":45,"navigation":46,"path":47,"seo":48,"stem":49,"tighten":46,"__hash__":50},"blog/blog/web-workers-a-second-thread-for-your-javascript.md","Web Workers: A Second Thread for Your JavaScript",{"type":8,"value":9,"toc":36},"minimark",[10,14,25,28],[11,12,13],"p",{},"JavaScript runs on a single thread. Give it a heavy task, like resizing a large photo before uploading it, and the page freezes until it finishes.",[11,15,16,17,24],{},"Web Workers solve this by running that work on a second thread. In this article for ",[18,19,23],"a",{"href":20,"rel":21},"https://tighten.com",[22],"nofollow","Tighten",", I cover how they work, their two main limitations, how they differ from Service Workers, and how to use one to resize and compress images in the browser before they reach your server.",[11,26,27],{},"Read the full article here:",[29,30,33],"article-preview",{"image":31,"link":32,"title":6},"https://tighten.com/assets/images/insights/web-workers-a-second-thread-for-your-javascript-preview.jpg","https://tighten.com/insights/web-workers-a-second-thread-for-your-javascript/",[11,34,35],{},"Suppose you have a web app that lets users upload photos. Cool! But there's a catch: photos straight from a phone camera can easily be around 8 MB each. On a weak mobile connection, uploading the full-size file can be painfully slow for them, and expensive for you.",{"title":37,"searchDepth":38,"depth":38,"links":39},"",2,[],"JavaScript","2026-09-21",false,"md","/img/blog/web-workers-a-second-thread-for-your-javascript.png",{},true,"/blog/web-workers-a-second-thread-for-your-javascript",{"title":6,"description":13},"blog/web-workers-a-second-thread-for-your-javascript","y3SUXoJPmVOBMKdgNEzLNSaJPaSGkhKMQueHzuujzl0",{"id":52,"title":53,"body":54,"category":77,"date":78,"description":58,"draft":42,"extension":43,"image":79,"meta":80,"navigation":46,"path":81,"seo":82,"stem":83,"tighten":46,"__hash__":84},"blog/blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js.md","Run AI in the Browser: A Practical Guide to Transformers.js",{"type":8,"value":55,"toc":75},[56,59,66,68],[11,57,58],{},"Transformers.js lets you run open source AI models directly in the browser. No API keys, no server round trips, and it keeps working offline once the model is cached.",[11,60,61,62,65],{},"I wrote a guide for ",[18,63,23],{"href":20,"rel":64},[22]," on how it works, which browsers support it, what to expect from small models, and how to build a grammar checker that runs entirely on the user's device.",[11,67,27],{},[29,69,72],{"image":70,"link":71,"title":53},"https://tighten.com/assets/images/insights/run-ai-in-the-browser-a-practical-guide-to-transformers-js-preview.jpg","https://tighten.com/insights/run-ai-in-the-browser-a-practical-guide-to-transformers-js/",[11,73,74],{},"Imagine you need to integrate AI into your web app. Let's say you want to quickly translate text. Preferably, you'd like to use something free and open source (you don't want any surprise bills at the end of the month). One more thing: people use your app while traveling, so it must continue working when they have poor connections or no connection at all.",{"title":37,"searchDepth":38,"depth":38,"links":76},[],"AI","2026-09-03","/img/blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js.png",{},"/blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js",{"title":53,"description":58},"blog/run-ai-in-the-browser-a-practical-guide-to-transformers-js","x_41mO5AOnw12R9hYDKPz_VfUPUqdnopJv_EEqtOxCM",{"id":86,"title":87,"body":88,"category":77,"date":109,"description":92,"draft":42,"extension":43,"image":110,"meta":111,"navigation":46,"path":112,"seo":113,"stem":114,"tighten":46,"__hash__":115},"blog/blog/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk.md","Build Your Own AI-Powered Slack Bot with the Laravel AI SDK",{"type":8,"value":89,"toc":107},[90,93,96,103],[11,91,92],{},"You can build your own AI teammate and talk to it via Slack! 🤖",[11,94,95],{},"Sure, you can use a service, but building your own means you own the memory, choose the model, and can tweak it to fit your needs.",[11,97,98,99,102],{},"Learn how in my latest article for ",[18,100,23],{"href":20,"rel":101},[22]," (Spoiler alert: Laravel AI SDK 🔥)",[29,104],{"image":105,"link":106,"title":87},"https://tighten.com/assets/images/insights/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk-preview.jpg","https://tighten.com/insights/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk/",{"title":37,"searchDepth":38,"depth":38,"links":108},[],"2026-08-26","/img/blog/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk.png",{},"/blog/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk",{"title":87,"description":92},"blog/build-your-own-ai-powered-slack-bot-with-the-laravel-ai-sdk","7tTbzmSWADzFKiLWDjyF_KR0vwbNimwP82vZeL1fVFE",{"id":117,"title":118,"body":119,"category":77,"date":1443,"description":1444,"draft":42,"extension":43,"image":1445,"meta":1446,"navigation":46,"path":1447,"seo":1448,"stem":1449,"tighten":42,"__hash__":1450},"blog/blog/climail-the-email-client-for-your-agent.md","climail: The Email Client for Your Agent",{"type":8,"value":120,"toc":1427},[121,131,151,154,163,166,171,182,187,193,248,257,261,264,278,300,304,307,322,577,584,599,603,617,620,624,627,641,644,881,885,888,1130,1140,1156,1160,1166,1254,1260,1275,1278,1300,1310,1331,1335,1338,1341,1344,1353,1360,1363,1369,1385,1389,1392,1410,1414,1420,1423],[11,122,123],{},[124,125,126,130],"strong",{},[127,128,129],"code",{},"climail"," is a clanker-friendly command line email client.",[132,133,134,138,145,148],"ul",{},[135,136,137],"li",{},"📬 Read, write & organize emails",[135,139,140,141,144],{},"⚡ Run instantly with ",[127,142,143],{},"npx"," (no install)",[135,146,147],{},"🔐 IMAP + SMTP",[135,149,150],{},"💌 Gmail ready",[11,152,153],{},"Built for agents 🤖 (and humans, too!)",[11,155,156],{},[157,158],"video",{"src":159,"poster":160,"controls":46,"playsInline":46,"preload":161,"style":162},"https://github.com/user-attachments/assets/192c5f2f-4456-49e3-9851-2b9b5c797a44","/img/blog/give-your-agent-an-inbox-with-climail.png","metadata","width:100%;border:0;border-radius:16px;margin:1.5rem 0;display:block",[11,164,165],{},"It's a minimalist email client that lives in your terminal. Every command hands back clean JSON, so an AI agent can read your inbox as easily as you can.",[167,168,170],"h2",{"id":169},"features","Features",[11,172,173,174,176,177,181],{},"There isn't much to ",[127,175,129],{},", and that's on purpose. It does a handful of things, and it tries to do each of them in the ",[178,179,180],"em",{},"least"," surprising way possible.",[183,184,186],"h3",{"id":185},"_1-just-npx","1) Just npx",[11,188,189,190,192],{},"There's nothing to install and no process to keep alive. It's just an ",[127,191,143],{}," command that uses IMAP and SMTP under the hood.",[194,195,199],"pre",{"className":196,"code":197,"language":198,"meta":37,"style":37},"language-bash shiki shiki-themes monokai","npx climail list --unread\nnpx climail mark 167 read\nnpx climail archive 167\n","bash",[127,200,201,220,235],{"__ignoreMap":37},[202,203,206,209,213,216],"span",{"class":204,"line":205},"line",1,[202,207,143],{"class":208},"sHkqI",[202,210,212],{"class":211},"s_Ekj"," climail",[202,214,215],{"class":211}," list",[202,217,219],{"class":218},"s7s5_"," --unread\n",[202,221,222,224,226,229,232],{"class":204,"line":38},[202,223,143],{"class":208},[202,225,212],{"class":211},[202,227,228],{"class":211}," mark",[202,230,231],{"class":218}," 167",[202,233,234],{"class":211}," read\n",[202,236,238,240,242,245],{"class":204,"line":237},3,[202,239,143],{"class":208},[202,241,212],{"class":211},[202,243,244],{"class":211}," archive",[202,246,247],{"class":218}," 167\n",[11,249,250,251,253,254,256],{},"If you can run ",[127,252,143],{},", you can run ",[127,255,129],{},": on your laptop, on a server, everywhere.",[183,258,260],{"id":259},"_2-configure-it-in-a-minute","2) Configure It in a Minute",[11,262,263],{},"Run the setup wizard and answer a few questions:",[194,265,267],{"className":196,"code":266,"language":198,"meta":37,"style":37},"npx climail init\n",[127,268,269],{"__ignoreMap":37},[202,270,271,273,275],{"class":204,"line":205},[202,272,143],{"class":208},[202,274,212],{"class":211},[202,276,277],{"class":211}," init\n",[11,279,280,281,284,285,288,289,291,292,295,296,299],{},"It asks where to save the config: ",[124,282,283],{},"global"," (",[127,286,287],{},"~/.config/climail.conf",", which lets ",[127,290,129],{}," work from any directory, no per-project setup) or ",[124,293,294],{},"local"," (a ",[127,297,298],{},"climail.conf"," in the current directory, handy when a project has its own mailbox). Pick global and you're done. We'll look at the file itself further down.",[183,301,303],{"id":302},"_3-all-commands-return-json","3) All Commands Return JSON",[11,305,306],{},"Every command prints clean JSON to stdout, so there's nothing to scrape and no columns to line up. Ask for your unread mail:",[194,308,310],{"className":196,"code":309,"language":198,"meta":37,"style":37},"npx climail list --unread\n",[127,311,312],{"__ignoreMap":37},[202,313,314,316,318,320],{"class":204,"line":205},[202,315,143],{"class":208},[202,317,212],{"class":211},[202,319,215],{"class":211},[202,321,219],{"class":218},[194,323,327],{"className":324,"code":325,"language":326,"meta":37,"style":37},"language-json shiki shiki-themes monokai","{\n  \"ok\": true,\n  \"mailbox\": \"INBOX\",\n  \"total\": 2481,\n  \"returned\": 2,\n  \"messages\": [\n    {\n      \"seq\": 2480,\n      \"uid\": 166,\n      \"date\": \"2026-07-12T09:03:11.000Z\",\n      \"from\": \"newsletter@somesaas.io\",\n      \"subject\": \"🚀 10 game-changing tips\",\n      \"unread\": true\n    },\n    {\n      \"seq\": 2481,\n      \"uid\": 167,\n      \"date\": \"2026-07-12T09:41:55.000Z\",\n      \"from\": \"sarah@acme.co\",\n      \"subject\": \"Export button throws a 500\",\n      \"unread\": true\n    }\n  ]\n}\n","json",[127,328,329,335,350,363,376,389,398,404,417,430,443,456,469,480,486,491,502,514,526,538,550,559,565,571],{"__ignoreMap":37},[202,330,331],{"class":204,"line":205},[202,332,334],{"class":333},"sCdxs","{\n",[202,336,337,341,344,347],{"class":204,"line":38},[202,338,340],{"class":339},"sOx1s","  \"ok\"",[202,342,343],{"class":333},": ",[202,345,346],{"class":218},"true",[202,348,349],{"class":333},",\n",[202,351,352,355,357,361],{"class":204,"line":237},[202,353,354],{"class":339},"  \"mailbox\"",[202,356,343],{"class":333},[202,358,360],{"class":359},"susgL","\"INBOX\"",[202,362,349],{"class":333},[202,364,366,369,371,374],{"class":204,"line":365},4,[202,367,368],{"class":339},"  \"total\"",[202,370,343],{"class":333},[202,372,373],{"class":218},"2481",[202,375,349],{"class":333},[202,377,379,382,384,387],{"class":204,"line":378},5,[202,380,381],{"class":339},"  \"returned\"",[202,383,343],{"class":333},[202,385,386],{"class":218},"2",[202,388,349],{"class":333},[202,390,392,395],{"class":204,"line":391},6,[202,393,394],{"class":339},"  \"messages\"",[202,396,397],{"class":333},": [\n",[202,399,401],{"class":204,"line":400},7,[202,402,403],{"class":333},"    {\n",[202,405,407,410,412,415],{"class":204,"line":406},8,[202,408,409],{"class":339},"      \"seq\"",[202,411,343],{"class":333},[202,413,414],{"class":218},"2480",[202,416,349],{"class":333},[202,418,420,423,425,428],{"class":204,"line":419},9,[202,421,422],{"class":339},"      \"uid\"",[202,424,343],{"class":333},[202,426,427],{"class":218},"166",[202,429,349],{"class":333},[202,431,433,436,438,441],{"class":204,"line":432},10,[202,434,435],{"class":339},"      \"date\"",[202,437,343],{"class":333},[202,439,440],{"class":359},"\"2026-07-12T09:03:11.000Z\"",[202,442,349],{"class":333},[202,444,446,449,451,454],{"class":204,"line":445},11,[202,447,448],{"class":339},"      \"from\"",[202,450,343],{"class":333},[202,452,453],{"class":359},"\"newsletter@somesaas.io\"",[202,455,349],{"class":333},[202,457,459,462,464,467],{"class":204,"line":458},12,[202,460,461],{"class":339},"      \"subject\"",[202,463,343],{"class":333},[202,465,466],{"class":359},"\"🚀 10 game-changing tips\"",[202,468,349],{"class":333},[202,470,472,475,477],{"class":204,"line":471},13,[202,473,474],{"class":339},"      \"unread\"",[202,476,343],{"class":333},[202,478,479],{"class":218},"true\n",[202,481,483],{"class":204,"line":482},14,[202,484,485],{"class":333},"    },\n",[202,487,489],{"class":204,"line":488},15,[202,490,403],{"class":333},[202,492,494,496,498,500],{"class":204,"line":493},16,[202,495,409],{"class":339},[202,497,343],{"class":333},[202,499,373],{"class":218},[202,501,349],{"class":333},[202,503,505,507,509,512],{"class":204,"line":504},17,[202,506,422],{"class":339},[202,508,343],{"class":333},[202,510,511],{"class":218},"167",[202,513,349],{"class":333},[202,515,517,519,521,524],{"class":204,"line":516},18,[202,518,435],{"class":339},[202,520,343],{"class":333},[202,522,523],{"class":359},"\"2026-07-12T09:41:55.000Z\"",[202,525,349],{"class":333},[202,527,529,531,533,536],{"class":204,"line":528},19,[202,530,448],{"class":339},[202,532,343],{"class":333},[202,534,535],{"class":359},"\"sarah@acme.co\"",[202,537,349],{"class":333},[202,539,541,543,545,548],{"class":204,"line":540},20,[202,542,461],{"class":339},[202,544,343],{"class":333},[202,546,547],{"class":359},"\"Export button throws a 500\"",[202,549,349],{"class":333},[202,551,553,555,557],{"class":204,"line":552},21,[202,554,474],{"class":339},[202,556,343],{"class":333},[202,558,479],{"class":218},[202,560,562],{"class":204,"line":561},22,[202,563,564],{"class":333},"    }\n",[202,566,568],{"class":204,"line":567},23,[202,569,570],{"class":333},"  ]\n",[202,572,574],{"class":204,"line":573},24,[202,575,576],{"class":333},"}\n",[11,578,579,580,583],{},"Every message carries a ",[127,581,582],{},"uid",", and that little number is the ID you pass to every other command.",[11,585,586,587,590,591,594,595,598],{},"It's the same idea as ",[127,588,589],{},"gh"," or ",[127,592,593],{},"kubectl -o json",": a small tool that pipes cleanly into the next thing, whether that next thing is ",[127,596,597],{},"jq"," or an agent.",[183,600,602],{"id":601},"_4-gmail-ready-out-of-the-box","4) Gmail-Ready, Out of the Box",[11,604,605,606,608,609,612,613,616],{},"Labels, archiving, and stars are the operations you actually perform on a Gmail inbox, so ",[127,607,129],{}," speaks them natively. Labeling applies a real Gmail label, archiving drops the ",[127,610,611],{},"Inbox"," label instead of trashing the message (exactly like the archive button in the web UI), and starring toggles ",[127,614,615],{},"\\Flagged",".",[11,618,619],{},"But if you are not using a Google email, don't worry: any email works, as long as you have the IMAP and SMTP credentials.",[167,621,623],{"id":622},"usage","Usage",[11,625,626],{},"Every interaction follows the same shape:",[194,628,630],{"className":196,"code":629,"language":198,"meta":37,"style":37},"npx climail ⟪command⟫\n",[127,631,632],{"__ignoreMap":37},[202,633,634,636,638],{"class":204,"line":205},[202,635,143],{"class":208},[202,637,212],{"class":211},[202,639,640],{"class":211}," ⟪command⟫\n",[11,642,643],{},"Here's the full set of commands. It looks like a lot, but you'll live in five or six of them and reach for the rest when you need them.",[645,646,647,660],"table",{},[648,649,650],"thead",{},[651,652,653,657],"tr",{},[654,655,656],"th",{},"Command",[654,658,659],{},"What it does",[661,662,663,676,696,733,747,762,772,803,819,833,848,861,871],"tbody",{},[651,664,665,671],{},[666,667,668],"td",{},[127,669,670],{},"init",[666,672,673,674,616],{},"Setup wizard for your IMAP credentials. Writes ",[127,675,287],{},[651,677,678,683],{},[666,679,680],{},[127,681,682],{},"list",[666,684,685,686,689,690,689,693,616],{},"List recent messages as JSON, each with its UID. Flags: ",[127,687,688],{},"--count \u003Cn>",", ",[127,691,692],{},"--unread",[127,694,695],{},"--mailbox \u003Cname>",[651,697,698,703],{},[666,699,700],{},[127,701,702],{},"search",[666,704,705,706,689,709,689,712,689,715,689,718,689,721,689,724,689,727,689,729,689,731,616],{},"Search a mailbox. Flags: ",[127,707,708],{},"--from",[127,710,711],{},"--to",[127,713,714],{},"--subject",[127,716,717],{},"--body",[127,719,720],{},"--text",[127,722,723],{},"--since",[127,725,726],{},"--before",[127,728,692],{},[127,730,688],{},[127,732,695],{},[651,734,735,740],{},[666,736,737],{},[127,738,739],{},"labels",[666,741,742,743,746],{},"List available labels/folders. Flag: ",[127,744,745],{},"--counts"," adds message counts per label.",[651,748,749,754],{},[666,750,751],{},[127,752,753],{},"read \u003Cuid>",[666,755,756,757,689,760,616],{},"Fetch one message: parsed text, HTML, and attachment metadata. Flags: ",[127,758,759],{},"--save-attachments \u003Cdir>",[127,761,695],{},[651,763,764,769],{},[666,765,766],{},[127,767,768],{},"label \u003Cuid> \u003Cname>",[666,770,771],{},"Apply a Gmail label, creating it if needed.",[651,773,774,779],{},[666,775,776],{},[127,777,778],{},"mark \u003Cuid> \u003Caction>",[666,780,781,782,785,786,689,789,785,792,795,796,785,799,802],{},"Set flags: ",[127,783,784],{},"read","/",[127,787,788],{},"unread",[127,790,791],{},"flag",[127,793,794],{},"unflag"," (aliases ",[127,797,798],{},"star",[127,800,801],{},"unstar",").",[651,804,805,810],{},[666,806,807],{},[127,808,809],{},"draft-reply \u003Cuid>",[666,811,812,813,689,815,818],{},"Stage a threaded reply in Drafts. Flags: ",[127,814,717],{},[127,816,817],{},"--all",". Nothing is sent.",[651,820,821,826],{},[666,822,823],{},[127,824,825],{},"send",[666,827,828,829,832],{},"Send over SMTP (needs ",[127,830,831],{},"smtp.host","): a new email, a threaded reply, or an existing draft.",[651,834,835,840],{},[666,836,837],{},[127,838,839],{},"forward \u003Cuid>",[666,841,842,843,845,846,616],{},"Forward a message over SMTP. Flag: ",[127,844,711],{}," (required), ",[127,847,717],{},[651,849,850,855],{},[666,851,852],{},[127,853,854],{},"delete \u003Cuid>",[666,856,857,858,616],{},"Delete a message (Trash on Gmail). Flag: ",[127,859,860],{},"--from \u003Cmailbox>",[651,862,863,868],{},[666,864,865],{},[127,866,867],{},"archive \u003Cuid>",[666,869,870],{},"Archive a message (drops the Inbox label on Gmail).",[651,872,873,878],{},[666,874,875],{},[127,876,877],{},"move \u003Cuid> \u003Cmailbox>",[666,879,880],{},"Move a message to another mailbox.",[183,882,884],{"id":883},"examples","Examples",[11,886,887],{},"Reading the table is one thing, but the commands click faster when you see them lined up. Here's a full triage session, top to bottom:",[194,889,891],{"className":196,"code":890,"language":198,"meta":37,"style":37},"npx climail init\nnpx climail list --unread\nnpx climail search --from boss@work.com --since 2026-06-01 --unread\nnpx climail labels --counts\nnpx climail read 167 --save-attachments ./att\nnpx climail mark 167 read\nnpx climail list --mailbox \"[Gmail]/All Mail\" --unread\nnpx climail mark 167 read --mailbox \"[Gmail]/All Mail\"\nnpx climail label 167 Triaged\nnpx climail draft-reply 167 --all --body \"Thanks, will take a look.\"\nnpx climail send --to them@example.com --reply-to 167 --all --body \"On it.\"\nnpx climail send --to a@b.com --subject \"Report\" --body \"Attached.\" --attach ./report.pdf\nnpx climail forward 167 --to colleague@work.com --body \"FYI\"\nnpx climail archive 167\n",[127,892,893,901,911,934,946,963,975,991,1008,1022,1042,1069,1099,1120],{"__ignoreMap":37},[202,894,895,897,899],{"class":204,"line":205},[202,896,143],{"class":208},[202,898,212],{"class":211},[202,900,277],{"class":211},[202,902,903,905,907,909],{"class":204,"line":38},[202,904,143],{"class":208},[202,906,212],{"class":211},[202,908,215],{"class":211},[202,910,219],{"class":218},[202,912,913,915,917,920,923,926,929,932],{"class":204,"line":237},[202,914,143],{"class":208},[202,916,212],{"class":211},[202,918,919],{"class":211}," search",[202,921,922],{"class":218}," --from",[202,924,925],{"class":211}," boss@work.com",[202,927,928],{"class":218}," --since",[202,930,931],{"class":211}," 2026-06-01",[202,933,219],{"class":218},[202,935,936,938,940,943],{"class":204,"line":365},[202,937,143],{"class":208},[202,939,212],{"class":211},[202,941,942],{"class":211}," labels",[202,944,945],{"class":218}," --counts\n",[202,947,948,950,952,955,957,960],{"class":204,"line":378},[202,949,143],{"class":208},[202,951,212],{"class":211},[202,953,954],{"class":211}," read",[202,956,231],{"class":218},[202,958,959],{"class":218}," --save-attachments",[202,961,962],{"class":211}," ./att\n",[202,964,965,967,969,971,973],{"class":204,"line":391},[202,966,143],{"class":208},[202,968,212],{"class":211},[202,970,228],{"class":211},[202,972,231],{"class":218},[202,974,234],{"class":211},[202,976,977,979,981,983,986,989],{"class":204,"line":400},[202,978,143],{"class":208},[202,980,212],{"class":211},[202,982,215],{"class":211},[202,984,985],{"class":218}," --mailbox",[202,987,988],{"class":211}," \"[Gmail]/All Mail\"",[202,990,219],{"class":218},[202,992,993,995,997,999,1001,1003,1005],{"class":204,"line":406},[202,994,143],{"class":208},[202,996,212],{"class":211},[202,998,228],{"class":211},[202,1000,231],{"class":218},[202,1002,954],{"class":211},[202,1004,985],{"class":218},[202,1006,1007],{"class":211}," \"[Gmail]/All Mail\"\n",[202,1009,1010,1012,1014,1017,1019],{"class":204,"line":419},[202,1011,143],{"class":208},[202,1013,212],{"class":211},[202,1015,1016],{"class":211}," label",[202,1018,231],{"class":218},[202,1020,1021],{"class":211}," Triaged\n",[202,1023,1024,1026,1028,1031,1033,1036,1039],{"class":204,"line":432},[202,1025,143],{"class":208},[202,1027,212],{"class":211},[202,1029,1030],{"class":211}," draft-reply",[202,1032,231],{"class":218},[202,1034,1035],{"class":218}," --all",[202,1037,1038],{"class":218}," --body",[202,1040,1041],{"class":211}," \"Thanks, will take a look.\"\n",[202,1043,1044,1046,1048,1051,1054,1057,1060,1062,1064,1066],{"class":204,"line":445},[202,1045,143],{"class":208},[202,1047,212],{"class":211},[202,1049,1050],{"class":211}," send",[202,1052,1053],{"class":218}," --to",[202,1055,1056],{"class":211}," them@example.com",[202,1058,1059],{"class":218}," --reply-to",[202,1061,231],{"class":218},[202,1063,1035],{"class":218},[202,1065,1038],{"class":218},[202,1067,1068],{"class":211}," \"On it.\"\n",[202,1070,1071,1073,1075,1077,1079,1082,1085,1088,1090,1093,1096],{"class":204,"line":458},[202,1072,143],{"class":208},[202,1074,212],{"class":211},[202,1076,1050],{"class":211},[202,1078,1053],{"class":218},[202,1080,1081],{"class":211}," a@b.com",[202,1083,1084],{"class":218}," --subject",[202,1086,1087],{"class":211}," \"Report\"",[202,1089,1038],{"class":218},[202,1091,1092],{"class":211}," \"Attached.\"",[202,1094,1095],{"class":218}," --attach",[202,1097,1098],{"class":211}," ./report.pdf\n",[202,1100,1101,1103,1105,1108,1110,1112,1115,1117],{"class":204,"line":471},[202,1102,143],{"class":208},[202,1104,212],{"class":211},[202,1106,1107],{"class":211}," forward",[202,1109,231],{"class":218},[202,1111,1053],{"class":218},[202,1113,1114],{"class":211}," colleague@work.com",[202,1116,1038],{"class":218},[202,1118,1119],{"class":211}," \"FYI\"\n",[202,1121,1122,1124,1126,1128],{"class":204,"line":482},[202,1123,143],{"class":208},[202,1125,212],{"class":211},[202,1127,244],{"class":211},[202,1129,247],{"class":218},[11,1131,1132,1133,1136,1137,1139],{},"Notice that ",[127,1134,1135],{},"draft-reply"," and ",[127,1138,825],{}," are two different commands:",[132,1141,1142,1151],{},[135,1143,1144,1146,1147,1150],{},[127,1145,1135],{}," writes a proper threaded reply, correct subject and headers, straight into your ",[124,1148,1149],{},"Drafts"," folder. Nothing leaves your account.",[135,1152,1153,1155],{},[127,1154,825],{}," is the one that actually puts mail on the wire, and it only works once you've configured SMTP. Reading your inbox and emailing people on your behalf are two very different levels of trust, so they're two different commands you opt into separately.",[167,1157,1159],{"id":1158},"configuration","Configuration",[11,1161,1162,1165],{},[127,1163,1164],{},"npx climail init"," writes the config for you, but it's a plain text file, so you can just as easily write it yourself:",[194,1167,1171],{"className":1168,"code":1169,"filename":287,"language":1170,"meta":37,"style":37},"language-dotenv shiki shiki-themes monokai","imap.host=imap.gmail.com\nimap.port=993\nimap.secure=true\nimap.username=you@gmail.com\nimap.password=\"app password here\"\n\n# Optional: only needed for the `send` command\nsmtp.host=smtp.gmail.com\nsmtp.port=465\n","dotenv",[127,1172,1173,1185,1195,1204,1214,1224,1229,1235,1244],{"__ignoreMap":37},[202,1174,1175,1178,1182],{"class":204,"line":205},[202,1176,1177],{"class":333},"imap.host",[202,1179,1181],{"class":1180},"s8I7P","=",[202,1183,1184],{"class":333},"imap.gmail.com\n",[202,1186,1187,1190,1192],{"class":204,"line":38},[202,1188,1189],{"class":333},"imap.port",[202,1191,1181],{"class":1180},[202,1193,1194],{"class":333},"993\n",[202,1196,1197,1200,1202],{"class":204,"line":237},[202,1198,1199],{"class":333},"imap.secure",[202,1201,1181],{"class":1180},[202,1203,479],{"class":333},[202,1205,1206,1209,1211],{"class":204,"line":365},[202,1207,1208],{"class":333},"imap.username",[202,1210,1181],{"class":1180},[202,1212,1213],{"class":333},"you@gmail.com\n",[202,1215,1216,1219,1221],{"class":204,"line":378},[202,1217,1218],{"class":333},"imap.password",[202,1220,1181],{"class":1180},[202,1222,1223],{"class":211},"\"app password here\"\n",[202,1225,1226],{"class":204,"line":391},[202,1227,1228],{"emptyLinePlaceholder":46},"\n",[202,1230,1231],{"class":204,"line":400},[202,1232,1234],{"class":1233},"snpHw","# Optional: only needed for the `send` command\n",[202,1236,1237,1239,1241],{"class":204,"line":406},[202,1238,831],{"class":333},[202,1240,1181],{"class":1180},[202,1242,1243],{"class":333},"smtp.gmail.com\n",[202,1245,1246,1249,1251],{"class":204,"line":419},[202,1247,1248],{"class":333},"smtp.port",[202,1250,1181],{"class":1180},[202,1252,1253],{"class":333},"465\n",[11,1255,1256,1257,1259],{},"The IMAP block is all you need to start reading. The SMTP block is optional, and its absence is exactly why ",[127,1258,825],{}," stays locked until you deliberately add it.",[1261,1262,1265],"callout",{"color":1263,"icon":1264},"#057ccd","fa6-solid:circle-info",[11,1266,1267,1268,1271,1272,1274],{},"Gmail won't let a raw IMAP client sign in with your normal account password. You need an ",[124,1269,1270],{},"app password",", a 16-character token you generate in your Google account settings. Drop that into ",[127,1273,1218],{}," and you're in.",[11,1276,1277],{},"Always keep your config file out of version control, since it holds your credentials.",[11,1279,1280,1282,1283,1286,1287,1289,1290,1292,1293,1296,1297,1299],{},[127,1281,670],{}," lets you choose where this file lives. ",[124,1284,1285],{},"Global"," puts it at ",[127,1288,287],{},", following the XDG convention, so ",[127,1291,129],{}," works from any directory. ",[124,1294,1295],{},"Local"," drops a ",[127,1298,298],{}," in the current directory, which takes precedence over the global one when you run commands from there, perfect for a project with its own inbox.",[11,1301,1302,1303,1306,1307,1309],{},"If you manage more than one email address, you can also point any command at a specific file with ",[127,1304,1305],{},"--config"," (which skips the prompt in ",[127,1308,670],{}," too):",[194,1311,1313],{"className":196,"code":1312,"language":198,"meta":37,"style":37},"npx climail list --config ~/work/mail.conf --unread\n",[127,1314,1315],{"__ignoreMap":37},[202,1316,1317,1319,1321,1323,1326,1329],{"class":204,"line":205},[202,1318,143],{"class":208},[202,1320,212],{"class":211},[202,1322,215],{"class":211},[202,1324,1325],{"class":218}," --config",[202,1327,1328],{"class":211}," ~/work/mail.conf",[202,1330,219],{"class":218},[167,1332,1334],{"id":1333},"an-email-client-for-agents","An Email Client for Agents",[11,1336,1337],{},"So that's the tool. I created it to give my agent an easy way of interacting with an inbox.",[11,1339,1340],{},"These days our agents can automate an incredible amount of our daily work. They write code, review pull requests, update documentation, and poke at APIs through a handful of little CLIs. Yet for many of us, email is still one of the last manual parts of the workflow. And that's odd, because email is exactly the kind of tedious, repetitive triage I'd love to hand off.",[11,1342,1343],{},"The problem was never the agent, it was the interface. A full mail client is a GUI, useless to a clanker. A raw IMAP library means the agent has to write code every time it wants to ask a simple question. Neither one lets you hand the agent a command and get back something it can read.",[11,1345,1346,1347,1349,1350,1352],{},"That's the gap ",[127,1348,129],{}," fills, and it's why the two boring-sounding features up top are actually the important ones: zero-install ",[127,1351,143],{}," and JSON on stdout aren't just convenient for you at the terminal. They're the exact two things an agent needs to use a tool with no help from anyone.",[11,1354,1355,1356,1359],{},"Once your agent runs ",[127,1357,1358],{},"npx climail",", it learns how to use it just by reading the help text. No MCP server to stand up, no SDK to wire in.",[11,1361,1362],{},"You can say something like:",[1364,1365,1366],"blockquote",{},[11,1367,1368],{},"Go through my unread mail. Label anything from a customer as \"Triaged\", and for the ones reporting a bug, draft a short reply letting them know we're on it.",[11,1370,1371,1372,1374,1375,1377,1378,1381,1382,1384],{},"And it will just... do it, by chaining the commands you saw above: ",[127,1373,702],{}," to find them, ",[127,1376,784],{}," to understand each one, ",[127,1379,1380],{},"label"," to file them, ",[127,1383,1135],{}," to stage the answers, checking the JSON at every step before taking the next.",[183,1386,1388],{"id":1387},"the-skill","The Skill",[11,1390,1391],{},"Do you like agent skills? We got you:",[194,1393,1395],{"className":196,"code":1394,"language":198,"meta":37,"style":37},"npx skills add nicodevs/climail\n",[127,1396,1397],{"__ignoreMap":37},[202,1398,1399,1401,1404,1407],{"class":204,"line":205},[202,1400,143],{"class":208},[202,1402,1403],{"class":211}," skills",[202,1405,1406],{"class":211}," add",[202,1408,1409],{"class":211}," nicodevs/climail\n",[167,1411,1413],{"id":1412},"in-closing","In Closing",[11,1415,1416,1417,1419],{},"I hope you enjoy ",[127,1418,129],{},". If you have any comments or questions, feel free to create an issue. And throw in a star on GitHub, if you want to help!",[11,1421,1422],{},"See you next time.",[1424,1425,1426],"style",{},"html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}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 pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .susgL, html code.shiki .susgL{--shiki-default:#CFCFC2}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .snpHw, html code.shiki .snpHw{--shiki-default:#88846F}",{"title":37,"searchDepth":38,"depth":38,"links":1428},[1429,1435,1438,1439,1442],{"id":169,"depth":38,"text":170,"children":1430},[1431,1432,1433,1434],{"id":185,"depth":237,"text":186},{"id":259,"depth":237,"text":260},{"id":302,"depth":237,"text":303},{"id":601,"depth":237,"text":602},{"id":622,"depth":38,"text":623,"children":1436},[1437],{"id":883,"depth":237,"text":884},{"id":1158,"depth":38,"text":1159},{"id":1333,"depth":38,"text":1334,"children":1440},[1441],{"id":1387,"depth":237,"text":1388},{"id":1412,"depth":38,"text":1413},"2026-07-15","A clanker-friendly command line email client. Read, write, and organize your inbox from the terminal, with clean JSON that any AI agent can pick up.","/img/blog/climail-the-email-client-for-your-agent.png",{},"/blog/climail-the-email-client-for-your-agent",{"title":118,"description":1444},"blog/climail-the-email-client-for-your-agent","jZNqP6GquUg4sN1I-ki5zP8-RDPfr-xmKo1gaEXateg",{"id":52,"title":53,"body":1452,"category":77,"date":78,"description":58,"draft":42,"extension":43,"image":79,"meta":1469,"navigation":46,"path":81,"seo":1470,"stem":83,"tighten":46,"__hash__":84},{"type":8,"value":1453,"toc":1467},[1454,1456,1461,1463],[11,1455,58],{},[11,1457,61,1458,65],{},[18,1459,23],{"href":20,"rel":1460},[22],[11,1462,27],{},[29,1464,1465],{"image":70,"link":71,"title":53},[11,1466,74],{},{"title":37,"searchDepth":38,"depth":38,"links":1468},[],{},{"title":53,"description":58},[1472,1490,2467],{"id":86,"title":87,"body":1473,"category":77,"date":109,"description":92,"draft":42,"extension":43,"image":110,"meta":1488,"navigation":46,"path":112,"seo":1489,"stem":114,"tighten":46,"__hash__":115},{"type":8,"value":1474,"toc":1486},[1475,1477,1479,1484],[11,1476,92],{},[11,1478,95],{},[11,1480,98,1481,102],{},[18,1482,23],{"href":20,"rel":1483},[22],[29,1485],{"image":105,"link":106,"title":87},{"title":37,"searchDepth":38,"depth":38,"links":1487},[],{},{"title":87,"description":92},{"id":117,"title":118,"body":1491,"category":77,"date":1443,"description":1444,"draft":42,"extension":43,"image":1445,"meta":2465,"navigation":46,"path":1447,"seo":2466,"stem":1449,"tighten":42,"__hash__":1450},{"type":8,"value":1492,"toc":2449},[1493,1499,1511,1513,1517,1519,1521,1527,1529,1533,1569,1575,1577,1579,1591,1603,1605,1607,1621,1815,1819,1827,1829,1837,1839,1841,1843,1855,1857,2031,2033,2035,2243,2249,2261,2263,2267,2335,2339,2347,2349,2363,2369,2387,2389,2391,2393,2395,2401,2405,2407,2411,2421,2423,2425,2439,2441,2445,2447],[11,1494,1495],{},[124,1496,1497,130],{},[127,1498,129],{},[132,1500,1501,1503,1507,1509],{},[135,1502,137],{},[135,1504,140,1505,144],{},[127,1506,143],{},[135,1508,147],{},[135,1510,150],{},[11,1512,153],{},[11,1514,1515],{},[157,1516],{"src":159,"poster":160,"controls":46,"playsInline":46,"preload":161,"style":162},[11,1518,165],{},[167,1520,170],{"id":169},[11,1522,173,1523,176,1525,181],{},[127,1524,129],{},[178,1526,180],{},[183,1528,186],{"id":185},[11,1530,189,1531,192],{},[127,1532,143],{},[194,1534,1535],{"className":196,"code":197,"language":198,"meta":37,"style":37},[127,1536,1537,1547,1559],{"__ignoreMap":37},[202,1538,1539,1541,1543,1545],{"class":204,"line":205},[202,1540,143],{"class":208},[202,1542,212],{"class":211},[202,1544,215],{"class":211},[202,1546,219],{"class":218},[202,1548,1549,1551,1553,1555,1557],{"class":204,"line":38},[202,1550,143],{"class":208},[202,1552,212],{"class":211},[202,1554,228],{"class":211},[202,1556,231],{"class":218},[202,1558,234],{"class":211},[202,1560,1561,1563,1565,1567],{"class":204,"line":237},[202,1562,143],{"class":208},[202,1564,212],{"class":211},[202,1566,244],{"class":211},[202,1568,247],{"class":218},[11,1570,250,1571,253,1573,256],{},[127,1572,143],{},[127,1574,129],{},[183,1576,260],{"id":259},[11,1578,263],{},[194,1580,1581],{"className":196,"code":266,"language":198,"meta":37,"style":37},[127,1582,1583],{"__ignoreMap":37},[202,1584,1585,1587,1589],{"class":204,"line":205},[202,1586,143],{"class":208},[202,1588,212],{"class":211},[202,1590,277],{"class":211},[11,1592,280,1593,284,1595,288,1597,291,1599,295,1601,299],{},[124,1594,283],{},[127,1596,287],{},[127,1598,129],{},[124,1600,294],{},[127,1602,298],{},[183,1604,303],{"id":302},[11,1606,306],{},[194,1608,1609],{"className":196,"code":309,"language":198,"meta":37,"style":37},[127,1610,1611],{"__ignoreMap":37},[202,1612,1613,1615,1617,1619],{"class":204,"line":205},[202,1614,143],{"class":208},[202,1616,212],{"class":211},[202,1618,215],{"class":211},[202,1620,219],{"class":218},[194,1622,1623],{"className":324,"code":325,"language":326,"meta":37,"style":37},[127,1624,1625,1629,1639,1649,1659,1669,1675,1679,1689,1699,1709,1719,1729,1737,1741,1745,1755,1765,1775,1785,1795,1803,1807,1811],{"__ignoreMap":37},[202,1626,1627],{"class":204,"line":205},[202,1628,334],{"class":333},[202,1630,1631,1633,1635,1637],{"class":204,"line":38},[202,1632,340],{"class":339},[202,1634,343],{"class":333},[202,1636,346],{"class":218},[202,1638,349],{"class":333},[202,1640,1641,1643,1645,1647],{"class":204,"line":237},[202,1642,354],{"class":339},[202,1644,343],{"class":333},[202,1646,360],{"class":359},[202,1648,349],{"class":333},[202,1650,1651,1653,1655,1657],{"class":204,"line":365},[202,1652,368],{"class":339},[202,1654,343],{"class":333},[202,1656,373],{"class":218},[202,1658,349],{"class":333},[202,1660,1661,1663,1665,1667],{"class":204,"line":378},[202,1662,381],{"class":339},[202,1664,343],{"class":333},[202,1666,386],{"class":218},[202,1668,349],{"class":333},[202,1670,1671,1673],{"class":204,"line":391},[202,1672,394],{"class":339},[202,1674,397],{"class":333},[202,1676,1677],{"class":204,"line":400},[202,1678,403],{"class":333},[202,1680,1681,1683,1685,1687],{"class":204,"line":406},[202,1682,409],{"class":339},[202,1684,343],{"class":333},[202,1686,414],{"class":218},[202,1688,349],{"class":333},[202,1690,1691,1693,1695,1697],{"class":204,"line":419},[202,1692,422],{"class":339},[202,1694,343],{"class":333},[202,1696,427],{"class":218},[202,1698,349],{"class":333},[202,1700,1701,1703,1705,1707],{"class":204,"line":432},[202,1702,435],{"class":339},[202,1704,343],{"class":333},[202,1706,440],{"class":359},[202,1708,349],{"class":333},[202,1710,1711,1713,1715,1717],{"class":204,"line":445},[202,1712,448],{"class":339},[202,1714,343],{"class":333},[202,1716,453],{"class":359},[202,1718,349],{"class":333},[202,1720,1721,1723,1725,1727],{"class":204,"line":458},[202,1722,461],{"class":339},[202,1724,343],{"class":333},[202,1726,466],{"class":359},[202,1728,349],{"class":333},[202,1730,1731,1733,1735],{"class":204,"line":471},[202,1732,474],{"class":339},[202,1734,343],{"class":333},[202,1736,479],{"class":218},[202,1738,1739],{"class":204,"line":482},[202,1740,485],{"class":333},[202,1742,1743],{"class":204,"line":488},[202,1744,403],{"class":333},[202,1746,1747,1749,1751,1753],{"class":204,"line":493},[202,1748,409],{"class":339},[202,1750,343],{"class":333},[202,1752,373],{"class":218},[202,1754,349],{"class":333},[202,1756,1757,1759,1761,1763],{"class":204,"line":504},[202,1758,422],{"class":339},[202,1760,343],{"class":333},[202,1762,511],{"class":218},[202,1764,349],{"class":333},[202,1766,1767,1769,1771,1773],{"class":204,"line":516},[202,1768,435],{"class":339},[202,1770,343],{"class":333},[202,1772,523],{"class":359},[202,1774,349],{"class":333},[202,1776,1777,1779,1781,1783],{"class":204,"line":528},[202,1778,448],{"class":339},[202,1780,343],{"class":333},[202,1782,535],{"class":359},[202,1784,349],{"class":333},[202,1786,1787,1789,1791,1793],{"class":204,"line":540},[202,1788,461],{"class":339},[202,1790,343],{"class":333},[202,1792,547],{"class":359},[202,1794,349],{"class":333},[202,1796,1797,1799,1801],{"class":204,"line":552},[202,1798,474],{"class":339},[202,1800,343],{"class":333},[202,1802,479],{"class":218},[202,1804,1805],{"class":204,"line":561},[202,1806,564],{"class":333},[202,1808,1809],{"class":204,"line":567},[202,1810,570],{"class":333},[202,1812,1813],{"class":204,"line":573},[202,1814,576],{"class":333},[11,1816,579,1817,583],{},[127,1818,582],{},[11,1820,586,1821,590,1823,594,1825,598],{},[127,1822,589],{},[127,1824,593],{},[127,1826,597],{},[183,1828,602],{"id":601},[11,1830,605,1831,608,1833,612,1835,616],{},[127,1832,129],{},[127,1834,611],{},[127,1836,615],{},[11,1838,619],{},[167,1840,623],{"id":622},[11,1842,626],{},[194,1844,1845],{"className":196,"code":629,"language":198,"meta":37,"style":37},[127,1846,1847],{"__ignoreMap":37},[202,1848,1849,1851,1853],{"class":204,"line":205},[202,1850,143],{"class":208},[202,1852,212],{"class":211},[202,1854,640],{"class":211},[11,1856,643],{},[645,1858,1859,1867],{},[648,1860,1861],{},[651,1862,1863,1865],{},[654,1864,656],{},[654,1866,659],{},[661,1868,1869,1879,1893,1921,1931,1943,1951,1971,1983,1993,2005,2015,2023],{},[651,1870,1871,1875],{},[666,1872,1873],{},[127,1874,670],{},[666,1876,673,1877,616],{},[127,1878,287],{},[651,1880,1881,1885],{},[666,1882,1883],{},[127,1884,682],{},[666,1886,685,1887,689,1889,689,1891,616],{},[127,1888,688],{},[127,1890,692],{},[127,1892,695],{},[651,1894,1895,1899],{},[666,1896,1897],{},[127,1898,702],{},[666,1900,705,1901,689,1903,689,1905,689,1907,689,1909,689,1911,689,1913,689,1915,689,1917,689,1919,616],{},[127,1902,708],{},[127,1904,711],{},[127,1906,714],{},[127,1908,717],{},[127,1910,720],{},[127,1912,723],{},[127,1914,726],{},[127,1916,692],{},[127,1918,688],{},[127,1920,695],{},[651,1922,1923,1927],{},[666,1924,1925],{},[127,1926,739],{},[666,1928,742,1929,746],{},[127,1930,745],{},[651,1932,1933,1937],{},[666,1934,1935],{},[127,1936,753],{},[666,1938,756,1939,689,1941,616],{},[127,1940,759],{},[127,1942,695],{},[651,1944,1945,1949],{},[666,1946,1947],{},[127,1948,768],{},[666,1950,771],{},[651,1952,1953,1957],{},[666,1954,1955],{},[127,1956,778],{},[666,1958,781,1959,785,1961,689,1963,785,1965,795,1967,785,1969,802],{},[127,1960,784],{},[127,1962,788],{},[127,1964,791],{},[127,1966,794],{},[127,1968,798],{},[127,1970,801],{},[651,1972,1973,1977],{},[666,1974,1975],{},[127,1976,809],{},[666,1978,812,1979,689,1981,818],{},[127,1980,717],{},[127,1982,817],{},[651,1984,1985,1989],{},[666,1986,1987],{},[127,1988,825],{},[666,1990,828,1991,832],{},[127,1992,831],{},[651,1994,1995,1999],{},[666,1996,1997],{},[127,1998,839],{},[666,2000,842,2001,845,2003,616],{},[127,2002,711],{},[127,2004,717],{},[651,2006,2007,2011],{},[666,2008,2009],{},[127,2010,854],{},[666,2012,857,2013,616],{},[127,2014,860],{},[651,2016,2017,2021],{},[666,2018,2019],{},[127,2020,867],{},[666,2022,870],{},[651,2024,2025,2029],{},[666,2026,2027],{},[127,2028,877],{},[666,2030,880],{},[183,2032,884],{"id":883},[11,2034,887],{},[194,2036,2037],{"className":196,"code":890,"language":198,"meta":37,"style":37},[127,2038,2039,2047,2057,2075,2085,2099,2111,2125,2141,2153,2169,2191,2215,2233],{"__ignoreMap":37},[202,2040,2041,2043,2045],{"class":204,"line":205},[202,2042,143],{"class":208},[202,2044,212],{"class":211},[202,2046,277],{"class":211},[202,2048,2049,2051,2053,2055],{"class":204,"line":38},[202,2050,143],{"class":208},[202,2052,212],{"class":211},[202,2054,215],{"class":211},[202,2056,219],{"class":218},[202,2058,2059,2061,2063,2065,2067,2069,2071,2073],{"class":204,"line":237},[202,2060,143],{"class":208},[202,2062,212],{"class":211},[202,2064,919],{"class":211},[202,2066,922],{"class":218},[202,2068,925],{"class":211},[202,2070,928],{"class":218},[202,2072,931],{"class":211},[202,2074,219],{"class":218},[202,2076,2077,2079,2081,2083],{"class":204,"line":365},[202,2078,143],{"class":208},[202,2080,212],{"class":211},[202,2082,942],{"class":211},[202,2084,945],{"class":218},[202,2086,2087,2089,2091,2093,2095,2097],{"class":204,"line":378},[202,2088,143],{"class":208},[202,2090,212],{"class":211},[202,2092,954],{"class":211},[202,2094,231],{"class":218},[202,2096,959],{"class":218},[202,2098,962],{"class":211},[202,2100,2101,2103,2105,2107,2109],{"class":204,"line":391},[202,2102,143],{"class":208},[202,2104,212],{"class":211},[202,2106,228],{"class":211},[202,2108,231],{"class":218},[202,2110,234],{"class":211},[202,2112,2113,2115,2117,2119,2121,2123],{"class":204,"line":400},[202,2114,143],{"class":208},[202,2116,212],{"class":211},[202,2118,215],{"class":211},[202,2120,985],{"class":218},[202,2122,988],{"class":211},[202,2124,219],{"class":218},[202,2126,2127,2129,2131,2133,2135,2137,2139],{"class":204,"line":406},[202,2128,143],{"class":208},[202,2130,212],{"class":211},[202,2132,228],{"class":211},[202,2134,231],{"class":218},[202,2136,954],{"class":211},[202,2138,985],{"class":218},[202,2140,1007],{"class":211},[202,2142,2143,2145,2147,2149,2151],{"class":204,"line":419},[202,2144,143],{"class":208},[202,2146,212],{"class":211},[202,2148,1016],{"class":211},[202,2150,231],{"class":218},[202,2152,1021],{"class":211},[202,2154,2155,2157,2159,2161,2163,2165,2167],{"class":204,"line":432},[202,2156,143],{"class":208},[202,2158,212],{"class":211},[202,2160,1030],{"class":211},[202,2162,231],{"class":218},[202,2164,1035],{"class":218},[202,2166,1038],{"class":218},[202,2168,1041],{"class":211},[202,2170,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189],{"class":204,"line":445},[202,2172,143],{"class":208},[202,2174,212],{"class":211},[202,2176,1050],{"class":211},[202,2178,1053],{"class":218},[202,2180,1056],{"class":211},[202,2182,1059],{"class":218},[202,2184,231],{"class":218},[202,2186,1035],{"class":218},[202,2188,1038],{"class":218},[202,2190,1068],{"class":211},[202,2192,2193,2195,2197,2199,2201,2203,2205,2207,2209,2211,2213],{"class":204,"line":458},[202,2194,143],{"class":208},[202,2196,212],{"class":211},[202,2198,1050],{"class":211},[202,2200,1053],{"class":218},[202,2202,1081],{"class":211},[202,2204,1084],{"class":218},[202,2206,1087],{"class":211},[202,2208,1038],{"class":218},[202,2210,1092],{"class":211},[202,2212,1095],{"class":218},[202,2214,1098],{"class":211},[202,2216,2217,2219,2221,2223,2225,2227,2229,2231],{"class":204,"line":471},[202,2218,143],{"class":208},[202,2220,212],{"class":211},[202,2222,1107],{"class":211},[202,2224,231],{"class":218},[202,2226,1053],{"class":218},[202,2228,1114],{"class":211},[202,2230,1038],{"class":218},[202,2232,1119],{"class":211},[202,2234,2235,2237,2239,2241],{"class":204,"line":482},[202,2236,143],{"class":208},[202,2238,212],{"class":211},[202,2240,244],{"class":211},[202,2242,247],{"class":218},[11,2244,1132,2245,1136,2247,1139],{},[127,2246,1135],{},[127,2248,825],{},[132,2250,2251,2257],{},[135,2252,2253,1146,2255,1150],{},[127,2254,1135],{},[124,2256,1149],{},[135,2258,2259,1155],{},[127,2260,825],{},[167,2262,1159],{"id":1158},[11,2264,2265,1165],{},[127,2266,1164],{},[194,2268,2269],{"className":1168,"code":1169,"filename":287,"language":1170,"meta":37,"style":37},[127,2270,2271,2279,2287,2295,2303,2311,2315,2319,2327],{"__ignoreMap":37},[202,2272,2273,2275,2277],{"class":204,"line":205},[202,2274,1177],{"class":333},[202,2276,1181],{"class":1180},[202,2278,1184],{"class":333},[202,2280,2281,2283,2285],{"class":204,"line":38},[202,2282,1189],{"class":333},[202,2284,1181],{"class":1180},[202,2286,1194],{"class":333},[202,2288,2289,2291,2293],{"class":204,"line":237},[202,2290,1199],{"class":333},[202,2292,1181],{"class":1180},[202,2294,479],{"class":333},[202,2296,2297,2299,2301],{"class":204,"line":365},[202,2298,1208],{"class":333},[202,2300,1181],{"class":1180},[202,2302,1213],{"class":333},[202,2304,2305,2307,2309],{"class":204,"line":378},[202,2306,1218],{"class":333},[202,2308,1181],{"class":1180},[202,2310,1223],{"class":211},[202,2312,2313],{"class":204,"line":391},[202,2314,1228],{"emptyLinePlaceholder":46},[202,2316,2317],{"class":204,"line":400},[202,2318,1234],{"class":1233},[202,2320,2321,2323,2325],{"class":204,"line":406},[202,2322,831],{"class":333},[202,2324,1181],{"class":1180},[202,2326,1243],{"class":333},[202,2328,2329,2331,2333],{"class":204,"line":419},[202,2330,1248],{"class":333},[202,2332,1181],{"class":1180},[202,2334,1253],{"class":333},[11,2336,1256,2337,1259],{},[127,2338,825],{},[1261,2340,2341],{"color":1263,"icon":1264},[11,2342,1267,2343,1271,2345,1274],{},[124,2344,1270],{},[127,2346,1218],{},[11,2348,1277],{},[11,2350,2351,1282,2353,1286,2355,1289,2357,1292,2359,1296,2361,1299],{},[127,2352,670],{},[124,2354,1285],{},[127,2356,287],{},[127,2358,129],{},[124,2360,1295],{},[127,2362,298],{},[11,2364,1302,2365,1306,2367,1309],{},[127,2366,1305],{},[127,2368,670],{},[194,2370,2371],{"className":196,"code":1312,"language":198,"meta":37,"style":37},[127,2372,2373],{"__ignoreMap":37},[202,2374,2375,2377,2379,2381,2383,2385],{"class":204,"line":205},[202,2376,143],{"class":208},[202,2378,212],{"class":211},[202,2380,215],{"class":211},[202,2382,1325],{"class":218},[202,2384,1328],{"class":211},[202,2386,219],{"class":218},[167,2388,1334],{"id":1333},[11,2390,1337],{},[11,2392,1340],{},[11,2394,1343],{},[11,2396,1346,2397,1349,2399,1352],{},[127,2398,129],{},[127,2400,143],{},[11,2402,1355,2403,1359],{},[127,2404,1358],{},[11,2406,1362],{},[1364,2408,2409],{},[11,2410,1368],{},[11,2412,1371,2413,1374,2415,1377,2417,1381,2419,1384],{},[127,2414,702],{},[127,2416,784],{},[127,2418,1380],{},[127,2420,1135],{},[183,2422,1388],{"id":1387},[11,2424,1391],{},[194,2426,2427],{"className":196,"code":1394,"language":198,"meta":37,"style":37},[127,2428,2429],{"__ignoreMap":37},[202,2430,2431,2433,2435,2437],{"class":204,"line":205},[202,2432,143],{"class":208},[202,2434,1403],{"class":211},[202,2436,1406],{"class":211},[202,2438,1409],{"class":211},[167,2440,1413],{"id":1412},[11,2442,1416,2443,1419],{},[127,2444,129],{},[11,2446,1422],{},[1424,2448,1426],{},{"title":37,"searchDepth":38,"depth":38,"links":2450},[2451,2457,2460,2461,2464],{"id":169,"depth":38,"text":170,"children":2452},[2453,2454,2455,2456],{"id":185,"depth":237,"text":186},{"id":259,"depth":237,"text":260},{"id":302,"depth":237,"text":303},{"id":601,"depth":237,"text":602},{"id":622,"depth":38,"text":623,"children":2458},[2459],{"id":883,"depth":237,"text":884},{"id":1158,"depth":38,"text":1159},{"id":1333,"depth":38,"text":1334,"children":2462},[2463],{"id":1387,"depth":237,"text":1388},{"id":1412,"depth":38,"text":1413},{},{"title":118,"description":1444},{"id":2468,"title":2469,"body":2470,"category":77,"date":3432,"description":3433,"draft":42,"extension":43,"image":3434,"meta":3435,"navigation":46,"path":3436,"seo":3437,"stem":3438,"tighten":42,"__hash__":3439},"blog/blog/advanced-agentic-workflows-with-stacked-skills.md","Advanced Agentic Workflows With Stacked Skills",{"type":8,"value":2471,"toc":3423},[2472,2475,2480,2483,2486,2491,2494,2497,2501,2512,2515,2534,2541,2547,2551,2554,2557,2566,2569,2586,2589,2592,2635,2638,2680,2683,2686,2690,2697,2704,2809,2812,2815,2822,3198,3202,3205,3315,3324,3327,3331,3334,3339,3342,3349,3353,3356,3404,3407,3411,3414,3417,3420],[11,2473,2474],{},"We all know that simply asking a clanker:",[1364,2476,2477],{},[11,2478,2479],{},"Build a million-dollar app. Make no mistakes.",[11,2481,2482],{},"...won't work as magically as you'd expect, no matter what the vibe coders on Twitter say. However, there are far more effective ways to automate workflows for our agents.",[11,2484,2485],{},"Imagine having a list of 12 to-do cards in Trello and simply saying:",[1364,2487,2488],{},[11,2489,2490],{},"Tackle all my pending tasks.",[11,2492,2493],{},"...then letting the agent run until you have 12 PRs ready for review. Pretty cool, right?",[11,2495,2496],{},"Today, we'll explore how to create skills, compose them together, and build exactly that kind of workflow.",[167,2498,2500],{"id":2499},"skills-can-call-other-skills","Skills Can Call Other Skills",[11,2502,2503,2504,2508,2509],{},"In ",[18,2505,2507],{"href":2506},"/blog/skills-i-know-kung-fu","the last post"," I built a skill to ",[124,2510,2511],{},"automate creating PRs in GitHub.",[11,2513,2514],{},"What's great is that not only I can run it in my agent session, but also call it from other skills. A skill can call another skill by naming its slash command as a step in its own instructions, the same way you would type it into the prompt.",[194,2516,2520],{"className":2517,"code":2518,"filename":2519,"language":43,"meta":37,"style":37},"language-md shiki shiki-themes monokai","- Once you are done, create a PR using `/github-pr main`\n","SKILL.md",[127,2521,2522],{"__ignoreMap":37},[202,2523,2524,2527,2530],{"class":204,"line":205},[202,2525,2526],{"class":208},"-",[202,2528,2529],{"class":333}," Once you are done, create a PR using ",[202,2531,2533],{"class":2532},"szGx7","`/github-pr main`\n",[11,2535,2536,2537,2540],{},"When reading that line, the agent will run that ",[127,2538,2539],{},"/github-pr"," skill. There is no API or plugin system to set up, the agent is smart enough to figure it out.",[11,2542,2543,2544,2546],{},"That may sound like a small detail, but it changes how you design skills: instead of one large ",[127,2545,2519],{}," that tries to handle an entire process, you can write skills that each do one thing well and let them delegate the rest.",[167,2548,2550],{"id":2549},"the-goal","The Goal",[11,2552,2553],{},"Let's explore a complex workflow to put this in practice.",[11,2555,2556],{},"I'd love to leave my agent running in the background, tackling all the pending items of a given project.",[11,2558,2559,2560,2565],{},"How can we organize the tasks? Well, perhaps we can use ",[18,2561,2564],{"href":2562,"rel":2563},"https://trello.com",[22],"Trello",". A simple board will do: \"To Do\", \"Doing\", \"Under Review\" and \"Done\".",[11,2567,2568],{},"The full agentic workflow will be:",[132,2570,2571,2574,2577,2580,2583],{},[135,2572,2573],{},"Pick the first card from the \"To Do\" list",[135,2575,2576],{},"Move the card to \"Doing\"",[135,2578,2579],{},"Complete the task",[135,2581,2582],{},"Create a PR",[135,2584,2585],{},"Move the card to \"Under Review\"",[11,2587,2588],{},"We'll keep things simple: each card will represent a feature, a concrete and rather small amount of work. Each card will have its own, dedicated PR.",[11,2590,2591],{},"If we are building an online shop, a card could be:",[194,2593,2595],{"className":2517,"code":2594,"language":43,"meta":37,"style":37},"# Implement Product Search\n\n- Add a search input to the product listing page\n- As the user types, filter the list of products by name\n- If no products match the search, display a \"No products found\" message\n- Clearing the search input should restore the full product list\n",[127,2596,2597,2603,2607,2614,2621,2628],{"__ignoreMap":37},[202,2598,2599],{"class":204,"line":205},[202,2600,2602],{"class":2601},"ssRA_","# Implement Product Search\n",[202,2604,2605],{"class":204,"line":38},[202,2606,1228],{"emptyLinePlaceholder":46},[202,2608,2609,2611],{"class":204,"line":237},[202,2610,2526],{"class":208},[202,2612,2613],{"class":333}," Add a search input to the product listing page\n",[202,2615,2616,2618],{"class":204,"line":365},[202,2617,2526],{"class":208},[202,2619,2620],{"class":333}," As the user types, filter the list of products by name\n",[202,2622,2623,2625],{"class":204,"line":378},[202,2624,2526],{"class":208},[202,2626,2627],{"class":333}," If no products match the search, display a \"No products found\" message\n",[202,2629,2630,2632],{"class":204,"line":391},[202,2631,2526],{"class":208},[202,2633,2634],{"class":333}," Clearing the search input should restore the full product list\n",[11,2636,2637],{},"Looks like a good unit of work, right? Another card could be:",[194,2639,2641],{"className":2517,"code":2640,"language":43,"meta":37,"style":37},"# Implement Product Categories\n\n- Display the list of available categories on the product listing page\n- Selecting a category should filter the displayed products\n- The user can clear the selected category to view all products again\n- The selected category should remain active while navigating the product list\n",[127,2642,2643,2648,2652,2659,2666,2673],{"__ignoreMap":37},[202,2644,2645],{"class":204,"line":205},[202,2646,2647],{"class":2601},"# Implement Product Categories\n",[202,2649,2650],{"class":204,"line":38},[202,2651,1228],{"emptyLinePlaceholder":46},[202,2653,2654,2656],{"class":204,"line":237},[202,2655,2526],{"class":208},[202,2657,2658],{"class":333}," Display the list of available categories on the product listing page\n",[202,2660,2661,2663],{"class":204,"line":365},[202,2662,2526],{"class":208},[202,2664,2665],{"class":333}," Selecting a category should filter the displayed products\n",[202,2667,2668,2670],{"class":204,"line":378},[202,2669,2526],{"class":208},[202,2671,2672],{"class":333}," The user can clear the selected category to view all products again\n",[202,2674,2675,2677],{"class":204,"line":391},[202,2676,2526],{"class":208},[202,2678,2679],{"class":333}," The selected category should remain active while navigating the product list\n",[11,2681,2682],{},"And so on and so forth!",[11,2684,2685],{},"We already have a skill that takes care of interacting with GitHub. Now, we only need one to interact with Trello.",[167,2687,2689],{"id":2688},"the-trello-skill","The Trello Skill",[11,2691,2692,2693,2696],{},"Let's create this simple skill to teach the agent how to work with Trello's API. Of course, the reasoning model can ",[178,2694,2695],{},"figure it out,"," but it's faster (and less token consuming) to steer it in the right direction.",[11,2698,2699,2700,2703],{},"First, we need a file with the credentials and the IDs of the board and lists we want to work with. In the root of our project, we can save a ",[127,2701,2702],{},"trello.json"," that would look something like this:",[194,2705,2707],{"className":324,"code":2706,"filename":2702,"language":326,"meta":37,"style":37},"{\n  \"apiKey\": \"...\",\n  \"apiToken\": \"...\",\n  \"board\": \"\u003Cboard-id>\",\n  \"lists\": {\n    \"To Do\": \"\u003Clist-id>\",\n    \"Doing\": \"\u003Clist-id>\",\n    \"Under Review\": \"\u003Clist-id>\",\n    \"Done\": \"\u003Clist-id>\"\n  }\n}\n",[127,2708,2709,2713,2725,2736,2748,2756,2768,2779,2790,2800,2805],{"__ignoreMap":37},[202,2710,2711],{"class":204,"line":205},[202,2712,334],{"class":333},[202,2714,2715,2718,2720,2723],{"class":204,"line":38},[202,2716,2717],{"class":339},"  \"apiKey\"",[202,2719,343],{"class":333},[202,2721,2722],{"class":359},"\"...\"",[202,2724,349],{"class":333},[202,2726,2727,2730,2732,2734],{"class":204,"line":237},[202,2728,2729],{"class":339},"  \"apiToken\"",[202,2731,343],{"class":333},[202,2733,2722],{"class":359},[202,2735,349],{"class":333},[202,2737,2738,2741,2743,2746],{"class":204,"line":365},[202,2739,2740],{"class":339},"  \"board\"",[202,2742,343],{"class":333},[202,2744,2745],{"class":359},"\"\u003Cboard-id>\"",[202,2747,349],{"class":333},[202,2749,2750,2753],{"class":204,"line":378},[202,2751,2752],{"class":339},"  \"lists\"",[202,2754,2755],{"class":333},": {\n",[202,2757,2758,2761,2763,2766],{"class":204,"line":391},[202,2759,2760],{"class":339},"    \"To Do\"",[202,2762,343],{"class":333},[202,2764,2765],{"class":359},"\"\u003Clist-id>\"",[202,2767,349],{"class":333},[202,2769,2770,2773,2775,2777],{"class":204,"line":400},[202,2771,2772],{"class":339},"    \"Doing\"",[202,2774,343],{"class":333},[202,2776,2765],{"class":359},[202,2778,349],{"class":333},[202,2780,2781,2784,2786,2788],{"class":204,"line":406},[202,2782,2783],{"class":339},"    \"Under Review\"",[202,2785,343],{"class":333},[202,2787,2765],{"class":359},[202,2789,349],{"class":333},[202,2791,2792,2795,2797],{"class":204,"line":419},[202,2793,2794],{"class":339},"    \"Done\"",[202,2796,343],{"class":333},[202,2798,2799],{"class":359},"\"\u003Clist-id>\"\n",[202,2801,2802],{"class":204,"line":432},[202,2803,2804],{"class":333},"  }\n",[202,2806,2807],{"class":204,"line":445},[202,2808,576],{"class":333},[11,2810,2811],{},"Of course, remember to add that file to gitignore so you don't leak your credentials.",[11,2813,2814],{},"Now, we only need to tell our agent how to operate with Trello so it knows exactly what to do when we ask it to \"create a new card\" or \"move this card to Done\".",[11,2816,2817,2818,2821],{},"This is how the ",[127,2819,2820],{},"/trello"," skill could look like:",[194,2823,2826],{"className":2517,"code":2824,"filename":2825,"language":43,"meta":37,"style":37},"---\nname: trello\ndescription: Interacts with a Trello board. Triggered by \"/trello\".\n---\n\nUse this skill whenever the user asks you to read, create, update, delete, or move cards.\n\n## Credentials\n\nRead board credentials from `trello.json` in the project root:\n\n```json\n{\n  \"apiKey\": \"...\",\n  \"apiToken\": \"...\",\n  \"board\": \"\u003Cboard-id>\",\n  \"lists\": {\n    \"To Do\": \"\u003Clist-id>\",\n    \"Doing\": \"\u003Clist-id>\",\n    \"Under Review\": \"\u003Clist-id>\",\n    \"Done\": \"\u003Clist-id>\"\n  }\n}\n```\n\n## Read list\n\n`curl -s \"https://api.trello.com/1/lists/\u003Clist-id>/cards?key=\u003CapiKey>&token=\u003CapiToken>\"`\n\n## Create card\n\nThe card name is the title and the description holds the body. Use `--data-urlencode` so spaces and line breaks survive:\n\n`curl -s -X POST \"https://api.trello.com/1/cards?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"idList=\u003Clist-id>\" --data-urlencode \"name=\u003Ccard-title>\" --data-urlencode \"desc=\u003Ccard-body>\"`\n\n## Move card\n\n`curl -s -X PUT \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>&idList=\u003Clist-id>\"`\n\n## Update card\n\nSame endpoint as moving, just pass whichever fields changed:\n\n`curl -s -X PUT \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"name=\u003Ccard-title>\" --data-urlencode \"desc=\u003Ccard-body>\"`\n\n## Delete card\n\n`curl -s -X DELETE \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>\"`\n\n## Attach a URL\n\nAttach a link to a card, for example the PR you just opened:\n\n`curl -s -X POST \"https://api.trello.com/1/cards/\u003Ccard-id>/attachments?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"url=https://google.com\"`\n\n## Error handling\n\n- If `trello.json` is missing or malformed, stop and tell the user.\n- If the Trello API returns a non-2xx status, show the error and stop.\n",".claude/skills/trello/SKILL.md",[127,2827,2828,2833,2843,2853,2857,2861,2866,2870,2875,2879,2890,2894,2899,2903,2913,2923,2933,2939,2949,2959,2969,2977,2981,2985,2990,2995,3001,3006,3012,3017,3023,3028,3040,3045,3051,3056,3062,3067,3073,3078,3084,3089,3095,3100,3106,3111,3117,3122,3128,3133,3139,3144,3150,3155,3161,3166,3172,3177,3190],{"__ignoreMap":37},[202,2829,2830],{"class":204,"line":205},[202,2831,2832],{"class":333},"---\n",[202,2834,2835,2838,2840],{"class":204,"line":38},[202,2836,2837],{"class":1180},"name",[202,2839,343],{"class":333},[202,2841,2842],{"class":211},"trello\n",[202,2844,2845,2848,2850],{"class":204,"line":237},[202,2846,2847],{"class":1180},"description",[202,2849,343],{"class":333},[202,2851,2852],{"class":211},"Interacts with a Trello board. Triggered by \"/trello\".\n",[202,2854,2855],{"class":204,"line":365},[202,2856,2832],{"class":333},[202,2858,2859],{"class":204,"line":378},[202,2860,1228],{"emptyLinePlaceholder":46},[202,2862,2863],{"class":204,"line":391},[202,2864,2865],{"class":333},"Use this skill whenever the user asks you to read, create, update, delete, or move cards.\n",[202,2867,2868],{"class":204,"line":400},[202,2869,1228],{"emptyLinePlaceholder":46},[202,2871,2872],{"class":204,"line":406},[202,2873,2874],{"class":2601},"## Credentials\n",[202,2876,2877],{"class":204,"line":419},[202,2878,1228],{"emptyLinePlaceholder":46},[202,2880,2881,2884,2887],{"class":204,"line":432},[202,2882,2883],{"class":333},"Read board credentials from ",[202,2885,2886],{"class":2532},"`trello.json`",[202,2888,2889],{"class":333}," in the project root:\n",[202,2891,2892],{"class":204,"line":445},[202,2893,1228],{"emptyLinePlaceholder":46},[202,2895,2896],{"class":204,"line":458},[202,2897,2898],{"class":333},"```json\n",[202,2900,2901],{"class":204,"line":471},[202,2902,334],{"class":333},[202,2904,2905,2907,2909,2911],{"class":204,"line":482},[202,2906,2717],{"class":339},[202,2908,343],{"class":333},[202,2910,2722],{"class":359},[202,2912,349],{"class":333},[202,2914,2915,2917,2919,2921],{"class":204,"line":488},[202,2916,2729],{"class":339},[202,2918,343],{"class":333},[202,2920,2722],{"class":359},[202,2922,349],{"class":333},[202,2924,2925,2927,2929,2931],{"class":204,"line":493},[202,2926,2740],{"class":339},[202,2928,343],{"class":333},[202,2930,2745],{"class":359},[202,2932,349],{"class":333},[202,2934,2935,2937],{"class":204,"line":504},[202,2936,2752],{"class":339},[202,2938,2755],{"class":333},[202,2940,2941,2943,2945,2947],{"class":204,"line":516},[202,2942,2760],{"class":339},[202,2944,343],{"class":333},[202,2946,2765],{"class":359},[202,2948,349],{"class":333},[202,2950,2951,2953,2955,2957],{"class":204,"line":528},[202,2952,2772],{"class":339},[202,2954,343],{"class":333},[202,2956,2765],{"class":359},[202,2958,349],{"class":333},[202,2960,2961,2963,2965,2967],{"class":204,"line":540},[202,2962,2783],{"class":339},[202,2964,343],{"class":333},[202,2966,2765],{"class":359},[202,2968,349],{"class":333},[202,2970,2971,2973,2975],{"class":204,"line":552},[202,2972,2794],{"class":339},[202,2974,343],{"class":333},[202,2976,2799],{"class":359},[202,2978,2979],{"class":204,"line":561},[202,2980,2804],{"class":333},[202,2982,2983],{"class":204,"line":567},[202,2984,576],{"class":333},[202,2986,2987],{"class":204,"line":573},[202,2988,2989],{"class":333},"```\n",[202,2991,2993],{"class":204,"line":2992},25,[202,2994,1228],{"emptyLinePlaceholder":46},[202,2996,2998],{"class":204,"line":2997},26,[202,2999,3000],{"class":2601},"## Read list\n",[202,3002,3004],{"class":204,"line":3003},27,[202,3005,1228],{"emptyLinePlaceholder":46},[202,3007,3009],{"class":204,"line":3008},28,[202,3010,3011],{"class":2532},"`curl -s \"https://api.trello.com/1/lists/\u003Clist-id>/cards?key=\u003CapiKey>&token=\u003CapiToken>\"`\n",[202,3013,3015],{"class":204,"line":3014},29,[202,3016,1228],{"emptyLinePlaceholder":46},[202,3018,3020],{"class":204,"line":3019},30,[202,3021,3022],{"class":2601},"## Create card\n",[202,3024,3026],{"class":204,"line":3025},31,[202,3027,1228],{"emptyLinePlaceholder":46},[202,3029,3031,3034,3037],{"class":204,"line":3030},32,[202,3032,3033],{"class":333},"The card name is the title and the description holds the body. Use ",[202,3035,3036],{"class":2532},"`--data-urlencode`",[202,3038,3039],{"class":333}," so spaces and line breaks survive:\n",[202,3041,3043],{"class":204,"line":3042},33,[202,3044,1228],{"emptyLinePlaceholder":46},[202,3046,3048],{"class":204,"line":3047},34,[202,3049,3050],{"class":2532},"`curl -s -X POST \"https://api.trello.com/1/cards?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"idList=\u003Clist-id>\" --data-urlencode \"name=\u003Ccard-title>\" --data-urlencode \"desc=\u003Ccard-body>\"`\n",[202,3052,3054],{"class":204,"line":3053},35,[202,3055,1228],{"emptyLinePlaceholder":46},[202,3057,3059],{"class":204,"line":3058},36,[202,3060,3061],{"class":2601},"## Move card\n",[202,3063,3065],{"class":204,"line":3064},37,[202,3066,1228],{"emptyLinePlaceholder":46},[202,3068,3070],{"class":204,"line":3069},38,[202,3071,3072],{"class":2532},"`curl -s -X PUT \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>&idList=\u003Clist-id>\"`\n",[202,3074,3076],{"class":204,"line":3075},39,[202,3077,1228],{"emptyLinePlaceholder":46},[202,3079,3081],{"class":204,"line":3080},40,[202,3082,3083],{"class":2601},"## Update card\n",[202,3085,3087],{"class":204,"line":3086},41,[202,3088,1228],{"emptyLinePlaceholder":46},[202,3090,3092],{"class":204,"line":3091},42,[202,3093,3094],{"class":333},"Same endpoint as moving, just pass whichever fields changed:\n",[202,3096,3098],{"class":204,"line":3097},43,[202,3099,1228],{"emptyLinePlaceholder":46},[202,3101,3103],{"class":204,"line":3102},44,[202,3104,3105],{"class":2532},"`curl -s -X PUT \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"name=\u003Ccard-title>\" --data-urlencode \"desc=\u003Ccard-body>\"`\n",[202,3107,3109],{"class":204,"line":3108},45,[202,3110,1228],{"emptyLinePlaceholder":46},[202,3112,3114],{"class":204,"line":3113},46,[202,3115,3116],{"class":2601},"## Delete card\n",[202,3118,3120],{"class":204,"line":3119},47,[202,3121,1228],{"emptyLinePlaceholder":46},[202,3123,3125],{"class":204,"line":3124},48,[202,3126,3127],{"class":2532},"`curl -s -X DELETE \"https://api.trello.com/1/cards/\u003Ccard-id>?key=\u003CapiKey>&token=\u003CapiToken>\"`\n",[202,3129,3131],{"class":204,"line":3130},49,[202,3132,1228],{"emptyLinePlaceholder":46},[202,3134,3136],{"class":204,"line":3135},50,[202,3137,3138],{"class":2601},"## Attach a URL\n",[202,3140,3142],{"class":204,"line":3141},51,[202,3143,1228],{"emptyLinePlaceholder":46},[202,3145,3147],{"class":204,"line":3146},52,[202,3148,3149],{"class":333},"Attach a link to a card, for example the PR you just opened:\n",[202,3151,3153],{"class":204,"line":3152},53,[202,3154,1228],{"emptyLinePlaceholder":46},[202,3156,3158],{"class":204,"line":3157},54,[202,3159,3160],{"class":2532},"`curl -s -X POST \"https://api.trello.com/1/cards/\u003Ccard-id>/attachments?key=\u003CapiKey>&token=\u003CapiToken>\" --data-urlencode \"url=https://google.com\"`\n",[202,3162,3164],{"class":204,"line":3163},55,[202,3165,1228],{"emptyLinePlaceholder":46},[202,3167,3169],{"class":204,"line":3168},56,[202,3170,3171],{"class":2601},"## Error handling\n",[202,3173,3175],{"class":204,"line":3174},57,[202,3176,1228],{"emptyLinePlaceholder":46},[202,3178,3180,3182,3185,3187],{"class":204,"line":3179},58,[202,3181,2526],{"class":208},[202,3183,3184],{"class":333}," If ",[202,3186,2886],{"class":2532},[202,3188,3189],{"class":333}," is missing or malformed, stop and tell the user.\n",[202,3191,3193,3195],{"class":204,"line":3192},59,[202,3194,2526],{"class":208},[202,3196,3197],{"class":333}," If the Trello API returns a non-2xx status, show the error and stop.\n",[167,3199,3201],{"id":3200},"the-orchestrator-skill","The Orchestrator Skill",[11,3203,3204],{},"Great! Now that we have skills that know how to deal with Trello and GitHub, we can create an orchestrator skill that makes use of both.",[194,3206,3209],{"className":2517,"code":3207,"filename":3208,"language":43,"meta":37,"style":37},"---\nname: tackle-task\ndescription: Pick the first card in the Trello \"To Do\" list, build the feature it describes, open a PR, and move the card to \"Under Review\".\n---\n\n## Steps\n\n1. Use `/trello` to read the first card in \"To Do\". If the list is empty, stop here.\n2. Use `/trello` to move that card to \"Doing\".\n3. Treat the card's title and description as the feature spec and build it, following the conventions already in the codebase.\n4. Use `/github-pr main` to open the pull request. If this card builds on an earlier one, target that branch instead so the PRs stack: `/github-pr \u003Cbase-branch>`.\n5. Use `/trello` to move the card to \"Under Review\" and attach the PR link.\n",".claude/skills/tackle-task/SKILL.md",[127,3210,3211,3215,3224,3233,3237,3241,3246,3250,3264,3276,3284,3303],{"__ignoreMap":37},[202,3212,3213],{"class":204,"line":205},[202,3214,2832],{"class":333},[202,3216,3217,3219,3221],{"class":204,"line":38},[202,3218,2837],{"class":1180},[202,3220,343],{"class":333},[202,3222,3223],{"class":211},"tackle-task\n",[202,3225,3226,3228,3230],{"class":204,"line":237},[202,3227,2847],{"class":1180},[202,3229,343],{"class":333},[202,3231,3232],{"class":211},"Pick the first card in the Trello \"To Do\" list, build the feature it describes, open a PR, and move the card to \"Under Review\".\n",[202,3234,3235],{"class":204,"line":365},[202,3236,2832],{"class":333},[202,3238,3239],{"class":204,"line":378},[202,3240,1228],{"emptyLinePlaceholder":46},[202,3242,3243],{"class":204,"line":391},[202,3244,3245],{"class":2601},"## Steps\n",[202,3247,3248],{"class":204,"line":400},[202,3249,1228],{"emptyLinePlaceholder":46},[202,3251,3252,3255,3258,3261],{"class":204,"line":406},[202,3253,3254],{"class":208},"1.",[202,3256,3257],{"class":333}," Use ",[202,3259,3260],{"class":2532},"`/trello`",[202,3262,3263],{"class":333}," to read the first card in \"To Do\". If the list is empty, stop here.\n",[202,3265,3266,3269,3271,3273],{"class":204,"line":419},[202,3267,3268],{"class":208},"2.",[202,3270,3257],{"class":333},[202,3272,3260],{"class":2532},[202,3274,3275],{"class":333}," to move that card to \"Doing\".\n",[202,3277,3278,3281],{"class":204,"line":432},[202,3279,3280],{"class":208},"3.",[202,3282,3283],{"class":333}," Treat the card's title and description as the feature spec and build it, following the conventions already in the codebase.\n",[202,3285,3286,3289,3291,3294,3297,3300],{"class":204,"line":445},[202,3287,3288],{"class":208},"4.",[202,3290,3257],{"class":333},[202,3292,3293],{"class":2532},"`/github-pr main`",[202,3295,3296],{"class":333}," to open the pull request. If this card builds on an earlier one, target that branch instead so the PRs stack: ",[202,3298,3299],{"class":2532},"`/github-pr \u003Cbase-branch>`",[202,3301,3302],{"class":333},".\n",[202,3304,3305,3308,3310,3312],{"class":204,"line":458},[202,3306,3307],{"class":208},"5.",[202,3309,3257],{"class":333},[202,3311,3260],{"class":2532},[202,3313,3314],{"class":333}," to move the card to \"Under Review\" and attach the PR link.\n",[11,3316,3317,3318,3320,3321,3323],{},"As you can see, this skill never explains how to name a branch, write a PR body, or talk to GitHub. It delegates all of that to ",[127,3319,2539],{},". Same goes for the Trello operations: saying \"move card to Doing\" is enough, since ",[127,3322,2820],{}," knows how to do it.",[11,3325,3326],{},"Each skill stays small, and neither has to absorb the other's job.",[167,3328,3330],{"id":3329},"running-the-whole-board","Running the Whole Board",[11,3332,3333],{},"Because the workflow lives in skills rather than in my head, scaling from one card to the entire backlog takes a single instruction:",[1364,3335,3336],{},[11,3337,3338],{},"Sequentially tackle every task in our Trello.",[11,3340,3341],{},"That's it! That instruction makes the agent work until we have no more cards in \"To Do\".",[11,3343,3344,3345,3348],{},"The end result is a bunch of PRs in GitHub, ready for human review. Yeah, ",[178,3346,3347],{},"human review:"," I think it's important to take a moment to read the code the clanker generated. We saved so much time already (not only in the coding aspect, but all the ceremony that goes with it: writing PRs, moving cards, etc.) that taking a look can't hurt anyone... and can save you from shipping slop.",[167,3350,3352],{"id":3351},"designing-skills-that-stack","Designing Skills That Stack",[11,3354,3355],{},"A few principles made the difference between skills that compose cleanly and skills that fight each other:",[132,3357,3358,3374,3387,3395],{},[135,3359,3360,3363,3364,3366,3367,3369,3370,3373],{},[124,3361,3362],{},"One job per skill."," ",[127,3365,2539],{}," only knows how to open a PR, and ",[127,3368,2820],{}," only knows how to talk to the board. That's exactly why ",[127,3371,3372],{},"/tackle-task"," can lean on both without dragging their guts into its own instructions.",[135,3375,3376,3379,3380,1136,3383,3386],{},[124,3377,3378],{},"Give each skill a small handle to grab."," The base-branch argument is the whole reason ",[127,3381,3382],{},"/github-pr main",[127,3384,3385],{},"/github-pr some-feature"," both work. One little parameter turns a fixed command into a building block.",[135,3388,3389,3363,3392,3394],{},[124,3390,3391],{},"Let the orchestrator do the deciding.",[127,3393,3372],{}," owns the order of things and the \"does this card stack on another?\" call. The skills it invokes never need to know that context exists.",[135,3396,3397,3400,3401,3403],{},[124,3398,3399],{},"Stop when something looks off."," An empty \"To Do\" list, a missing ",[127,3402,2702],{},", a non-2xx from the API: any of those halts the run instead of quietly plowing ahead and making a mess you have to untangle later.",[11,3405,3406],{},"None of this depends on an exotic feature. It's the same design thinking you already apply to code, pointed at the skills your agent runs.",[167,3408,3410],{"id":3409},"where-this-goes","Where This Goes",[11,3412,3413],{},"Once you start seeing skills as stackable recipes, it's hard not to notice more places to apply them.",[11,3415,3416],{},"The one I'm building now checks my inbox, reads emails from my app users, turns their requests into Trello cards, works on them, opens a PR for each one, and drafts an email reply for me to send later.",[11,3418,3419],{},"Here's a challenge for you: think about what you can automate with skills. Build it, and let me know how it goes!",[1424,3421,3422],{},"html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .szGx7, html code.shiki .szGx7{--shiki-default:#FD971F}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 pre.shiki code .ssRA_, html code.shiki .ssRA_{--shiki-default:#A6E22E;--shiki-default-font-weight:bold}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .susgL, html code.shiki .susgL{--shiki-default:#CFCFC2}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}",{"title":37,"searchDepth":38,"depth":38,"links":3424},[3425,3426,3427,3428,3429,3430,3431],{"id":2499,"depth":38,"text":2500},{"id":2549,"depth":38,"text":2550},{"id":2688,"depth":38,"text":2689},{"id":3200,"depth":38,"text":3201},{"id":3329,"depth":38,"text":3330},{"id":3351,"depth":38,"text":3352},{"id":3409,"depth":38,"text":3410},"2026-07-09","A single skill removes one repetitive task. Stacking skills, letting one call another, turns a backlog into a workflow your agent can run end to end.","/img/blog/advanced-agentic-workflows-with-stacked-skills.png",{},"/blog/advanced-agentic-workflows-with-stacked-skills",{"title":2469,"description":3433},"blog/advanced-agentic-workflows-with-stacked-skills","dhbCP-c8nD_KIMpxzUk_rO96Kxg8PL8uJl3vdqTIGwM",1790452840362]