[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DG2TPZAT3SMWSYFJ3K82TE":3,"skill-same:01M6DG2TPZAT3SMWSYFJ3K82TE":106},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":38,"commitSha":39,"skillDirectory":40,"forksCount":13,"repositoryHtmlUrl":41,"repositoryHomepageUrl":6,"ownerHtmlUrl":42,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":43,"install":44,"resources":57},"01M6DG2TPZAT3SMWSYFJ3K82TE","x-integration",null,"X (Twitter) integration for NanoClaw. Post tweets, like, reply, retweet, and quote. Use for setup, testing, or troubleshooting X functionality. Triggers on \"setup x\", \"x integration\", \"twitter\", \"post tweet\", \"tweet\".","mbaker95\u002Fnanodex","mbaker95","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F11398294?v=4","01M6DFXVNAJHZG78Y2D700Y6V3","01M6DFXVTTW83QGKTJ9FJ5YCH5",0,"MIT","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32],"x integration","integration","testing","setup","troubleshooting","twitter","use","reply","quote","triggers","2026-11-10 10:12:30",false,"---\nname: x-integration\ndescription: X (Twitter) integration for NanoClaw. Post tweets, like, reply, retweet, and quote. Use for setup, testing, or troubleshooting X functionality. Triggers on \"setup x\", \"x integration\", \"twitter\", \"post tweet\", \"tweet\".\n---\n\n# X (Twitter) Integration\n\nBrowser automation for X interactions via WhatsApp.\n\n> **Compatibility:** NanoClaw v1.0.0. Directory structure may change in future versions.\n\n## Features\n\n| Action | Tool | Description |\n|--------|------|-------------|\n| Post | `x_post` | Publish new tweets |\n| Like | `x_like` | Like any tweet |\n| Reply | `x_reply` | Reply to tweets |\n| Retweet | `x_retweet` | Retweet without comment |\n| Quote | `x_quote` | Quote tweet with comment |\n\n## Prerequisites\n\nBefore using this skill, ensure:\n\n1. **NanoClaw is installed and running** - WhatsApp connected, service active\n2. **Dependencies installed**:\n   ```bash\n   npm ls playwright dotenv-cli || npm install playwright dotenv-cli\n   ```\n3. **CHROME_PATH configured** in `.env` (if Chrome is not at default location):\n   ```bash\n   # Find your Chrome path\n   mdfind \"kMDItemCFBundleIdentifier == 'com.google.Chrome'\" 2>\u002Fdev\u002Fnull | head -1\n   # Add to .env\n   CHROME_PATH=\u002Fpath\u002Fto\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome\n   ```\n\n## Quick Start\n\n```bash\n# 1. Setup authentication (interactive)\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n# Verify: data\u002Fx-auth.json should exist after successful login\n\n# 2. Rebuild container to include skill\n.\u002Fcontainer\u002Fbuild.sh\n# Verify: Output shows \"COPY .claude\u002Fskills\u002Fx-integration\u002Fagent.ts\"\n\n# 3. Rebuild host and restart service\nnpm run build\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n# Verify: launchctl list | grep nanoclaw (macOS) or systemctl --user status nanoclaw (Linux)\n```\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `CHROME_PATH` | `\u002FApplications\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome` | Chrome executable path |\n| `NANOCLAW_ROOT` | `process.cwd()` | Project root directory |\n| `LOG_LEVEL` | `info` | Logging level (debug, info, warn, error) |\n\nSet in `.env` file (loaded via `dotenv-cli` at runtime):\n\n```bash\n# .env\nCHROME_PATH=\u002FApplications\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome\n```\n\n### Configuration File\n\nEdit `lib\u002Fconfig.ts` to modify defaults:\n\n```typescript\nexport const config = {\n    \u002F\u002F Browser viewport\n    viewport: { width: 1280, height: 800 },\n\n    \u002F\u002F Timeouts (milliseconds)\n    timeouts: {\n        navigation: 30000,    \u002F\u002F Page navigation\n        elementWait: 5000,    \u002F\u002F Wait for element\n        afterClick: 1000,     \u002F\u002F Delay after click\n        afterFill: 1000,      \u002F\u002F Delay after form fill\n        afterSubmit: 3000,    \u002F\u002F Delay after submit\n        pageLoad: 3000,       \u002F\u002F Initial page load\n    },\n\n    \u002F\u002F Tweet limits\n    limits: {\n        tweetMaxLength: 280,\n    },\n};\n```\n\n### Data Directories\n\nPaths relative to project root:\n\n| Path | Purpose | Git |\n|------|---------|-----|\n| `data\u002Fx-browser-profile\u002F` | Chrome profile with X session | Ignored |\n| `data\u002Fx-auth.json` | Auth state marker | Ignored |\n| `logs\u002Fnanoclaw.log` | Service logs (contains X operation logs) | Ignored |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  Container (Linux VM)                                       │\n│  └── agent.ts → MCP tool definitions (x_post, etc.)    │\n│      └── Writes IPC request to \u002Fworkspace\u002Fipc\u002Ftasks\u002F       │\n└──────────────────────┬──────────────────────────────────────┘\n                       │ IPC (file system)\n                       ▼\n┌─────────────────────────────────────────────────────────────┐\n│  Host (macOS)                                               │\n│  └── src\u002Fipc.ts → processTaskIpc()                         │\n│      └── host.ts → handleXIpc()                         │\n│          └── spawn subprocess → scripts\u002F*.ts               │\n│              └── Playwright → Chrome → X Website           │\n└─────────────────────────────────────────────────────────────┘\n```\n\n### Why This Design?\n\n- **API is expensive** - X official API requires paid subscription ($100+\u002Fmonth) for posting\n- **Bot browsers get blocked** - X detects and bans headless browsers and common automation fingerprints\n- **Must use user's real browser** - Reuses the user's actual Chrome on Host with real browser fingerprint to avoid detection\n- **One-time authorization** - User logs in manually once, session persists in Chrome profile for future use\n\n### File Structure\n\n```\n.claude\u002Fskills\u002Fx-integration\u002F\n├── SKILL.md          # This documentation\n├── host.ts           # Host-side IPC handler\n├── agent.ts          # Container-side MCP tool definitions\n├── lib\u002F\n│   ├── config.ts     # Centralized configuration\n│   └── browser.ts    # Playwright utilities\n└── scripts\u002F\n    ├── setup.ts      # Interactive login\n    ├── post.ts       # Post tweet\n    ├── like.ts       # Like tweet\n    ├── reply.ts      # Reply to tweet\n    ├── retweet.ts    # Retweet\n    └── quote.ts      # Quote tweet\n```\n\n### Integration Points\n\nTo integrate this skill into NanoClaw, make the following modifications:\n\n---\n\n**1. Host side: `src\u002Fipc.ts`**\n\nAdd import after other local imports:\n```typescript\nimport { handleXIpc } from '..\u002F.claude\u002Fskills\u002Fx-integration\u002Fhost.js';\n```\n\nModify `processTaskIpc` function's switch statement default case:\n```typescript\n\u002F\u002F Find:\ndefault:\nlogger.warn({ type: data.type }, 'Unknown IPC task type');\n\n\u002F\u002F Replace with:\ndefault:\nconst handled = await handleXIpc(data, sourceGroup, isMain, DATA_DIR);\nif (!handled) {\n    logger.warn({ type: data.type }, 'Unknown IPC task type');\n}\n```\n\n---\n\n**2. Container side: `container\u002Fagent-runner\u002Fsrc\u002Fipc-mcp.ts`**\n\nAdd import after `cron-parser` import:\n```typescript\n\u002F\u002F @ts-ignore - Copied during Docker build from .claude\u002Fskills\u002Fx-integration\u002F\nimport { createXTools } from '.\u002Fskills\u002Fx-integration\u002Fagent.js';\n```\n\nAdd to the end of tools array (before the closing `]`):\n```typescript\n    ...createXTools({ groupFolder, isMain })\n```\n\n---\n\n**3. Build script: `container\u002Fbuild.sh`**\n\nChange build context from `container\u002F` to project root (required to access `.claude\u002Fskills\u002F`):\n```bash\n# Find:\ndocker build -t \"${IMAGE_NAME}:${TAG}\" .\n\n# Replace with:\ncd \"$SCRIPT_DIR\u002F..\"\ndocker build -t \"${IMAGE_NAME}:${TAG}\" -f container\u002FDockerfile .\n```\n\n---\n\n**4. Dockerfile: `container\u002FDockerfile`**\n\nFirst, update the build context paths (required to access `.claude\u002Fskills\u002F` from project root):\n```dockerfile\n# Find:\nCOPY agent-runner\u002Fpackage*.json .\u002F\n...\nCOPY agent-runner\u002F .\u002F\n\n# Replace with:\nCOPY container\u002Fagent-runner\u002Fpackage*.json .\u002F\n...\nCOPY container\u002Fagent-runner\u002F .\u002F\n```\n\nThen add COPY line after `COPY container\u002Fagent-runner\u002F .\u002F` and before `RUN npm run build`:\n```dockerfile\n# Copy skill MCP tools\nCOPY .claude\u002Fskills\u002Fx-integration\u002Fagent.ts .\u002Fsrc\u002Fskills\u002Fx-integration\u002F\n```\n\n## Setup\n\nAll paths below are relative to project root (`NANOCLAW_ROOT`).\n\n### 1. Check Chrome Path\n\n```bash\n# Check if Chrome exists at configured path\ncat .env | grep CHROME_PATH\nls -la \"$(grep CHROME_PATH .env | cut -d= -f2)\" 2>\u002Fdev\u002Fnull || \\\necho \"Chrome not found - update CHROME_PATH in .env\"\n```\n\n### 2. Run Authentication\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n```\n\nThis opens Chrome for manual X login. Session saved to `data\u002Fx-browser-profile\u002F`.\n\n**Verify success:**\n```bash\ncat data\u002Fx-auth.json  # Should show {\"authenticated\": true, ...}\n```\n\n### 3. Rebuild Container\n\n```bash\n.\u002Fcontainer\u002Fbuild.sh\n```\n\n**Verify success:**\n```bash\n.\u002Fcontainer\u002Fbuild.sh 2>&1 | grep -i \"agent.ts\"  # Should show COPY line\n```\n\n### 4. Restart Service\n\n```bash\nnpm run build\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n```\n\n**Verify success:**\n```bash\nlaunchctl list | grep nanoclaw  # macOS — should show PID and exit code 0 or -\n# Linux: systemctl --user status nanoclaw\n```\n\n## Usage via WhatsApp\n\nReplace `@Assistant` with your configured trigger name (`ASSISTANT_NAME` in `.env`):\n\n```\n@Assistant post a tweet: Hello world!\n\n@Assistant like this tweet https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\n\n@Assistant reply to https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123 with: Great post!\n\n@Assistant retweet https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\n\n@Assistant quote https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123 with comment: Interesting\n```\n\n**Note:** Only the main group can use X tools. Other groups will receive an error.\n\n## Testing\n\nScripts require environment variables from `.env`. Use `dotenv-cli` to load them:\n\n### Check Authentication Status\n\n```bash\n# Check if auth file exists and is valid\ncat data\u002Fx-auth.json 2>\u002Fdev\u002Fnull && echo \"Auth configured\" || echo \"Auth not configured\"\n\n# Check if browser profile exists\nls -la data\u002Fx-browser-profile\u002F 2>\u002Fdev\u002Fnull | head -5\n```\n\n### Re-authenticate (if expired)\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n```\n\n### Test Post (will actually post)\n\n```bash\necho '{\"content\":\"Test tweet - please ignore\"}' | npx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fpost.ts\n```\n\n### Test Like\n\n```bash\necho '{\"tweetUrl\":\"https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\"}' | npx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Flike.ts\n```\n\nOr export `CHROME_PATH` manually before running:\n\n```bash\nexport CHROME_PATH=\"\u002Fpath\u002Fto\u002Fchrome\"\necho '{\"content\":\"Test\"}' | npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fpost.ts\n```\n\n## Troubleshooting\n\n### Authentication Expired\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n```\n\n### Browser Lock Files\n\nIf Chrome fails to launch:\n\n```bash\nrm -f data\u002Fx-browser-profile\u002FSingletonLock\nrm -f data\u002Fx-browser-profile\u002FSingletonSocket\nrm -f data\u002Fx-browser-profile\u002FSingletonCookie\n```\n\n### Check Logs\n\n```bash\n# Host logs (relative to project root)\ngrep -i \"x_post\\|x_like\\|x_reply\\|handleXIpc\" logs\u002Fnanoclaw.log | tail -20\n\n# Script errors\ngrep -i \"error\\|failed\" logs\u002Fnanoclaw.log | tail -20\n```\n\n### Script Timeout\n\nDefault timeout is 2 minutes (120s). Increase in `host.ts`:\n\n```typescript\nconst timer = setTimeout(() => {\n  proc.kill('SIGTERM');\n  resolve({ success: false, message: 'Script timed out (120s)' });\n}, 120000);  \u002F\u002F ← Increase this value\n```\n\n### X UI Selector Changes\n\nIf X updates their UI, selectors in scripts may break. Current selectors:\n\n| Element | Selector |\n|---------|----------|\n| Tweet input | `[data-testid=\"tweetTextarea_0\"]` |\n| Post button | `[data-testid=\"tweetButtonInline\"]` |\n| Reply button | `[data-testid=\"reply\"]` |\n| Like | `[data-testid=\"like\"]` |\n| Unlike | `[data-testid=\"unlike\"]` |\n| Retweet | `[data-testid=\"retweet\"]` |\n| Unretweet | `[data-testid=\"unretweet\"]` |\n| Confirm retweet | `[data-testid=\"retweetConfirm\"]` |\n| Modal dialog | `[role=\"dialog\"][aria-modal=\"true\"]` |\n| Modal submit | `[data-testid=\"tweetButton\"]` |\n\n### Container Build Issues\n\nIf MCP tools not found in container:\n\n```bash\n# Verify build copies skill\n.\u002Fcontainer\u002Fbuild.sh 2>&1 | grep -i skill\n\n# Check container has the file\ndocker run nanoclaw-agent ls -la \u002Fapp\u002Fsrc\u002Fskills\u002F\n```\n\n## Security\n\n- `data\u002Fx-browser-profile\u002F` - Contains X session cookies (in `.gitignore`)\n- `data\u002Fx-auth.json` - Auth state marker (in `.gitignore`)\n- Only main group can use X tools (enforced in `agent.ts` and `host.ts`)\n- Scripts run as subprocesses with limited environment","# X (Twitter) Integration\n\nBrowser automation for X interactions via WhatsApp.\n\n> **Compatibility:** NanoClaw v1.0.0. Directory structure may change in future versions.\n\n## Features\n\n| Action | Tool | Description |\n|--------|------|-------------|\n| Post | `x_post` | Publish new tweets |\n| Like | `x_like` | Like any tweet |\n| Reply | `x_reply` | Reply to tweets |\n| Retweet | `x_retweet` | Retweet without comment |\n| Quote | `x_quote` | Quote tweet with comment |\n\n## Prerequisites\n\nBefore using this skill, ensure:\n\n1. **NanoClaw is installed and running** - WhatsApp connected, service active\n2. **Dependencies installed**:\n   ```bash\n   npm ls playwright dotenv-cli || npm install playwright dotenv-cli\n   ```\n3. **CHROME_PATH configured** in `.env` (if Chrome is not at default location):\n   ```bash\n   # Find your Chrome path\n   mdfind \"kMDItemCFBundleIdentifier == 'com.google.Chrome'\" 2>\u002Fdev\u002Fnull | head -1\n   # Add to .env\n   CHROME_PATH=\u002Fpath\u002Fto\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome\n   ```\n\n## Quick Start\n\n```bash\n# 1. Setup authentication (interactive)\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n# Verify: data\u002Fx-auth.json should exist after successful login\n\n# 2. Rebuild container to include skill\n.\u002Fcontainer\u002Fbuild.sh\n# Verify: Output shows \"COPY .claude\u002Fskills\u002Fx-integration\u002Fagent.ts\"\n\n# 3. Rebuild host and restart service\nnpm run build\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n# Verify: launchctl list | grep nanoclaw (macOS) or systemctl --user status nanoclaw (Linux)\n```\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `CHROME_PATH` | `\u002FApplications\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome` | Chrome executable path |\n| `NANOCLAW_ROOT` | `process.cwd()` | Project root directory |\n| `LOG_LEVEL` | `info` | Logging level (debug, info, warn, error) |\n\nSet in `.env` file (loaded via `dotenv-cli` at runtime):\n\n```bash\n# .env\nCHROME_PATH=\u002FApplications\u002FGoogle Chrome.app\u002FContents\u002FMacOS\u002FGoogle Chrome\n```\n\n### Configuration File\n\nEdit `lib\u002Fconfig.ts` to modify defaults:\n\n```typescript\nexport const config = {\n    \u002F\u002F Browser viewport\n    viewport: { width: 1280, height: 800 },\n\n    \u002F\u002F Timeouts (milliseconds)\n    timeouts: {\n        navigation: 30000,    \u002F\u002F Page navigation\n        elementWait: 5000,    \u002F\u002F Wait for element\n        afterClick: 1000,     \u002F\u002F Delay after click\n        afterFill: 1000,      \u002F\u002F Delay after form fill\n        afterSubmit: 3000,    \u002F\u002F Delay after submit\n        pageLoad: 3000,       \u002F\u002F Initial page load\n    },\n\n    \u002F\u002F Tweet limits\n    limits: {\n        tweetMaxLength: 280,\n    },\n};\n```\n\n### Data Directories\n\nPaths relative to project root:\n\n| Path | Purpose | Git |\n|------|---------|-----|\n| `data\u002Fx-browser-profile\u002F` | Chrome profile with X session | Ignored |\n| `data\u002Fx-auth.json` | Auth state marker | Ignored |\n| `logs\u002Fnanoclaw.log` | Service logs (contains X operation logs) | Ignored |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  Container (Linux VM)                                       │\n│  └── agent.ts → MCP tool definitions (x_post, etc.)    │\n│      └── Writes IPC request to \u002Fworkspace\u002Fipc\u002Ftasks\u002F       │\n└──────────────────────┬──────────────────────────────────────┘\n                       │ IPC (file system)\n                       ▼\n┌─────────────────────────────────────────────────────────────┐\n│  Host (macOS)                                               │\n│  └── src\u002Fipc.ts → processTaskIpc()                         │\n│      └── host.ts → handleXIpc()                         │\n│          └── spawn subprocess → scripts\u002F*.ts               │\n│              └── Playwright → Chrome → X Website           │\n└─────────────────────────────────────────────────────────────┘\n```\n\n### Why This Design?\n\n- **API is expensive** - X official API requires paid subscription ($100+\u002Fmonth) for posting\n- **Bot browsers get blocked** - X detects and bans headless browsers and common automation fingerprints\n- **Must use user's real browser** - Reuses the user's actual Chrome on Host with real browser fingerprint to avoid detection\n- **One-time authorization** - User logs in manually once, session persists in Chrome profile for future use\n\n### File Structure\n\n```\n.claude\u002Fskills\u002Fx-integration\u002F\n├── SKILL.md          # This documentation\n├── host.ts           # Host-side IPC handler\n├── agent.ts          # Container-side MCP tool definitions\n├── lib\u002F\n│   ├── config.ts     # Centralized configuration\n│   └── browser.ts    # Playwright utilities\n└── scripts\u002F\n    ├── setup.ts      # Interactive login\n    ├── post.ts       # Post tweet\n    ├── like.ts       # Like tweet\n    ├── reply.ts      # Reply to tweet\n    ├── retweet.ts    # Retweet\n    └── quote.ts      # Quote tweet\n```\n\n### Integration Points\n\nTo integrate this skill into NanoClaw, make the following modifications:\n\n---\n\n**1. Host side: `src\u002Fipc.ts`**\n\nAdd import after other local imports:\n```typescript\nimport { handleXIpc } from '..\u002F.claude\u002Fskills\u002Fx-integration\u002Fhost.js';\n```\n\nModify `processTaskIpc` function's switch statement default case:\n```typescript\n\u002F\u002F Find:\ndefault:\nlogger.warn({ type: data.type }, 'Unknown IPC task type');\n\n\u002F\u002F Replace with:\ndefault:\nconst handled = await handleXIpc(data, sourceGroup, isMain, DATA_DIR);\nif (!handled) {\n    logger.warn({ type: data.type }, 'Unknown IPC task type');\n}\n```\n\n---\n\n**2. Container side: `container\u002Fagent-runner\u002Fsrc\u002Fipc-mcp.ts`**\n\nAdd import after `cron-parser` import:\n```typescript\n\u002F\u002F @ts-ignore - Copied during Docker build from .claude\u002Fskills\u002Fx-integration\u002F\nimport { createXTools } from '.\u002Fskills\u002Fx-integration\u002Fagent.js';\n```\n\nAdd to the end of tools array (before the closing `]`):\n```typescript\n    ...createXTools({ groupFolder, isMain })\n```\n\n---\n\n**3. Build script: `container\u002Fbuild.sh`**\n\nChange build context from `container\u002F` to project root (required to access `.claude\u002Fskills\u002F`):\n```bash\n# Find:\ndocker build -t \"${IMAGE_NAME}:${TAG}\" .\n\n# Replace with:\ncd \"$SCRIPT_DIR\u002F..\"\ndocker build -t \"${IMAGE_NAME}:${TAG}\" -f container\u002FDockerfile .\n```\n\n---\n\n**4. Dockerfile: `container\u002FDockerfile`**\n\nFirst, update the build context paths (required to access `.claude\u002Fskills\u002F` from project root):\n```dockerfile\n# Find:\nCOPY agent-runner\u002Fpackage*.json .\u002F\n...\nCOPY agent-runner\u002F .\u002F\n\n# Replace with:\nCOPY container\u002Fagent-runner\u002Fpackage*.json .\u002F\n...\nCOPY container\u002Fagent-runner\u002F .\u002F\n```\n\nThen add COPY line after `COPY container\u002Fagent-runner\u002F .\u002F` and before `RUN npm run build`:\n```dockerfile\n# Copy skill MCP tools\nCOPY .claude\u002Fskills\u002Fx-integration\u002Fagent.ts .\u002Fsrc\u002Fskills\u002Fx-integration\u002F\n```\n\n## Setup\n\nAll paths below are relative to project root (`NANOCLAW_ROOT`).\n\n### 1. Check Chrome Path\n\n```bash\n# Check if Chrome exists at configured path\ncat .env | grep CHROME_PATH\nls -la \"$(grep CHROME_PATH .env | cut -d= -f2)\" 2>\u002Fdev\u002Fnull || \\\necho \"Chrome not found - update CHROME_PATH in .env\"\n```\n\n### 2. Run Authentication\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n```\n\nThis opens Chrome for manual X login. Session saved to `data\u002Fx-browser-profile\u002F`.\n\n**Verify success:**\n```bash\ncat data\u002Fx-auth.json  # Should show {\"authenticated\": true, ...}\n```\n\n### 3. Rebuild Container\n\n```bash\n.\u002Fcontainer\u002Fbuild.sh\n```\n\n**Verify success:**\n```bash\n.\u002Fcontainer\u002Fbuild.sh 2>&1 | grep -i \"agent.ts\"  # Should show COPY line\n```\n\n### 4. Restart Service\n\n```bash\nnpm run build\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n```\n\n**Verify success:**\n```bash\nlaunchctl list | grep nanoclaw  # macOS — should show PID and exit code 0 or -\n# Linux: systemctl --user status nanoclaw\n```\n\n## Usage via WhatsApp\n\nReplace `@Assistant` with your configured trigger name (`ASSISTANT_NAME` in `.env`):\n\n```\n@Assistant post a tweet: Hello world!\n\n@Assistant like this tweet https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\n\n@Assistant reply to https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123 with: Great post!\n\n@Assistant retweet https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\n\n@Assistant quote https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123 with comment: Interesting\n```\n\n**Note:** Only the main group can use X tools. Other groups will receive an error.\n\n## Testing\n\nScripts require environment variables from `.env`. Use `dotenv-cli` to load them:\n\n### Check Authentication Status\n\n```bash\n# Check if auth file exists and is valid\ncat data\u002Fx-auth.json 2>\u002Fdev\u002Fnull && echo \"Auth configured\" || echo \"Auth not configured\"\n\n# Check if browser profile exists\nls -la data\u002Fx-browser-profile\u002F 2>\u002Fdev\u002Fnull | head -5\n```\n\n### Re-authenticate (if expired)\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\n```\n\n### Test Post (will actually post)\n\n```bash\necho '{\"content\":\"Test tweet - please ignore\"}' | npx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fpost.ts\n```\n\n### Test Like\n\n```bash\necho '{\"tweetUrl\":\"https:\u002F\u002Fx.com\u002Fuser\u002Fstatus\u002F123\"}' | npx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Flike.ts\n```\n\nOr export `CHROME_PATH` manually before running:\n\n```bash\nexport CHROME_PATH=\"\u002Fpath\u002Fto\u002Fchrome\"\necho '{\"content\":\"Test\"}' | npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fpost.ts\n```\n\n## Troubleshooting\n\n### Authentication Expired\n\n```bash\nnpx dotenv -e .env -- npx tsx .claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts\nlaunchctl kickstart -k gui\u002F$(id -u)\u002Fcom.nanoclaw  # macOS\n# Linux: systemctl --user restart nanoclaw\n```\n\n### Browser Lock Files\n\nIf Chrome fails to launch:\n\n```bash\nrm -f data\u002Fx-browser-profile\u002FSingletonLock\nrm -f data\u002Fx-browser-profile\u002FSingletonSocket\nrm -f data\u002Fx-browser-profile\u002FSingletonCookie\n```\n\n### Check Logs\n\n```bash\n# Host logs (relative to project root)\ngrep -i \"x_post\\|x_like\\|x_reply\\|handleXIpc\" logs\u002Fnanoclaw.log | tail -20\n\n# Script errors\ngrep -i \"error\\|failed\" logs\u002Fnanoclaw.log | tail -20\n```\n\n### Script Timeout\n\nDefault timeout is 2 minutes (120s). Increase in `host.ts`:\n\n```typescript\nconst timer = setTimeout(() => {\n  proc.kill('SIGTERM');\n  resolve({ success: false, message: 'Script timed out (120s)' });\n}, 120000);  \u002F\u002F ← Increase this value\n```\n\n### X UI Selector Changes\n\nIf X updates their UI, selectors in scripts may break. Current selectors:\n\n| Element | Selector |\n|---------|----------|\n| Tweet input | `[data-testid=\"tweetTextarea_0\"]` |\n| Post button | `[data-testid=\"tweetButtonInline\"]` |\n| Reply button | `[data-testid=\"reply\"]` |\n| Like | `[data-testid=\"like\"]` |\n| Unlike | `[data-testid=\"unlike\"]` |\n| Retweet | `[data-testid=\"retweet\"]` |\n| Unretweet | `[data-testid=\"unretweet\"]` |\n| Confirm retweet | `[data-testid=\"retweetConfirm\"]` |\n| Modal dialog | `[role=\"dialog\"][aria-modal=\"true\"]` |\n| Modal submit | `[data-testid=\"tweetButton\"]` |\n\n### Container Build Issues\n\nIf MCP tools not found in container:\n\n```bash\n# Verify build copies skill\n.\u002Fcontainer\u002Fbuild.sh 2>&1 | grep -i skill\n\n# Check container has the file\ndocker run nanoclaw-agent ls -la \u002Fapp\u002Fsrc\u002Fskills\u002F\n```\n\n## Security\n\n- `data\u002Fx-browser-profile\u002F` - Contains X session cookies (in `.gitignore`)\n- `data\u002Fx-auth.json` - Auth state marker (in `.gitignore`)\n- Only main group can use X tools (enforced in `agent.ts` and `host.ts`)\n- Scripts run as subprocesses with limited environment",{"name":5,"description":7},"main","b6b2f4d4a0c7243409d1684119a6772629bfeba6",".claude\u002Fskills\u002Fx-integration","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex","https:\u002F\u002Fgithub.com\u002Fmbaker95","2026-03-12 23:02:12",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add mbaker95\u002Fnanodex --skill x-integration",{"tool":50,"command":51},"bunx","bunx skills add mbaker95\u002Fnanodex --skill x-integration",{"tool":53,"command":54},"pnpm","pnpm dlx skills add mbaker95\u002Fnanodex --skill x-integration","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002FSKILL.md",true,[58,64,70,74,78,82,86,90,94,98,102],{"path":59,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":62,"sourceHtmlUrl":63},"agent.ts","SCRIPT","脚本",7495,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fagent.ts",{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"bmad-skill-manifest.yaml","REFERENCE","参考文档",922,"https:\u002F\u002Fgithub.com\u002Fdanielmtt9\u002Fbloomcornerkiddies\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbmad-agent-architect\u002Fbmad-skill-manifest.yaml",{"path":71,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":72,"sourceHtmlUrl":73},"host.ts",4519,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fhost.ts",{"path":75,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":76,"sourceHtmlUrl":77},"lib\u002Fbrowser.ts",4066,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Flib\u002Fbrowser.ts",{"path":79,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":80,"sourceHtmlUrl":81},"lib\u002Fconfig.ts",1505,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Flib\u002Fconfig.ts",{"path":83,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":84,"sourceHtmlUrl":85},"scripts\u002Flike.ts",1662,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Flike.ts",{"path":87,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":88,"sourceHtmlUrl":89},"scripts\u002Fpost.ts",2300,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fpost.ts",{"path":91,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":92,"sourceHtmlUrl":93},"scripts\u002Fquote.ts",2815,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fquote.ts",{"path":95,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":96,"sourceHtmlUrl":97},"scripts\u002Freply.ts",2557,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Freply.ts",{"path":99,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":100,"sourceHtmlUrl":101},"scripts\u002Fretweet.ts",2006,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fretweet.ts",{"path":103,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":104,"sourceHtmlUrl":105},"scripts\u002Fsetup.ts",2782,"https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fx-integration\u002Fscripts\u002Fsetup.ts",[107,135,158,170,182,196],{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":116,"publishedAt":33},"01M6DG2XYNH5XV7XGTJJ1SF0TG","agent-browser","Browse the web for any task — research topics, read articles, interact with web apps, fill forms, take screenshots, extract data, and test web pages. Use whenever a browser would be useful, not just when the user explicitly asks.","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002Fcontainer\u002Fskills\u002Fagent-browser\u002FSKILL.md",[113],{"categoryNo":114,"categoryName":115,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[117,118,119,120,121,122,123,124,125,29,126,127,128,129,130,131,132,133,134],"browser","agent browser","research","read","pages","forms","task","test web","take","extract","user","just","Bash","browse","data","web","test","screenshots",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":33},"01M6DG2Q4BJQ046BZSR9N7GZ72","use-local-whisper","Use when the user wants local voice transcription instead of OpenAI Whisper API. Switches to whisper.cpp running on Apple Silicon. WhatsApp only for now. Requires voice-transcription skill to be applied first.","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fuse-local-whisper\u002FSKILL.md",[],[29,142,143,144,145,127,146,147,148,149,150,151,152,153,154,155,156,157],"whisper","local whisper","use local whisper","local","only","transcription","voice","apple silicon","api","first","openai","cpp","openai whisper","apple","whatsapp","openai whisper api",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":33},"01M6DG2N2Z1V8VV4MQFRKFAQV0","update-skills","Check for and apply updates to installed skill branches from upstream.","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fupdate-skills\u002FSKILL.md",[],[165,166,167,168,169],"update skills","update","check","upstream","branches",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":33},"01M6DG2J5D7Y3R2BF8V87XZAXF","update-nanoclaw","Efficiently bring upstream NanoClaw updates into a customized install, with preview, selective cherry-pick, and low token usage.","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fupdate-nanoclaw\u002FSKILL.md",[],[177,166,178,179,180,168,181],"update nanoclaw","pick","token","preview","install",{"skillNo":183,"name":26,"nameZh":6,"description":184,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":185,"categories":186,"keywords":187,"publishedAt":33},"01M6DG2FN7S7DVD337CNT5Y67D","Run initial NanoClaw setup. Use when user wants to install dependencies, authenticate messaging channels, register their main channel, or start the background services. Triggers on \"setup\", \"install\", \"configure nanoclaw\", or first-time setup requests.","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fsetup\u002FSKILL.md",[],[26,151,188,189,190,191,192,32,193,194,38,29,195,181,127],"services","start","messaging","run","configure","time","dependencies","channel",{"skillNo":197,"name":198,"nameZh":6,"description":199,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":200,"categories":201,"keywords":205,"publishedAt":33},"01M6DG2CA81Z3VV7H5AYXYEAXG","qodo-pr-resolver","Review and resolve PR issues with Qodo - get AI-powered code review issues and fix them interactively (GitHub, GitLab, Bitbucket, Azure DevOps)","https:\u002F\u002Fgithub.com\u002Fmbaker95\u002Fnanodex\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fqodo-pr-resolver\u002FSKILL.md",[202],{"categoryNo":203,"categoryName":204,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000007","代码审查",[206,207,208,209,210,211,212,213,214,215,216,217,218,219,220],"pr","qodo pr resolver","devops","get","bitbucket","show","fix","issues","code","gitlab","azure","azure devops","github","review","code review"]