Skip to content

Implement interactive terminal wizard for Seam integration setup - #2

Merged
razor-x merged 11 commits into
mainfrom
claude/wizard-internal-migration-xrirlp
Jul 29, 2026
Merged

Implement interactive terminal wizard for Seam integration setup#2
razor-x merged 11 commits into
mainfrom
claude/wizard-internal-migration-xrirlp

Conversation

@razor-x

@razor-x razor-x commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

This PR implements a complete interactive terminal wizard that guides developers through setting up a Seam integration in their project. The wizard is a full-featured CLI application built with Ink (React for terminals) that handles project detection, authentication, SDK installation, and AI-powered integration code generation.

Key Changes

  • Core wizard application (src/lib/app.tsx): Main state machine-driven Ink component that orchestrates the entire onboarding flow through multiple phases (init → method → browser/paste → verify → sdk → install → analyze → integrate → done)

  • Project detection (src/lib/steps/detect-project.ts): Detects JavaScript/TypeScript and Python projects, identifies package managers (npm, pnpm, yarn, bun) and Python installers (pip, poetry, uv)

  • Authentication flow (src/lib/steps/authenticate.ts, src/lib/steps/connect-web.ts): Supports three authentication methods:

    • Reusing existing API keys from environment or .env* files
    • Browser-based OAuth flow with localhost callback
    • Manual key pasting with validation
  • Project analysis (src/lib/steps/analyze-project.ts): Gathers project signals (dependencies, framework detection, README) and recommends integration approach via LLM with deterministic fallback

  • Integration planning (src/lib/steps/build-plan.ts): Defines core and common building blocks (access grants, webhooks, etc.) and composes agent goals from selected options

  • AI-powered integration (src/lib/steps/integrate.ts): Runs embedded Claude agent with file read/write tools and Seam MCP access to write integration code into the project

  • UI components (src/lib/components/checkbox-list.tsx): Custom multi-select checkbox component for building block selection

  • Utility modules:

    • env-file.ts: Reads/writes .env* files for API key persistence
    • seam-api.ts: Minimal Seam API client for key validation and token exchange
    • run-install.ts: Streams command output for SDK/plugin installation progress
  • Rendering (src/lib/render.tsx): Manages alternate screen buffer for full-screen terminal UI

  • Tests: Comprehensive test coverage for project detection, build planning, and environment file handling

  • Documentation: Updated README with detailed description of wizard capabilities and flow

Notable Implementation Details

  • The wizard uses a phase-based state machine to manage complex async flows (browser handoff, API calls, streaming installations)
  • Authentication is never password-based; keys are created in the browser or pasted, never minted locally
  • The embedded agent runs with restricted tool access (read/search/write + Seam docs MCP, no shell execution)
  • Project analysis combines heuristic signals with optional LLM-based recommendations for robustness
  • Installation progress is streamed to the UI instead of inherited stdio to maintain clean terminal rendering
  • The onboarding plan is persisted to .seam/onboarding.json before agent execution for auditability

https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx

claude added 11 commits July 29, 2026 01:48
Move the Ink-based setup wizard implementation from seamapi/wizard-internal
into src/lib verbatim, so the wizard lives in the published package instead
of a separate internal repository.

This commit is a pure move: no source edits. The only change is that
wizard-internal's src/index.tsx becomes src/lib/render.tsx, since
src/lib/index.ts is already this package's library entrypoint. Nothing
here is wired up yet — the stubbed entrypoint still runs, and the
following commits adapt the moved code to this repository's conventions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
The moved source is an Ink application, so add its runtime dependencies
(Ink and its input components, React, open, and the Claude Agent SDK)
alongside the React types and Ink testing library used for development.

Teach the build, packaging, and test configuration about .tsx: exclude
.test.tsx from the published build and include it in the Vitest run. The
tsconfig already sets jsx: react-jsx, so no compiler option changes are
needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
This repository addresses modules under src/lib through the `lib/*` path
alias rather than parent-relative specifiers: the alias is declared in
tsconfig.json, mapped for tests in vitest.config.ts, rewritten at build
time by tsc-alias, given its own import-sort group in eslint.config.ts,
and enforced by import/no-relative-parent-imports.

Point the step modules at lib/util/* instead of ../util/*.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
Wire the moved Ink application up to the package's public API, replacing
the stub that printed "not implemented yet". The API is unchanged for
consumers: the default export still takes `argv` and `commandName`, still
handles `--help` itself, and now renders the wizard instead of returning.

Turn the moved src/lib/render.tsx from a bin script with top-level side
effects into an exported `renderApp` function, so importing the package
never touches the terminal. Rendering full-screen and reprinting the
transcript on the way out is unchanged, except that the transcript is now
also reprinted when the app fails, and failures reject instead of quietly
setting an exit code — the caller, e.g., src/bin/cli.ts, reports them.

Add a `cwd` option for the project root the wizard sets up, defaulting to
process.cwd() as the bin script previously hardcoded. The Seam CLI can now
mount the wizard against an explicit directory, and it makes the
entrypoint testable without touching the working tree.

Replace the scaffold `todo` placeholder, its tests, and its example with
the wizard itself: `npm run example -- wizard` mounts the package the way
a consumer does. Switch help output from console.log to process.stdout,
matching the renderer and dropping the no-console suppression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
Apply this repository's Prettier configuration and the autofixable ESLint
rules to the moved source: single quotes, no semicolons, and sorted import
groups. No behavior changes — this is the mechanical part of the migration,
kept separate so the substantive conformance changes that follow are
reviewable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
…e rules

The migrated source was written against a looser configuration than this
repository's. Bring it into line, with no behavior changes:

- camelCase for local variables, parameters, and destructuring bindings.
  Property names are left alone: the repo's camelcase rule sets
  `properties: 'never'`, and these types mirror the Seam API wire format
  and the on-disk .seam/onboarding.json record, so renaming them would
  change the protocol. Snake_case properties destructured into locals are
  aliased, e.g. `const { api_key: apiKey } = phase`.
- Replace `void (async () => {…})()` in the app's effects, which the
  no-void rule rejects, with a named async function and an explicit
  rejection handler that routes to the app's existing error phase. `void`
  had turned an unexpected rejection into an unhandled rejection, so
  failures that previously vanished are now shown to the user.
- Access environment variables and Record members by index, as
  noPropertyAccessFromIndexSignature requires.
- Drop the unneeded default React imports in favor of named imports,
  since the repo compiles with jsx: react-jsx and verbatimModuleSyntax.
- Omit Ink's `color` prop on non-cursor checkbox rows rather than passing
  undefined, which exactOptionalPropertyTypes rejects. The rows still
  inherit the terminal default.
- Brace multi-line conditionals for the curly rule, and drop a dead local.

`npm run lint` and `npm run typecheck` now pass over src.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
The migrated source checked that the app mounts via scripts/smoke.tsx, a
standalone script run by its own npm script. It reported results with
console calls, which the no-console rule rejects, and it sat outside the
tsconfig, so it was never typechecked.

Move that check into the test suite as src/lib/app.test.tsx, where it runs
under `npm test` with the rest of the suite and is covered by lint and
typecheck. The assertion is unchanged: render the app against a project
root that does not exist with no key in the environment, which keeps the
render offline, and confirm the first frame.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
The migrated source arrived with no unit tests. Cover the pure, non-UI
modules that the wizard's behavior depends on, so the coverage report this
repository already produces means something:

- env-file: which key wins between the environment and each dotenv file,
  quote stripping, and the created/added/updated outcomes of upserting a
  variable without disturbing neighboring entries.
- detect-project: SDK detection from project markers, including the
  ambiguous cases, package manager and installer detection, and the
  install command produced for each.
- build-plan: goal composition for both modes, the agent hints contributed
  by each selected building block, and the on-disk onboarding record.

Every test that touches the filesystem uses its own temporary directory,
and those reading SEAM_API_KEY stub it, so the suite neither depends on
nor leaks ambient state. The networked modules and the Ink components are
left out: the app's render is covered by src/lib/app.test.tsx.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
Replace the placeholder description with what the wizard actually does,
now that it is implemented: the steps it runs, that keys are never minted
here, and that inference is routed through Seam so no developer Anthropic
key is needed.

Document the `cwd` option and what the entrypoint's promise means, the two
environment variables that affect a run, and the src/lib layout including
the lib/* alias and why the wire-format types keep snake_case properties.

Note that `npm run wizard` sets up whichever project it runs in — that is
this repository, which is rarely what you want — and show how to run it
against a scratch project instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
The scaffold's landlubber examples harness does not fit this package. The
wizard is a single interactive entrypoint that takes over the terminal, not
a set of commands worth demonstrating one by one, and the README already
shows the two lines a consumer needs to mount it.

Remove the examples directory, the landlubber dependency, the example
scripts, and the examples entries in the tsconfig include and the coverage
exclude list.

The examples were the only way to run the wizard against a directory other
than this repository, so move that onto the development CLI as a '--cwd
<path>' flag. Running the CLI from another directory is not an option:
tsx resolves the tsconfig from the working directory, so the lib/* alias
fails to resolve. The flag is consumed by the CLI and becomes the wizard's
`cwd` option; every other argument is still forwarded untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
Document running it against a project with '--cwd' directly, rather than
explaining the default of setting up this repository and working up to the
flag from there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1qhuuZQZnA1FB1fBSxrqx
@razor-x
razor-x marked this pull request as ready for review July 29, 2026 02:20
@razor-x
razor-x merged commit 9ccd0eb into main Jul 29, 2026
11 checks passed
@razor-x
razor-x deleted the claude/wizard-internal-migration-xrirlp branch July 29, 2026 02:21
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.

2 participants