Skip to content

skill(x-integration): port to v2 + open to Linux + full feature parity (25 tools)#2409

Open
jorgenclaw wants to merge 1 commit into
nanocoai:mainfrom
jorgenclaw:pr/x-integration-v2-linux
Open

skill(x-integration): port to v2 + open to Linux + full feature parity (25 tools)#2409
jorgenclaw wants to merge 1 commit into
nanocoai:mainfrom
jorgenclaw:pr/x-integration-v2-linux

Conversation

@jorgenclaw
Copy link
Copy Markdown

Summary

Rewrites /x-integration for NanoClaw v2, ports it to Linux, and expands the surface from 5 v1 tools (post/like/reply/retweet/quote) to 25 tools covering every common X (Twitter) action a human can do via the web UI — read, compose with media + native scheduling, full engagement-toggle pairs, delete (with text-echo safety guard), scheduled-tweet management, DMs, and bulk bookmark export.

The v1 skill was macOS-only and targeted v2-removed integration points. This PR adopts the v2 idiom: kind:'system' outbound rows dispatched via registerDeliveryAction() and result delivery via notifyAgent() from src/modules/approvals/primitive.ts.

What's new vs v1

  • v2 mechanism. Tools write kind:'system' rows; host dispatches by action key through pacedRun() (10s mutex), spawns the matching scripts/<name>.ts via tsx, and calls notifyAgent() so the result wakes the container.
  • Cross-platform browser detection (lib/chrome-detect.ts): CHROME_PATH env → Chrome → Brave → Chromium on macOS (mdfind) and Linux (which). Flatpak browsers detected and rejected with a .deb-install hint. No fallback to Playwright's bundled Chromium — that's what defeats X's bot detection in the first place.
  • 20 new tools. Read (8), Compose (3 with media[] + native schedule_at), Engagement (9 including x_delete_tweet), Schedule queue (2), DMs (3), Bulk bookmark export (1).
  • x_delete_tweet with text-echo safety guard. Delete is the only irreversible action without built-in undo. The tool requires text_must_match (≥5-char substring of the tweet body). The host script reads the live tweet first and refuses to delete unless the substring is present — guards against URL hallucinations and copy-paste mistakes without adding an approval round-trip.
  • Concentrated DOM volatility. lib/locators.ts (all CSS/data-testid selectors) and lib/extract.ts (all parsers). When X redeploys, those are the only files to update.
  • Privacy + safety: DM bodies redacted from nanoclaw.log; per-action 120s timeout + screenshot+DOM-dump capture on failure (logs/x-failures/); 10s mutex pacing.

How it works

Container (Bun)                 Host (Node)
mcp-tools/x-integration.ts  →   src/modules/x-integration/index.ts
  25 tools, makeXTool()          25 registerDeliveryAction() handlers
  writeMessageOut(system)        pacedRun ⟶ spawn tsx script ⟶ notifyAgent
        ↓                                    ↑
   outbound.db  ─────────────────────────  inbound.db + wake

How it was tested

End-to-end on Linux (Pop!_OS) over several weeks: every read flow, every compose flow with media + native scheduling, all engagement toggle pairs, schedule queue list/cancel-by-index/cancel-by-text-match, full DM round-trip, bulk bookmark export across paginated calls. Delete tested in two modes — valid text_must_match (success) and deliberately-wrong text_must_match (refused with clear error). macOS compatibility preserved from prior v1 skill.

Usage

What's in my bookmarks lately?
Summarize this thread: https://x.com/<user>/status/<id>
Post a tweet: gm. Sovereignty is a daily practice.
Reply to https://x.com/<id> with: this matches my experience.
Schedule this for tomorrow 9am PT: <text>
What DMs are in my inbox?
DM @<friend>: thanks for the link earlier.
Delete my tweet https://x.com/<id> — it contains "deprecated"

Each command produces one acknowledgment, then a follow-up with the result ~10–60s later.

Test plan

  • Linux (Pop!_OS) end-to-end on all 25 tools
  • Browser detection: Chrome, Brave, Chromium auto-detected; Flatpak rejected
  • DM body redaction verified in nanoclaw.log
  • Delete tested with both valid and deliberately-wrong text_must_match
  • macOS smoke test (preserved from v1; reviewer-side verification welcome)

🤖 Generated with Claude Code

…y (25 tools incl. delete)

Rewrites /x-integration for NanoClaw v2, ports it to Linux, and expands the
surface from 5 v1 tools (post/like/reply/retweet/quote) to **25 tools** covering
every common X (Twitter) action a human can do via the web UI — read, compose
with media + native scheduling, full engagement-toggle pairs, delete (with
text-echo safety guard), scheduled-tweet management, DMs, and bulk bookmark export.

The v1 skill was macOS-only and targeted v2-removed integration points
(`src/ipc.ts`, `container/agent-runner/src/ipc-mcp.ts`, file-IPC at
`/workspace/ipc/tasks/`). This PR adopts the v2 idiom — `kind:'system'`
outbound rows dispatched via `registerDeliveryAction()` and result delivery
via `notifyAgent()` from `src/modules/approvals/primitive.ts`.

## What changed

**v2 mechanism.** Every tool writes a `kind:'system'` row to `messages_out`
with `content: JSON.stringify({ action, requestId, ...args })` (mirrors
`mcp-tools/self-mod.ts`). The host's `src/delivery.ts` dispatches by action
key. Each handler routes through a `pacedRun()` mutex (10-second floor
between any two X actions, host-process-wide), spawns the matching script
via `tsx`, awaits the JSON result, and calls `notifyAgent()` — which writes
the outcome back into `inbound.db` AND wakes the container so the result
lands on the next agent turn.

**Cross-platform browser detection.** `lib/chrome-detect.ts` resolves the
executable: `CHROME_PATH` env var → platform-specific probe (Chrome → Brave
→ Chromium on macOS via `mdfind` and Linux via `which`) → throw with an
actionable install hint. Flatpak browsers are detected and rejected with
a specific message pointing at the `.deb` install. No fallback to
Playwright's bundled Chromium — that defeats the bot-detection-survival
design that's the whole point of this skill.

**Full feature surface (25 tools).**
- Read (8): x_read_tweet, x_read_thread, x_read_user, x_read_bookmarks,
  x_read_list, x_read_timeline, x_read_notifications, x_search.
- Compose (3, with optional `media[]` + `schedule_at`): x_post, x_reply,
  x_quote. Scheduling drives X's native composer calendar UI — scheduled
  tweets sit in X's queue regardless of NanoClaw uptime.
- Engagement (9): x_like / x_unlike, x_retweet / x_unretweet,
  x_bookmark / x_unbookmark, x_follow / x_unfollow, x_delete_tweet.
- Schedule queue (2): x_list_scheduled, x_cancel_scheduled.
- DMs (3): x_read_dm_inbox, x_read_dm_thread, x_send_dm.
- Bulk (1): x_export_bookmarks (resumable CSV dump for full-history walks).

**x_delete_tweet safety: text-echo guard.** Delete is the only irreversible
action without a built-in undo. To guard against URL hallucinations and
copy-paste mistakes without adding an approval round-trip, the tool requires
a `text_must_match` parameter — a ≥5-char substring of the tweet body. The
host script reads the live tweet first and refuses to delete unless the
substring is present. Consistent with the skill's existing per-action trust
model (no approval gating; wrap in another skill if approvals are wanted).

**Concentrated DOM volatility.** Two new files isolate everything that can
break when X redeploys:
- `lib/locators.ts` — every CSS / data-testid selector across all 25 scripts.
- `lib/extract.ts` — every tweet/profile/list/DM parser (parseTweetCard,
  collectTweets, parseDmConversation, plus pretty-printers like
  renderTweetList for notifyAgent payloads).

When X breaks something, those are the only places to update.

**Privacy + safety knobs.**
- DM action handlers redact body content from nanoclaw.log (only action +
  requestId logged).
- Per-action timeout (120s default) + screenshot+DOM-dump capture on failure
  (logs/x-failures/ for post-mortem).
- 10s mutex pacing protects accounts from X's anti-spam tripping.
- DM read-receipt caveat surfaced in tool descriptions so the agent knows
  opening a thread marks it read.

## How it works

```
┌── Container (Bun) ─────────────────────────────────────────────┐
│  mcp-tools/x-integration.ts                                    │
│   └─ 25 tools via makeXTool() factory                          │
│      └─ writeMessageOut({ kind:'system', content: ... })       │
└────────────────┬───────────────────────────────────────────────┘
                 │  outbound.db (the v2 IPC surface)
                 ▼
┌── Host (Node) ─────────────────────────────────────────────────┐
│  src/modules/x-integration/index.ts                            │
│   └─ registerDeliveryAction('x_*', handler) × 25               │
│      └─ pacedRun() ⟶ spawn tsx script ⟶ notifyAgent(result)    │
└────────────────┬───────────────────────────────────────────────┘
                 │  inbound.db + wake
                 ▼
        (agent gets the result on its next turn)
```

## How it was tested

End-to-end on Linux (EVO box, Pop!_OS) over several weeks: read flows
(timeline, bookmarks, search, single-tweet, thread, user, list,
notifications), compose flows (post, reply, quote with media attachments
and with native X scheduling), engagement toggles (like/unlike,
bookmark/unbookmark, follow/unfollow, retweet/unretweet), schedule queue
(list, cancel by index AND by text-match), DMs (inbox listing, thread
read, send), bulk bookmark export (~hundreds of tweets across multiple
paginated calls). Delete tested in two modes: with valid text_must_match
(success), and with deliberately-wrong text_must_match (refused with clear
error message). Macos compatibility preserved from the prior v1 skill.

## Usage

```
What's in my bookmarks lately?
Summarize this thread: https://x.com/<user>/status/<id>
Post a tweet: gm. Sovereignty is a daily practice.
Reply to https://x.com/<id> with: this matches my experience.
Schedule this for tomorrow 9am PT: <text>
What DMs are in my inbox?
DM @<friend>: thanks for the link earlier.
Delete my tweet https://x.com/<id> — it contains "deprecated"
```

Each command produces one acknowledgment from the agent, then a follow-up
with the result ~10–60s later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant