$npx -y skills add PHY041/claude-agent-skills --skill reddit-cultivateReddit account cultivation for founders and indie developers. Uses AppleScript to control real Chrome — undetectable by anti-bot systems. Checks karma, finds rising posts, drafts comments, and posts directly. Triggers on "/reddit-cultivate", "check my reddit", "reddit maintenance
| 1 | # Reddit Cultivation Skill (AppleScript Chrome Control) |
| 2 | |
| 3 | Build and maintain Reddit presence by controlling your real Chrome browser via AppleScript. No Playwright, no Selenium, no API tokens. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## How It Works |
| 8 | |
| 9 | ``` |
| 10 | Claude Code → osascript → Chrome (real browser, logged in) → Reddit |
| 11 | ``` |
| 12 | |
| 13 | - AppleScript executes JavaScript in Chrome's active tab |
| 14 | - Chrome is already logged into Reddit → cookies sent automatically |
| 15 | - Same-origin fetch → no CORS, no detection, no IP blocks |
| 16 | - Reddit cannot distinguish this from human browsing |
| 17 | |
| 18 | --- |
| 19 | |
| 20 | ## Prerequisites |
| 21 | |
| 22 | - **macOS only** (AppleScript is a macOS technology) |
| 23 | - Chrome: View → Developer → Allow JavaScript from Apple Events ✓ (restart Chrome after enabling) |
| 24 | - User logged into Reddit in Chrome |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ## Method Detection (Run First) |
| 29 | |
| 30 | Chrome multi-profile can cause AppleScript to not see windows. Always detect first: |
| 31 | |
| 32 | ```bash |
| 33 | WINDOWS=$(osascript -e 'tell application "Google Chrome" to return count of windows' 2>/dev/null) |
| 34 | if [ "$WINDOWS" = "0" ] || [ -z "$WINDOWS" ]; then |
| 35 | echo "Use Method 2 (System Events + Console)" |
| 36 | else |
| 37 | echo "Use Method 1 (execute javascript)" |
| 38 | fi |
| 39 | ``` |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## Method 1: AppleScript Execute JavaScript (Preferred) |
| 44 | |
| 45 | Works when `count of windows > 0`. |
| 46 | |
| 47 | ### Navigate |
| 48 | |
| 49 | ```bash |
| 50 | osascript -e 'tell application "Google Chrome" to tell active tab of first window to set URL to "https://www.reddit.com/r/SideProject/rising/"' |
| 51 | ``` |
| 52 | |
| 53 | ### Execute JS & Read Result (document.title trick) |
| 54 | |
| 55 | ```bash |
| 56 | # Run JS that writes result to document.title |
| 57 | osascript -e 'tell application "Google Chrome" to tell active tab of first window to execute javascript "fetch(\"/api/me.json\",{credentials:\"include\"}).then(r=>r.json()).then(d=>{document.title=\"R:\"+JSON.stringify({name:d.data.name,karma:d.data.total_karma})})"' |
| 58 | |
| 59 | # Wait, then read title |
| 60 | sleep 2 |
| 61 | osascript -e 'tell application "Google Chrome" to return title of active tab of first window' |
| 62 | ``` |
| 63 | |
| 64 | ### JXA for Complex JS (avoids escaping hell) |
| 65 | |
| 66 | ```bash |
| 67 | osascript -l JavaScript -e ' |
| 68 | var chrome = Application("Google Chrome"); |
| 69 | var tab = chrome.windows[0].activeTab; |
| 70 | tab.execute({javascript: "(" + function() { |
| 71 | // Complex JS here — no escaping needed |
| 72 | fetch("/r/SideProject/rising.json?limit=10", {credentials: "include"}) |
| 73 | .then(r => r.json()) |
| 74 | .then(d => { |
| 75 | var posts = d.data.children.map(p => ({ |
| 76 | title: p.data.title.substring(0, 60), |
| 77 | score: p.data.score, |
| 78 | comments: p.data.num_comments, |
| 79 | id: p.data.name, |
| 80 | url: "https://reddit.com" + p.data.permalink |
| 81 | })); |
| 82 | document.title = "POSTS:" + JSON.stringify(posts); |
| 83 | }); |
| 84 | } + ")();"}); |
| 85 | ' |
| 86 | ``` |
| 87 | |
| 88 | --- |
| 89 | |
| 90 | ## Method 2: System Events + Console (Multi-Profile Fallback) |
| 91 | |
| 92 | When AppleScript can't see Chrome windows (multi-profile bug), use keyboard automation. |
| 93 | |
| 94 | ### Step 1: Copy JS to Clipboard |
| 95 | |
| 96 | ```bash |
| 97 | python3 -c " |
| 98 | import subprocess |
| 99 | js = '''(async()=>{ |
| 100 | let resp = await fetch('/api/me.json', {credentials: 'include'}); |
| 101 | let data = await resp.json(); |
| 102 | document.title = 'R:' + JSON.stringify({name: data.data.name, karma: data.data.total_karma}); |
| 103 | })()''' |
| 104 | subprocess.run(['pbcopy'], input=js.encode(), check=True) |
| 105 | " |
| 106 | ``` |
| 107 | |
| 108 | ### Step 2: Execute via Chrome Console Keyboard Shortcuts |
| 109 | |
| 110 | ```bash |
| 111 | osascript -e ' |
| 112 | tell application "System Events" |
| 113 | tell process "Google Chrome" |
| 114 | set frontmost to true |
| 115 | delay 0.3 |
| 116 | -- Cmd+Option+J = open/close Console |
| 117 | key code 38 using {command down, option down} |
| 118 | delay 1 |
| 119 | -- Select all + Paste + Enter |
| 120 | keystroke "a" using {command down} |
| 121 | delay 0.2 |
| 122 | keystroke "v" using {command down} |
| 123 | delay 0.5 |
| 124 | key code 36 |
| 125 | delay 0.3 |
| 126 | -- Close Console |
| 127 | key code 38 using {command down, option down} |
| 128 | end tell |
| 129 | end tell' |
| 130 | ``` |
| 131 | |
| 132 | ### Step 3: Read Title via System Events |
| 133 | |
| 134 | ```bash |
| 135 | sleep 3 |
| 136 | osascript -e ' |
| 137 | tell application "System Events" |
| 138 | tell process "Google Chrome" |
| 139 | return name of window 1 |
| 140 | end tell |
| 141 | end tell' |
| 142 | ``` |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | ## Workflow |
| 147 | |
| 148 | ### Step 1: Check Account Status |
| 149 | |
| 150 | Get username, karma, verify login using `/api/me.json`. |
| 151 | |
| 152 | ### Step 2: Scan Rising Posts |
| 153 | |
| 154 | For each tar |