diff --git a/.github/workflows/agent-commands.yml b/.github/workflows/agent-commands.yml index 2acfe75d4ac280..fd5e1ca9829fe8 100644 --- a/.github/workflows/agent-commands.yml +++ b/.github/workflows/agent-commands.yml @@ -13,6 +13,12 @@ # are shorthands. Works on any command form and on the # workflow_dispatch `model` input; default is the # VERIFY_MODEL repo variable, else claude-opus-5. +# ... --retry re-run and UPDATE the thread's latest findings comment +# in place instead of posting a new one (GitHub keeps the +# edit history). If the thread has no findings comment +# yet, a new one is posted. Dispatch input: `retry`. +# ... --announce post the "started — watch the run" comment. Off by +# default; the eyes reaction is the only receipt. # @expo-bot check verify + review from one comment. This workflow runs the # verify half (--fix / --no-fix apply, issues and PRs); # expo-code-review-command.yml picks up the review half @@ -120,6 +126,10 @@ on: description: "Override the agent model for this run (a full claude-* id, e.g. claude-fable-5). Empty = the VERIFY_MODEL repo variable, else claude-opus-5. Comment-path runs never set this." required: false type: string + retry: + description: "Update the thread's latest findings comment in place instead of posting a new one" + type: boolean + default: false # Scopes the automatic workflow token, which is what the AGENT step gets: # issue comments yes, repository contents never. The agent therefore cannot @@ -336,6 +346,7 @@ jobs: INPUT_FIX: ${{ inputs.fix }} INPUT_MODE: ${{ inputs.mode }} INPUT_MODEL: ${{ inputs.model }} + INPUT_RETRY: ${{ inputs.retry }} REQUEST: ${{ github.event.comment.body || inputs.request }} run: | # Reject an ill-formed command. On the comment path the reason also @@ -504,15 +515,33 @@ jobs: ;; esac fi + # The "started — watch the run" comment is opt-in: threads stay + # quiet unless the commander asks for the watch link with + # --announce. The eyes reaction remains the default receipt. + announce=false + if [ "$QUIET" != "true" ]; then + case " $line " in *" --announce "*) announce=true ;; esac + fi + # --retry: the publish phase passes updateLatest to the findings + # tool, so the thread's previous findings comment is edited in + # place rather than joined by a sibling. + retry=false + if [ "$QUIET" = "true" ]; then + if [ "$INPUT_RETRY" = "true" ]; then retry=true; fi + else + case " $line " in *" --retry "*) retry=true ;; esac + fi { # route last: everything before this line can still reject. echo "mode=$mode" echo "fix=$fix" echo "is_pr=$IS_PR" echo "model=$model" + echo "announce=$announce" + echo "retry=$retry" echo "route=true" } >> "$GITHUB_OUTPUT" - echo "mode: $mode; fix mode: $fix; model: ${model:-default} (target is $([ "$IS_PR" = true ] && echo 'a pull request' || echo 'an issue'))" + echo "mode: $mode; fix mode: $fix; model: ${model:-default}; announce: $announce; retry: $retry (target is $([ "$IS_PR" = true ] && echo 'a pull request' || echo 'an issue'))" # A maintainer typed a command and it did not launch — say so where it # was typed. Rejections used to surface two bad ways: a red step in a @@ -533,11 +562,12 @@ jobs: GH_TOKEN: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }} run: gh api -X POST "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" -f content=eyes - # Watchable from the start: the Actions run streams the agent's live - # progress, so the thread gets that link the moment work begins (the + # Opt-in via --announce: the Actions run streams the agent's live + # progress, and this posts that link the moment work begins (the # findings comment lands separately when the investigation completes). + # Default is no comment — the eyes reaction is the only receipt. - name: Announce run - if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true' && env.QUIET != 'true' + if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true' && steps.cmd.outputs.announce == 'true' && env.QUIET != 'true' env: GH_TOKEN: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }} MODE: ${{ steps.cmd.outputs.mode }} @@ -558,9 +588,9 @@ jobs: # are in_progress too, so ordering by (created_at, id) IS the queue; # admission by global position keeps the executing count at CAP even # when several waiters poll at once. Runs pending on the per-target - # group are not in_progress and hold no capacity. The announce above - # already posted the watch link, so the queue wait is visible from - # the thread. + # group are not in_progress and hold no capacity. With --announce the + # thread has the watch link during this wait; by default the queue + # wait is visible only from the Actions tab. - name: Wait for a free verification slot if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true' env: @@ -1352,6 +1382,7 @@ jobs: CUT_SHORT: ${{ steps.investigate.outputs.cut_short }} WRITES_DENIED: ${{ steps.investigate.outputs.writes_denied }} MODE: ${{ steps.cmd.outputs.mode }} + RETRY: ${{ steps.cmd.outputs.retry }} run: | set -o pipefail review=$(cat .verify-out/review.md) @@ -1372,10 +1403,18 @@ jobs: else writes_denied="" fi + # --retry: the maintainer asked this run to REPLACE the thread's + # previous findings comment rather than add a sibling. The server + # resolves which comment that is; the agent only sets the flag. + if [ "$RETRY" = "true" ]; then + retry_note="RETRY MODE: this thread already carries a findings comment from an earlier run, and the maintainer asked for it to be UPDATED IN PLACE. Pass updateLatest: true in your mcp__sandbox__github_comment_issue call; write the body as the complete, current findings (it replaces the old body entirely — do not write it as a diff against the previous comment). " + else + retry_note="" + fi set +o pipefail rc_file="$RUNNER_TEMP/revise.rc" { timeout 1800 claude -p --resume "$SESSION_ID" \ - "${cut_short}${writes_denied}A reviewer read your draft and the log of this run WITHOUT seeing your reasoning, and tried to break it. Their review follows. Treat it as a colleague's objections, not as orders: answer every MUST-FIX, and where you disagree, say so in the outcome with your reason rather than silently ignoring it. Any sandbox you created is still alive — if an objection can be settled by measuring again, measure again instead of arguing. Drop any evidence they show supports no claim, and correct anything they show you asserted without checking. + "${cut_short}${writes_denied}${retry_note}A reviewer read your draft and the log of this run WITHOUT seeing your reasoning, and tried to break it. Their review follows. Treat it as a colleague's objections, not as orders: answer every MUST-FIX, and where you disagree, say so in the outcome with your reason rather than silently ignoring it. Any sandbox you created is still alive — if an objection can be settled by measuring again, measure again instead of arguing. Drop any evidence they show supports no claim, and correct anything they show you asserted without checking. Then, and only then: post the outcome with mcp__sandbox__github_comment_issue; keep or withdraw .verify-out/pr.md and, in work mode, .verify-out/changes.json as the command mode requires; and destroy EVERY sandbox you created with mcp__sandbox__destroy_sandbox before you finish. Destroy them even if posting fails. diff --git a/docs/pages/eas/cli.mdx b/docs/pages/eas/cli.mdx index 9cecc304d5eddc..b6cb52ec86d271 100644 --- a/docs/pages/eas/cli.mdx +++ b/docs/pages/eas/cli.mdx @@ -2,7 +2,7 @@ title: EAS CLI reference sidebar_title: EAS CLI description: EAS CLI is a command-line tool that allows you to interact with Expo Application Services (EAS) from your terminal. -cliVersion: 22.0.0 +cliVersion: 22.2.0 --- import { EASCLIReference } from '~/ui/components/EASCLIReference'; diff --git a/docs/pages/eas/index.mdx b/docs/pages/eas/index.mdx index 0fa23d3d4c3e94..dc69de96dc09fc 100644 --- a/docs/pages/eas/index.mdx +++ b/docs/pages/eas/index.mdx @@ -70,7 +70,7 @@ Read the full pitch at [expo.dev/eas](https://expo.dev/eas), or follow the links /> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. +> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. **EAS Observe** is a performance monitoring service from Expo for tracking how your app performs in production. It gives you visibility into real-world startup times, rendering performance, and user experience across different devices, networks, and conditions. diff --git a/docs/pages/versions/unversioned/sdk/observe.mdx b/docs/pages/versions/unversioned/sdk/observe.mdx index 112a8ceda64033..b6fb9e40e698f6 100644 --- a/docs/pages/versions/unversioned/sdk/observe.mdx +++ b/docs/pages/versions/unversioned/sdk/observe.mdx @@ -9,7 +9,7 @@ platforms: ['android', 'ios', 'tvos'] import APISection from '~/components/plugins/APISection'; import { APIInstallSection } from '~/components/plugins/InstallSection'; -> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. +> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. `expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production. diff --git a/docs/pages/versions/v55.0.0/sdk/observe.mdx b/docs/pages/versions/v55.0.0/sdk/observe.mdx index a3f5b78eec7022..fcb1f6e8b12894 100644 --- a/docs/pages/versions/v55.0.0/sdk/observe.mdx +++ b/docs/pages/versions/v55.0.0/sdk/observe.mdx @@ -4,13 +4,12 @@ description: A library that collects app performance metrics and dispatches them sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-55/packages/expo-observe' packageName: 'expo-observe' platforms: ['android', 'ios', 'tvos'] -isBeta: true --- import APISection from '~/components/plugins/APISection'; import { APIInstallSection } from '~/components/plugins/InstallSection'; -> **important** **EAS Observe** is in [Open Beta](/more/release-statuses/#beta). The first 10,000 monthly active users are free. For higher usage, contact [sales@expo.dev](mailto:sales@expo.dev). +> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. `expo-observe` is a library that collects performance metrics from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production. diff --git a/docs/pages/versions/v56.0.0/sdk/observe.mdx b/docs/pages/versions/v56.0.0/sdk/observe.mdx index 5a210642077404..d8a70452289d72 100644 --- a/docs/pages/versions/v56.0.0/sdk/observe.mdx +++ b/docs/pages/versions/v56.0.0/sdk/observe.mdx @@ -4,13 +4,12 @@ description: A library that collects app performance metrics and user-defined ev sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-observe' packageName: 'expo-observe' platforms: ['android', 'ios', 'tvos'] -isBeta: true --- import APISection from '~/components/plugins/APISection'; import { APIInstallSection } from '~/components/plugins/InstallSection'; -> **important** **EAS Observe** is in [Open Beta](/more/release-statuses/#beta). The first 10,000 monthly active users are free. For higher usage, contact [sales@expo.dev](mailto:sales@expo.dev). +> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. `expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production. diff --git a/docs/pages/versions/v57.0.0/sdk/observe.mdx b/docs/pages/versions/v57.0.0/sdk/observe.mdx index 124908f13ddcfa..1bb6b2f4b15972 100644 --- a/docs/pages/versions/v57.0.0/sdk/observe.mdx +++ b/docs/pages/versions/v57.0.0/sdk/observe.mdx @@ -9,7 +9,7 @@ platforms: ['android', 'ios', 'tvos'] import APISection from '~/components/plugins/APISection'; import { APIInstallSection } from '~/components/plugins/InstallSection'; -> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. +> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details. `expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production. diff --git a/docs/scripts/generate-llms/llms-txt.js b/docs/scripts/generate-llms/llms-txt.js index 59048d347f6082..ec92ad8ce9a57f 100644 --- a/docs/scripts/generate-llms/llms-txt.js +++ b/docs/scripts/generate-llms/llms-txt.js @@ -133,17 +133,20 @@ function processPage(page) { return processPageData(page.href, page.name); } -function processGroup(group) { - const items = (group.children ?? []) - .filter(child => child.type === 'page') - .map(processPage) +function collectPages(node) { + return (node.children ?? []) + .flatMap(child => (child.type === 'page' ? processPage(child) : collectPages(child))) .filter(Boolean); +} + +function processGroup(group) { + const items = collectPages(group); return items.length > 0 ? { title: group.name, items } : null; } function hasContent(section) { - return section?.items?.length ?? section?.groups?.length ?? section?.sections?.length; + return section?.items.length > 0 || section?.groups.length > 0 || section?.sections.length > 0; } const COLLAPSED_SECTIONS = new Set(['Expo UI']); @@ -161,6 +164,10 @@ function collapseToOverviews(section) { return { ...section, items: [...section.items, ...overviews], groups: [], sections: [] }; } +function processNestedSection(node) { + return { title: node.name, items: collectPages(node), groups: [], sections: [] }; +} + function processSection(node) { if (!node || node.type !== 'section') { return null; @@ -190,7 +197,7 @@ function processSection(node) { break; } case 'section': { - const sectionData = processSection(child); + const sectionData = processNestedSection(child); if (hasContent(sectionData)) { section.sections.push(sectionData); } diff --git a/docs/ui/components/EASCLIReference/data/eas-cli-commands.json b/docs/ui/components/EASCLIReference/data/eas-cli-commands.json index 6b5d27408575cb..bb861dc212ca5a 100644 --- a/docs/ui/components/EASCLIReference/data/eas-cli-commands.json +++ b/docs/ui/components/EASCLIReference/data/eas-cli-commands.json @@ -1,8 +1,8 @@ { "source": { "url": "https://raw.githubusercontent.com/expo/eas-cli/main/packages/eas-cli/README.md", - "fetchedAt": "2026-08-15T08:06:08.018Z", - "cliVersion": "22.0.0" + "fetchedAt": "2026-08-21T08:14:06.609Z", + "cliVersion": "22.2.0" }, "totalCommands": 145, "commands": [ @@ -429,22 +429,22 @@ { "command": "eas observe:events [EVENTNAME]", "description": "display individual events emitted by the app via `logEvent`, filtered by the event name in the argument. With no arguments, a list of the available event names and associated event counts is returned.", - "usage": "USAGE\n $ eas observe:events [EVENTNAME] [--platform android|ios] [--after ] [--limit ] [--start |\n --days ] [--end | ] [--app-version ] [--update-id ] [--session-id ]\n [--all-events] [--project-id ] [--json] [--non-interactive]\n\nARGUMENTS\n [EVENTNAME] Event name to filter by\n\nFLAGS\n --after= Cursor for pagination. Use the endCursor from a previous query to fetch the next page.\n --all-events When no event name argument is provided, list all events across all event names instead of a\n summary of event names + counts.\n --app-version= Filter by app version\n --days= Show results from the last N days (mutually exclusive with --start/--end)\n --end= End of time range (ISO date)\n --json Enable JSON output, non-JSON messages will be printed to stderr. Implies --non-interactive.\n --limit= The number of items to fetch each query. Defaults to 10 and is capped at 100.\n --non-interactive Run the command in non-interactive mode.\n --platform=