grow-instagram
Skill1 starsChanged 3 months agoApache-2.0View on GitHub
Runs one autonomous Instagram growth tick — reply, measure, refill, publish. Use when the user asks to "인스타 키워", "릴스 성장 루프", "인스타그램 성장 틱", "grow the Instagram account", or wants the growth loop running. One tick replies to inbox comments (golden hour first), snapshots account and reel insights (skip rate, average watch time), refills the publish queue by authoring a new reel end to end through autoproduce when it runs dry, and publishes queue-marked reels in the plan's slots — all inside the standing authorization in data/[channel]/growth/instagram/growth-plan.md. Recur with /loop [interval] /social-flow:grow-instagram [channel]. First run needs the init argument.
In this repository30 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- autoproduceSkill
- brandingSkill
- channelSkill
- compose-blenderSkill
- datagoSkill
- edit-speechSkill
- generate-cut-imagesSkill
- generate-cut-videosSkill
- grow-threadsSkill
- grow-youtubeSkill
- ingestSkill
- inspect-contentSkill
- introSkill
- platform-guideSkill
- produceSkill
- project-initSkill
- publishSkill
- review-cameraSkill
- review-contextSkill
- review-cut-imagesSkill
- review-cut-videosSkill
- review-recentSkill
- setup-instagramSkill
- setup-threadsSkill
- setup-youtubeSkill
- storyboardSkill
- topic-scoutSkill
- upscale-videoSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 3 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 207
Write growth-plan.md from the template, show the full text and get explicit approval, then save with status: approved.
- Distinctive: line 226
If the state's date bucket isn't today, reset the daily counters.
- Distinctive: line 271
Read the type of the top-reach reels (subject, length, format) and report the observations to the user — without this learning loop, …
- Very specific: line 289
If the plan isn't autoproduce.enabled: true, skip this stage whole.
- Distinctive: line 336
When research is insufficient, narrow unsupported claims or choose a supported candidate.
- Very specific: line 355
If several, the one with the oldest queue_at (file mtime if absent).
- Very specific: line 375
Never select a historical video-sub.mp4 or add captions because Instagram lacks a separate subtitle parameter.
- Very specific: line 383
Update storyboard.md frontmatter to queue_instagram: done (leave status: alone — rule 3)
- Very specific: line 400
If the plan's account_status_check cadence has lapsed (per state.lastAccountStatusPrompt), append one line to the report: "Check …
- Very specific: line 411
If autoproduce is on, also report from autoproduce.json: today's and this week's spend against the caps, the topic-pool level, the topics …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Automatic execution policy: Read automation guidance first. Creative rules and review findings guide improvements; scores, review/hash/freeze records, CTA choices and intermediate approval steps do not block authorized work. Preserve actual external-action authorization and stop at the artifact the user requested. This policy supersedes older quality-gate wording below.
Instagram growth loop — one autonomous tick
Growth skills are split per platform (same skeleton as grow-threads and
grow-youtube: plan = standing authorization · tick ≠ publish · idempotent
state). State paths are scoped under growth/instagram/.
/social-flow:grow-instagram <channel> init # once — fix the plan (HITL)
/loop 1h /social-flow:grow-instagram <channel> # hourly autonomous loopTwo differences from Threads set this skill's structure.
First, a reel is a video. One episode takes research, authoring, images,
voice, and a build. So publishing happens only from the queue, and there
are two ways to fill it. A human builds with
/social-flow:storyboard → produce and stamps queue_instagram: ready, or —
with plan autoproduce.enabled: true — the moment the queue runs dry, the
tick builds an episode itself and stamps the marker (§2.5, same as
grow-youtube).
Second, there is no stage for joining other people's conversations. Threads finds posts to join via keyword search, but the Instagram Login API this pipeline uses has no public post search. We don't imitate a feature that doesn't exist.
The channel's breakout push doesn't live here either. A caption carries no clickable link, this pipeline has no Stories endpoint, and this skill has no publishing stage outside the queue — three reasons a push can't be wired in later either. The push goes out on Threads only (grow-youtube §2b). What Instagram contributes to a breakout is the same episode, published as a reel through this queue.
references/growth-playbook.md is the source of truth for tactics (always
load before writing). That document carries only claims that passed
verification, and circulating folklore that was rejected sits in its own
do-not-cite list.
Absolute rules (violation = stop immediately)
- No publishing without a plan — if
data/<channel>/growth/instagram/growth-plan.mdis missing or its frontmatter isn'tstatus: approved, call no publish tool. The plan is the standing authorization the publish tools' descriptions refer to (growth-plan.mdstatus: approved) — if out-of-scope publishing is needed, ask the user to update the plan. - No publishing a reel without
queue_instagram: ready— queue eligibility is only topics whose storyboard.md frontmatterstatusisproducedorpublishedwithqueue_instagram: ready. produced only means "production finished", not "cleared to post" — whether it goes out is decided by thequeue_instagrammarker. Whypublishedcounts toward eligibility is in §3 (when another platform publishes first, the status flips). The marker is stamped by a human or by §2.5's autoproduce. Autoproduce stampsreadyon an episode it finished building, with the review findings recorded beside it;holdis for a technical delivery failure (the build or its media missing), a cost cap hit or missing authorization (skills/autoproduce/references/unattended-lifecycle.md#completion) — creative scores and style findings never hold an episode. And autoproduce itself runs only when the plan'sautoproduce.enabledturned it on. - Never change
status:as a publish result — even on success, all this loop updates isqueue_instagram. Flip it tostatus: publishedand grow-youtube's queue eligibility (status: produced) disappears — the same topic never reaches YouTube. The two loops read the same file. The one exception is a new topic §2.5's autoproduce creates — writingstatus: producedwhile creating a file that didn't exist is production-stage work, not publish-result recording.producedsatisfies both loops' queue eligibility. - The loop never creates hosting —
videoUrlmust be a public HTTPS URL. An autonomous loop standing up a temporary tunnel is forbidden (no one is there to verify the teardown). With planmedia_hosting: off, skip the publish stage whole and report only the queue level. The authoring stage (§2.5) turns off with it — don't spend money building a video with no road out. - No blind retry of non-idempotent publishes — on instagram_publish
failure, don't blindly repeat the same call. Duplicate reels cannibalize
each other's reach, and the metrics stay even after deletion. On a timeout,
next tick first check whether it published via the recent media list in
instagram_insights. - Don't cross the originality line — publish targets are our pipeline's outputs only. Files with another platform's watermark left on, re-edits that only change speed: never published. Fail the originality verdict and it doesn't end with one post — the whole account drops out of non-follower recommendations.
- No plaintext token exposure — tokens live only in files under
<SNS_TOKEN_DIR>/<slug>/(default~/.config/social-flow/<slug>/). Don't store whole API responses in state.json or growth-log.md (needed fields only). - Hard caps — publishing up to the plan's slot count (max 3/day);
autoproduce up to
daily_produce_cap(default 1) and the cost caps — never past 2 episodes/day for this plan (skills/autoproduce/references/unattended-lifecycle.md#daily-cap). Only inbox replies are uncapped. - Every tool call sets
channel: <channel-slug>— channel tokens only, no default-token fallback (prevents posting to the wrong account).
Deliberately not done
- Commenting on others' posts. The API has no public search, so there's no way to find targets — and a brand account commenting on unknown posts is spam-adjacent behavior that erodes account signals.
- Hiding comments.
sns_comment_moderatesupports Instagram, but the hide judgment is outside autonomous scope — spam gets reported only; the user decides. - Auto-checking Account Status. There is no API — the loop goes only as far as reminding (same pattern as grow-youtube's Related video).
- Hashtag-count optimization. Ground with no primary evidence, so the playbook offers no number. Putting them in the caption as topic markers is as far as evidence-backed execution goes.
File layout (all local — data/ is not committed)
data/<channel slug>/growth/
├── instagram/
│ ├── growth-plan.md # standing authorization (init writes it via HITL, status: approved)
│ ├── state.json # state carried across ticks (the basis for double-publish prevention)
│ └── growth-log.md # one line per tick + metric deltas (observation ledger)
├── autoproduce.json # channel-shared — authoring budget and history (shared with the YouTube loop)
├── .autoproduce.lock/ # channel-shared lock — keeps the two loops from authoring at once
└── breakout.json # channel-shared — YouTube writes the breakout candidate, Threads reads itThe last three are channel-shared (not split per platform). One video goes
out to both platforms, so budget and lock must be channel-level too — count
per platform and the caps leak double; lock per platform and the lock stops
nothing. breakout.json sits at the same level because the YouTube and Threads
loops are talking about one video across it. This loop neither reads nor
writes it — see the push note above.
The template and state schema are in references/growth-plan-template.md.
init — fix the plan (once, HITL)
- Load
data/<channel>/profile.md— if missing, point to/social-flow:channel addfirst and stop. Inherit tone, target, and forbidden subjects as the plan defaults, and read §4 media public hosting to set themedia_hostingdefault (undecided →off). Confirm the INSTAGRAM token with
sns_account_check(channel)→ oneinstagram_insights(channel, days: 7, mediaLimit: 3)call to verify scopes. Also verifysns_comment_inbox(channel, platforms: ["INSTAGRAM"], postLimit: 3)once (the comment scope is separate).A scope error at this step is normal — an existing token issued for publishing may lack
instagram_business_manage_insights. Relay the reissue guide carried in the error (token-setup.md) to the user and stop. Don't start the loop without insights (observing results is the loop's eyes).Fix the plan items with AskUserQuestion: 1–3 publish slots (the target's active hours), the weekly publish target (default 4 — the 3–5-per-week balance point in playbook §Rhythm), the media hosting mode (
base_url/staged/off), comment-reply tone (profile inheritance is the default), reply exclusions (spam, hostility), and the Account Status check cadence (default weekly).Autoproduce is asked separately — it's the point where the loop's authority widens from publishing to authoring, so it's never on by default. On or off; where topics come from (
pooldefault /keywords); the topic-pool entries; the per-episode cost cap (default $1.00); daily and weekly cost caps; authoring runs per day (default 1 — within this platform's hard cap of 2); minimum queue level (default 1); which platform queues to stamp on success (mark_queues— offer only platforms with an approved growth plan). If hosting isoff, say right there that autoproduce won't run either. If the profile §2 TTS engine isgeminiorelevenlabs, also say each episode carries a voice cost (about $0.015 per 400 characters on Gemini, $0.04 on ElevenLabs — switching to local changes the voice).- Write
growth-plan.mdfrom the template, show the full text and get explicit approval, then save withstatus: approved. Always state: "This plan is the standing authorization — the loop publishes reels stampedqueue_instagram: readypublicly in their slots with no per-publish approval, and replies to incoming comments on its own. Publishing goes out only by the public-URL rule written in the plan, and with hostingoffit doesn't publish. Stop by stopping /loop; change scope by editing the plan." If autoproduce is on, add one line: "When the queue runs dry, the loop picks a topic, builds a reel itself and publishes it before a human sees it; review findings are recorded with the episode, not a precondition. It stays within N episodes/day and $X per episode." - Initialize
state.json, write the growth-log.md header.
tick — autonomous cycle (default mode)
0. Load and gate
Load growth-plan.md (confirm approved), state.json,
references/growth-playbook.md.
If the state's date bucket isn't today, reset the daily counters.
1. Inbox replies (top priority — golden hour)
sns_comment_inbox(channel, platforms: ["INSTAGRAM"], sinceHours: 48) →
if summary.withinGoldenHour isn't 0, those comments come first. Write reply
copy per the playbook §Replies and post with
sns_comment_reply(platform: "INSTAGRAM").
Instagram replies attach only to top-level comments — to answer a nested
comment, pass the inbox response's parentCommentId as the commentId. Dedup
is guaranteed by the inbox's answeredByUs filter (if a top-level comment's
children include ours, it counts as handled).
Two reads help before the reply goes out — the style check
(check-style.py --surface reply) and the growth-post-reviewer agent, batched once
per tick. Use concrete copy findings to revise within the existing reply authorization.
A reply stays unsent only for an unresolved factual or context problem or missing
authorization; a creative shortfall is revised, not a reason to skip, and there is no
round count. Log the reason whenever a reply is not sent. The context to read, the exact
command, and what to attach per platform: reply-gate.md.
Instagram's row in that table: attach the original comment and the caption of our post it was left on.
Spam and hate comments get no reply, only a mention in the next tick summary (hiding is outside autonomous scope).
2. Insights snapshot (observation)
instagram_insights(channel, days: 7, mediaLimit: 10) → compare with
state.lastInsights, write one growth-log line with the follower delta and
reach trend, and update state.
The follower count uses account.followersCount — insights' follower_count
is an empty value for accounts under 100 followers, so it's unusable on a new
channel.
Read two metrics off reels (mediaProductType: "REELS").
reels_skip_rate is the hook verdict and ig_reels_avg_watch_time (ms) the
retention verdict — the ranking model directly predicts "probability of
watching under 3 seconds" and "probability of watching longer than 95% of
same-length viewers", so these two are the closest observables. Images and
carousels don't have these metrics (platform doesn't support them).
Read the type of the top-reach reels (subject, length, format) and report the observations to the user — without this learning loop, automation repeats the same video. Planning changes happen with a human in the storyboard pipeline.
Per-reel follow conversion can't be read. follows and profile_visits
are FEED-only (images, carousels); request them on a reel and you get a 400
while that media's metrics come back empty altogether (measured
2026-08-15). Account-side follows_and_unfollows is also empty on new
channels (same pattern as follower_count), so judge by the account's
profile_views delta and account.followersCount instead.
When reach craters, don't suspect the editing first. The diagnosis order is ① check Account Status ② the last 30 days' originals share ③ content (playbook §Diagnosis order). ① has no API, so put a check request in the tick report.
2.5 Queue refill — autoproduce (only when the plan turned it on)
If the plan isn't autoproduce.enabled: true, skip this stage whole.
Skip it on media_hosting: off too — don't spend money building a video
with no road out. If a hosting-less channel needs authoring, a human calls
/social-flow:autoproduce directly.
Build only when the queue runs dry. Author only when the count of
unpublished queue_instagram: ready topics is below the plan's
autoproduce.min_queue (default 1). Don't build ahead to stockpile —
inventory is money, and time-sensitive material goes stale into wrong
information.
Pre-authoring checks, in order (any failure: don't author, report the reason only):
- Queue
readylevel <min_queue autoproduce.json's today and this-week totals withindaily_cost_capandweekly_cap- Today's authoring count for this plan (success and failure included,
autoproduce.json
counts.<platform>) below min(daily_produce_cap, 2) — 2 is the per-platform daily cap (skills/autoproduce/references/unattended-lifecycle.md#daily-cap). Other platform loops' authoring doesn't count here - The channel lock (
data/<channel>/growth/.autoproduce.lock) can be taken — if not, the YouTube loop is authoring, so just move on
On pass, Read ${CLAUDE_PLUGIN_ROOT}/skills/autoproduce/SKILL.md and follow
the unattended-mode procedure as written — the invocation is
autoproduce <channel> unattended instagram (no topic: the plan's topic_source decides). Engines follow the saved
directory model settings in .social-flow/models.json (skills/_shared/model-settings.md);
no metric trend promotes a cut to another engine, and cost-tiers.md prices the
grades rather than choosing them. The budget rule is the cap check
(skills/autoproduce/references/unattended-lifecycle.md#cap-check): over the cap,
the plan shrinks under the same saved models. Pass the §2 metrics (skip rate,
average watch time) along as observations for the human's planning, not as an
engine switch.
On authoring success, stamp every platform queue in the plan's
autoproduce.mark_queues. One 9:16 master goes out to both platforms —
build per platform and the cost doubles. But stamp approved platforms only
(that platform's growth-plan.md at status: approved).
The loop cannot create the hosting URL (rule 4). With
media_hosting: base_url, the authored episode's public URL is determined as
<media_base_url>/<topic slug>/video.mp4, but putting the file there is
a human's job. So §3 rarely publishes right after autoproduce — being
blocked at the URL check (§3-4) is the normal case; put
"authored · awaiting upload" in the tick report so a human uploads the file.
When research is insufficient, narrow unsupported claims or choose a supported
candidate. Preserve completed work and actual findings; creative quality scores
alone do not drop the topic or hold its queue. Record all spend, including failed
attempts, in autoproduce.json. Missing hosting inputs, actual budget limits and
publication authorization remain operational boundaries.
3. Slot publishing (queue drain)
With plan media_hosting: off, skip this stage and report only the queue
level.
Scan the queue only when today has a slot whose time already passed · within
3 hours of it · not in filledSlots[today] (if several, the earliest one).
Slots more than 3 hours past are skipped (prevents the accident where a loop
that was off all day dumps the morning slot at night).
Queue candidates are the data/<channel>/episodes/*/storyboard/storyboard.md
whose frontmatter status is produced or published with
queue_instagram: ready, and whose topic isn't in state.publishedTopics.
If several, the one with the oldest queue_at (file mtime if absent).
Not excluding published matters. status is a single field with no
platform dimension, and grow-youtube flips it to published on publish
success. Accept only produced and, with the same topic queued on both
platforms, the moment YouTube goes out first the Instagram side is silently
dropped with no error — while the human believes it's queued. What prevents
double publishing is the queue_instagram marker and
state.publishedTopics, not status.
Pre-publish checks:
- Look for an existing INSTAGRAM row in
output/publish-log.md. If present, the topic already went out via the publish skill: don't publish, clean up toqueue_instagram: done, and report (this closes the gap opened by widening eligibility topublished). output/instagram/caption.mdexists — the caption body is read from here.- Resolve the video's public URL — with
media_hosting: base_urlit's<media_base_url>/<topic slug>/**video.mp4**; withstaged, the frontmattervideo_url. IG receives the clean master with no burned subtitles. Never select a historicalvideo-sub.mp4or add captions because Instagram lacks a separate subtitle parameter. Verify that a staged URL references the clean master. curl -sI <URL>to confirm 200 and a video MIME. On failure, don't publish and report the reason (the file isn't on the hosting yet — a human handles it).
instagram_publish(channel, videoUrl, caption). On success:
- Update storyboard.md frontmatter to
queue_instagram: done(leavestatus:alone — rule 3) - Record the permalink in
output/publish-log.md(same format as the publish skill) - Record in state's
filledSlotsandpublishedTopics
On failure, don't record filledSlots and don't retry within this tick either (the next tick retries the same slot). Write the failure reason in growth-log.
4. Save and report
Save state.json (update lastTickAt) → append one tick-summary line to
growth-log.md → one report line to the user:
[tick hh:mm] replies n · authored n($x, tier) · published n(slot) · followers ±n · skip rate 0.nn · avg watch n.n s.
A tick with no actions logs "observation only" — quiet ticks are normal.
If the plan's account_status_check cadence has lapsed (per
state.lastAccountStatusPrompt), append one line to the report: "Check
recommendation eligibility at app > Settings > Account Status".
status — state report
Summarize from state.json + the last 20 lines of growth-log.md + one
instagram_insights call: follower and reach trends, publishes in the last
7 days against the weekly target (3–5), the top 3 reach reels with skip rate
and average watch time, topics left in the queue, the next slot, and the last
Account Status check date. No publishing.
If autoproduce is on, also report from autoproduce.json: today's and this
week's spend against the caps, the topic-pool level, the topics stuck at
queue_instagram: hold, and the topics authored but not yet uploaded to
hosting — report the actual missing input or authorization.
Error handling (the loop keeps running)
- Scope error (with the reissue guide) → skip just that stage and put the guide in the tick summary. If it repeats next tick, recommend stopping the loop. The insights and comment scopes are separate, so one side can be blocked alone.
- Publish quota exceeded — 100 per 24-hour rolling window. This loop's cap (3/day) can't reach it, so if it trips, it's combined with another path (the publish skill, manual publishing).
- Container processing failure — reels are encoded after upload. If the server's polling ends in failure, it's URL accessibility or a codec problem, so don't retry; report the reason.
- Token expiry (Meta 60 days) → all publishing is blocked, so recommend stopping the loop and walk through the token-setup.md renewal steps.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

