Interactive Plugin Setup
Design Log #157 — Interactive Plugin Setup
Written for AI agents. See Log Methodology Note below for details.
Background
create-jay (DL#153) currently hardcodes plugin-specific setup logic — Wix CLI login, API key prompts, wix.config.json parsing, credential file creation. This couples the scaffolder to specific plugins and doesn't scale as more plugins with setup requirements are added.
The jay-stack-cli setup command already exists (DL#87) and runs plugin setup handlers. But it's non-interactive — plugins can create config templates and report what's missing, but they can't prompt the user for input.
Related
- DL#87 — Jay-stack setup command
- DL#153 — npm create jay
Problem
Two problems:
Plugin-specific logic in create-jay — The scaffolder knows about Wix CLI login, API key prompts,
wix.config.jsonstructure. When a new plugin needs interactive setup (e.g., a CMS plugin asking for API credentials), create-jay would need more hardcoded logic.Setup command can't ask questions —
jay-stack-cli setupruns plugin handlers that create config files and validate credentials. But if credentials are missing, the handler can only report "config template created, fill in your values." It can't prompt the user interactively.
Questions
Q1: Should setup handlers be able to prompt the user?
A1: Yes. The setup handler should receive an interactive context that provides prompt functions (input, confirm, select). When running in interactive mode, prompts work normally. In non-interactive mode (CI, scripts), prompts are skipped and the handler falls back to config templates.
Q2: How should create-jay interact with setup?
A2: create-jay should only scaffold files and install dependencies. After install, it runs jay-stack-cli setup --interactive. Each plugin's setup handler handles its own configuration — Wix login, API keys, etc. No plugin-specific code in create-jay.
Q3: What about the Wix CLI login flow?
A3: The wix-server-client plugin's setup handler would:
- Check
npx @wix/cli whoami— if not logged in, prompt to login - Run
npm create @wix/new@latest init— connects to a Wix site - Read
wix.config.json— extractsiteIdandappId - Prompt for API key (with link to Wix dashboard)
- Write
config/.wix.yamlwith credentials - Update
.gitignore
All of this currently lives in create-jay/lib/index.ts — it would move to the wix-server-client plugin.
Q4: What about the auth callback page?
A4: The wix-members plugin's setup handler would check if src/pages/auth/callback/page.jay-html exists and create it if missing. Currently create-jay does this.
Design
Interactive setup context
Extend PluginSetupContext with prompt functions:
interface PluginSetupContext {
projectRoot: string;
configDir: string;
interactive: boolean;
prompt: {
input(options: {
message: string;
validate?: (v: string) => boolean | string;
}): Promise<string>;
confirm(options: { message: string; default?: boolean }): Promise<boolean>;
select(options: {
message: string;
choices: Array<{ label: string; value: string }>;
}): Promise<string>;
};
run(cmd: string): void;
log(message: string): void;
warn(message: string): void;
}
When interactive is false, prompt functions:
input→ returns empty string (handler should check and skip)confirm→ returns the default valueselect→ returns the first choice
Plugin setup handler signature
export async function setupMyPlugin(ctx: PluginSetupContext): Promise<PluginSetupResult> {
// Check if already configured
if (fs.existsSync(path.join(ctx.configDir, '.wix.yaml'))) {
ctx.log('Wix credentials already configured');
return { status: 'ok' };
}
if (!ctx.interactive) {
ctx.warn('Run `jay-stack setup` interactively to configure Wix credentials');
return { status: 'needs-config' };
}
// Interactive flow
const apiKey = await ctx.prompt.input({ message: 'Wix API Key:' });
// ... rest of setup
}
CLI modes
jay-stack-cli setup # Interactive (default)
jay-stack-cli setup --no-interactive # Non-interactive (CI, scripts)
create-jay changes
After scaffolding and installing:
// Before: hardcoded Wix logic
if (hasWixPlugins) {
await promptWixApiKey();
await setupWix(projectDir, apiKey);
}
run('npx jay-stack-cli agent-kit', projectDir);
run('npx jay-stack-cli setup', projectDir);
// After: just run setup interactively
run('npx jay-stack-cli agent-kit', projectDir);
run('npx jay-stack-cli setup', projectDir); // interactive by default
All plugin-specific logic moves to plugin setup handlers.
No declaration needed in plugin.yaml
The setup handler receives ctx.interactive and decides at runtime whether to prompt or skip. No interactive: true flag in plugin.yaml — every handler gets the same context and can check ctx.interactive to decide its behavior.
Implementation Plan
Phase 1: Extend setup context
- Add prompt functions to
PluginSetupContext - Add
--no-interactiveflag tojay-stack-cli setup - Wire up
@inquirer/promptsin the CLI for interactive mode - Provide no-op prompts for non-interactive mode
Phase 2: Move Wix setup to plugin
- Move Wix CLI login, API key prompt, credential file creation to
wix-server-clientsetup handler - Move auth callback page creation to
wix-memberssetup handler - Remove Wix-specific code from
create-jay
Phase 3: Simplify create-jay
- Remove
promptWixApiKey,setupWix,hasWixPluginsfrom create-jay - Just run
jay-stack-cli setupafter install (interactive by default)
Trade-offs
| Approach | Pro | Con |
|---|---|---|
| Interactive setup in plugins (chosen) | Scales to any plugin, no scaffolder coupling | Requires framework change to setup context |
| Hardcoded in create-jay (current) | Works now, simple | Doesn't scale, couples scaffolder to plugins |
| Separate setup CLI per plugin | No framework change | Users run multiple commands |
Agent-driven setup (non-interactive with answers file)
Problem
AI coding agents (Claude Code, Cursor, etc.) run CLI commands but can't type into interactive prompts. They need a way to provide answers programmatically.
Design: Iterative answer flow
Non-interactive mode is the default. When a prompt has no answer, the CLI exits with structured output telling the agent what's needed. The agent provides the answer and re-runs.
Flow:
- Agent runs
jay-stack-cli setup - Plugin handler calls
ctx.prompt.input({ key: 'api-key', message: 'Enter your API key' }) - No answer available → throws
SetupNeedsAnswerError - CLI catches the error, prints structured YAML, exits with code 2:
setup-needs-answer:
plugin: wix-server-client
key: api-key
type: input
message: 'Enter your API key (create at https://manage.wix.com/account/api-keys)'
Provide the answer: jay-stack-cli setup --answers answers.yaml
answers.yaml format:
api-key: 'your-answer-here'
- Agent creates
answers.yamlwith the value and re-runs:jay-stack-cli setup --answers /tmp/answers.yaml - Plugin gets the answer, proceeds. If it asks another question → repeat.
- When all plugins return
configured→ done.
Prompt API changes
Add required key to all prompt methods:
interface PluginSetupPrompt {
input(options: { key: string; message: string; validate?: ... }): Promise<string>;
confirm(options: { key: string; message: string; default?: boolean }): Promise<boolean>;
select(options: { key: string; message: string; choices: Array<{ name: string; value: string }> }): Promise<string>;
}
Three prompt implementations
| Implementation | When used | Behavior on missing answer |
|---|---|---|
| Interactive | jay-stack-cli setup --interactive or create-jay |
Prompts user via terminal |
| Answers file | jay-stack-cli setup --answers file.yaml |
Reads from file, throws if missing |
| Default (no flag) | jay-stack-cli setup |
Throws with structured output |
SetupNeedsAnswerError
class SetupNeedsAnswerError extends Error {
plugin: string;
key: string;
type: 'input' | 'confirm' | 'select';
promptMessage: string;
choices?: Array<{ name: string; value: string }>;
}
The CLI catches this specific error type and formats the structured output. Other errors are reported normally.
Handler requirements
- Idempotent — re-running with the same answers produces the same result. If config already exists and is valid, skip prompts.
- Stable keys — prompt keys don't change between runs. Use descriptive kebab-case:
api-key,region,site-id. - Check before prompt — if the value is already in the config file, don't prompt for it again.
CLI flags (updated)
jay-stack-cli setup # Default: non-interactive, exits on missing answer
jay-stack-cli setup --interactive # Terminal prompts (for humans, create-jay uses this)
jay-stack-cli setup --answers file.yaml # Reads answers from file
jay-stack-cli setup --no-interactive # Same as default (explicit)
create-jay: interactive and non-interactive
create-jay supports both humans and agents:
Human (default — no flags):
npm create jay
Interactive prompts for project name and plugin selection, then runs jay-stack-cli setup --interactive.
Agent (with flags):
npm create jay -- --name my-project --plugins wix-stores,wix-cart,ui-kit
Skips prompts, scaffolds with provided values, runs jay-stack-cli setup (non-interactive — agent handles setup answers separately).
Flags:
--name <name>— project name (skips name prompt)--plugins <list>— comma-separated plugin names (skips plugin selection)- When both flags are provided, no interactive prompts are shown
Verification Criteria
jay-stack-cli setup(default) exits with structured YAML when a plugin needs inputjay-stack-cli setup --interactiveprompts for credentials via terminaljay-stack-cli setup --answers file.yamlreads answers and configures plugins- Iterative flow: agent provides one answer at a time, re-runs until all configured
- Handlers are idempotent — re-running with same answers is a no-op
create-jaywith no flags prompts interactively, runssetup --interactivecreate-jay --name x --plugins yscaffolds without prompts, runssetup(non-interactive)- Existing handlers without prompts continue to work unchanged
Implementation Results
Phase 1: Framework changes (implemented)
stack-server-runtime/lib/plugin-setup.ts:
- Added
PluginSetupPromptinterface withinput(),confirm(),select()methods - Added
interactive: booleanandprompt: PluginSetupPrompttoPluginSetupContext - Updated
executePluginSetup()to accept and passinteractiveandpromptin options
stack-cli/lib/setup-prompts.ts (new):
createInteractivePrompt()— wraps@inquirer/promptsfor real user inputcreateNonInteractivePrompt()— returns empty string / defaults without prompting
stack-cli/lib/cli.ts:
- Added
--no-interactiveflag tosetupcommand
stack-cli/lib/run-setup.ts:
- Reads
interactiveoption (defaults to true) - Creates appropriate prompt implementation
- Passes both to
executePluginSetup
Phase 3: create-jay simplified (implemented)
Removed all Wix-specific logic from create-jay:
- Removed
promptWixApiKey(),setupWix(),hasWixPlugins()check - Removed
@inquirer/promptsandfsimports (no longer needed) - Flow is now: scaffold → install → agent-kit → setup (interactive by default)
- File size reduced from 13.55KB to 9.96KB
Scaffolding concerns kept (these are project structure, not credentials):
- Auto-including
wix-server-clientdependency when Wix plugins selected - Wix-specific npm scripts (
wix:deploy,wix:serve) - Auth callback page template for wix-members
Phase 2: Move Wix setup to plugin (deferred)
Deferred to Wix DL#27. The framework infrastructure is ready — plugin handlers can now use ctx.prompt.input() etc. The actual Wix credential flow needs to move to the wix-server-client plugin's setup handler in the wix monorepo.
Backward compatibility
Existing setup handlers (e.g., gemini-agent) continue to work unchanged. They receive interactive and prompt in their context but are not required to use them.
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.