CleanSlice agent skills — architecture patterns, vertical slices, conventional commits for Claude Code and AI coding agents.
npx -y skills add CleanSlice/skills --skill instagramAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Instagram automation through a logged-in browser session — read profiles, post content, send DMs, scrape feeds. Use whenever the agent needs to interact with Instagram on behalf of a user.
SKILL.md
9.7 KB, as published. Nobody here has run it
Instagram doesn't have an official API for what most agents need (reading feeds, posting from personal accounts, DMs). The ranch platform solves this by driving a logged-in Chromium session from the browser-pool, with cookies kept on a persistent volume across pod restarts.
Quick Reference
| Need | Answer |
|---|---|
| Tool | browser_play |
| Profile | instagram:<handle> (e.g. instagram:miybot) |
| Login flow | User pushes cookies via the Ranch Cookies extension (no VNC) |
| Cookies persist | Yes — across pod restarts and reset commands |
| Fingerprint | Stealth plugin handles navigator.webdriver etc. |
| Logged-out detection | browser_play returns { needsLogin, hint } |
Step 0 — discover the profile (do this first)
Never guess the browser_play profile. Call integration_list and use
the exact profile it returns for the Instagram account:
const { accounts } = await integration_list()
const ig = accounts.find(a => a.service === "instagram")
// ig.profile is e.g. "instagram:miybot" — use it verbatim
If there's no Instagram account, the user hasn't connected one — tell
them to open /integrations. If status isn't connected, run the
login flow below.
Connecting an account (no VNC — extension-driven)
The user logs in to Instagram in their own Chrome and pushes cookies to Ranch via the Ranch Cookies extension. Ranch never opens an embedded browser or VNC view.
First-time setup:
- User opens
/integrationsin admin → clicks Instagram → enters handle (e.g.miybot) → Continue. - User opens
https://instagram.com/in their normal Chrome and logs in (handles 2FA, verification challenges, etc. — same as any regular browser session). - User clicks the Ranch Cookies extension icon → it auto-detects "instagram" → Send cookies.
- The integration row flips to
connectedautomatically. The agent can now callbrowser_play(profile: "instagram:miybot", …).
If the agent tries browser_play before this, it gets
{ needsLogin: true }. Do NOT try to do the login yourself —
instead, ask Ranch for instructions and forward them to the user (see
next section).
Login recovery (cookies expired)
Instagram drops sessions periodically. When browser_play returns
needsLogin: true, follow this exact flow:
// 1) Ask Ranch for the help URL + textual instructions
const help = await integration_request_login({
service: 'instagram',
accountKey: 'miybot',
})
// → { helpUrl, siteUrl, instructions }
// 2) Forward the instructions verbatim via the active channel
await ctx.send(help.instructions)
// (`help.helpUrl` is a clickable admin page that walks them through it.
// `help.siteUrl` opens Instagram directly. Both are inside `instructions`.)
// 3) STOP. Wait for the user to confirm they pushed cookies. Do NOT
// retry browser_play in a tight loop — give them time to log in.
// On Telegram: wait for any user message. In admin chat: poll the
// integration status until it flips to "connected".
// 4) When the user confirms, retry the original browser_play call
// as-is. The runtime's fallback will pick up the freshly-pushed
// cookies on the next attempt.
Never tell the user to "log in" without giving them the help link. Never try to handle the login yourself by filling username/password fields — Instagram detects this immediately and locks the account.
Common actions
Reading content — the reliable way. Instagram's rendered DOM uses obfuscated, rotating class names —
getText/clickon guessed selectors time out (the #1 failure mode). Read from two stable sources instead:
<meta property="og:*">tags — present in the static<head>of every profile and post page, before React renders. Selectors never rot.screenshot— the tool screenshots and runs vision automatically; use it as the fallback / cross-check.Never build a read on
waitForSelector+getText.
Read an account's latest post
One browser_play call: open the profile, jump to the newest post's
permalink, read the caption from og:description. The grid is
newest-first, so the first /p/ or /reel/ link is the latest post.
const result = await browser_play({
profile: ig.profile, // from integration_list — never guess
actions: [
{ kind: 'navigate', url: `https://www.instagram.com/${handle}/` },
{ kind: 'wait', ms: 5000 },
// Find the newest post, navigate to its permalink. setTimeout defers
// the nav so evaluate's return value marshals cleanly first —
// assigning location.href mid-evaluate destroys the JS context.
{ kind: 'evaluate', code: `
const a = document.querySelector('a[href*="/p/"], a[href*="/reel/"]');
if (a) setTimeout(() => { location.href = a.href; }, 150);
return a ? a.href : 'NO_POSTS';
` },
{ kind: 'wait', ms: 5000 },
// Caption lives in og:description / og:title — static <head>, no
// rotating selectors. h1 holds the rendered caption when React was
// fast enough.
{ kind: 'evaluate', code: `
const meta = p => document.querySelector('meta[property="'+p+'"]')?.content || null;
return {
url: location.href,
ogTitle: meta('og:title'),
ogDescription: meta('og:description'),
caption: document.querySelector('h1')?.innerText || null,
};
` },
{ kind: 'screenshot', fullPage: false },
],
})
og:description reads like "42 likes, 3 comments - handle on May 18, 2026: \"<caption>\"" — the caption is the quoted tail (truncated for
long posts). caption (the h1) has the full text when React rendered
in time; the screenshot vision description is the final fallback.
Report whichever field is non-empty.
Read a profile (bio, counts)
await browser_play({
profile: ig.profile,
actions: [
{ kind: 'navigate', url: `https://www.instagram.com/${handle}/` },
{ kind: 'wait', ms: 4000 },
{ kind: 'evaluate', code: `
const meta = p => document.querySelector('meta[property="'+p+'"]')?.content || null;
return { ogTitle: meta('og:title'), ogDescription: meta('og:description') };
` },
{ kind: 'screenshot', fullPage: false },
],
})
Read the user's own home feed
The home feed is the heaviest page on Instagram — give it a long wait.
await browser_play({
profile: ig.profile,
actions: [
{ kind: 'navigate', url: 'https://www.instagram.com/' },
{ kind: 'wait', ms: 8000 },
{ kind: 'evaluate', code: `
return [...document.querySelectorAll('article')].slice(0, 10).map(a => ({
text: a.innerText.slice(0, 500),
href: a.querySelector('a[href*="/p/"]')?.getAttribute('href') ?? null,
}));
` },
],
})
If browser_play crashes or stalls
Instagram intermittently crashes or hangs headless Chromium —
browser_play returns browser has been closed or hits its 100s hard
deadline. Retry the same call ONCE. A single retry usually succeeds;
do not loop beyond that — report the failure to the user instead.
Send a DM
await browser_play({
profile: 'instagram:miybot',
actions: [
{ kind: 'navigate', url: 'https://instagram.com/direct/t/<thread-id>/' },
{ kind: 'waitForSelector', selector: '[contenteditable="true"]', timeout: 10000 },
{ kind: 'fill', selector: '[contenteditable="true"]', value: 'Hello!' },
{ kind: 'press', selector: '[contenteditable="true"]', key: 'Enter' },
],
})
Read tokens for raw GraphQL
When the agent needs to make many parallel requests, browser_play with evaluate can extract fb_dtsg, lsd, and cookies from the live session:
await browser_play({
profile: 'instagram:miybot',
actions: [
{ kind: 'navigate', url: 'https://instagram.com/' },
{
kind: 'evaluate',
code: `
const html = document.documentElement.outerHTML
const dtsg = html.match(/"DTSGInitialData",\\[\\],\\{"token":"([^"]+)"/)?.[1]
const lsd = html.match(/"LSD",\\[\\],\\{"token":"([^"]+)"/)?.[1]
const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1]
return { dtsg, lsd, csrf, userAgent: navigator.userAgent }
`,
},
],
})
Then the agent can hand these to its HTTP layer for high-volume scraping — see references/raw-graphql.md if the workload needs it (most don't).
Anti-detection notes
- The pool already runs
puppeteer-extra-plugin-stealth. You don't need to disable headless mode or do anything special — the user-agent is replayed exactly as it was when cookies were captured. - Avoid rapid bursts (>1 navigate/second). Instagram's rate limits are aggressive on
/api/v1/*paths. - Never log in via raw HTTP. The login flow must run inside the pool browser via the VNC URL, otherwise the device-bound 2FA challenges won't validate.
Don't
- Don't fill the Instagram login form yourself. Always hand off to the user via
integration_request_login. IG detects scripted logins fast. - Don't store session cookies anywhere outside Ranch's per-user vault —
agent/secretis the wrong place for them. - Don't retry
browser_playautomatically afterneedsLogin— callintegration_request_login, forward instructions, then wait for the user's confirmation. - Don't reference
browser_login/browser_login_done/ VNC / "live browser" anywhere in your responses. Those tools no longer exist in this runtime. Useintegration_request_logininstead.