Skip to content

feat(cli): add init command for scaffolding manifest, client, and provider#7052

Merged
sarahdayan merged 16 commits into
masterfrom
feat/init-command
Jun 2, 2026
Merged

feat(cli): add init command for scaffolding manifest, client, and provider#7052
sarahdayan merged 16 commits into
masterfrom
feat/init-command

Conversation

@sarahdayan

@sarahdayan sarahdayan commented May 21, 2026

Copy link
Copy Markdown
Member

Summary

Adds the first real subcommand: instantsearch init. It sets up an InstantSearch project in the current directory. Builds on #7050.

What it does

  1. Inspects the project (using the detector from feat(cli): add detector for flavor, framework, TypeScript, aliases #7049) to figure out flavor, framework, and whether TypeScript is in use
  2. Writes instantsearch.json (using the manifest module from feat(cli): add manifest module for reading, writing, validating instantsearch.json #7050)
  3. If any required InstantSearch packages aren't installed, asks the user (or auto-confirms under --yes / --json) and runs the project's package manager — yarn/npm/pnpm/bun chosen by which lockfile is present. Always includes algoliasearch (peer dep of react-instantsearch) and react-instantsearch; adds react-instantsearch-nextjs for Next App Router projects.
  4. Generates an Algolia client file and a provider file in <libPath>/algolia-client.{ts,js} and <libPath>/algolia-provider.{tsx,jsx}

The generated <AlgoliaProvider> does not set an indexName — it's an index-agnostic root. Each feature added later wraps itself in <Index indexName="..."> for the index it targets.

For projects that aren't Next.js App Router (Vite, CRA, Webpack, Remix, Astro, …), the generated code is plain React and runs as-is. No framework-specific certification is needed for the CLI to scaffold them.

Examples

Non-interactive, machine-readable:

instantsearch init --json --app-id XYZ --search-api-key abc

Interactive (prompts for credentials, paths, and install confirmation):

instantsearch init

Override auto-detection (for example, to resolve an ambiguous Next.js layout with both app/ and pages/, or to force the Next.js code path in a setup where next isn't directly in package.json):

instantsearch init --framework next-app

Flags

  • --app-id, --search-api-key — Algolia credentials
  • --components-path, --lib-path — output directories; if only --components-path is passed, libPath is derived (src/componentssrc/lib; otherwise → lib). Both must be relative paths inside the project — absolute paths and .. segments are refused.
  • --framework <name> — asserts the host framework, bypassing auto-detection. Accepts only next-app today; future values land alongside detector support for them. The flag is trusted: the CLI does not re-validate it against package.json. Bad invocations fail at install time.

Failure codes added

  • missing_required_flag — credentials missing in non-interactive mode (--yes or --json)
  • manifest_exists — re-running over an existing manifest
  • install_declined — user said no to the install prompt
  • install_failed — the package manager exited non-zero
  • cancelled — user cancelled an interactive prompt (Ctrl-C, or an empty submit, on either the credentials prompt or the install confirm)
  • write_failed — filesystem error while scaffolding the lib directory or writing the generated files (inherited from the manifest module; also produced by init's own writes). Partial state is rolled back so retries don't trip manifest_exists.
  • invalid_lib_path--lib-path (or its interactive value) is absolute or contains .. segments — refused before any filesystem writes

Checklist

  • React + Vite + TS project: writes manifest (no framework field), client, provider; filesCreated lists them in JSON output
  • Next App Router project: provider imports from react-instantsearch-nextjs and includes 'use client'
  • Generated provider does not set indexName (consumers add <Index> per feature)
  • --components-path and --lib-path overrides land files at the right paths
  • Default libPath derives correctly from componentsPath
  • Absolute or ..-traversing --lib-pathinvalid_lib_path (no filesystem writes)
  • Re-running on a project that already has a manifest → manifest_exists
  • Missing --app-id / --search-api-key in non-interactive mode → missing_required_flag (message mentions both --yes and --json)
  • Interactive mode prompts for credentials and paths
  • Empty submit at a prompt → cancelled (consistent with Ctrl-C, no misleading "missing flag" message)
  • --framework next-app resolves an ambiguous Next.js layout
  • --framework next-app is trusted in projects where next isn't directly in package.json (monorepo hoisting, peerDeps, custom installs)
  • An invalid --framework value → invalid_flag envelope
  • User declines the install prompt → install_declined
  • Cancelling the install prompt or the credentials prompt (e.g. Ctrl-C) → cancelled
  • Package manager exits non-zero → install_failed
  • Windows: defaultInstaller appends .cmd to npm/yarn/pnpm so the shims resolve without shell: true
  • Filesystem error while scaffolding → write_failed envelope (no uncaught throw); partial files (and the manifest) are rolled back
  • Human-mode failure messages reach stderr (not silently dropped by the action layer)
  • Generated algolia-client includes a comment explaining why it's module-scoped (stable reference across renders preserves InstantSearch's request cache)
  • Generated provider scaffold includes algoliasearch in the install list so it builds in any host project

Test isolation

Both the prompt function and the installer are passed in as dependencies, so tests run without network access or interactive input.

Deferred

  • --flavor flag is not added yet — the only supported flavor is react, so the flag would have no effect. Will be added when vanilla-JS scaffolding lands.
  • Algolia-credential validation (e.g. credentials_invalid on bad app ID / API key) is handled in feat(cli): add introspect command for index facets and searchable attributes #7053 (introspect) where the first real query happens. Init writes the manifest as given.
  • Version-range checking on already-installed react-instantsearch / algoliasearch — today the install list filters on package name only. A pinned incompatible major won't be reinstalled, and the user gets a build-time error instead of a CLI-time one. Acceptable for alpha; revisit if users hit it.

@codacy-production

codacy-production Bot commented May 21, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 97 complexity

Metric Results
Complexity 97

View in Codacy

TIP This summary will be updated as you push new changes.

@pkg-pr-new

pkg-pr-new Bot commented May 21, 2026

Copy link
Copy Markdown
More templates

algoliasearch-helper

npm i https://pkg.pr.new/algolia/instantsearch/algoliasearch-helper@7052

instantsearch-ui-components

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch-ui-components@7052

instantsearch.css

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.css@7052

instantsearch.js

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.js@7052

react-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch@7052

react-instantsearch-core

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-core@7052

react-instantsearch-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-nextjs@7052

react-instantsearch-router-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-router-nextjs@7052

vue-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/vue-instantsearch@7052

commit: d22bb02

@sarahdayan sarahdayan force-pushed the feat/init-command branch from e2a0541 to 3f16a31 Compare May 26, 2026 12:30
@sarahdayan sarahdayan force-pushed the feat/init-command branch from fe3797b to 3dabed2 Compare May 26, 2026 13:27
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 4efb18f to 537bbf3 Compare May 26, 2026 13:29
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 537bbf3 to e7a58e4 Compare May 26, 2026 13:49
@sarahdayan sarahdayan force-pushed the feat/init-command branch from e7a58e4 to b13bfc9 Compare May 26, 2026 13:55
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 185fed5 to c6815e1 Compare May 26, 2026 14:18
@sarahdayan sarahdayan force-pushed the feat/init-command branch from c6815e1 to ba9898f Compare May 26, 2026 14:41
@sarahdayan sarahdayan force-pushed the feat/init-command branch from ba9898f to 6c69153 Compare May 26, 2026 14:51
sarahdayan and others added 4 commits May 28, 2026 13:07
…ntsearch.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manifest.framework is now optional. validateManifest no longer requires
it; if present, it must be a non-empty string. Matches the detector's
new contract where framework is set only when special handling is
needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Premature versioning. The package is in alpha — no v2 in sight, no
migration story to build for. When the manifest shape needs to change
breakingly, we can introduce apiVersion (or another versioning signal)
at the same time as the migrator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 6c69153 to 1d717ad Compare May 28, 2026 11:28
…quire non-empty credentials

Two issues flagged by review:

- writeManifest used to throw on any error other than EEXIST. Common
  cases (missing parent dir, permission denied, no space) crashed the
  CLI and bypassed --json. Now returns a write_failed refusal envelope.
- checkAlgolia previously accepted empty-string appId / searchApiKey,
  which would fail at the first Algolia call. Now rejects empty strings
  the same way as other required fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 1d717ad to 869f24b Compare May 28, 2026 11:33
…vider

Adds the `instantsearch init` subcommand. Detects flavor and framework
via the detector module from #7049, then writes `instantsearch.json`,
installs missing InstantSearch packages, and generates an Algolia
client + provider in `<libPath>/`.

Features:
- Interactive mode (default): prompts for credentials, paths, and
  confirmation before installing packages.
- Non-interactive mode (`--yes`, implied by `--json`): all inputs must
  come from flags; missing credentials refuse with
  `missing_required_flag`.
- `--framework <name>` override (accepts `next-app`): asserts the host
  framework when auto-detection isn't enough (monorepo hoisting,
  peerDeps, custom installs). Trusted — not re-validated against
  `package.json`.
- `--components-path` and `--lib-path` override output directories. If
  only `--components-path` is given, `libPath` derives from it
  (`src/components` → `src/lib`, otherwise → `lib`).
- Generated `algolia-client` is module-scoped with a one-line comment
  explaining why: stable reference across renders preserves
  InstantSearch's request cache.
- For Next.js App Router projects, also installs
  `react-instantsearch-nextjs` and emits a provider with `'use client'`
  + `<InstantSearchNext>`. Plain React projects (Vite, CRA, Webpack,
  Remix, …) get the generic `<InstantSearch>` provider.

Failure codes added on top of the parser-layer ones:
- `missing_required_flag` — credentials missing in `--yes` mode
- `manifest_exists` — re-running over an existing manifest
- `install_declined` — user said no to the install prompt
- `install_failed` — package manager exited non-zero
- `cancelled` — user cancelled an interactive prompt (Ctrl-C)

`runInit`'s non-zero return propagates to the process exit code via a
`HandledFailure` sentinel caught in `run.ts`.

Deferred:
- `--flavor` flag (one valid value today; will land with JS scaffolding)
- Algolia-credential validation (deferred to first real query)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarahdayan sarahdayan force-pushed the feat/init-command branch from 04450c2 to 4438ad7 Compare May 28, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a real instantsearch init subcommand to the CLI to scaffold an InstantSearch setup in the current project directory, leveraging the existing detector/manifest infrastructure.

Changes:

  • Introduces init command wiring in the CLI program, including --framework next-app support and exit-code propagation.
  • Implements runInit to detect project shape, write instantsearch.json, optionally install missing deps, and generate Algolia client/provider source files.
  • Adds Jest coverage for init behavior, including prompts, install flows, and framework override handling.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/instantsearch-cli/src/types/prompts.d.ts Adds a minimal module declaration for prompts to satisfy TypeScript.
packages/instantsearch-cli/src/run.ts Adds HandledFailure for clean command exit codes.
packages/instantsearch-cli/src/program.ts Wires up the new init subcommand and its flags.
packages/instantsearch-cli/src/init.ts Implements instantsearch init scaffolding, prompting, installing, manifest writing, and file generation.
packages/instantsearch-cli/src/detector.ts Adds support for a trusted --framework override during detection.
packages/instantsearch-cli/package.json Adds prompts dependency for interactive input.
packages/instantsearch-cli/tests/*.test.ts Adds and updates tests for init and flag-validation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/instantsearch-cli/src/run.ts
Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts
sarahdayan and others added 2 commits May 28, 2026 15:27
- run.ts now flushes the buffered program stderr before returning from
  the HandledFailure branch, so human-mode command failures are visible.
- init's installer list now includes algoliasearch (peer dep of
  react-instantsearch). Without it, the scaffold imports algoliasearch/lite
  from a package the host project may not have.
- Generated provider uses indexName="YOUR_INDEX_NAME" instead of "" so
  the scaffold doesn't crash at runtime when first rendered.
- nextSteps now contains a project-relative module specifier
  (./<libPath>/algolia-provider) instead of an absolute filesystem path,
  and includes a hint to set the indexName.
- Filesystem operations (mkdirSync, writeFileSync) for the scaffold are
  wrapped in try/catch and surface as a write_failed envelope instead of
  internal_error.
- The credentials prompt now detects cancellation (answer key absent)
  and emits the dedicated cancelled code, matching the install-prompt
  behavior. Was conflated with missing_required_flag.
- missing_required_flag messages for --app-id / --search-api-key
  mention non-interactive mode (--yes or --json), not just --yes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The provider is meant to be index-agnostic; each feature wraps itself
in <Index indexName="..."> for the specific index it targets. Setting
indexName on the top-level <InstantSearch> contradicted that model.
The previous YOUR_INDEX_NAME placeholder was a fix to a symptom (Copilot
flagged the empty string); the root cause was the prop having no
business being there.

nextSteps now points the user toward <Index> instead of "set the
indexName in the provider".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts Outdated
Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts
Base automatically changed from feat/manifest to master May 28, 2026 15:09
sarahdayan and others added 2 commits May 28, 2026 17:09
- manifest_exists check now runs before detect(), so re-running init on
  a project where the detector would now refuse still gets the more
  actionable manifest_exists message instead of a detector failure.
- defaultInstaller no longer uses stdio: 'inherit' (which writes child
  output directly to stdout and corrupts the JSON envelope). Both child
  stdout and stderr now pipe to process.stderr.
- Generated algolia-client and algolia-provider files are now written
  with flag: 'wx', so init refuses with write_failed instead of
  silently overwriting an existing file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/src/init.ts
sarahdayan and others added 3 commits June 1, 2026 09:58
When scaffolding fails after the manifest is written (mkdir fails,
client or provider write fails on EEXIST, etc.), init now undoes the
files this run created so the user can retry without hitting
manifest_exists on the next attempt.

The rollback tracks only files this run successfully wrote — a
pre-existing user file that caused the EEXIST never gets touched.
Rollback is best-effort: any IO error during cleanup is swallowed so
the original write_failed envelope stays the headline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cancel

Three fixes from the latest review pass:

- libPath is now validated to be a non-absolute, non-traversing
  relative path before any filesystem writes. An absolute libPath
  (e.g. /tmp/x) used to silently scaffold files outside the project
  and emit an invalid import specifier in nextSteps. Refuses with the
  new invalid_lib_path code.
- defaultInstaller now passes shell: process.platform === 'win32' so
  the .cmd shims for npm/yarn/pnpm/bun resolve via PATHEXT on Windows.
  Without it, spawn('npm', …) throws ENOENT on every Windows host,
  blocking install entirely.
- The interactive cancellation check now also treats empty-string
  prompt submissions as cancellation (matching the Ctrl-C path).
  Previously a user who pressed Enter without typing at the Algolia
  credential prompts got a misleading missing_required_flag error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… managers

Codacy flagged shell:true as a generic shell-injection risk. Our args
are controlled, but the cleaner fix removes the false positive: append
the .cmd suffix explicitly on Windows for npm/yarn/pnpm (which install
as .cmd shims), keep bun bare (bun.exe is a real executable). spawn
now defaults to shell:false on every platform.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.

Comment thread packages/instantsearch-cli/src/init.ts
Comment thread packages/instantsearch-cli/package.json
Two Copilot findings:

- componentsPath was not validated against absolute or '..' paths,
  even though the PR description says both componentsPath and libPath
  must be relative paths inside the project. Now refuses with the new
  invalid_components_path code, mirroring the libPath check.
- prompts was installed with a caret range while other deps in this
  package pin exact versions. Switched to "2.4.2" for consistency, so
  transitive upgrades don't land unreviewed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 1 comment.

Comment thread packages/instantsearch-cli/src/program.ts
…lar import

program.ts imported HandledFailure from run.ts; run.ts imported
createProgram from program.ts. The cycle worked only because the
HandledFailure usage was inside an async action callback, not at
module load time. Copilot flagged it as fragile across bundlers and
future refactors.

Extracted HandledFailure into src/handled-failure.ts. Both run.ts and
program.ts now import from there. Dependencies flow one way.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarahdayan sarahdayan requested a review from Haroenv June 1, 2026 08:56
Comment thread packages/instantsearch-cli/__tests__/global-options.test.ts
@sarahdayan sarahdayan requested a review from Haroenv June 1, 2026 11:16
@sarahdayan sarahdayan merged commit 64d170c into master Jun 2, 2026
15 checks passed
@sarahdayan sarahdayan deleted the feat/init-command branch June 2, 2026 11:51
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.

3 participants