Integrate & migrate commands - #5
Open
eandreeva-twr wants to merge 15 commits into
Open
Conversation
eandreeva-twr
marked this pull request as draft
July 24, 2026 06:40
Print a one-line notice after every run that sends the usage event, stating what is collected and how to disable it (--no-telemetry, ADAPTY_TELEMETRY_DISABLED=1, DO_NOT_TRACK=1). Nothing is sent or printed when disabled; --copy runs never send anything.
Split drivers.ts into a driver registry (src/lib/agent/drivers/) with one module per agent and shared runners for the two output protocols (stream-json and plain text). Adds an interactive agent picker when several agents are installed, derives --driver options and install/login hints from the registry, and hardens the runners: idle-timeout kill, NO_COLOR, bounded stdout tail, anchored [STATUS] parsing, and an exit-code fallback for Cursor's variable result event.
- integrate now offers an interactive switch to `adapty migrate` when an existing billing SDK is detected (before the wizard, so no question runs twice); headless runs keep the informational banner - telemetry disclosure moved to the start of the run, Superwall-style, and no longer repeats at the end - without real store product IDs the agent now creates neither products nor paywall/placement - the full ready-to-run command sequence goes to ADAPTY_SETUP.md, with the placement ID in code matching those commands - ADAPTY_SETUP.md items link Adapty docs only where a guide genuinely helps (store credentials, server notifications, flows), slugs via llms.txt
… client, telemetry Unit/integration tests replacing the manual checklist's cheap tier: platform detection fixtures (incl. iOS name extraction and Unity-over-gradle precedence), billing detection (incl. the transitive pubspec.lock false positive), prompt content for headless vs copy mode and the migrate mapping rules, the RevenueCat client against a local mock API (pagination cursors, 429 retry, builder-paywall classification, incomplete-catalog degradation), and telemetry gating. Shared useTmpDir fixture helper; token/key leak tests assert on the real seams (PromptContext and the full catalog object).
The fixture used a hardcoded POSIX path, so on Windows CI the expectation diverged from the dirname/join output (backslashes). Build both the input and the expectation with node:path.
A staged, always-skippable interview: a which-stores select of mutually exclusive combos (irrelevant stores hidden per platform, exhaustive Record<Platform, stores> mapping), then per-product prompts - App Store ID, Google Play ID (Enter reuses the App Store one, "-" = not in that store), period from VALID_PERIODS (now exported from api-schemas), and base plan only for non-lifetime Google Play subscriptions. Collected products render into a <store_products> ground-truth prompt block; with them the agent creates the full dashboard setup instead of deferring to ADAPTY_SETUP.md. Review fixes baked in: cancel (Esc/Ctrl-C) aborts the command instead of being folded into skip/defaults (text() now returns null on cancel); go/no-go gates run before the interview so declined confirms don't discard typed answers; whitespace IDs are rejected and uppercase Google Play IDs warned (immutability makes typos unfixable); the create-now prompt branch keeps the Google Play AAB prerequisite; migrate treats code-found SKUs as authoritative and the user's list as a complement; the platform playbook is prefetched during the questions to hide network latency.
eandreeva-twr
marked this pull request as ready for review
July 27, 2026 14:24
The notice used to print last, just before the run spinner, so it stayed pinned above the progress line for the whole integration. Moved into the wizard right after auth: said once among the other setup lines, it scrolls away with them. --copy and --no-telemetry still print nothing (and --copy sends nothing). Also: auth login declared user.email as required, but the API does not always return it, so a successful login greeted people with "Authenticated as undefined". Fields are optional now, with a name fallback and a plain "Authenticated" when neither is present.
The CLI prompt never said where in the app the paywall should live, so the agent created a placement hardcoded as "main" and wired the paywall wherever it happened to look. SKILL.md has a whole step for this (analyze the project, offer the candidate spots, confirm) which headless runs never saw - it is read only by interactive sessions, since the CLI appends only references/<platform>.md. Now the agent reads the project for the natural spots (onboarding, premium feature entry points, settings/upgrade, locked content), names the placement after the one it picks, shows the paywall from there, and records the choice plus the other candidates in ADAPTY_SETUP.md so the user can move it without hunting.
The mapping rules, the never-create list and the ADAPTY_SETUP.md contract lived in a template literal here, duplicating what the skill now carries in references/migration.md. Two copies of the same rules in two repos drift, and the skill repo's drift cron cannot see inside TypeScript. Worse, the copy here was the older one. The skill's spine has since gained things this prompt never had, each of them fixing a measured failure: the handoff contract that stops store reconnection and server-notification rewiring being dropped, historical data import, the rule that an inferred value is a placeholder in every ready-to-run command rather than a literal, and the rule that code establishes what the app uses and never what exists in the source's account. `adapty migrate` was producing migrations without any of that. skill-source.ts gains loadMigrationReference(source): the spine is required, references/migration-<source>.md is optional since only RevenueCat ships one today, and a 404 or ENOENT for it means "use the spine's unknown-source path" rather than an error. Anything else still throws, so a network failure is never mistaken for an absent file. What stays here is only what this CLI knows and the skill cannot: which source the user picked, whether an RC catalog was actually fetched, and that --rc-key can automate the dashboard comparison for RevenueCat specifically. Verified against a local skill checkout via ADAPTY_SKILL_DIR: the spine and the RevenueCat file both land, the four rules above are present, no <mapping_rules> block remains, and the platform playbook still comes last. Migrate prompts run 55-67k chars (~14-17k tokens) depending on platform, about double the integrate prompt.
…totype # Conflicts: # src/commands/products/create.ts
Finding no agent used to end the command: we printed install hints for five CLIs and quit, throwing away the answers the user had already given. But most people run their agent inside an editor - Cursor without cursor-agent, Copilot in VS Code, Windsurf - so "install one of these and start over" is the wrong answer for the largest cohort we have. The wizard now decides who does the work rather than which driver to use: resolveExecution() returns copyOnly + installSkill, and offerAgentlessPath() asks whether to put the prompt on the clipboard and whether to install the Adapty skill into the agent they actually use. Accepting continues the same wizard run, so the prompt still carries the app ID and SDK key; the skill is installed after the clipboard, since that install can take a minute. Commands now branch on setup.copyOnly instead of the --copy flag, which is what makes the switch safe mid-run: it also disables the go/no-go gate, the dirty-tree gate, and telemetry. Headless runs keep the old message - there is nobody there to take a clipboard.
The Flow Builder branch told the agent to create the placement and attach a flow to it in the dashboard afterwards. That cannot work: a placement carries a type - flow, paywall, or onboarding - fixed at creation, this CLI can only create paywall placements (an audience entry requires a paywall_id), and a developer ID can never be changed or reused. So the placement the agent created burned the exact ID it had just written into the code, and the user had to invent another one and edit the code to match. The migration playbook already carries this rule from the other direction: a source paywall built in a visual builder gets nothing created, because paywall and flow placements share one ID namespace. The greenfield path contradicted it - on the default, recommended approach, and only for users who supplied real store IDs, since the branch runs after products are created. Now nothing is created for Flow Builder, and ADAPTY_SETUP.md carries the dashboard steps with the developer ID the code already uses. The deferred sequence for unknown store IDs loses its paywalls/placements commands on this path too - those were the same mistake, one step later.
An agent rewrites files all over the project, and until now it did that on whatever branch the user happened to be on. Migrate warned about a dirty tree, which helps read the diff but does nothing for undoing it; integrate did not even do that. Both commands now create `adapty-integrate` / `adapty-migrate` before the agent starts, suffixing the name when a previous run already took it. It is not a question: the branch costs the user nothing, its absence costs them a lot, and `git switch -` is the undo we could not otherwise offer. The closing line names the branch alongside the `git diff` hint. No git at all is the one case worth stopping for - there is then no way to see or revert what changed - so that asks, defaulting to no. Headless runs get the warning and continue, having nobody to ask. The git plumbing moves into src/lib/project/git.ts, taking migrate's inlined dirty-tree check with it, and gets tests against real repositories: repo detection, dirty detection inside and outside a repo, name suffixing, and a failed branch creation reported rather than thrown.
Users who created their app and migrated its entities in the dashboard had no path to "just the code changes": the migrate prompt told the agent to build the dashboard unconditionally, so it recreated products (rejected - store IDs are immutable) and guessed placement developer IDs (worse - a wrong one ships and silently returns nothing). The CLI now snapshots the bound app (access levels, products, paywalls, placements - the same four list endpoints) and settles the mode before any agent launches. A populated app asks "use these or create what's missing", naming the entities so the user can recognize their own setup; --[no-]code-only overrides in either direction; headless runs refuse rather than guess, since a wrong guess against a populated app is unrecoverable while a refused run is cheap to restart. The refusal is resolved outside the app-binding try/catch on purpose: its --copy catch downgrades errors to warnings, and a copy prompt generated in create mode against a populated app is exactly the damage this exists to prevent. In code-only mode the placement developer ID is also settled up front - picked from the app when it can be, asked when it cannot (flow placements are not in the endpoint yet) - and the prompt forbids creating any entity, routing genuine gaps to ADAPTY_SETUP.md as ready-to-run commands. Create mode gains a list-before-create rule, which also makes a re-run after a half-finished session safe. The store-product interview is skipped in code-only (Adapty already has better IDs than the user can type), and --rc-key conflicts with --code-only at parse time.
eandreeva-twr
force-pushed
the
integrate-command-prototype
branch
from
August 14, 2026 14:31
6e1ec7a to
4ba961e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.