Skip to content

[eas-cli] Add eas chat command#4005

Draft
jonsamp wants to merge 12 commits into
mainfrom
jon/eng-24455-eas-chat-command
Draft

[eas-cli] Add eas chat command#4005
jonsamp wants to merge 12 commits into
mainfrom
jon/eng-24455-eas-chat-command

Conversation

@jonsamp

@jonsamp jonsamp commented Jul 12, 2026

Copy link
Copy Markdown
Member

Why

ENG-24455: bring the dashboard AI chat assistant to the terminal so developers (and other agents) can ask questions about their Expo account and EAS projects without leaving the CLI.

The assistant, its tools, and the model all live in the website's POST /api/chat Next.js route (not the GraphQL API). This command reuses that same endpoint.

How

  • New top-level command eas chat "message" (src/commands/chat.ts) plus a small client (src/chat/chatClient.ts).
  • Auth: the /api/chat route only accepts a session cookie, which it forwards to GraphQL as the expo-session header. The CLI's stored session secret is exactly the JSON that cookie expects, so the command sends it as Cookie: <authStorageKey>.sessionSecret=… (mirrors what a browser session does). EXPO_TOKEN access tokens are not supported by the endpoint, so the command fails fast with a clear message telling the user to eas login.
  • Streaming: parses the AI SDK UI message stream (SSE) and writes the assistant's text to stdout incrementally, surfacing tool calls as dim status lines.
  • Interactive multi-turn: after each answer it prompts Reply (default) so you can follow up; the full history is resent each turn. Type exit/quit/q (or empty Enter / Ctrl+C) to end.
  • Flags: -p/--project (focus on a project, validated via AppQuery.byFullNameAsync, resolves the owner account and frames the question), -a/--account (scope), --non-interactive, and --json (agent-friendly single-turn structured output including the assistant's response and the toolCalls it used).
  • The command is hidden (static hidden = true) for now: the backend chat feature is still behind the DASHBOARD_CHAT user feature gate (prod = admins only today), requires per-account aiChatEnabled, and a paid plan for a non-zero token budget. It should stay hidden until the backend ships and the gate is widened.

Test Plan

  • yarn typecheck, yarn lint, and yarn fmt:check pass.
  • 13 unit tests (src/chat/__tests__/chatClient.test.ts, src/commands/__tests__/chat.test.ts) cover: SSE streaming + text accumulation, tool-call capture, cookie/account header construction, HTTP 401/403/429 → friendly errors, primary/--account/--project scoping, project-not-found, the interactive follow-up→exit loop, JSON output shape, and the access-token guard.
  • Manual: eas chat --help renders the args/flags; command is absent from the README and top-level help (hidden) but runnable directly. End-to-end against production is gated by the in-progress backend rollout described above.

Ask the Expo dashboard AI assistant about your account and EAS projects from the terminal. Streams the reply, supports interactive multi-turn follow-ups, `--project`/`--account` scoping, `--non-interactive`, and `--json` for agent-friendly structured output.

Calls the website's `/api/chat` route using the CLI session secret; command is hidden while the backend feature gate rolls out.
@linear-code

linear-code Bot commented Jul 12, 2026

Copy link
Copy Markdown

ENG-24455

@jonsamp jonsamp force-pushed the jon/eng-24455-eas-chat-command branch from ca4d866 to effddeb Compare July 12, 2026 15:36
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.28982% with 64 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.89%. Comparing base (4b1f785) to head (30f82f0).

Files with missing lines Patch % Lines
packages/eas-cli/src/chat/chatClient.ts 80.36% 33 Missing ⚠️
packages/eas-cli/src/chat/detectProject.ts 20.84% 19 Missing ⚠️
packages/eas-cli/src/commands/chat.ts 94.02% 7 Missing ⚠️
packages/eas-cli/src/chat/renderMarkdown.ts 90.48% 4 Missing ⚠️
packages/eas-cli/src/chat/replInput.ts 96.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4005      +/-   ##
==========================================
+ Coverage   59.67%   59.89%   +0.22%     
==========================================
  Files         941      946       +5     
  Lines       41319    41702     +383     
  Branches     8675     8775     +100     
==========================================
+ Hits        24654    24973     +319     
- Misses      16571    16635      +64     
  Partials       94       94              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

jonsamp added 9 commits July 12, 2026 11:34
Render the streamed assistant reply as ANSI (bold, inline code, headings, bullets, links) one line at a time. Prepend a client-side system message steering the assistant to write terminal-friendly answers and include expo.dev links for referenced artifacts (builds, updates, etc.).
…hat`

Replace the per-turn prompt with a reusable Node readline interface: arrow-key history across turns and /help, /clear, /exit slash commands, all in the normal scrolling buffer (no new dependency).
The ora spinner leaves stdin paused when it stops, so the readline prompt had nothing keeping the event loop alive and the process exited the moment the prompt appeared. Resume stdin before each prompt and stop the spinner from grabbing stdin (discardStdin: false).
When run inside a linked EAS project and neither --project nor --account is given, chat now focuses on that project automatically. Detection is silent and read-only: it never prompts, links, or creates a config, and falls back to account scope when the directory is not a linked project.
The first message comes from argv, not the readline prompt, so Up-arrow had nothing to recall at the first follow-up. Pass it as readline's initial history.
Prefix the assistant reply with a magenta "Expo > " label and indent its continuation lines to match, and label the user's turn "Chat > " (both 4-char names so the two align).
Wrap each rendered line to the terminal width minus the indent (ANSI-aware, hard-breaking long URLs) and re-apply the indent to every wrapped piece, so terminal soft-wrapping no longer drops wrapped text to column 0. Also indent the ora spinner to align with the reply body.
The indented ora spinner leaves the cursor at its indent column when it stops (ora's clear() ends with cursorTo(indent)), which pushed the whole reply right by the indent. Reset the cursor to column 0 after stopping. Also move the commands hint above the echoed message and shorten it.
Use ora's prefixText to show "Expo > <spinner> Thinking…" from the first frame instead of the indent option, which rendered unindented for a tick then snapped right. The spinner now reads as the reply and is cleared/replaced by the streamed text.
@jonsamp jonsamp changed the title [eas-cli] Add hidden eas chat command [eas-cli] Add eas chat command Jul 12, 2026
@github-actions

Copy link
Copy Markdown

✅ Thank you for adding the changelog entry!

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