feat(cli): add init command for scaffolding manifest, client, and provider#7052
Merged
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 97 |
TIP This summary will be updated as you push new changes.
More templates
algoliasearch-helper
instantsearch-ui-components
instantsearch.css
instantsearch.js
react-instantsearch
react-instantsearch-core
react-instantsearch-nextjs
react-instantsearch-router-nextjs
vue-instantsearch
commit: |
9 tasks
e2a0541 to
3f16a31
Compare
fe3797b to
3dabed2
Compare
4efb18f to
537bbf3
Compare
537bbf3 to
e7a58e4
Compare
e7a58e4 to
b13bfc9
Compare
185fed5 to
c6815e1
Compare
c6815e1 to
ba9898f
Compare
ba9898f to
6c69153
Compare
…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>
6c69153 to
1d717ad
Compare
…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>
1d717ad to
869f24b
Compare
…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>
04450c2 to
4438ad7
Compare
Contributor
There was a problem hiding this comment.
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
initcommand wiring in the CLI program, including--framework next-appsupport and exit-code propagation. - Implements
runInitto detect project shape, writeinstantsearch.json, optionally install missing deps, and generate Algolia client/provider source files. - Adds Jest coverage for
initbehavior, 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.
- 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>
- 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>
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>
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>
…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>
Haroenv
reviewed
Jun 1, 2026
Haroenv
approved these changes
Jun 2, 2026
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.
Summary
Adds the first real subcommand:
instantsearch init. It sets up an InstantSearch project in the current directory. Builds on #7050.What it does
instantsearch.json(using the manifest module from feat(cli): add manifest module for reading, writing, validating instantsearch.json #7050)--yes/--json) and runs the project's package manager — yarn/npm/pnpm/bun chosen by which lockfile is present. Always includesalgoliasearch(peer dep ofreact-instantsearch) andreact-instantsearch; addsreact-instantsearch-nextjsfor Next App Router projects.<libPath>/algolia-client.{ts,js}and<libPath>/algolia-provider.{tsx,jsx}The generated
<AlgoliaProvider>does not set anindexName— 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:
Interactive (prompts for credentials, paths, and install confirmation):
Override auto-detection (for example, to resolve an ambiguous Next.js layout with both
app/andpages/, or to force the Next.js code path in a setup wherenextisn't directly inpackage.json):Flags
--app-id,--search-api-key— Algolia credentials--components-path,--lib-path— output directories; if only--components-pathis passed,libPathis derived (src/components→src/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 onlynext-apptoday; future values land alongside detector support for them. The flag is trusted: the CLI does not re-validate it againstpackage.json. Bad invocations fail at install time.Failure codes added
missing_required_flag— credentials missing in non-interactive mode (--yesor--json)manifest_exists— re-running over an existing manifestinstall_declined— user said no to the install promptinstall_failed— the package manager exited non-zerocancelled— 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 tripmanifest_exists.invalid_lib_path—--lib-path(or its interactive value) is absolute or contains..segments — refused before any filesystem writesChecklist
filesCreatedlists them in JSON outputreact-instantsearch-nextjsand includes'use client'indexName(consumers add<Index>per feature)--components-pathand--lib-pathoverrides land files at the right pathslibPathderives correctly fromcomponentsPath..-traversing--lib-path→invalid_lib_path(no filesystem writes)manifest_exists--app-id/--search-api-keyin non-interactive mode →missing_required_flag(message mentions both--yesand--json)cancelled(consistent with Ctrl-C, no misleading "missing flag" message)--framework next-appresolves an ambiguous Next.js layout--framework next-appis trusted in projects wherenextisn't directly inpackage.json(monorepo hoisting, peerDeps, custom installs)--frameworkvalue →invalid_flagenvelopeinstall_declinedcancelledinstall_faileddefaultInstallerappends.cmdto npm/yarn/pnpm so the shims resolve withoutshell: truewrite_failedenvelope (no uncaught throw); partial files (and the manifest) are rolled backalgolia-clientincludes a comment explaining why it's module-scoped (stable reference across renders preserves InstantSearch's request cache)algoliasearchin the install list so it builds in any host projectTest isolation
Both the prompt function and the installer are passed in as dependencies, so tests run without network access or interactive input.
Deferred
--flavorflag is not added yet — the only supported flavor isreact, so the flag would have no effect. Will be added when vanilla-JS scaffolding lands.credentials_invalidon 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.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.