[{"data":1,"prerenderedAt":818},["ShallowReactive",2],{"blog-article-/blog/from-prompt-files-to-agent-skills":3,"blog-surround-/blog/from-prompt-files-to-agent-skills":810},{"id":4,"title":5,"body":6,"canonical":796,"date":797,"description":798,"extension":799,"featured":800,"image":796,"meta":801,"navigation":105,"ogimage":796,"path":802,"provider":796,"published":105,"seo":803,"stem":804,"tags":805,"url":796,"__hash__":809},"blog/blog/from-prompt-files-to-agent-skills.md","From Prompt Files to Agent Skills: How I Unified My Content Automation",{"type":7,"value":8,"toc":786},"minimark",[9,24,29,37,47,50,175,178,181,185,192,197,211,217,223,236,242,246,264,275,278,321,324,328,337,343,349,358,375,385,388,391,395,398,401,423,426,446,455,461,467,470,490,501,505,508,696,703,707,710,748,751,755,773,776,779,782],[10,11,12,13,18,19,23],"p",{},"A few weeks ago I wrote about ",[14,15,17],"a",{"href":16},"/blog/ai-agents-mcp-automate-content","how I use AI agents and MCP to automate my website's content",". That post covered the ",[20,21,22],"em",{},"why"," — I create a lot of content and keeping my site up to date was tedious. This post is about what happened next: how I took those initial prompt files and evolved them into something much more powerful.",[25,26,28],"h2",{"id":27},"where-i-started-three-prompt-files","Where I Started: Three Prompt Files",[10,30,31,32,36],{},"My original setup lived in ",[33,34,35],"code",{},".github/prompts/"," — three separate markdown files, one for each content type:",[38,39,44],"pre",{"className":40,"code":42,"language":43},[41],"language-text",".github/prompts/\n├── playwright-add-video.prompt.md\n├── playwright-add-podcast.prompt.md\n└── playwright-add-blog.prompt.md\n","text",[33,45,42],{"__ignoreMap":46},"",[10,48,49],{},"Each one was pretty simple. Here's what the video prompt looked like:",[38,51,55],{"className":52,"code":53,"language":54,"meta":46,"style":46},"language-markdown shiki shiki-themes github-light github-dark","---\ndescription: 'Add a new video to the content/videos directory'\ntools: ['playwright/*']\n---\n\n# Add a new video\n\nAdd a new video using the MCP server to navigate to the URL to get the required info you need.\n- Ask the user for the url if not provided.\n- Do not invent titles and descriptions.\n- Do not add extra tags only add ones that already exist in the other video files.\n- Make sure the date for the video is correct.\n- Make sure you add a host\n- Close the browser when done.\n","markdown",[33,56,57,66,80,95,100,107,114,119,125,135,143,151,159,167],{"__ignoreMap":46},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"sVt8B","---\n",[58,67,69,73,76],{"class":60,"line":68},2,[58,70,72],{"class":71},"s9eBZ","description",[58,74,75],{"class":64},": ",[58,77,79],{"class":78},"sZZnC","'Add a new video to the content/videos directory'\n",[58,81,83,86,89,92],{"class":60,"line":82},3,[58,84,85],{"class":71},"tools",[58,87,88],{"class":64},": [",[58,90,91],{"class":78},"'playwright/*'",[58,93,94],{"class":64},"]\n",[58,96,98],{"class":60,"line":97},4,[58,99,65],{"class":64},[58,101,103],{"class":60,"line":102},5,[58,104,106],{"emptyLinePlaceholder":105},true,"\n",[58,108,110],{"class":60,"line":109},6,[58,111,113],{"class":112},"sq-ep","# Add a new video\n",[58,115,117],{"class":60,"line":116},7,[58,118,106],{"emptyLinePlaceholder":105},[58,120,122],{"class":60,"line":121},8,[58,123,124],{"class":64},"Add a new video using the MCP server to navigate to the URL to get the required info you need.\n",[58,126,128,132],{"class":60,"line":127},9,[58,129,131],{"class":130},"s4XuR","-",[58,133,134],{"class":64}," Ask the user for the url if not provided.\n",[58,136,138,140],{"class":60,"line":137},10,[58,139,131],{"class":130},[58,141,142],{"class":64}," Do not invent titles and descriptions.\n",[58,144,146,148],{"class":60,"line":145},11,[58,147,131],{"class":130},[58,149,150],{"class":64}," Do not add extra tags only add ones that already exist in the other video files.\n",[58,152,154,156],{"class":60,"line":153},12,[58,155,131],{"class":130},[58,157,158],{"class":64}," Make sure the date for the video is correct.\n",[58,160,162,164],{"class":60,"line":161},13,[58,163,131],{"class":130},[58,165,166],{"class":64}," Make sure you add a host\n",[58,168,170,172],{"class":60,"line":169},14,[58,171,131],{"class":130},[58,173,174],{"class":64}," Close the browser when done.\n",[10,176,177],{},"About 15 lines of loose instructions. The podcast and blog prompts were nearly identical — same structure, same rules, just slightly different fields. And they worked! I'd open VS Code, run the prompt with Copilot, paste a YouTube URL, and it would create the markdown file for me.",[10,179,180],{},"But over time I started noticing the cracks.",[25,182,184],{"id":183},"what-worked-and-what-didnt","What Worked and What Didn't",[10,186,187,191],{},[188,189,190],"strong",{},"What worked:"," The core idea was solid. Give the AI a URL, let it browse the page, extract metadata, and create a file. That part was great.",[10,193,194],{},[188,195,196],{},"What didn't work:",[10,198,199,202,203,206,207,210],{},[188,200,201],{},"Duplicated instructions everywhere."," All three prompts had the same rules: \"don't invent content\", \"only use existing tags\", \"verify the date\". If I wanted to change how tags were validated, I had to update three files. And they were already starting to drift — the podcast prompt referenced ",[33,204,205],{},"microsoft/playwright-mcp/*"," while the video prompt referenced ",[33,208,209],{},"playwright/*",".",[10,212,213,216],{},[188,214,215],{},"No verification step."," The prompts created the file and that was it. I had no way to know if the content actually rendered correctly on my site without manually starting the dev server and checking.",[10,218,219,222],{},[188,220,221],{},"No PR creation."," After the file was created, I still had to manually create a branch, commit, push, and open a PR. That's the boring part that I wanted automated in the first place.",[10,224,225,228,229,232,233,235],{},[188,226,227],{},"Locked to VS Code + Copilot."," The ",[33,230,231],{},".prompt.md"," format with its ",[33,234,85],{}," frontmatter was specific to VS Code's Copilot agent mode. I couldn't use these prompts with Goose, Claude Code, or any other AI agent.",[10,237,238,241],{},[188,239,240],{},"Too vague for reliability."," \"Use the MCP server to navigate to the URL\" is fine for a human reading instructions, but an AI agent needs more specifics. What happens when YouTube shows a cookie consent dialog? How do you extract the exact publish date when YouTube only shows \"7 days ago\"? The prompts didn't capture any of this operational knowledge.",[25,243,245],{"id":244},"the-migration-building-the-first-skill","The Migration: Building the First Skill",[10,247,248,249,255,256,259,260,263],{},"I decided to convert these prompts into ",[14,250,254],{"href":251,"rel":252},"https://block.github.io/goose/docs/guides/context-engineering/using-skills",[253],"nofollow","agent skills"," — portable instruction sets that work across AI coding agents. Skills live in ",[33,257,258],{},".agents/skills/"," and follow a standard format with a ",[33,261,262],{},"SKILL.md"," file that any compatible agent can discover and use.",[10,265,266,267,274],{},"I started with the video prompt since that was the one I used most. Instead of the Playwright MCP server (which requires a specific MCP configuration), I used ",[14,268,271],{"href":269,"rel":270},"https://www.npmjs.com/package/@anthropic-ai/playwright-cli",[253],[33,272,273],{},"playwright-cli"," — a standalone CLI tool for browser automation that works through regular shell commands. This meant any agent with shell access could use it.",[10,276,277],{},"The first version was straightforward — translate the 15-line prompt into a detailed skill with actual steps:",[38,279,283],{"className":280,"code":281,"language":282,"meta":46,"style":46},"language-bash shiki shiki-themes github-light github-dark","# Instead of \"use the MCP server to navigate\"\nplaywright-cli open \"https://www.youtube.com/watch?v=VIDEO_ID\"\nplaywright-cli snapshot\n# Read the snapshot YAML to extract metadata\nplaywright-cli close\n","bash",[33,284,285,291,302,309,314],{"__ignoreMap":46},[58,286,287],{"class":60,"line":61},[58,288,290],{"class":289},"sJ8bj","# Instead of \"use the MCP server to navigate\"\n",[58,292,293,296,299],{"class":60,"line":68},[58,294,273],{"class":295},"sScJk",[58,297,298],{"class":78}," open",[58,300,301],{"class":78}," \"https://www.youtube.com/watch?v=VIDEO_ID\"\n",[58,303,304,306],{"class":60,"line":82},[58,305,273],{"class":295},[58,307,308],{"class":78}," snapshot\n",[58,310,311],{"class":60,"line":97},[58,312,313],{"class":289},"# Read the snapshot YAML to extract metadata\n",[58,315,316,318],{"class":60,"line":102},[58,317,273],{"class":295},[58,319,320],{"class":78}," close\n",[10,322,323],{},"Then I tested it by actually adding a real video. And that's where it got interesting.",[25,325,327],{"id":326},"real-world-learnings","Real-World Learnings",[10,329,330,331,336],{},"Testing the skill on a real YouTube video (",[14,332,335],{"href":333,"rel":334},"https://youtu.be/Numb52aJkJw",[253],"this NDC London talk",") revealed a whole set of things the original prompt never accounted for:",[10,338,339,342],{},[188,340,341],{},"Cookie consent dialogs."," YouTube showed a full-page cookie consent dialog that blocked all the content. The skill needed to detect and accept it before extracting any metadata.",[10,344,345,348],{},[188,346,347],{},"Relative dates."," YouTube initially shows \"7 days ago\" instead of the actual date. You have to click the \"...more\" button to expand the description, which reveals the exact publish date like \"11 Feb 2026\".",[10,350,351,228,354,357],{},[188,352,353],{},"Snapshot files need reading.",[33,355,356],{},"playwright-cli snapshot"," command saves a YAML file to disk. You can't just look at the command output — you need to actually read the file and parse through it to find the title, description, channel name, and date.",[10,359,360,363,364,366,367,370,371,374],{},[188,361,362],{},"Shell environment issues."," Tools like ",[33,365,273],{}," and ",[33,368,369],{},"npm"," are installed via nvm and aren't on the default shell PATH. Every single shell command needs to source nvm first. The GitHub CLI is at ",[33,372,373],{},"/opt/homebrew/bin/gh",", not on PATH either.",[10,376,377,380,381,384],{},[188,378,379],{},"Git authentication."," Pushing to GitHub over HTTPS requires running ",[33,382,383],{},"gh auth setup-git"," first.",[10,386,387],{},"None of this was in the original prompt. And none of it needed to be — because a human was there to handle the edge cases. But for a fully autonomous workflow where the agent creates a branch, makes the file, verifies it on the dev server, and opens a PR? Every one of these details matters.",[10,389,390],{},"I captured all of these learnings directly into the skill. Each time something went wrong, I updated the instructions. This is exactly the iteration loop that makes skills powerful — they accumulate operational knowledge over time.",[25,392,394],{"id":393},"the-architecture-decision-one-skill-not-three","The Architecture Decision: One Skill, Not Three",[10,396,397],{},"With the video skill working end-to-end, I looked at the podcast and blog prompts and realized something: about 70% of the instructions were identical across all three.",[10,399,400],{},"The shared parts:",[402,403,404,408,411,414,417,420],"ul",{},[405,406,407],"li",{},"Shell environment setup (nvm, gh path)",[405,409,410],{},"Browser automation workflow (open, snapshot, extract, close)",[405,412,413],{},"Tag validation (only existing tags)",[405,415,416],{},"Git workflow (branch, commit, push)",[405,418,419],{},"Dev server verification (start, screenshot, confirm)",[405,421,422],{},"PR creation",[10,424,425],{},"The unique parts per content type:",[402,427,428,434,440],{},[405,429,430,433],{},[188,431,432],{},"Video:"," YouTube-specific extraction (video ID, thumbnail URL, expanding description)",[405,435,436,439],{},[188,437,438],{},"Podcast:"," Podcast platform extraction, image upload to Cloudinary",[405,441,442,445],{},[188,443,444],{},"Blog:"," Full article body extraction, canonical URL handling",[10,447,448,449,454],{},"Three separate skills would mean tripling the shared instructions and tripling the metadata that's always loaded into the agent's context. Following the ",[14,450,453],{"href":451,"rel":452},"https://skills.sh/anthropics/skills/skill-creator",[253],"progressive disclosure pattern"," from Anthropic's skill-creator guide, I structured it as one skill with reference files:",[38,456,459],{"className":457,"code":458,"language":43},[41],".agents/skills/add-content/\n├── SKILL.md                     # Core workflow + routing (75 lines)\n└── references/\n    ├── environment.md           # Shell env, git, dev server, PR creation\n    ├── video.md                 # YouTube-specific extraction + frontmatter\n    ├── podcast.md               # Podcast extraction + Cloudinary upload\n    └── blog.md                  # Blog content extraction + canonical URLs\n",[33,460,458],{"__ignoreMap":46},[10,462,463,464,466],{},"The ",[33,465,262],{}," file is lean — 75 lines. It determines the content type from the URL, points to the right reference file, and defines the core workflow. The agent only loads the reference files it actually needs for the task at hand.",[10,468,469],{},"When I say \"add this YouTube video\", the agent loads:",[471,472,473,478,484],"ol",{},[405,474,475,477],{},[33,476,262],{}," (75 lines) — always",[405,479,480,483],{},[33,481,482],{},"references/environment.md"," (126 lines) — for shell/git/PR setup",[405,485,486,489],{},[33,487,488],{},"references/video.md"," (79 lines) — for YouTube-specific steps",[10,491,492,493,496,497,500],{},"It never loads ",[33,494,495],{},"podcast.md"," or ",[33,498,499],{},"blog.md",". That's 280 lines of context instead of loading three separate 275-line skills worth of metadata.",[25,502,504],{"id":503},"before-vs-after","Before vs After",[10,506,507],{},"Here's what changed:",[509,510,511,526],"table",{},[512,513,514],"thead",{},[515,516,517,520,523],"tr",{},[518,519],"th",{},[518,521,522],{},"Prompt Files (Before)",[518,524,525],{},"Agent Skill (After)",[527,528,529,546,559,575,588,603,616,631,644,657,670,683],"tbody",{},[515,530,531,537,543],{},[532,533,534],"td",{},[188,535,536],{},"Files",[532,538,539,540,542],{},"3 separate ",[33,541,231],{}," files",[532,544,545],{},"1 skill with 4 reference files",[515,547,548,553,556],{},[532,549,550],{},[188,551,552],{},"Lines of instructions",[532,554,555],{},"~15 per prompt (45 total)",[532,557,558],{},"480 total (but loaded progressively)",[515,560,561,566,569],{},[532,562,563],{},[188,564,565],{},"Duplicated logic",[532,567,568],{},"~70% duplicated across files",[532,570,571,572],{},"Zero — shared logic in ",[33,573,574],{},"environment.md",[515,576,577,582,585],{},[532,578,579],{},[188,580,581],{},"IDE/Agent support",[532,583,584],{},"VS Code + Copilot only",[532,586,587],{},"Goose, Claude Code, and any agent supporting skills",[515,589,590,595,598],{},[532,591,592],{},[188,593,594],{},"Browser automation",[532,596,597],{},"Playwright MCP server (requires MCP config)",[532,599,600,602],{},[33,601,273],{}," (standalone CLI, shell only)",[515,604,605,610,613],{},[532,606,607],{},[188,608,609],{},"Verification",[532,611,612],{},"None — manual check",[532,614,615],{},"Auto: starts dev server, screenshots with playwright-cli",[515,617,618,622,625],{},[532,619,620],{},[188,621,422],{},[532,623,624],{},"Manual",[532,626,627,628],{},"Auto: branch, commit, push, ",[33,629,630],{},"gh pr create",[515,632,633,638,641],{},[532,634,635],{},[188,636,637],{},"Cookie consent handling",[532,639,640],{},"Not addressed",[532,642,643],{},"Built-in step",[515,645,646,651,654],{},[532,647,648],{},[188,649,650],{},"Date extraction",[532,652,653],{},"\"Make sure the date is correct\"",[532,655,656],{},"Specific: click \"...more\", read expanded description",[515,658,659,664,667],{},[532,660,661],{},[188,662,663],{},"Error recovery knowledge",[532,665,666],{},"None",[532,668,669],{},"nvm sourcing, gh path, git auth, URL quoting",[515,671,672,677,680],{},[532,673,674],{},[188,675,676],{},"Image handling (podcasts)",[532,678,679],{},"\"Ask the user\"",[532,681,682],{},"Auto: extract from page → upload via Cloudinary MCP",[515,684,685,690,693],{},[532,686,687],{},[188,688,689],{},"End-to-end automation",[532,691,692],{},"URL → file (then manual steps)",[532,694,695],{},"URL → file → verify → PR (fully autonomous)",[10,697,698,699,702],{},"The biggest shift isn't any single feature — it's that the skill captures ",[20,700,701],{},"operational knowledge",". Every edge case I hit during testing is now encoded in the instructions. The next time the agent runs this workflow, it won't hit the same problems.",[25,704,706],{"id":705},"what-a-run-looks-like-now","What a Run Looks Like Now",[10,708,709],{},"Here's what happens when I say \"Add this YouTube video to the site\" and paste a URL:",[471,711,712,715,721,724,727,730,733,736,739,742,745],{},[405,713,714],{},"The agent detects it's a YouTube URL and loads the video reference",[405,716,717,718,720],{},"Opens a browser with ",[33,719,273],{},", navigates to the video",[405,722,723],{},"Handles the cookie consent dialog if it appears",[405,725,726],{},"Expands the description to get the exact publish date",[405,728,729],{},"Extracts title, description, date, channel name, and video ID",[405,731,732],{},"Closes the browser",[405,734,735],{},"Checks existing tags and picks only valid ones",[405,737,738],{},"Creates a git branch",[405,740,741],{},"Creates the markdown file with correct frontmatter",[405,743,744],{},"Starts the dev server and verifies the video appears on the site",[405,746,747],{},"Commits, pushes, and opens a PR",[10,749,750],{},"I just merge the PR. That's my only step.",[25,752,754],{"id":753},"whats-next","What's Next",[10,756,757,758,760,761,766,767,772],{},"The skill is in ",[33,759,258],{}," which means it's portable across AI coding agents. I'm using it with ",[14,762,765],{"href":763,"rel":764},"https://github.com/block/goose",[253],"Goose"," today, but the same skill works with Claude Code or any agent that supports the ",[14,768,771],{"href":769,"rel":770},"https://agentskills.io",[253],"Agent Skills"," standard.",[10,774,775],{},"The podcast workflow now automatically uploads images to Cloudinary instead of asking me to do it manually. The blog workflow extracts full article content and handles canonical URLs for posts hosted on other platforms.",[10,777,778],{},"And because skills accumulate knowledge through iteration, they'll keep getting better. Every time something unexpected happens, I update the reference file, and the next run is smoother.",[10,780,781],{},"If you're using prompt files today and finding yourself duplicating instructions or manually handling the steps after the AI creates a file, consider migrating to skills. The initial investment in writing detailed instructions pays off quickly when you stop having to babysit every run.",[783,784,785],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sq-ep, html code.shiki .sq-ep{--shiki-default:#005CC5;--shiki-default-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":46,"searchDepth":68,"depth":68,"links":787},[788,789,790,791,792,793,794,795],{"id":27,"depth":68,"text":28},{"id":183,"depth":68,"text":184},{"id":244,"depth":68,"text":245},{"id":326,"depth":68,"text":327},{"id":393,"depth":68,"text":394},{"id":503,"depth":68,"text":504},{"id":705,"depth":68,"text":706},{"id":753,"depth":68,"text":754},null,"2026-02-18","How I migrated from three separate VS Code prompt files to a single portable agent skill that handles videos, podcasts, and blog posts — with browser verification and automatic PR creation built in.","md",false,{},"/blog/from-prompt-files-to-agent-skills",{"title":5,"description":798},"blog/from-prompt-files-to-agent-skills",[806,807,808],"ai","mcp","playwright","qr_fpnc_Rj3Wnd5q7yz2YeYISSW0fLYPvWQ6UybChqM",[811,815],{"title":812,"path":813,"stem":814,"children":-1},"What Are Agent Skills? Beginners Guide","/blog/what-are-agent-skills-beginners-guide","blog/what-are-agent-skills-beginners-guide",{"title":816,"path":16,"stem":817,"children":-1},"How I Use AI Agents + MCP to Fully Automate My Website's Content","blog/ai-agents-mcp-automate-content",1783716251238]