Skip to content

feat(twitter): add switch-account command with --list and @handle modes#1858

Open
Semonxue wants to merge 8 commits into
jackwener:mainfrom
Semonxue:feat-twitter-switch-account
Open

feat(twitter): add switch-account command with --list and @handle modes#1858
Semonxue wants to merge 8 commits into
jackwener:mainfrom
Semonxue:feat-twitter-switch-account

Conversation

@Semonxue
Copy link
Copy Markdown
Contributor

@Semonxue Semonxue commented Jun 4, 2026

Adds opencli twitter switch-account for the X account-switcher menu:

  • opencli twitter switch-account --list enumerates every account in the switcher, marking the active one.
  • opencli twitter switch-account @handle switches into the named account by clicking the matching aria-label="Switch to @handle" button. If handle already matches the active account, returns status: already_current.

isCurrent is detected from the menu's DOM: the current account renders as <li data-testid="UserCell"> (no "Switch to" button), while the other accounts are <button data-testid="UserCell" aria-label="Switch to @xxx">. The trigger avatar was unreliable for this because X caches the previous active account there until a full page reload.

After clicking the switch button, the page is reloaded so the Playwright session syncs to the new account before the next call.

Tests

  • npx vitest run --project adapter clis/twitter/switch-account.test.js — 15 / 15 pass
  • npm run test:adapter — 382 files / 3780 tests pass
  • npm run typecheck — clean
  • npm run build — clean, cli-manifest.json regenerates

Live verify

$ node dist/src/main.js twitter switch-account --list
- status: listed
  handle: 2nd_ai50196
  is_current: true
  message: Current account @2nd_ai50196
- ...

$ node dist/src/main.js twitter switch-account @semonxue
- status: switched
  handle: '@semonxue'
  is_current: true
  message: Switched to @semonxue

$ node dist/src/main.js twitter switch-account @semonxue   # already there
- status: already_current
  handle: '@semonxue'
  is_current: true
  message: '@semonxue is already the current account.'

Files

  • clis/twitter/switch-account.js — new
  • clis/twitter/switch-account.test.js — new, 15 unit tests covering argument routing, readAccountMenu, registration, --list mode, and switch-mode error paths (auth required, malformed handle, target not in menu, already current, switched)
  • CHANGELOG.md## Unreleased Features entry

Semonxue added 5 commits June 6, 2026 15:39
- openAccountMenu: click side-nav trigger, poll for UserCell
- readAccountMenu: exported for unit tests, parses all UserCell rows
- --list: open menu → read accounts → close menu → return table
- @handle: click aria-label button, poll for trigger update
- best-effort menu close (AppTabBar_Profile_Link click)
- graceful error messages with available-account list on switch miss
- 14 unit tests covering routing, readAccountMenu, --list, and switch modes
…ests

- Use LI-based current account detection (no Switch button) instead of
  relying on trigger avatar, which caches the previous active account
- Improve avatar container scoping to [data-testid=UserCell] to avoid
  counting page-level avatars
- Add list mode: --list to enumerate accounts without switching
- Rewrite tests to match new evaluate block structure with real error
  paths (AUTH_REQUIRED, ARGUMENT, NOT_FOUND, already_current, switched)
The current account renders as <li> in the switcher menu (no 'Switch to' button),
so switching to it via @handle previously threw NOT_FOUND. Detect via the
isCurrent flag on the parsed accounts list and return already_current.

Also drops the debug suffix from the already_current message.
@Semonxue Semonxue force-pushed the feat-twitter-switch-account branch from 6657691 to d166e7a Compare June 6, 2026 07:42
Semonxue added 3 commits June 6, 2026 19:55
…p lint

- readAccountMenu: rename displayName/isCurrent → display_name/is_current
- page.evaluate: return accounts array directly (list mode) to avoid
  returning a { mode, currentHandle, accounts } wrapper with non-column keys
- already_current / switched wrappers: mode → status, drop currentHandle
- NOT_FOUND: drop available field; bake the list into the message
- defensive: only unwrap a 1-element array (was always splitting list output)
- tests: update mocks to match new return shapes; assert on err.hint not err.message
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