Imported from d4ncboz/threads-affiliate (
AGENTS.md). Install upstream withnpx skills add d4ncboz/threads-affiliate. Copyright stays with the author.
AGENTS.md โ Instructions for AI Coding Agents
For: Claude Code, Cursor, GitHub Copilot, Codex, Cody, Continue, Aider, and any AI coding agent.
This document is the canonical operational guide for AI agents working with this repository. Read this BEFORE running any task. Treat each section as authoritative; the README is for humans.
โก Quick Identity
| Property | Value |
|---|---|
| Project | threads-affiliate |
| Domain | Indonesian affiliate marketing automation for Threads (Meta) |
| Language | Python 3.10+ |
| Browser engine | Playwright (Chromium) |
| Auth method | Session cookies (Instagram + Threads via Meta SSO) |
| License | MIT |
๐ฏ What This Project Does
Posts 2-3 post chains to Threads automatically, using affiliate links from a Markdown database, with anti-spam rotation and dedup logic. Battle-tested for skincare/parfum/haircare/makeup affiliate categories in Indonesian language.
Production use case: Posting 3x/day across 4 categories without manual intervention, while avoiding Threads' spam detection.
๐ Hard Constraints โ Read First
Things AI Agents MUST NOT Do
| Constraint | Why |
|---|---|
โ NEVER commit *.cookies.json, ~/.threads_poster/, data/affiliate_links.md, data/post_history.json |
Contains session secrets + personal data |
| โ NEVER post >5 times/day from a single account | Triggers Threads spam ban |
| โ NEVER reuse the same affiliate link (dedup will reject) | Spam detection |
| โ NEVER hardcode credentials into Python files | Use ~/.threads_poster/cookies/session.json |
| โ NEVER auto-follow / auto-DM / scrape other users | Violates Threads ToS hard |
โ NEVER modify posts[i] content in production without running dedup.check() first |
Will repost similar content โ spam flag |
Things AI Agents SHOULD Do
| Action | When |
|---|---|
โ
Always run dedup.check() before poster.post() |
Prevents duplicate posting |
| โ Always validate cookies before launching Playwright | Saves time on broken sessions |
| โ Always mark link USED after successful post | Maintains database integrity |
| โ Use exit codes (see below) for status communication | Allows cron monitoring |
โ
Log to logs/cron.log for batch runs |
Audit trail |
๐ Setup Checklist
When a human asks you to "set up" this project, complete this checklist:
[ ] 1. Python 3.10+ installed โ `python --version`
[ ] 2. Dependencies installed โ `pip install -r requirements.txt`
[ ] 3. Playwright browsers installed โ `playwright install chromium`
[ ] 4. User has Instagram + Threads account โ ask, then refer to docs/01-account-setup.md
[ ] 5. User has Shopee Affiliate approval โ ask, then refer to docs/01-account-setup.md
[ ] 6. Cookies extracted to disk โ run `python -m threads_poster.cli setup --extract-cookies`
[ ] 7. Cookies validated โ run `python -m threads_poster.cli setup --validate-cookies`
[ ] 8. Affiliate database populated (โฅ30 links) โ ask user or refer to docs/02-database-setup.md
[ ] 9. First test post (in --headed mode) โ `python -m threads_poster.cli post-auto --category skincare --headed`
[ ] 10. Cron configured โ see examples/cron_integration.sh
If any step fails, STOP and report which step + the specific error. Do NOT proceed.
๐ Canonical Workflows
Workflow A: Single Post
# Use when: user says "post about [product]"
python -m threads_poster.cli post \
--product "PRODUCT_NAME" \
--link "https://s.shopee.co.id/XXXXX" \
--category {skincare|parfum|haircare|makeup} \
--hook-style {edukasi|validasi_mental|storytelling|problem_solving|hook_pancingan|transformasi|social_proof|urgency|controversy} \
--image /path/to/image.jpg # optional but recommended
Pre-conditions check:
- Cookies file exists at
~/.threads_poster/cookies/session.json - Image file exists if
--imageprovided - Affiliate link starts with
https://s.shopee.co.id/
Post-conditions check:
- Exit code 0 = success
data/post_history.jsonhas new entry with today's timestamp- If
--dbwas used, link is marked USED in database
Workflow B: Auto-Pick from Database
# Use when: user says "post next product in [category]"
python -m threads_poster.cli post-auto \
--category skincare \
--db data/affiliate_links.md \
--hook-style edukasi # optional, random if omitted
Pre-conditions check:
data/affiliate_links.mdexists and has โฅ1 UNUSED entry in target category- Cookies valid
Workflow C: Scheduled Batch
# Use when: user says "schedule posting" or "set up cron"
# Show: cat examples/cron_integration.sh
# Customize times/categories based on user's timezone (WIB = UTC+7)
Workflow D: Adding New Hook Style
User wants to add a new hook style "controversy_v2":
# 1. Edit templates/hooks_{category}.json
# 2. Add new key under "hooks":
# "controversy_v2": ["Variant 1 {product}", "Variant 2 {product}", ...]
# 3. Restart any running scheduler
# No code changes needed
Workflow E: Adding New Category
User wants to add "fashion" category:
# 1. Create templates/hooks_fashion.json (copy structure from hooks_skincare.json)
# 2. Add to templates/post2_templates.json:
# "fashion": ["Outfit review template 1 {product}", ...]
# 3. Update threads_poster/content_generator.py:
# CATEGORIES = ["skincare", "parfum", "haircare", "makeup", "fashion"]
# 4. Update threads_poster/cli.py:
# Find `choices=CATEGORIES` โ already references the constant, no change needed
# 5. Run tests: pytest tests/
๐ File Map for AI Agents
When user references a concept, here's where to find it:
| Concept | File |
|---|---|
| Hook templates | templates/hooks_{category}.json |
| Mid-post review templates | templates/post2_templates.json |
| CTA templates | templates/post3_cta_templates.json |
| Hook generation logic | threads_poster/content_generator.py |
| Dedup rules | threads_poster/dedup.py |
| Database operations | threads_poster/database.py |
| Playwright posting logic | threads_poster/poster.py |
| Cookie extraction | threads_poster/cookie_manager.py |
| CLI commands | threads_poster/cli.py |
| Cookie storage | ~/.threads_poster/cookies/session.json |
| Database | data/affiliate_links.md |
| Post history | data/post_history.json |
| Logs | logs/cron.log |
๐จ Exit Codes
The CLI uses standardized exit codes. Cron monitors should check these:
| Code | Meaning | Action |
|---|---|---|
| 0 | Success | Continue |
| 1 | Generic error / exception | Check logs, retry once |
| 2 | Cookies expired or invalid | Run setup --extract-cookies to refresh |
| 3 | Dedup rejected (link/hook reused) | Skip; try different category or hook |
| 4 | No UNUSED links available in DB | Add more links via db --add |
| 5 | Playwright/network failure | Retry with backoff; check internet |
| 6 | Image file not found | Continue without image OR fix path |
| 7 | Account locked / suspended | STOP all automation; manual intervention |
For Python callers:
result = poster.post(...)
if not result.success:
if "cookies" in result.error.lower():
sys.exit(2)
elif "dedup" in result.error.lower():
sys.exit(3)
# etc.
๐ Environment Variables
The toolkit honors these env vars (override defaults):
| Variable | Default | Purpose |
|---|---|---|
THREADS_COOKIES_PATH |
~/.threads_poster/cookies/session.json |
Cookie file location |
THREADS_DB_PATH |
data/affiliate_links.md |
Database file path |
THREADS_HISTORY_PATH |
data/post_history.json |
Post history file |
THREADS_TEMPLATES_DIR |
templates/ |
Template directory |
THREADS_HEADLESS |
true |
false for visible browser (debug) |
THREADS_USERNAME |
(none) | For post verification (e.g. @yourname) |
THREADS_TIMEOUT_MS |
30000 |
Playwright operation timeout |
THREADS_LOG_LEVEL |
INFO |
DEBUG/INFO/WARNING/ERROR |
THREADS_IMAGE_DIR |
data/product_images/ |
Image lookup directory |
๐งช Testing
Before any PR/commit, run:
# Unit tests (no Playwright needed, fast)
pytest tests/ -v
# All tests should pass without --skip flags
# Coverage target: >70% for threads_poster/*.py
When asked to add a feature, you MUST also add tests.
Tests go in tests/test_*.py. Use pytest fixtures for templates_dir (see existing test_core.py).
๐ Code Style
- Python: PEP 8, max 100 cols
- Type hints: Required for public functions
- Docstrings: Google style, required for classes + public methods
- Imports: stdlib โ third-party โ local, alphabetical within each group
- String formatting: f-strings preferred over
.format()or% - Path handling: Use
pathlib.Path, notos.path - JSON: Always
indent=2,ensure_ascii=Falsefor ID text
๐ Common Tasks Quick Reference
# Refresh cookies (run after Chrome re-login)
python -m threads_poster.cli setup --extract-cookies
# Check database stats
python -m threads_poster.cli db --stats
# List unused links by category
python -m threads_poster.cli db --list-unused --category skincare
# Add new link to database
python -m threads_poster.cli db --add \
--category skincare \
--product "Product Name" \
--link "https://s.shopee.co.id/XXXXX"
# Reset all links to UNUSED (start new month)
python -m threads_poster.cli db --reset
โ ๏ธ Error Handling Patterns
When you encounter these errors during execution, here's the canonical fix:
"Cookies expired"
python -m threads_poster.cli setup --extract-cookies
# Then retry the original command
"No UNUSED links available"
Either:
- Tell the user to add more links (provide command above)
- Or suggest
db --resetif it's a new posting cycle
"Editor element not found"
This means Threads UI changed. Steps:
- Run with
--headedflag to see what's happening - Inspect DOM in DevTools
- Update selectors in
poster.pyโ look for_click_text()and editor search
"Account locked"
STOP. Tell the user:
- Manually log in to Instagram + Threads via mobile app
- Complete any security challenges (selfie, ID upload)
- Wait 24-48 hours
- Resume with reduced frequency (1 post/day for 1 week)
๐ Indonesian Language Conventions
Hook templates use specific Indonesian gen-Z casual register:
- Pronouns:
gw/gue(1st person),lo(2nd person) โ NOTaku/kamu - Emojis: Max 2 per hook, placed at sentence end
- Length: Hook โค 80 chars (mobile first-line visibility)
- Tone: Confident but not aggressive, conversational not formal
- Forbidden words: "promo", "diskon gila", "beli sekarang" (triggers Threads spam filter)
When generating new hooks, follow this pattern. When user asks for translations to formal Indonesian, output them in a separate templates/hooks_*_formal.json file.
๐ When to Escalate to Human
You SHOULD NOT proceed and MUST ask the human when:
- Cookies fail to extract (Chrome profile not found, browser_cookie3 errors)
- Threads UI shows captcha/2FA prompt during posting
- Account shows "Action Blocked" or "Suspended" notice
- User wants to post >5 times/day (against project guidelines)
- User asks to bypass dedup with
--forceflag (warn first) - User wants to add scraping of external user data (out of scope)
- Database has <5 UNUSED links (warn before draining)
๐ Additional Documentation
For deeper context, read in order:
- README.md โ User-facing intro
- docs/01-account-setup.md โ Human signup steps
- docs/02-database-setup.md โ Database structure
- docs/03-cookie-extraction.md โ Auth flow
- docs/04-quickstart.md โ First post tutorial
- docs/05-customization.md โ Hooks, voice, schedule
- docs/06-troubleshooting.md โ Error patterns
- docs/ethics-and-tos.md โ Legal/ethics
๐ค Contributing as an AI Agent
If you're an AI agent making changes:
- Read the relevant doc(s) before editing
- Run tests before committing
- Update tests if you change behavior
- Update this AGENTS.md if you change workflows/exit codes/env vars
- Write commit messages in conventional commit format:
feat:new featurefix:bug fixdocs:documentationrefactor:no behavior changetest:test only
- Never commit secrets (use
.gitignore)
Repository: https://github.com/d4ncboz/threads-affiliate
Issues: https://github.com/d4ncboz/threads-affiliate/issues
License: MIT