From 6194d240d1db5992a1e9c9a8277c352a4f4c4f89 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 22:30:17 +0200 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20xurl=20skill=20=E2=80=94=20cost-o?= =?UTF-8?q?ptimized=20X/Twitter=20via=20sandbox=20+=20live=20modes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add xurl skill to fiale-awesome-skills plugin wrapping the official X CLI with pay-per-use cost awareness and local playground sandbox integration. Key features: - Sandbox-first via API_BASE_URL → playground (zero cost, same commands) - Batch reads (/2/tweets?ids=...) for ~75% bulk savings - Compose + confirm gate for live writes with trust mode opt-in - Configurable session spending limits ($0.25 / $2.00 / $10.00 tiers) - Mode prefixes [SANDBOX] / [LIVE $X.XX] on every output - Error recovery for playground down, auth expired, 429, limit hit - Quick-pick menu and workflow templates (post+monitor, search+engage) Also adds fiale-awesome-skills to root README plugin table. Bumps plugin version to 0.2.0. --- .claude-plugin/marketplace.json | 10 +- README.md | 23 + .../.claude-plugin/plugin.json | 6 +- plugins/fiale-awesome-skills/README.md | 38 +- plugins/fiale-awesome-skills/commands/xurl.md | 10 + plugins/fiale-awesome-skills/skills/xurl.md | 405 ++++++++++++++++++ 6 files changed, 484 insertions(+), 8 deletions(-) create mode 100644 plugins/fiale-awesome-skills/commands/xurl.md create mode 100644 plugins/fiale-awesome-skills/skills/xurl.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0c2ebd2..1d80ad1 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -115,8 +115,8 @@ }, { "name": "fiale-awesome-skills", - "description": "Curated Claude Code skills for developer security and productivity — supply chain hardening across npm, Python, Go, Rust, and Homebrew (macOS)", - "version": "0.1.0", + "description": "Curated Claude Code skills — supply chain hardening (macOS) and cost-optimized X/Twitter workflows via xurl CLI with local sandbox testing", + "version": "0.2.0", "author": { "name": "Pavel Fadeev", "email": "pavel.fadeev@gmail.com", @@ -131,7 +131,11 @@ "hardening", "npm", "pip", - "macos" + "macos", + "xurl", + "twitter", + "x-api", + "social-media" ] }, { diff --git a/README.md b/README.md index 6be39bd..2cd392b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Plugins for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). | [mdbrowser](#mdbrowser) | Browse any URL as clean markdown | | [Vibes](#vibes) | Sentiment-driven musical phrases on task completion | | [Remote Layout](#remote-layout) | Mobile-friendly response formatting for remote sessions | +| [Awesome Skills](#awesome-skills) | Supply chain hardening, cost-optimized X/Twitter via xurl | --- @@ -177,6 +178,28 @@ Switch Claude's response format for small screens. Persists across sessions via --- +## Awesome Skills + +**Curated skills that turn Claude into a specialist.** + +Developer security and productivity skills — supply chain hardening across npm/Python/Go/Rust/Homebrew, and cost-optimized X/Twitter workflows via [xurl](https://github.com/xdevplatform/xurl) with local sandbox testing through [playground](https://github.com/xdevplatform/playground). + +| Skill | What it does | +|-------|-------------| +| `/supply-chain-hardening` | Audit and harden your macOS system against supply chain attacks | +| `/xurl` | Post, search, engage on X/Twitter with spending guardrails and free sandbox mode | + +**Install:** +```bash +/plugin marketplace add fiale-plus/fiale-claude-plugins +/plugin install fiale-awesome-skills +/xurl +``` + +→ [Full documentation](plugins/fiale-awesome-skills) + +--- + ## License MIT diff --git a/plugins/fiale-awesome-skills/.claude-plugin/plugin.json b/plugins/fiale-awesome-skills/.claude-plugin/plugin.json index 833ba7a..e1dd9b6 100644 --- a/plugins/fiale-awesome-skills/.claude-plugin/plugin.json +++ b/plugins/fiale-awesome-skills/.claude-plugin/plugin.json @@ -1,8 +1,8 @@ { "name": "fiale-awesome-skills", - "version": "0.1.0", - "description": "Curated Claude Code skills for developer security and productivity — supply chain hardening across npm, Python, Go, Rust, and Homebrew (macOS)", + "version": "0.2.0", + "description": "Curated Claude Code skills — supply chain hardening (macOS) and cost-optimized X/Twitter workflows via xurl CLI with local sandbox testing", "author": { "name": "Pavel Fadeev", "email": "pavel.fadeev@gmail.com", "url": "https://fiale.plus" }, "license": "MIT", - "keywords": ["claude-code", "security", "supply-chain", "hardening", "npm", "pip", "macos"] + "keywords": ["claude-code", "security", "supply-chain", "hardening", "npm", "pip", "macos", "xurl", "twitter", "x-api", "social-media"] } diff --git a/plugins/fiale-awesome-skills/README.md b/plugins/fiale-awesome-skills/README.md index cee026c..80f58fe 100644 --- a/plugins/fiale-awesome-skills/README.md +++ b/plugins/fiale-awesome-skills/README.md @@ -4,13 +4,12 @@ Curated Claude Code skills for developer security and productivity. -**Platform: macOS only** (for now). - ## Skills | Skill | Command | What it does | |-------|---------|-------------| | supply-chain-hardening | `/supply-chain-hardening` | Audit and harden your system against supply chain attacks across npm, Python, Go, Rust, and Homebrew | +| xurl | `/xurl` | Cost-optimized X/Twitter workflows via xurl CLI with local sandbox testing | ## Supply Chain Hardening @@ -30,12 +29,47 @@ Prompted by the axios npm RAT and LiteLLM PyPI credential stealer (both March 20 All changes require user confirmation. Existing configs are backed up before modification. Rollback instructions included. +## xurl + +Post, reply, search, and engage on X/Twitter through [xurl](https://github.com/xdevplatform/xurl) — the official X CLI — with pay-per-use cost awareness baked in. + +**Two operating modes:** + +| Mode | Cost | How | +|------|------|-----| +| **Sandbox** | $0 | Local [playground](https://github.com/xdevplatform/playground) server simulates the full X API v2 | +| **Live** | Real credits | Compose → confirm → post. Spending guardrails with configurable limits | + +**Cost optimization features:** +- Defaults to sandbox when playground is running — unlimited free testing +- Cost preview: see what sandbox operations would cost before going live +- Configurable session spending limits (default $0.25) +- Estimated cost shown before every live write operation +- Real usage tracking via X API's `/2/usage/tweets` endpoint +- 24h UTC deduplication awareness (same resource = 1 charge per day) + +**Pay-per-use pricing** (community-sourced estimates, check Developer Console for authoritative rates): + +| Operation | Cost | +|-----------|------| +| Post read | $0.005 | +| User lookup | $0.010 | +| Post write | $0.010 | +| DM send | $0.015 | +| Like / follow / repost | $0.015 | + +**Prerequisites:** +- [xurl](https://github.com/xdevplatform/xurl) CLI installed (`brew install --cask xdevplatform/tap/xurl`) +- X Developer account with app credentials (registered manually) +- Optional: [playground](https://github.com/xdevplatform/playground) for free sandbox testing + ## Install ```bash /plugin marketplace add fiale-plus/fiale-claude-plugins /plugin install fiale-awesome-skills /supply-chain-hardening +/xurl ``` ## License diff --git a/plugins/fiale-awesome-skills/commands/xurl.md b/plugins/fiale-awesome-skills/commands/xurl.md new file mode 100644 index 0000000..3f80f76 --- /dev/null +++ b/plugins/fiale-awesome-skills/commands/xurl.md @@ -0,0 +1,10 @@ +--- +name: xurl +description: Cost-optimized X/Twitter workflows via xurl CLI with local sandbox +--- + +# xurl + +Run the xurl skill. Post, reply, search, and engage on X/Twitter with pay-per-use cost awareness. Defaults to local sandbox (playground) when available — zero cost for testing. Switches to live API on explicit request with spending guardrails and confirmation gates. + +Requires xurl CLI installed. Optional: playground server for free sandbox testing. diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md new file mode 100644 index 0000000..0cdd08f --- /dev/null +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -0,0 +1,405 @@ +--- +name: xurl +description: Cost-optimized X/Twitter workflows via xurl CLI with local sandbox testing. Posts, replies, search, timeline, engagement — all wrapped with spending guardrails, sandbox-first defaults, and pay-per-use cost awareness. Triggers on "/xurl", "post to X", "tweet", "post on twitter", "search X", "check mentions", "xurl post", "send a tweet", "check my timeline", "search twitter", "search and engage", "monitor engagement", "like from search", "review timeline", "batch like", "post and watch". +--- + +# xurl — Cost-Optimized X/Twitter Operations + +Run the X API through xurl with aggressive cost optimization. Every penny counts — default to sandbox, confirm before spending, track costs in real time. + +**Announce at start:** "Using xurl for cost-optimized X/Twitter operations." + +## Security Rules (mandatory, from xurl SKILL.md) + +These are non-negotiable. Violating any of them leaks credentials. + +- **Never read, print, parse, or send `~/.xurl`** to the conversation. This file contains secrets. +- **Never use `--verbose` / `-v`** — it exposes auth headers/tokens in output. +- **Never use inline secret flags** in agent commands: `--bearer-token`, `--consumer-key`, `--consumer-secret`, `--access-token`, `--token-secret`, `--client-id`, `--client-secret`. +- **Credential registration is manual.** The user must run `xurl auth apps add ...` and `xurl auth oauth2` themselves, outside this session. Do not execute auth commands with secrets. +- To check auth state safely: `xurl auth status` (shows apps and token status without exposing secrets). + +## Step 1: Environment Detection + +Run silently — only surface issues, not successes: + +```bash +which xurl 2>/dev/null && echo "xurl: installed" || echo "xurl: NOT FOUND" +which playground 2>/dev/null && echo "playground: installed" || echo "playground: NOT FOUND" +playground status 2>&1 || true +echo "API_BASE_URL=${API_BASE_URL:-'(not set)'}" +xurl auth status 2>&1 || true +cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" +``` + +**Determine mode and act:** + +- **Everything configured, session config exists**: skip setup entirely. Show one-line header and proceed with the user's request: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` +- **Playground running, no session config**: "Ready. Sandbox mode active. What would you like to do?" +- **Only xurl auth, no playground**: "No sandbox running. Live mode — real credits will be used." +- **`API_BASE_URL` set to localhost**: announce "Sandbox mode active via API_BASE_URL." +- **xurl not installed**: show install options and stop +- **Auth not configured**: show manual setup instructions and stop + +**If xurl is not installed:** +```bash +brew install --cask xdevplatform/tap/xurl # macOS +npm install -g @xdevplatform/xurl # npm +go install github.com/xdevplatform/xurl@latest # Go +``` + +**If playground is not installed** and user wants sandbox: +```bash +go install github.com/xdevplatform/playground/cmd/playground@latest +playground start # serves at http://localhost:8080 +playground refresh # update OpenAPI spec cache (optional) +``` + +**If xurl auth is not configured**, tell the user to run these themselves (outside this session): +``` +xurl auth apps add my-app --client-id --client-secret +xurl auth oauth2 +``` +They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. + +## Step 2: Sandbox Mode (playground) + +When playground is running, **default to sandbox** for all operations. This costs $0. + +Set the `API_BASE_URL` env var to redirect all xurl commands to the local playground: + +```bash +export API_BASE_URL=http://localhost:8080 +``` + +Now **all shortcut commands work against sandbox** — same syntax as live mode: + +```bash +xurl post "Hello from sandbox!" +xurl search "query" -n 5 +xurl user @handle +xurl timeline -n 20 +xurl mentions -n 10 +xurl read POST_ID +xurl reply POST_ID "Nice!" +xurl like POST_ID +``` + +Raw path access also works (playground accepts any auth token): + +```bash +xurl /2/users/me +xurl -X POST /2/tweets -d '{"text":"sandbox post"}' +``` + +Prefix all sandbox output with `[SANDBOX]`. + +### Cost preview + +After testing a workflow in sandbox, show what it would cost on the real API: + +```bash +# Simulated cost breakdown +curl -s http://localhost:8080/api/accounts/0/cost | jq '.' + +# Current pricing rates (may reflect real X API rates) +curl -s http://localhost:8080/api/credits/pricing | jq '.' + +# Usage breakdown by event type and request type +curl -s "http://localhost:8080/api/accounts/0/usage?interval=30days&groupBy=eventType" | jq '.' +curl -s "http://localhost:8080/api/accounts/0/usage?interval=30days&groupBy=requestType" | jq '.' +``` + +Present the cost preview to the user before they switch to live mode. + +### State management and error simulation + +```bash +# Save current state +curl -s http://localhost:8080/state/export > /tmp/xurl-playground-state.json + +# Restore a saved state +curl -s -X POST -H "Content-Type: application/json" \ + -d @/tmp/xurl-playground-state.json http://localhost:8080/state/import + +# Reset to fresh defaults +curl -s -X POST http://localhost:8080/state/reset + +# Force-save state to disk +curl -s -X POST http://localhost:8080/state/save + +# Simulate rate limiting to test error handling +curl -X PUT http://localhost:8080/config/update \ + -H "Content-Type: application/json" \ + -d '{"errors": {"enabled": true, "error_rate": 0.3, "error_type": "rate_limit"}}' + +# Disable error simulation +curl -X PUT http://localhost:8080/config/update \ + -H "Content-Type: application/json" \ + -d '{"errors": {"enabled": false}}' +``` + +Web UI for browsing sandbox data: `http://localhost:8080/playground` + +### Switching to live + +When the user says "go live", "publish", "send it for real", or similar: + +```bash +unset API_BASE_URL +echo "Now targeting: ${API_BASE_URL:-https://api.x.com}" +``` + +Show the mode banner: "[LIVE MODE] Switched. Session limit: $X.XX. Every write will show its cost inline." + +**For any ambiguous write command** when mode is not explicit: default to sandbox if playground is running. If playground is not running, ask before going live — never silently default to live. + +## Step 3: Live Mode + +Prefix all live output with `[LIVE $X.XX]` showing the cost of each operation. + +Use xurl shortcut commands against the real API: + +```bash +xurl post "text" # $0.01 +xurl reply POST_ID "text" # $0.01 +xurl quote POST_ID "text" # $0.01 +xurl read POST_ID # $0.005 +xurl search "query" -n 10 # ~$0.005 × results (see note) +xurl user @handle # $0.01 +xurl timeline -n 20 # ~$0.005 × results +xurl mentions -n 10 # ~$0.005 × results +xurl like POST_ID # $0.015 +xurl repost POST_ID # $0.015 +xurl follow @handle # $0.015 +xurl bookmark POST_ID # $0.015 +xurl dm @handle "message" # $0.015 +xurl dms -n 10 # ~$0.01 × results +xurl media upload file.jpg # $0.01 +``` + +Note: search/timeline/mentions billing per-post vs per-request is not publicly confirmed by X. The ~$0.005/result is community consensus. Use the lowest `-n` value that satisfies your need — it directly controls cost. + +### Batch reads (major cost saver) + +When reading 3+ posts, prefer the batch endpoint over individual reads: + +```bash +# Individual: 10 reads × $0.005 = $0.05 +xurl read ID1 && xurl read ID2 && ... # expensive + +# Batch: up to 100 IDs in one request — significantly cheaper +xurl /2/tweets?ids=ID1,ID2,ID3,...,ID10 +``` + +Community data suggests batch reads cost ~75% less than equivalent individual reads. Always prefer batch when reading multiple known post IDs. + +### Write operations — compose + confirm + +**Single writes**: inline confirmation — "[LIVE] Post: «{text}» ($0.01) — confirm?" + +**Batch of same type** (user described the plan): one confirmation showing all items, total cost, and count — no per-item prompts. Example: "Like 5 posts ($0.075 total) — confirm?" + +**First write of session**: always individual confirmation, even in batch context. + +**Trust mode**: after the first confirmed write of a given type, offer "Skip confirmations for [likes/reposts/etc.] this session?" Store in `/tmp/xurl-session-config.json` under `trusted_actions`. The spending limit guardrail remains active regardless. + +**Before batch reads** (search, timeline, mentions with -n > 10), show estimated cost inline — no separate confirmation. + +Post IDs and full URLs both work: `xurl read https://x.com/user/status/123` extracts the ID automatically. + +## Step 4: Spending Guardrails + +Check for existing session config (already done in Step 1). If config exists with a spending limit, use it silently. + +If no config exists AND user is in live mode, ask once inline: +> "No session limit set. Suggestions: $0.25 (casual), $2.00 (dev testing), $10.00 (automation). Default $0.25 — proceed? (or say an amount)" + +Do not block sandbox operations for missing config — sandbox is always free. + +Store in `/tmp/xurl-session-config.json`: +```json +{"spending_limit": 0.25, "spent": 0.0, "operations": 0, "trusted_actions": []} +``` + +This file persists across sessions until reboot (`/tmp`). Delete manually to reset. + +Track spending locally using approximate costs: + +| Operation | Approximate Cost | +|-----------|-----------------| +| Post read | $0.005 | +| Batch read (N posts) | ~$0.005 × N × 0.25 | +| User lookup | $0.010 | +| Post write | $0.010 | +| DM read | $0.010 | +| DM send | $0.015 | +| Engagement (like/follow/repost) | $0.015 | + +These are community-sourced estimates. X does not publish per-endpoint rates publicly — the Developer Console is the only authoritative source, and prices can change without notice. + +Update the tracker after every live API call. Guardrails: +- **80% of limit** → yellow warning: "Approaching spending limit ($X.XX / $Y.YY)" +- **100% of limit** → hard stop with options: + 1. Raise limit: "set limit $X.XX" + 2. Continue in sandbox (no further live writes) + 3. End session — show summary + +The Developer Console spending limit is your absolute safety net — set it to your maximum (e.g., $10/month for casual use). The session limit here is your active-use guardrail. Both should be configured. + +## Step 5: Common Workflows + +### Post + Monitor Engagement +``` +1. xurl post "..." → [LIVE $0.01] → note the post ID +2. Wait (user-specified interval) +3. xurl read {ID} → check public_metrics +4. If engagement exceeds user's threshold, reply or repost +``` + +### Search + Engage +``` +1. xurl search "query" -n 10 → review results (sandbox first if available) +2. Go live: batch-confirm engagement (like/repost) in one prompt +3. Show summary: "Engaged with N posts, spent $X.XX" +``` + +### Timeline Review +``` +1. xurl timeline -n 20 → surface results +2. User flags interesting posts +3. Batch-engage with flagged posts: one confirmation for the whole set +``` + +## Step 6: Error Recovery + +**Playground not running (user expects sandbox):** +``` +Sandbox not available — playground is not running. + 1. Start sandbox: `playground start` (~2s) + 2. Switch to live mode (real credits) + 3. Cancel +Which? (default: 1) +``` + +**Auth token expired mid-session (401):** +``` +[LIVE] Auth error — token may have expired. +Run outside this session: xurl auth oauth2 +Sandbox available in the meantime. +``` + +**Rate limited (429) — not billed:** +``` +[LIVE] Rate limited (429) — this request was NOT billed. +Next retry: check x-rate-limit-reset header. + 1. Wait and retry automatically + 2. Reduce batch size (e.g., -n 5 instead of -n 20) + 3. Cancel +``` + +**Spending limit reached:** +``` +[LIVE] Spending limit reached ($X.XX / $Y.YY). + 1. Raise limit: "set limit $Z.ZZ" + 2. Continue in sandbox (no further live writes) + 3. End session — show summary +Sandbox operations remain available. +``` + +## Step 7: Multi-Profile Setup + +Show this recommendation **after the user's first successful live write**, not at session start. + +If the user has only one app configured, suggest: + +- **Playground** — local sandbox, unlimited free testing +- **dev** app — real API with $5 spending limit in Developer Console +- **prod** app — real API with production spending limit + +The user registers apps manually (outside this session): +``` +xurl auth apps add dev --client-id --client-secret +xurl auth apps add prod --client-id --client-secret +``` + +Switch: `xurl auth default dev` or `xurl --app prod post "..."` + +## Step 8: Session Summary + +When the user asks for a summary, or at session end: + +1. If playground was used, show simulated cost: + ```bash + curl -s http://localhost:8080/api/accounts/0/cost | jq '.' + ``` + +2. If live API was used, show real usage: + ```bash + xurl --auth app /2/usage/tweets?days=1 + ``` + +3. Show local tracker data from `/tmp/xurl-session-costs.json` + +Present as: + +``` +## X API Session Summary +| Category | Count | Est. Cost | +|----------|-------|-----------| +| Sandbox ops | N | $0.00 (free) | +| Live reads | N | $X.XX | +| Live writes | N | $X.XX | +| Total live spend | — | $X.XX | +| Would-cost (sandbox→live) | — | $X.XX | +``` + +## Step 9: Quick Pick (no context) + +If the user invokes `/xurl` with no argument and no prior context: + +``` +What would you like to do? + 1. Post / reply / quote + 2. Search X + 3. Check timeline or mentions + 4. Engage with posts (like, repost, bookmark) + 5. Send or read DMs + 6. Check usage and session costs + 7. Set up or review xurl configuration +``` + +## Billing Reference + +**24h UTC deduplication**: Same resource requested multiple times within one UTC day is billed once. Note: reads of the same post on different UTC days each incur a separate charge. + +**Only successful responses are billed.** Failed requests (4xx, 5xx) cost nothing. Rate-limited (429) requests are not billed. + +**Monthly cap**: 2M post reads. Enterprise ($42K+/mo) required beyond that. + +**Spending limits**: Set in the Developer Console to prevent runaway costs. Auto-recharge tops up when balance is low. + +**xAI credit rewards**: 10% back at $200+ cumulative spend, 15% at $500+, 20% at $1,000+. Only meaningful at $200+/month — solo/casual users won't hit this threshold. + +### Rate Limits (separate from billing) + +| Endpoint | Per App / 15min | Per User / 15min | +|----------|----------------|-----------------| +| Post lookup | 450 | 900 | +| Search recent | 450 | 300 | +| Post create | 10K / 24h | 100 / 15min | +| Like | — | 50 / 15min, 1K / 24h | +| User lookup | 300 | 900 | + +On 429: back off and retry. Rate-limited requests are not billed. + +### Reference Links + +For up-to-date information (pricing may change): +- Pricing: https://docs.x.com/x-api/getting-started/pricing +- Usage & billing: https://docs.x.com/x-api/fundamentals/post-cap +- Rate limits: https://docs.x.com/x-api/fundamentals/rate-limits +- Usage API: https://docs.x.com/x-api/usage/introduction +- xurl: https://github.com/xdevplatform/xurl +- Playground: https://github.com/xdevplatform/playground +- Docs source: https://github.com/xdevplatform/docs From cc03f729ae22852b8734eb47c120c3e9e98a6602 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 22:34:12 +0200 Subject: [PATCH 02/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20session?= =?UTF-8?q?=20config=20filename,=20conservative=20batch=20cost=20estimate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix session summary referencing non-existent /tmp/xurl-session-costs.json (correct: /tmp/xurl-session-config.json) - Use conservative full-rate estimate for batch reads in guardrail tracking (actual batch cost may be lower, but overestimating is safer) --- plugins/fiale-awesome-skills/skills/xurl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 0cdd08f..167d567 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -229,7 +229,7 @@ Track spending locally using approximate costs: | Operation | Approximate Cost | |-----------|-----------------| | Post read | $0.005 | -| Batch read (N posts) | ~$0.005 × N × 0.25 | +| Batch read (N posts) | ~$0.005 × N (conservative; actual may be lower) | | User lookup | $0.010 | | Post write | $0.010 | | DM read | $0.010 | @@ -339,7 +339,7 @@ When the user asks for a summary, or at session end: xurl --auth app /2/usage/tweets?days=1 ``` -3. Show local tracker data from `/tmp/xurl-session-costs.json` +3. Show local tracker data from `/tmp/xurl-session-config.json` Present as: From d85804d7a411634c7cbb044fcdaa5f3e9bc650f7 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 22:39:35 +0200 Subject: [PATCH 03/11] =?UTF-8?q?fix:=20PR=20gate=20=E2=80=94=20add=20perm?= =?UTF-8?q?issions,=20handle=20user-owned=20repos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add explicit issues:write + pull-requests:write permissions (pull_request_target doesn't grant these by default) - Short-circuit if PR author is repo owner (no org check needed) - Fall back to collaborator check when owner is a user account, not a GitHub org (orgs API returns 404 for user accounts) --- .github/workflows/pr-gate.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index e2fe3d8..3e4278d 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -3,6 +3,10 @@ on: pull_request_target: types: [opened] +permissions: + issues: write + pull-requests: write + jobs: check-membership: runs-on: ubuntu-latest @@ -13,13 +17,32 @@ jobs: script: | const { owner, repo } = context.repo; const author = context.payload.pull_request.user.login; + + // Check if PR author is the repo owner (covers user-owned repos) + if (author === owner) { + console.log(`${author} is the repo owner — PR allowed`); + return; + } + + // Check org membership (for org-owned repos) try { await github.rest.orgs.checkMembershipForUser({ org: owner, username: author, }); console.log(`${author} is an org member — PR allowed`); - } catch { + } catch (e) { + // Check if it's an org API error (user account, not an org) vs actual non-member + if (e.status === 404) { + // Not an org — check if author is a collaborator instead + try { + await github.rest.repos.checkCollaborator({ owner, repo, username: author }); + console.log(`${author} is a collaborator — PR allowed`); + return; + } catch {} + } + + console.log(`${author} is not a member — closing PR`); await github.rest.issues.createComment({ owner, repo, issue_number: context.payload.pull_request.number, @@ -30,5 +53,4 @@ jobs: pull_number: context.payload.pull_request.number, state: 'closed', }); - console.log(`${author} is not an org member — PR closed`); } From 5e332bc13ce67df39133b7372cb962744985acb2 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 22:48:04 +0200 Subject: [PATCH 04/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20address?= =?UTF-8?q?=20review=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Remove broken `xurl --auth app /2/usage/tweets` from session summary (requires bearer token auth, separate from OAuth2 setup user follows). Session summary now uses local spend tracker only. 2. Remove incorrect batch read 75% savings claim. X bills each post in a batch separately — batching saves rate limit quota, not credits. 3. Remove hardcoded playground account 0 from cost queries. Account ID is derived from auth token; use account-independent /api/credits/pricing. 4. Change playground default port to 3080 to avoid conflict with xurl OAuth callback on port 8080. 5. Soften README — "local session spend tracking" instead of "real usage tracking via /2/usage/tweets". --- plugins/fiale-awesome-skills/README.md | 2 +- plugins/fiale-awesome-skills/skills/xurl.md | 73 +++++++++------------ 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/plugins/fiale-awesome-skills/README.md b/plugins/fiale-awesome-skills/README.md index 80f58fe..93bb6c2 100644 --- a/plugins/fiale-awesome-skills/README.md +++ b/plugins/fiale-awesome-skills/README.md @@ -45,7 +45,7 @@ Post, reply, search, and engage on X/Twitter through [xurl](https://github.com/x - Cost preview: see what sandbox operations would cost before going live - Configurable session spending limits (default $0.25) - Estimated cost shown before every live write operation -- Real usage tracking via X API's `/2/usage/tweets` endpoint +- Local session spend tracking with estimated costs per operation - 24h UTC deduplication awareness (same resource = 1 charge per day) **Pay-per-use pricing** (community-sourced estimates, check Developer Console for authoritative rates): diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 167d567..263aa55 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -51,8 +51,8 @@ go install github.com/xdevplatform/xurl@latest # Go **If playground is not installed** and user wants sandbox: ```bash go install github.com/xdevplatform/playground/cmd/playground@latest -playground start # serves at http://localhost:8080 -playground refresh # update OpenAPI spec cache (optional) +playground start -p 3080 # port 3080 to avoid conflict with xurl OAuth callback on 8080 +playground refresh # update OpenAPI spec cache (optional) ``` **If xurl auth is not configured**, tell the user to run these themselves (outside this session): @@ -60,7 +60,7 @@ playground refresh # update OpenAPI spec cache (optional) xurl auth apps add my-app --client-id --client-secret xurl auth oauth2 ``` -They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. +They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. Note: playground must run on a different port (e.g., 3080) to avoid conflicting with the OAuth callback port. ## Step 2: Sandbox Mode (playground) @@ -69,7 +69,7 @@ When playground is running, **default to sandbox** for all operations. This cost Set the `API_BASE_URL` env var to redirect all xurl commands to the local playground: ```bash -export API_BASE_URL=http://localhost:8080 +export API_BASE_URL=http://localhost:3080 ``` Now **all shortcut commands work against sandbox** — same syntax as live mode: @@ -99,47 +99,42 @@ Prefix all sandbox output with `[SANDBOX]`. After testing a workflow in sandbox, show what it would cost on the real API: ```bash -# Simulated cost breakdown -curl -s http://localhost:8080/api/accounts/0/cost | jq '.' - -# Current pricing rates (may reflect real X API rates) -curl -s http://localhost:8080/api/credits/pricing | jq '.' - -# Usage breakdown by event type and request type -curl -s "http://localhost:8080/api/accounts/0/usage?interval=30days&groupBy=eventType" | jq '.' -curl -s "http://localhost:8080/api/accounts/0/usage?interval=30days&groupBy=requestType" | jq '.' +# Current pricing rates (account-independent, always works) +curl -s http://localhost:3080/api/credits/pricing | jq '.' ``` +Note: playground derives account ID from the auth token. The default `Bearer test` token maps to account `0`. If xurl sends a real OAuth token via `API_BASE_URL`, the account ID will differ. Check `/health` for active account info before querying per-account endpoints. + Present the cost preview to the user before they switch to live mode. ### State management and error simulation ```bash # Save current state -curl -s http://localhost:8080/state/export > /tmp/xurl-playground-state.json +curl -s http://localhost:3080/state/export > /tmp/xurl-playground-state.json # Restore a saved state curl -s -X POST -H "Content-Type: application/json" \ - -d @/tmp/xurl-playground-state.json http://localhost:8080/state/import + -d @/tmp/xurl-playground-state.json http://localhost:3080/state/import # Reset to fresh defaults -curl -s -X POST http://localhost:8080/state/reset +curl -s -X POST http://localhost:3080/state/reset # Force-save state to disk -curl -s -X POST http://localhost:8080/state/save +curl -s -X POST http://localhost:3080/state/save # Simulate rate limiting to test error handling -curl -X PUT http://localhost:8080/config/update \ +curl -X PUT http://localhost:3080/config/update \ -H "Content-Type: application/json" \ -d '{"errors": {"enabled": true, "error_rate": 0.3, "error_type": "rate_limit"}}' # Disable error simulation -curl -X PUT http://localhost:8080/config/update \ +curl -X PUT http://localhost:3080/config/update \ -H "Content-Type: application/json" \ -d '{"errors": {"enabled": false}}' ``` -Web UI for browsing sandbox data: `http://localhost:8080/playground` +Web UI for browsing sandbox data: `http://localhost:3080/playground` ### Switching to live @@ -180,19 +175,19 @@ xurl media upload file.jpg # $0.01 Note: search/timeline/mentions billing per-post vs per-request is not publicly confirmed by X. The ~$0.005/result is community consensus. Use the lowest `-n` value that satisfies your need — it directly controls cost. -### Batch reads (major cost saver) +### Batch reads -When reading 3+ posts, prefer the batch endpoint over individual reads: +When reading multiple posts, use the batch endpoint to save rate limit quota (up to 100 IDs in one request): ```bash -# Individual: 10 reads × $0.005 = $0.05 -xurl read ID1 && xurl read ID2 && ... # expensive +# Individual: 10 separate requests (eats rate limit faster) +xurl read ID1 && xurl read ID2 && ... -# Batch: up to 100 IDs in one request — significantly cheaper +# Batch: one request, same billing (each post still counts separately) xurl /2/tweets?ids=ID1,ID2,ID3,...,ID10 ``` -Community data suggests batch reads cost ~75% less than equivalent individual reads. Always prefer batch when reading multiple known post IDs. +Note: X bills each post in a batch separately — batching does NOT reduce credit cost. It saves rate limit quota and network round trips. ### Write operations — compose + confirm @@ -228,8 +223,7 @@ Track spending locally using approximate costs: | Operation | Approximate Cost | |-----------|-----------------| -| Post read | $0.005 | -| Batch read (N posts) | ~$0.005 × N (conservative; actual may be lower) | +| Post read (single or batch) | $0.005 per post | | User lookup | $0.010 | | Post write | $0.010 | | DM read | $0.010 | @@ -329,18 +323,12 @@ Switch: `xurl auth default dev` or `xurl --app prod post "..."` When the user asks for a summary, or at session end: -1. If playground was used, show simulated cost: - ```bash - curl -s http://localhost:8080/api/accounts/0/cost | jq '.' - ``` - -2. If live API was used, show real usage: +1. Read `/tmp/xurl-session-config.json` for local spend tracking +2. If playground was used, show pricing reference: ```bash - xurl --auth app /2/usage/tweets?days=1 + curl -s http://localhost:3080/api/credits/pricing | jq '.' ``` -3. Show local tracker data from `/tmp/xurl-session-config.json` - Present as: ``` @@ -348,12 +336,15 @@ Present as: | Category | Count | Est. Cost | |----------|-------|-----------| | Sandbox ops | N | $0.00 (free) | -| Live reads | N | $X.XX | -| Live writes | N | $X.XX | -| Total live spend | — | $X.XX | -| Would-cost (sandbox→live) | — | $X.XX | +| Live reads | N | ~$X.XX | +| Live writes | N | ~$X.XX | +| Live engagements | N | ~$X.XX | +| Est. total live spend | — | ~$X.XX | +| Session limit | — | $Y.YY | ``` +Costs are estimates from local tracking. For authoritative billing data, check the X Developer Console. + ## Step 9: Quick Pick (no context) If the user invokes `/xurl` with no argument and no prior context: From 988878b8e507ae6fc78e67936cae6faf7565d6d1 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 22:57:58 +0200 Subject: [PATCH 05/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20sandbox?= =?UTF-8?q?=20port=20detection,=20trust=20mode=20safety,=20first-use=20ins?= =?UTF-8?q?tall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replace `playground status` with `curl localhost:3080/health` for sandbox detection — upstream playground status only checks default ports (8080, 3000, 8081), not our configured 3080. 2. Add `-p 3080` to error recovery playground start command — was missing, would start on wrong port. 3. Make trust mode per-session only — reset trusted_actions to [] at session start. Previously trusted_actions persisted in /tmp across sessions, allowing a later session to skip write confirmations unexpectedly. 4. Integrate playground installation into first-use flow — offer to install when not found, check for Go, link to pre-built binaries as fallback. --- plugins/fiale-awesome-skills/skills/xurl.md | 30 ++++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 263aa55..c746202 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -26,7 +26,7 @@ Run silently — only surface issues, not successes: ```bash which xurl 2>/dev/null && echo "xurl: installed" || echo "xurl: NOT FOUND" which playground 2>/dev/null && echo "playground: installed" || echo "playground: NOT FOUND" -playground status 2>&1 || true +curl -sf http://localhost:3080/health > /dev/null 2>&1 && echo "playground: running on 3080" || echo "playground: not running on 3080" echo "API_BASE_URL=${API_BASE_URL:-'(not set)'}" xurl auth status 2>&1 || true cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" @@ -34,12 +34,13 @@ cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" **Determine mode and act:** -- **Everything configured, session config exists**: skip setup entirely. Show one-line header and proceed with the user's request: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` -- **Playground running, no session config**: "Ready. Sandbox mode active. What would you like to do?" +- **Everything configured, session config exists**: reset `trusted_actions` to `[]` (trust does not carry across sessions), then show one-line header and proceed: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` +- **Playground running on 3080, no session config**: "Ready. Sandbox mode active. What would you like to do?" - **Only xurl auth, no playground**: "No sandbox running. Live mode — real credits will be used." - **`API_BASE_URL` set to localhost**: announce "Sandbox mode active via API_BASE_URL." - **xurl not installed**: show install options and stop -- **Auth not configured**: show manual setup instructions and stop +- **Playground not installed**: offer to install it (see below) +- **Auth not configured and no playground**: show setup instructions for both **If xurl is not installed:** ```bash @@ -48,11 +49,20 @@ npm install -g @xdevplatform/xurl # npm go install github.com/xdevplatform/xurl@latest # Go ``` -**If playground is not installed** and user wants sandbox: +**If playground is not installed**, offer to install it (recommended for cost-free testing): ```bash go install github.com/xdevplatform/playground/cmd/playground@latest +``` +This requires Go. If Go is not installed, tell the user to install it first (`brew install go` on macOS) or download a pre-built binary from https://github.com/xdevplatform/playground/releases. + +After installation, start the sandbox: +```bash playground start -p 3080 # port 3080 to avoid conflict with xurl OAuth callback on 8080 -playground refresh # update OpenAPI spec cache (optional) +``` + +**If playground is installed but not running**, start it: +```bash +playground start -p 3080 ``` **If xurl auth is not configured**, tell the user to run these themselves (outside this session): @@ -60,7 +70,7 @@ playground refresh # update OpenAPI spec cache (optional) xurl auth apps add my-app --client-id --client-secret xurl auth oauth2 ``` -They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. Note: playground must run on a different port (e.g., 3080) to avoid conflicting with the OAuth callback port. +They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. Note: playground runs on port 3080 specifically to avoid conflicting with this OAuth callback port. ## Step 2: Sandbox Mode (playground) @@ -197,7 +207,7 @@ Note: X bills each post in a batch separately — batching does NOT reduce credi **First write of session**: always individual confirmation, even in batch context. -**Trust mode**: after the first confirmed write of a given type, offer "Skip confirmations for [likes/reposts/etc.] this session?" Store in `/tmp/xurl-session-config.json` under `trusted_actions`. The spending limit guardrail remains active regardless. +**Trust mode**: after the first confirmed write of a given type, offer "Skip confirmations for [likes/reposts/etc.] this session?" Track in `/tmp/xurl-session-config.json` under `trusted_actions`. Trust is **per-session only** — always reset `trusted_actions` to `[]` at the start of each new session (Step 1). The spending limit guardrail remains active regardless. **Before batch reads** (search, timeline, mentions with -n > 10), show estimated cost inline — no separate confirmation. @@ -269,8 +279,8 @@ The Developer Console spending limit is your absolute safety net — set it to y **Playground not running (user expects sandbox):** ``` -Sandbox not available — playground is not running. - 1. Start sandbox: `playground start` (~2s) +Sandbox not available — playground is not running on port 3080. + 1. Start sandbox: `playground start -p 3080` (~2s) 2. Switch to live mode (real credits) 3. Cancel Which? (default: 1) From c9b72827edfee49285ec49e4433a6c21e530dc3a Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 23:03:20 +0200 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20sandbox?= =?UTF-8?q?=20env=20var=20leak,=20per-session=20spend=20reset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Use inline API_BASE_URL=... prefix on every sandbox command instead of export. Export doesn't persist between separate Bash tool calls, so later commands could silently hit the real API. 2. Reset spent/operations/trusted_actions at session start — only spending_limit carries over. Prevents false warnings from stale spend data inherited from prior sessions. --- plugins/fiale-awesome-skills/skills/xurl.md | 41 ++++++++------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index c746202..19bfac9 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -34,7 +34,7 @@ cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" **Determine mode and act:** -- **Everything configured, session config exists**: reset `trusted_actions` to `[]` (trust does not carry across sessions), then show one-line header and proceed: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` +- **Everything configured, session config exists**: reset `trusted_actions` to `[]`, `spent` to `0.0`, and `operations` to `0` (these are per-session — only `spending_limit` carries over). Then show one-line header and proceed: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` - **Playground running on 3080, no session config**: "Ready. Sandbox mode active. What would you like to do?" - **Only xurl auth, no playground**: "No sandbox running. Live mode — real credits will be used." - **`API_BASE_URL` set to localhost**: announce "Sandbox mode active via API_BASE_URL." @@ -76,30 +76,24 @@ They must set the redirect URI to `http://localhost:8080/callback` in the X Deve When playground is running, **default to sandbox** for all operations. This costs $0. -Set the `API_BASE_URL` env var to redirect all xurl commands to the local playground: +Prefix every xurl command with `API_BASE_URL=http://localhost:3080` to route it to the sandbox. This must be on the same line — a separate `export` will not persist between tool calls. ```bash -export API_BASE_URL=http://localhost:3080 +API_BASE_URL=http://localhost:3080 xurl post "Hello from sandbox!" +API_BASE_URL=http://localhost:3080 xurl search "query" -n 5 +API_BASE_URL=http://localhost:3080 xurl user @handle +API_BASE_URL=http://localhost:3080 xurl timeline -n 20 +API_BASE_URL=http://localhost:3080 xurl mentions -n 10 +API_BASE_URL=http://localhost:3080 xurl read POST_ID +API_BASE_URL=http://localhost:3080 xurl reply POST_ID "Nice!" +API_BASE_URL=http://localhost:3080 xurl like POST_ID ``` -Now **all shortcut commands work against sandbox** — same syntax as live mode: +Raw path access also works: ```bash -xurl post "Hello from sandbox!" -xurl search "query" -n 5 -xurl user @handle -xurl timeline -n 20 -xurl mentions -n 10 -xurl read POST_ID -xurl reply POST_ID "Nice!" -xurl like POST_ID -``` - -Raw path access also works (playground accepts any auth token): - -```bash -xurl /2/users/me -xurl -X POST /2/tweets -d '{"text":"sandbox post"}' +API_BASE_URL=http://localhost:3080 xurl /2/users/me +API_BASE_URL=http://localhost:3080 xurl -X POST /2/tweets -d '{"text":"sandbox post"}' ``` Prefix all sandbox output with `[SANDBOX]`. @@ -148,12 +142,7 @@ Web UI for browsing sandbox data: `http://localhost:3080/playground` ### Switching to live -When the user says "go live", "publish", "send it for real", or similar: - -```bash -unset API_BASE_URL -echo "Now targeting: ${API_BASE_URL:-https://api.x.com}" -``` +When the user says "go live", "publish", "send it for real", or similar — simply stop prefixing commands with `API_BASE_URL=http://localhost:3080`. Plain `xurl ...` commands hit the real API. Show the mode banner: "[LIVE MODE] Switched. Session limit: $X.XX. Every write will show its cost inline." @@ -227,7 +216,7 @@ Store in `/tmp/xurl-session-config.json`: {"spending_limit": 0.25, "spent": 0.0, "operations": 0, "trusted_actions": []} ``` -This file persists across sessions until reboot (`/tmp`). Delete manually to reset. +This file persists in `/tmp` (survives across sessions until reboot). Only `spending_limit` carries over — `spent`, `operations`, and `trusted_actions` are reset at each session start (Step 1). Track spending locally using approximate costs: From 94a4f96cb8f28e935d588fee4c3c60d4e81ee3b3 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 23:10:23 +0200 Subject: [PATCH 07/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20auth=20?= =?UTF-8?q?required=20for=20sandbox,=20not=20just=20live?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xurl needs OAuth credentials to construct requests even against the local playground (which accepts any token without billing). A user with playground running but no auth configured would hit 401s. Now clearly states auth is required for both modes and guides users to set up auth first when playground-only state is detected. --- plugins/fiale-awesome-skills/skills/xurl.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 19bfac9..6683690 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -35,12 +35,13 @@ cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" **Determine mode and act:** - **Everything configured, session config exists**: reset `trusted_actions` to `[]`, `spent` to `0.0`, and `operations` to `0` (these are per-session — only `spending_limit` carries over). Then show one-line header and proceed: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` -- **Playground running on 3080, no session config**: "Ready. Sandbox mode active. What would you like to do?" -- **Only xurl auth, no playground**: "No sandbox running. Live mode — real credits will be used." +- **Playground running + auth configured**: "Ready. Sandbox mode active. What would you like to do?" +- **Auth configured, no playground**: "No sandbox running. Live mode — real credits will be used." +- **Playground running, no auth**: "Sandbox is running, but xurl auth is required even for sandbox (xurl needs credentials to construct requests — playground accepts any token, so no real billing occurs). Set up auth first." - **`API_BASE_URL` set to localhost**: announce "Sandbox mode active via API_BASE_URL." - **xurl not installed**: show install options and stop - **Playground not installed**: offer to install it (see below) -- **Auth not configured and no playground**: show setup instructions for both +- **Neither auth nor playground**: show setup instructions for both (auth first, then playground) **If xurl is not installed:** ```bash @@ -65,7 +66,7 @@ playground start -p 3080 # port 3080 to avoid conflict with xurl OAuth callba playground start -p 3080 ``` -**If xurl auth is not configured**, tell the user to run these themselves (outside this session): +**If xurl auth is not configured**, tell the user to run these themselves (outside this session). Auth is required for both sandbox and live mode — xurl needs credentials to construct requests: ``` xurl auth apps add my-app --client-id --client-secret xurl auth oauth2 @@ -74,7 +75,9 @@ They must set the redirect URI to `http://localhost:8080/callback` in the X Deve ## Step 2: Sandbox Mode (playground) -When playground is running, **default to sandbox** for all operations. This costs $0. +When playground is running AND xurl auth is configured, **default to sandbox** for all operations. This costs $0 — playground accepts any valid token without real billing. + +xurl auth is required even in sandbox mode. xurl needs credentials to construct requests (attach OAuth headers). The playground accepts these tokens — it just doesn't charge for them. Prefix every xurl command with `API_BASE_URL=http://localhost:3080` to route it to the sandbox. This must be on the same line — a separate `export` will not persist between tool calls. From 25bfb1898f7999a938fd3fb01822f9d887932d93 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Sun, 5 Apr 2026 23:13:47 +0200 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20launch?= =?UTF-8?q?=20playground=20in=20background=20to=20avoid=20blocking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit playground start is a foreground server — running it directly would hang the agent session. Now uses &>/dev/null & to detach, followed by a health check to verify it started. --- plugins/fiale-awesome-skills/skills/xurl.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 6683690..7a996c4 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -56,14 +56,18 @@ go install github.com/xdevplatform/playground/cmd/playground@latest ``` This requires Go. If Go is not installed, tell the user to install it first (`brew install go` on macOS) or download a pre-built binary from https://github.com/xdevplatform/playground/releases. -After installation, start the sandbox: +After installation, start the sandbox in the background (it's a foreground server — must be detached or it blocks the session): ```bash -playground start -p 3080 # port 3080 to avoid conflict with xurl OAuth callback on 8080 +playground start -p 3080 &>/dev/null & +``` +Then verify it's up: +```bash +curl -sf http://localhost:3080/health && echo "playground: running" ``` -**If playground is installed but not running**, start it: +**If playground is installed but not running**, start it the same way: ```bash -playground start -p 3080 +playground start -p 3080 &>/dev/null & ``` **If xurl auth is not configured**, tell the user to run these themselves (outside this session). Auth is required for both sandbox and live mode — xurl needs credentials to construct requests: @@ -272,7 +276,7 @@ The Developer Console spending limit is your absolute safety net — set it to y **Playground not running (user expects sandbox):** ``` Sandbox not available — playground is not running on port 3080. - 1. Start sandbox: `playground start -p 3080` (~2s) + 1. Start sandbox: `playground start -p 3080 &>/dev/null &` (~2s) 2. Switch to live mode (real credits) 3. Cancel Which? (default: 1) From 6c26db9db65c4b72b017a0dc1ff63cd5d0a45975 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Mon, 6 Apr 2026 00:03:47 +0200 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20add=20a?= =?UTF-8?q?uth=20default=20step=20to=20setup=20instructions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OAuth2 flow fails unless the app is set as default first. Add xurl auth default before xurl auth oauth2. --- plugins/fiale-awesome-skills/skills/xurl.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 7a996c4..1b32397 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -73,8 +73,10 @@ playground start -p 3080 &>/dev/null & **If xurl auth is not configured**, tell the user to run these themselves (outside this session). Auth is required for both sandbox and live mode — xurl needs credentials to construct requests: ``` xurl auth apps add my-app --client-id --client-secret +xurl auth default my-app xurl auth oauth2 ``` +The `auth default` step is required — OAuth2 flow fails unless the app is set as default first. They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. Note: playground runs on port 3080 specifically to avoid conflicting with this OAuth callback port. ## Step 2: Sandbox Mode (playground) From 5014dd864c804cce0f105f099983130a823208ca Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Mon, 6 Apr 2026 00:09:00 +0200 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20xurl=20skill=20=E2=80=94=20documen?= =?UTF-8?q?t=20sandbox=20auth=20limitation=20for=20user-context=20endpoint?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Playground validates OAuth token type. xurl shortcut commands that require user-context auth (whoami, post, like, follow, dm) fail with 403 because playground sees the token as app-only. App-auth shortcuts (search, read) work fine. Document raw path workaround for user-context endpoints in sandbox mode. --- plugins/fiale-awesome-skills/skills/xurl.md | 26 ++++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index 1b32397..f4a78a9 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -83,26 +83,34 @@ They must set the redirect URI to `http://localhost:8080/callback` in the X Deve When playground is running AND xurl auth is configured, **default to sandbox** for all operations. This costs $0 — playground accepts any valid token without real billing. -xurl auth is required even in sandbox mode. xurl needs credentials to construct requests (attach OAuth headers). The playground accepts these tokens — it just doesn't charge for them. +xurl auth is required even in sandbox mode — xurl needs credentials to construct requests. Prefix every xurl command with `API_BASE_URL=http://localhost:3080` to route it to the sandbox. This must be on the same line — a separate `export` will not persist between tool calls. +**App-auth shortcuts work directly** (search, read): + ```bash -API_BASE_URL=http://localhost:3080 xurl post "Hello from sandbox!" API_BASE_URL=http://localhost:3080 xurl search "query" -n 5 -API_BASE_URL=http://localhost:3080 xurl user @handle -API_BASE_URL=http://localhost:3080 xurl timeline -n 20 -API_BASE_URL=http://localhost:3080 xurl mentions -n 10 API_BASE_URL=http://localhost:3080 xurl read POST_ID -API_BASE_URL=http://localhost:3080 xurl reply POST_ID "Nice!" -API_BASE_URL=http://localhost:3080 xurl like POST_ID ``` -Raw path access also works: +**User-context shortcuts** (`whoami`, `post`, `reply`, `like`, `follow`, `dm`, etc.) may fail with a 403 "Unsupported Authentication" error because the playground validates the OAuth token type. For these, use raw path form instead: ```bash +# User profile API_BASE_URL=http://localhost:3080 xurl /2/users/me -API_BASE_URL=http://localhost:3080 xurl -X POST /2/tweets -d '{"text":"sandbox post"}' + +# Post +API_BASE_URL=http://localhost:3080 xurl -X POST /2/tweets -d '{"text":"Hello from sandbox!"}' + +# Like (requires your user ID — get it from /2/users/me first) +API_BASE_URL=http://localhost:3080 xurl -X POST /2/users/USER_ID/likes -d '{"tweet_id":"POST_ID"}' + +# Timeline +API_BASE_URL=http://localhost:3080 xurl /2/users/USER_ID/reverse_chronological + +# Mentions +API_BASE_URL=http://localhost:3080 xurl /2/users/USER_ID/mentions ``` Prefix all sandbox output with `[SANDBOX]`. From 11ed00449f508c2f3027d89784ea58b35fe80e41 Mon Sep 17 00:00:00 2001 From: Pavel Fadeev Date: Mon, 6 Apr 2026 00:21:27 +0200 Subject: [PATCH 11/11] =?UTF-8?q?feat:=20xurl=20skill=20=E2=80=94=20first-?= =?UTF-8?q?run=20interview,=20bearer=20token=20setup,=20bookmark=20workflo?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replace dump-all-setup with guided first-run interview: detect what's configured, show status checkmarks, ask what user wants to do, guide through only the missing pieces. 2. Document both auth types clearly: - OAuth2 for user-context ops (post, like, timeline, bookmarks) - Bearer token for usage tracking (/2/usage/tweets) Both recommended, each with setup instructions. 3. Add bookmark processing workflow: fetch → read → process into knowledge base → optionally unbookmark. Cost estimate included. 4. Restore usage API in session summary now that bearer token setup is documented. Notes it only tracks post reads, not all operations. --- plugins/fiale-awesome-skills/skills/xurl.md | 103 ++++++++++++++------ 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/plugins/fiale-awesome-skills/skills/xurl.md b/plugins/fiale-awesome-skills/skills/xurl.md index f4a78a9..ace4e86 100644 --- a/plugins/fiale-awesome-skills/skills/xurl.md +++ b/plugins/fiale-awesome-skills/skills/xurl.md @@ -19,9 +19,9 @@ These are non-negotiable. Violating any of them leaks credentials. - **Credential registration is manual.** The user must run `xurl auth apps add ...` and `xurl auth oauth2` themselves, outside this session. Do not execute auth commands with secrets. - To check auth state safely: `xurl auth status` (shows apps and token status without exposing secrets). -## Step 1: Environment Detection +## Step 1: Environment Detection and First-Run Setup -Run silently — only surface issues, not successes: +Run detection silently: ```bash which xurl 2>/dev/null && echo "xurl: installed" || echo "xurl: NOT FOUND" @@ -32,16 +32,41 @@ xurl auth status 2>&1 || true cat /tmp/xurl-session-config.json 2>/dev/null || echo "no session config" ``` -**Determine mode and act:** +### Returning user (session config exists) -- **Everything configured, session config exists**: reset `trusted_actions` to `[]`, `spent` to `0.0`, and `operations` to `0` (these are per-session — only `spending_limit` carries over). Then show one-line header and proceed: `[SANDBOX MODE]` or `[LIVE MODE — limit: $X.XX]` -- **Playground running + auth configured**: "Ready. Sandbox mode active. What would you like to do?" -- **Auth configured, no playground**: "No sandbox running. Live mode — real credits will be used." -- **Playground running, no auth**: "Sandbox is running, but xurl auth is required even for sandbox (xurl needs credentials to construct requests — playground accepts any token, so no real billing occurs). Set up auth first." -- **`API_BASE_URL` set to localhost**: announce "Sandbox mode active via API_BASE_URL." -- **xurl not installed**: show install options and stop -- **Playground not installed**: offer to install it (see below) -- **Neither auth nor playground**: show setup instructions for both (auth first, then playground) +Reset per-session fields (`trusted_actions` to `[]`, `spent` to `0.0`, `operations` to `0` — only `spending_limit` carries over). Show one-line header and proceed with the request. + +### First-run interview + +On first invocation, detect what's configured and present a status + guided setup: + +``` +Welcome to xurl. Checking your setup... + + xurl: ✓ installed / ✗ not found + OAuth2: ✓ configured / ✗ missing (needed for post, like, follow, timeline, bookmarks, DM) + Bearer: ✓ configured / ✗ missing (needed for usage tracking) + Playground: ✓ running on 3080 / ✗ not running / ✗ not installed + +What do you want to do? + 1. Post / engage / read timeline or bookmarks (needs OAuth2) + 2. Track my API usage and costs (needs bearer token) + 3. Test in sandbox first (needs playground + OAuth2) + 4. Full setup — all of the above +``` + +Only guide the user through what's missing for their chosen path. Don't dump all setup instructions at once. + +### Auth types explained + +Two auth types serve different purposes — both are recommended: + +| Auth type | What it unlocks | Setup command (user runs outside session) | +|-----------|----------------|------------------------------------------| +| **OAuth2 user context** | Post, reply, like, follow, DM, timeline, bookmarks, mentions | `xurl auth oauth2` | +| **Bearer token (app-only)** | Usage tracking (`/2/usage/tweets`), public search, public user lookup | `xurl auth app --bearer-token ` | + +### Setup instructions (user runs outside this session) **If xurl is not installed:** ```bash @@ -50,34 +75,34 @@ npm install -g @xdevplatform/xurl # npm go install github.com/xdevplatform/xurl@latest # Go ``` -**If playground is not installed**, offer to install it (recommended for cost-free testing): -```bash -go install github.com/xdevplatform/playground/cmd/playground@latest +**OAuth2 setup** (required for most operations): +``` +xurl auth apps add my-app --client-id --client-secret +xurl auth default my-app +xurl auth oauth2 ``` -This requires Go. If Go is not installed, tell the user to install it first (`brew install go` on macOS) or download a pre-built binary from https://github.com/xdevplatform/playground/releases. +The `auth default` step is required — OAuth2 flow fails unless the app is set as default first. +Redirect URI must be `http://localhost:8080/callback` in the X Developer Console. -After installation, start the sandbox in the background (it's a foreground server — must be detached or it blocks the session): -```bash -playground start -p 3080 &>/dev/null & +**Bearer token setup** (required for usage tracking): ``` -Then verify it's up: +xurl auth app --bearer-token +``` +Get the bearer token from X Developer Console → your app → "Keys and tokens" → Bearer Token → Generate. + +**Playground install** (recommended for free sandbox testing): ```bash -curl -sf http://localhost:3080/health && echo "playground: running" +go install github.com/xdevplatform/playground/cmd/playground@latest ``` +Requires Go (`brew install go` on macOS). Or download a pre-built binary from https://github.com/xdevplatform/playground/releases. -**If playground is installed but not running**, start it the same way: +Start the sandbox in the background: ```bash playground start -p 3080 &>/dev/null & ``` +Verify: `curl -sf http://localhost:3080/health && echo "playground: running"` -**If xurl auth is not configured**, tell the user to run these themselves (outside this session). Auth is required for both sandbox and live mode — xurl needs credentials to construct requests: -``` -xurl auth apps add my-app --client-id --client-secret -xurl auth default my-app -xurl auth oauth2 -``` -The `auth default` step is required — OAuth2 flow fails unless the app is set as default first. -They must set the redirect URI to `http://localhost:8080/callback` in the X Developer Console. Note: playground runs on port 3080 specifically to avoid conflicting with this OAuth callback port. +Playground runs on port 3080 to avoid conflicting with the OAuth callback on port 8080. ## Step 2: Sandbox Mode (playground) @@ -281,6 +306,16 @@ The Developer Console spending limit is your absolute safety net — set it to y 3. Batch-engage with flagged posts: one confirmation for the whole set ``` +### Bookmark Processing +``` +1. xurl bookmarks -n 10 → fetch recent bookmarks (~$0.05) +2. For each bookmark: read full post → extract key content +3. Process into knowledge base, notes, or action items +4. Optionally unbookmark processed items to track progress +``` + +Cost: ~$0.005 per bookmark fetched + ~$0.005 per full read if needed. A 10-bookmark processing run costs ~$0.05–$0.10. + ## Step 6: Error Recovery **Playground not running (user expects sandbox):** @@ -340,7 +375,12 @@ Switch: `xurl auth default dev` or `xurl --app prod post "..."` When the user asks for a summary, or at session end: 1. Read `/tmp/xurl-session-config.json` for local spend tracking -2. If playground was used, show pricing reference: +2. If bearer token is configured, query real usage: + ```bash + xurl --auth app "/2/usage/tweets?days=1" + ``` + This returns actual post consumption (reads) for today. Note: it only tracks post reads, not writes/likes/follows. +3. If playground was used, show pricing reference: ```bash curl -s http://localhost:3080/api/credits/pricing | jq '.' ``` @@ -357,9 +397,10 @@ Present as: | Live engagements | N | ~$X.XX | | Est. total live spend | — | ~$X.XX | | Session limit | — | $Y.YY | +| API usage (from /2/usage) | N posts consumed today | (real data) | ``` -Costs are estimates from local tracking. For authoritative billing data, check the X Developer Console. +Local estimates are approximate. The `/2/usage/tweets` endpoint (requires bearer token auth) gives real post consumption data. For full billing details, check the X Developer Console. ## Step 9: Quick Pick (no context)