[{"data":1,"prerenderedAt":371},["ShallowReactive",2],{"navigation":3,"\u002Fworks\u002Farti":54},[4],{"title":5,"path":6,"stem":7,"children":8,"page":53},"Works","\u002Fworks","works",[9,13,17,21,25,29,33,37,41,45,49],{"title":10,"path":11,"stem":12},"Arti","\u002Fworks\u002Farti","works\u002Farti",{"title":14,"path":15,"stem":16},"Currencia","\u002Fworks\u002Fcurrencia","works\u002Fcurrencia",{"title":18,"path":19,"stem":20},"Folio Magazine","\u002Fworks\u002Ffolio-magazine","works\u002Ffolio-magazine",{"title":22,"path":23,"stem":24},"Helpr","\u002Fworks\u002Fhelpr","works\u002Fhelpr",{"title":26,"path":27,"stem":28},"IV Patisserie","\u002Fworks\u002Fiv-patisserie","works\u002Fiv-patisserie",{"title":30,"path":31,"stem":32},"Mockline","\u002Fworks\u002Fmockline","works\u002Fmockline",{"title":34,"path":35,"stem":36},"Portfolio","\u002Fworks\u002Fportfolio","works\u002Fportfolio",{"title":38,"path":39,"stem":40},"Raftou - Mobile Game","\u002Fworks\u002Fraftou","works\u002Fraftou",{"title":42,"path":43,"stem":44},"Raycast x Bambulab","\u002Fworks\u002Fraycast-bambulab","works\u002Fraycast-bambulab",{"title":46,"path":47,"stem":48},"Combat Robot","\u002Fworks\u002Frobot","works\u002Frobot",{"title":50,"path":51,"stem":52},"Targeter","\u002Fworks\u002Ftargeter","works\u002Ftargeter",false,{"id":55,"title":10,"authors":56,"body":63,"contextPreview":352,"date":353,"description":354,"extension":355,"image":356,"meta":357,"minRead":358,"navigation":359,"path":11,"roles":360,"seo":363,"stem":12,"teamName":364,"technologies":365,"url":368,"year":369,"__hash__":370},"works\u002Fworks\u002Farti.md",[57],{"name":58,"to":59,"target":60,"avatar":61},"Johann Cavallucci","https:\u002F\u002Fx.com\u002FJohannCVL","_blank",{"src":62,"alt":58},"https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F72015679?v=4",{"type":64,"value":65,"toc":343},"minimark",[66,71,75,78,81,85,88,91,135,138,145,149,289,292,295,299,302,333,337,340],[67,68,70],"h2",{"id":69},"the-concept","The Concept",[72,73,74],"p",{},"Job hunting is time-consuming: scroll Welcome to the Jungle or LinkedIn, open every listing, read the description, check experience, English level, stack, seniority… and often a single detail disqualifies the role after ten minutes of reading.",[72,76,77],{},"Arti is a personal agent that does that work for me. It has my context — CV, preferences, what I’m actually looking for — explores Welcome to the Jungle and LinkedIn, pre-filters by role, reads the descriptions, scores each offer, and only keeps those above a configurable threshold (e.g. ~70%). It sends me those offers over iMessage, several times a day: fresh jobs, already scored, with an actionable summary and the link to apply.",[72,79,80],{},"I own it end-to-end as Full Stack and AI engineer — a side project built for my own search, not a polished public product.",[67,82,84],{"id":83},"design-features","Design & Features",[72,86,87],{},"The product idea is simple: cut the noise before I ever open a tab. Everything lands where I already am — iMessage — without another dashboard to babysit.",[72,89,90],{},"Key features:",[92,93,94,102,108,114,120],"ul",{},[95,96,97,101],"li",{},[98,99,100],"strong",{},"Scored offers on iMessage:"," score %, title, company, structured summary (activity, location, salary, stack, missions, requirements like degree \u002F English), plus the application link.",[95,103,104,107],{},[98,105,106],{},"Automatic pipeline (cron):"," scraping + matching + notification with no manual action.",[95,109,110,113],{},[98,111,112],{},"On-demand refresh:"," I can text Arti to run a search right now (ack with a like, then the results).",[95,115,116,119],{},[98,117,118],{},"Cover letters:"," generate a letter for a given offer (by number).",[95,121,122,125,126,130,131,134],{},[98,123,124],{},"Conversational iMessage UI:"," list recent offers, resend specific ones (",[127,128,129],"code",{},"#70",", ",[127,132,133],{},"#69","…), read or update the minimum score threshold.",[72,136,137],{},"Extras that keep the signal clean: title \u002F experience \u002F role-focus pre-filters, URL deduplication, and multi-source ingestion (WTTJ + LinkedIn).",[72,139,140],{},[141,142],"img",{"alt":143,"src":144},"Arti in testing","\u002Fassets\u002Fworks\u002Farti\u002Ftesting.png",[67,146,148],{"id":147},"technical-stack-architecture","Technical Stack & Architecture",[150,151,152,165],"table",{},[153,154,155],"thead",{},[156,157,158,162],"tr",{},[159,160,161],"th",{},"Technology",[159,163,164],{},"Role in the project",[166,167,168,179,189,199,209,219,229,239,249,259,269,279],"tbody",{},[156,169,170,176],{},[171,172,173],"td",{},[98,174,175],{},"TypeScript",[171,177,178],{},"Single language across the monorepo (apps + packages)",[156,180,181,186],{},[171,182,183],{},[98,184,185],{},"pnpm workspaces",[171,187,188],{},"Package split (core, db, sources, pipeline) + apps",[156,190,191,196],{},[171,192,193],{},[98,194,195],{},"Nitro",[171,197,198],{},"iMessage agent server (webhooks, runtime)",[156,200,201,206],{},[171,202,203],{},[98,204,205],{},"Chat SDK + Sendblue",[171,207,208],{},"Receive \u002F send iMessage, reactions (like)",[156,210,211,216],{},[171,212,213],{},[98,214,215],{},"Vercel AI SDK + AI Gateway",[171,217,218],{},"LLM calls (scoring, chat, cover letters) through a unified gateway",[156,220,221,226],{},[171,222,223],{},[98,224,225],{},"DeepSeek (via gateway)",[171,227,228],{},"Model used for match, chat, and cover letters",[156,230,231,236],{},[171,232,233],{},[98,234,235],{},"PostgreSQL",[171,237,238],{},"Persist offers, letters, and seen-URL history",[156,240,241,246],{},[171,242,243],{},[98,244,245],{},"Drizzle ORM",[171,247,248],{},"SQL schema, queries, migrations",[156,250,251,256],{},[171,252,253],{},[98,254,255],{},"Zod",[171,257,258],{},"Validate prefs \u002F profiles \u002F structured LLM outputs",[156,260,261,266],{},[171,262,263],{},[98,264,265],{},"Custom sources (WTTJ Algolia + LinkedIn guest)",[171,267,268],{},"Ingest listings and offer details",[156,270,271,276],{},[171,272,273],{},[98,274,275],{},"Cron \u002F sync worker",[171,277,278],{},"Periodic matching pipeline runs",[156,280,281,286],{},[171,282,283],{},[98,284,285],{},"evlog",[171,287,288],{},"Observability for LLM calls (tokens, cost, timing)",[72,290,291],{},"Architecture in short: monorepo → sources scrape WTTJ \u002F LinkedIn → pipeline pre-filters, calls the LLM to score and summarize, writes to Postgres → iMessage notification (Sendblue). In parallel, the Nitro app listens for messages: deterministic commands (refresh, threshold, listing, letter) plus a tool-using agent fallback for natural language.",[293,294],"hr",{},[67,296,298],{"id":297},"challenges","Challenges",[72,300,301],{},"Building a useful agent means the hard part is judgment, not just plumbing:",[92,303,304,310,316,322,327],{},[95,305,306,309],{},[98,307,308],{},"Signal vs noise:"," title \u002F experience filters + LLM threshold + dedup — so I never get flooded.",[95,311,312,315],{},[98,313,314],{},"Contextual matching:"," inject CV and preferences so the score and summary are actually useful, not generic.",[95,317,318,321],{},[98,319,320],{},"Reliable conversational channel:"," iMessage as UI (commands + LLM), without fake confirmations — e.g. a threshold change must really persist.",[95,323,324,326],{},[98,325,118],{}," generate something truly personalized and effective — still improvable, with more work planned.",[95,328,329,332],{},[98,330,331],{},"Fragile multi-sources:"," job-board APIs and pages (WTTJ, LinkedIn guest) keep changing and need hardening.",[67,334,336],{"id":335},"conclusion-and-next-steps","Conclusion and Next Steps",[72,338,339],{},"Arti already saves me a lot of time: every offer I receive is pre-filtered and relevant. It doesn’t replace all manual search, but it cuts most of the time wasted on roles that are incompatible from the first line of the description.",[72,341,342],{},"Next: harden the sources, improve cover-letter quality, and keep tuning the matching loop so the bar stays high as my search evolves.",{"title":344,"searchDepth":345,"depth":345,"links":346},"",2,[347,348,349,350,351],{"id":69,"depth":345,"text":70},{"id":83,"depth":345,"text":84},{"id":147,"depth":345,"text":148},{"id":297,"depth":345,"text":298},{"id":335,"depth":345,"text":336},"[object Object]","2026-06-25","Personal job-hunt agent that scrapes WTTJ and LinkedIn, scores offers against my profile, and pushes the best matches to iMessage.","md","\u002Fassets\u002Fworks\u002Farti\u002Fcover.png",{},7,true,[361,362],"Full Stack","AI Engineer",{"title":10,"description":354},"Author",[175,195,235,366,367],"AI","iMessage",null,"2026","DNvwhlhFW-SFGFHzTwbn4iBJYJRwwEYFVbuRM3T9amM",1790331913035]