Skip to content

Studio CLI: Load @inquirer/prompts when a prompt is shown - #5316

Merged
chubes4 merged 3 commits into
trunkfrom
cli/lazy-prompts
Oct 27, 2026
Merged

chubes4 merged 3 commits into
trunkfrom
cli/lazy-prompts

Conversation

@chubes4

@chubes4 chubes4 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

Claude (via the Pi coding agent in a Roadie session) traced the modules loaded at startup, wrote the change and the test, and took the timings below. I reviewed the diff and the numbers.

Proposed Changes

Nine command and migration modules import @inquirer/prompts at their top, and the CLI entry imports them all, so every studio command loaded every prompt type before running. Each prompt package carries its own copy of @inquirer/core: about 260 modules, for prompts that most commands never show, and none when the app runs the CLI.

  • New cli/lib/prompts.ts wraps the five prompts the CLI uses (confirm, input, password, select, search) and loads @inquirer/prompts when one is shown. The wrappers keep the original signatures, including the Value type of select and search.
  • The nine modules import from cli/lib/prompts instead. Existing tests that mock @inquirer/prompts work unchanged.
  • The startup-imports test now lists the modules kept off the startup path (agent runtime, date-fns root, @inquirer/prompts) and reports which ones a command loads. It fails on trunk with [ '@inquirer/prompts' ].

Testing Instructions

  • npx vitest run apps/cli/tests/startup-imports.test.ts passes; on trunk it fails.
  • CLI unit tests pass (124 files, 1,244 tests). npx tsc --noEmit -p apps/cli/tsconfig.json is clean.
  • Prompts still work from the built CLI: in a pseudo-terminal, the input, confirm and select wrappers took typed answers (text, y, arrow down + Enter) and returned {"a":"chubes","b":true,"c":2}.
  • Timings on an M-series Mac with Node 24.15, using each build's development dist/cli (vite.config.dev.ts) against trunk at Clear the graceful-stop timer so site stop returns as soon as the site is down #5476, interleaved:
Trunk This branch
Modules loaded by studio --version 890 626
studio --version, 30 runs median 0.259 s median 0.227 s

The saving is about 30 ms per command on this machine. CI's Performance Test Results show siteStartup flat; 30 ms is below that metric's run-to-run noise.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

Every studio command loaded every prompt type, each with its own copy of
@inquirer/core, before running.
@wpmobilebot

wpmobilebot commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 806db48 vs trunk

app-size

Metric trunk 806db48 Diff Change
App Size (Mac) 954.27 MB 954.27 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 806db48 Diff Change
load 1231 ms 1236 ms +5 ms ⚪ 0.0%

site-startup

Metric trunk 806db48 Diff Change
siteCreation 5977 ms 6080 ms +103 ms 🔴 1.7%
siteStartup 2893 ms 2875 ms 18 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@youknowriad

Copy link
Copy Markdown
Contributor

Given the perf impact, this feels worth shipping.

@chubes4
chubes4 merged commit edea6d8 into trunk Oct 27, 2026
13 of 14 checks passed
@chubes4
chubes4 deleted the cli/lazy-prompts branch October 27, 2026 11:12
chubes4 added a commit that referenced this pull request Oct 27, 2026
## Related issues

- Follows #5235, #5313 and #5316, which kept the MCP server, the
`date-fns` root and `@inquirer/prompts` off CLI startup.

## How AI was used in this PR

Claude (via the Pi coding agent in a Roadie session) profiled CLI
startup, wrote the change and the test, and measured the before/after
timings. I reviewed the diff and the measurements.

## Proposed Changes

`apps/cli/index.ts` statically imported every top-level command module,
and each module held the command's whole implementation. So every
`studio` invocation loaded the implementation of every command before
running one, including the export, import, push and pull code and its
archive, upload and certificate libraries: 1,375 dependency files from
109 packages for `studio --version`.

Each top-level command now has a `.command` module that defines the
command (name, description, options) and loads the implementation with
`await import()` in its handler, the pattern `wp` and `_events` already
use. The implementation modules keep their paths and their `runCommand`
exports, so their other callers are unchanged; each one's former handler
body is now its exported `commandHandler`.

- `studio list`, `start`, `stop`, `create` and `wp` load 520 dependency
files at startup instead of 1,375.
- Help output, unknown-command errors and option validation are
byte-identical to trunk.
- A `CommandArgs< typeof builder >` type keeps each handler typed from
its command's options.

Local timings on macOS (median of 8 alternating runs, Node 24):

| Command | trunk | this PR |
| --- | --- | --- |
| `studio --version` | 0.40 s | 0.21 s |
| `studio list` | 0.39 s | 0.23 s |
| `studio status` | 0.42 s | 0.24 s |
| `studio wp option get blogname` | 0.67 s | 0.48 s |
| `studio stop` / `start` (already in that state) | 0.44 s | 0.26 s |

The saving is a fixed cost per CLI process, so it applies to every
command the desktop app and Studio Code run through the CLI.

## Testing Instructions

- `npx vitest run --project cli apps/cli/tests/startup-imports.test.ts`:
loading what `index.ts` imports statically must not load any command
implementation. Making a `.command` module import its implementation
statically fails it.
- Build the CLI and compare `studio --help`, `studio create --help` and
`studio foo` with trunk's output.
- Time `studio list` or `studio wp option get blogname` against trunk.

## Pre-merge Checklist

- [x] Have you checked for TypeScript, React or other console errors?
chubes4 added a commit that referenced this pull request Oct 28, 2026
…ce-agent

Conflicts:
- apps/cli/index.ts, apps/cli/tests/startup-imports.test.ts: trunk's
  .command modules (#5495) load each command's implementation when it runs,
  which the spike's lazy top-level registration (2c7f7a8) did for
  `studio wp`; took trunk's.
- apps/cli/ai/providers.ts: the spike's custom endpoint imports, with trunk's
  lazy prompts from cli/lib/prompts (#5316).
- apps/cli/ai/slash-commands.ts: the spike's async getModels(), trunk's
  comment.
- apps/cli/ai/tools/ask-user-question.ts: trunk removed the appended "Other"
  choice and added skipping (#5474); the description says so, positively.
- apps/ui/.../site-actions-menu/index.tsx: trunk's Email item (#5359), the
  spike's Global Styles query.
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