One .zip per skill. Inside: SKILL.md with the intent and the frontmatter, and the code that runs in the page.
Settings → Personalize → Skills. This is Cowork's own skill library, shared across every chat.
Click Add and choose the file you just downloaded. Cowork installs it once; no CLAUDE.md, no per-project setup.
The skill stays available in every session. Cowork runs it in your own Chrome, riding the session you are already signed into.
Add a simple (no-required-options) DoorDash menu item to the cart by store id and item id.
{"storeId":"string (DoorDash store id, e.g. \"26013\")","itemId":"string (menu item id from doordash-get-menu, e.g. \"44480116629\")","quantity":"int (optional, default 1)"}{"ok":"bool (true when the cart accepted it)","cartId":"string|null","item":"{ id, name, price }","quantity":"int","quickAddEligible":"bool (false = the item needs required options; see Notes)","error":"string|null"}Adds an item to the cart. It resolves the item from the store page's server-rendered payload, so it runs from any doordash.com tab with no navigate first. Resolves in a single bridge call. The extractor lives as text in the site's localStorage (key doordash-add-to-cart) and is rebuilt on each call with synchronous eval. It is stored once and then persists across navigation and browser restarts, so on every run except the first it is already there.
WRITE. Every successful call adds the item to your real DoorDash cart. Reversible (remove it from the cart) and it never places or pays for an order, but the cart does change.
Default action: just call (step 1). The call itself reports NEEDS_STORE when the extractor is missing or out of date, only then do you store it (step 2) and call again.
Versioning. The stored extractor is gated by VER (must match the version in the frontmatter above). Both snippets carry VER = "1", keep them in sync with the frontmatter. When evaluate.js changes, bump version and VER in both snippets: the next call sees the mismatch, returns NEEDS_STORE, and re-stores automatically. Storage stays at two keys (doordash-add-to-cart and doordash-add-to-cart:ver), overwritten each store, versions never accumulate.
await (() => {
const KEY = "doordash-add-to-cart", VER = "1";
const s = localStorage.getItem(KEY);
if (!s || localStorage.getItem(KEY + ":ver") !== VER) return 'NEEDS_STORE';
const fn = eval('(' + s + ')');
return fn(document, { storeId: "26013", itemId: "44480116629", quantity: 1 });
})()
Returns ok: true and the cartId when the item landed. If it returns the string 'NEEDS_STORE', the extractor is missing or stale in this browser → do step 2 once, then run this exact call again.
Run this snippet verbatim. Never add an await before the eval, only the outer await is allowed.
Any doordash.com tab works, the extractor fetches everything itself and never reads the open page, so no navigation is needed. The tab only supplies the origin and session.
NEEDS_STORE; persists until localStorage is cleared)From any doordash.com tab, evaluate the following with the bridge, replacing <FN> with the contents of evaluate.js (the ( async (root, params) => { ... } ) function). Keep VER equal to the call snippet's:
(() => {
const KEY = "doordash-add-to-cart", VER = "1";
const fn = (<FN>);
localStorage.setItem(KEY, String(fn));
localStorage.setItem(KEY + ":ver", VER);
return 'stored';
})()
Returns "stored". The extractor passes through the model only here, once. Then go back to step 1.
Simple items only. quickAddEligible: false means the item needs required options (size, crust, ...) that a quick add cannot supply. The skill reports the flag but does not enforce it, DoorDash is the authority and rejects the mutation if the item truly cannot stand alone. If ok is false with quickAddEligible: false, that is why.
Needs a signed-in session. The mutation is signed with the csrf_token cookie, which only exists once you are logged in. Without it the call fails with an auth error.
NEEDS_STORE → extractor missing or version-mismatched in this browser; run step 2 once, then retry step 1.missing_params → storeId and itemId are both required.item_not_found_in_menu → no item with that id (and a quickAddContext) in that store; check the id against doordash-get-menu.menu_not_loaded / flight_not_found → the store page carried no usable payload; the storeId may be wrong.ok: false with an auth/csrf error → not signed in; log in at doordash.com once.{ "type": "json", "jsonPath": "ok" }