@ollie-shop/cli
v0.3.4
Published
Ollie Shop CLI for building custom checkouts
Readme
@ollie-shop/cli
Internal CLI for managing Ollie Shop components, functions, store versions, and projects.
Quick Start
# Run CLI from monorepo root
pnpm --filter @ollie-shop/cli cli
# Or from the CLI package directory
cd packages/cli
npm run cli
# With arguments
npm run cli -- component create --name my-component
npm run cli -- store-version create --store 123e4567-e89b-12d3-a456-426614174000 --name "Holiday Theme"
# Enable debug output
DEBUG=ollie:* npm run cli -- loginFeatures Overview
🏪 Store Version Management
- Create, manage, and deploy different store configurations
- A/B testing and gradual rollouts
- Template-based version creation
🧩 Component System
- Create React components for checkout customization
- Build, validate, and deploy components
- Slot-based component architecture (header, main, sidebar, footer)
- NEW: Interactive component creation wizard
- NEW: Rich deployment progress visualization
⚡ Function Management
- Create serverless functions for checkout logic
- Event-driven architecture (cart, customer, shipping, payment, order)
- Local testing and cloud deployment
📋 Template System
- Pre-built checkout templates
- Apply templates to store versions
- Preview before applying
🚀 Enhanced Developer Experience (Phase 1)
- Smart Error Recovery: Actionable suggestions for common errors
- Context-Aware Commands: Shows relevant commands based on current directory
- Interactive Mode: Step-by-step wizards for complex operations
- Rich Progress Visualization: Detailed deployment progress with stats
Architecture Overview
Command Flow
User Input
│
▼
CLI Entry (src/index.ts)
│
├─> Command Parser (Commander.js)
│ └─> Global Options (--verbose, --quiet, etc.)
│
├─> Command Registration (src/commands/*.ts)
│ └─> Option Schemas (Zod validation)
│
├─> Action Execution (src/actions/*.ts)
│ ├─> Console Output (utils/cliConsole.ts)
│ ├─> Spinner Management
│ └─> Lazy Load @ollie-shop/core
│
└─> Error Handling
└─> Recovery SuggestionsCore Files
src/index.ts - CLI Entry Point
export class OllieShopCLI {
private program: Command;
constructor() {
// Sets up Commander instance
// Registers global options
// Configures error handling
}
async run(argv: string[]): Promise<void> {
// Parses arguments
// Validates global options
// Executes command
}
}src/commands/ - Command Registration
Each command file exports a function that:
- Creates a Commander command
- Defines options with Zod schemas
- Binds to action handler
// Example: component.ts
export function registerComponentCommands(program: Command) {
const cmd = program.command('component');
cmd.command('create')
.option('--name <name>', 'Component name')
.option('--slot <slot>', 'Component slot')
.action(async (options) => {
const validated = componentOptionsSchema.parse(options);
await componentActions.createComponent(validated, cliConsole);
});
}src/actions/ - Business Logic Delegation
Actions handle:
- User feedback (spinners, messages)
- Core service delegation
- Error transformation
// Example: component.actions.ts
export async function createComponent(options: ComponentOptions, cliConsole: CliConsole) {
const spinner = cliConsole.spinner('Creating component...');
try {
// Lazy load core service
const { createComponent: createComponentService } = await import('@ollie-shop/core');
const result = await createComponentService(options);
spinner.succeed('Component created');
// Show next steps
cliConsole.nextSteps('Next steps', [
{ description: 'Navigate to directory', command: `cd ${result}` },
{ description: 'Install dependencies', command: 'npm install' },
{ description: 'Start development', command: 'npm run dev' }
]);
} catch (error) {
spinner.fail('Failed to create component');
throw error;
}
}CLI Console Utility
The cliConsole utility (src/utils/cliConsole.ts) provides consistent output formatting:
interface CliConsole {
// Basic output
log(message: string): void;
error(message: string): void;
warn(message: string): void;
info(message: string): void;
success(message: string): void;
// Formatting
dim(message: string): void;
bold(message: string): void;
// Complex output
list(items: string[]): void;
table(data: any[]): void;
json(data: any): void;
// Interactive
spinner(message: string): Ora;
confirm(message: string): Promise<boolean>;
prompt(questions: any[]): Promise<any>;
}Usage patterns:
// Simple messages
cliConsole.success('✓ Build completed');
cliConsole.error('✗ Validation failed');
// Formatted lists
cliConsole.info('Available templates:');
cliConsole.list(['default', 'grocery', 'sales']);
// Spinners for async operations
const spinner = cliConsole.spinner('Building...');
await longOperation();
spinner.succeed('Build complete');
// Interactive prompts
const confirm = await cliConsole.confirm('Deploy to production?');Authentication Flow
// src/actions/auth.actions.ts
1. Start local server on port 7777
2. Open browser to auth endpoint
3. Wait for callback with credentials
4. Save to ~/.ollie-shop/credentials.json
5. Return user info (currently mocked)Error Handling
Custom error class with recovery suggestions:
class OllieShopCLIError extends Error {
constructor(
message: string,
public context?: Record<string, any>,
public suggestions?: string[]
) {
super(message);
}
}
// Usage
throw new OllieShopCLIError(
'Component not found',
{ path: './src/components' },
[
'Run "ollieshop component create" to create a new component',
'Check that you are in the correct directory'
]
);Input Validation & DX Enhancements
All CLI commands now feature enhanced validation with helpful error messages:
Component Name Validation
# ❌ Invalid: uppercase letters
$ ollieshop component create --name TestComponent
Error: Invalid component name: Must be lowercase with hyphens only
Examples: header-nav, shopping-cart, product-list
# ✅ Valid: kebab-case
$ ollieshop component create --name test-componentFunction Validation
# ❌ Invalid: wrong invocation type
$ ollieshop function create --name test --invocation invalid
Error: Invalid function invocation type
Valid invocations: request, response
# ✅ Valid: proper invocation
$ ollieshop function create --name validate-order --invocation requestUUID Validation (Store Versions)
# ❌ Invalid: not a UUID
$ ollieshop store-version create --store abc123 --name v2
Error: Invalid store ID: Must be a valid store ID
Example: 123e4567-e89b-12d3-a456-426614174000
# ✅ Valid: proper UUID format
$ ollieshop store-version create --store 123e4567-e89b-12d3-a456-426614174000 --name v2Validation Features
- Early validation: Errors caught at parse time, not after processing
- Helpful examples: Error messages include valid examples
- Format hints: Shows expected formats (kebab-case, UUID, etc.)
- Enum suggestions: Lists all valid options for enums
- Required fields: Clear indication when fields are missing
- Help integration:
--helpshows examples for each command
Component Validation
The component validate command checks for:
- Required files:
index.tsx,package.json,meta.json - Valid TypeScript/JavaScript: Source files compile without errors
- Component metadata: Valid meta.json structure
- Package dependencies: All required dependencies present
# Validate a component (components are created in ./components/<name>/)
$ ollieshop component validate --path ./components/my-component
# Or validate from within component directory (automatic path detection)
$ cd components/my-component
$ ollieshop component validateAutomatic Path Detection
The CLI now automatically detects when you're inside a component or function directory:
# Navigate to component directory
$ cd components/header-nav
# These commands automatically use the current directory
$ ollieshop component validate # No --path needed
$ ollieshop component build # No --path needed
$ ollieshop component deploy # No --path needed
# Same works for functions
$ cd functions/validate-order
$ ollieshop function validate # No --path needed
$ ollieshop function build # No --path needed
$ ollieshop function test # No --path needed
$ ollieshop function deploy # No --path neededThe CLI detects the project type by looking for:
- Components:
meta.jsonwith type "component" or presence ofindex.tsx/index.jsx - Functions:
meta.jsonwith type "function" or presence ofindex.ts/index.js
If you're not in the right directory type, you'll get helpful error messages:
# In a function directory trying to run component command
$ ollieshop component validate
Error: Current directory appears to be a function, not a component.
Please navigate to a component directory or specify --path
# In project root
$ ollieshop component validate
Error: No component found in current directory.
Try navigating to a component directory:
cd components/<component-name>
Or specify the path:
--path ./components/<component-name>🚀 Enhanced Developer Experience Features
Smart Error Recovery
When errors occur, the CLI now provides actionable suggestions:
$ ollieshop component create --name MyComponent
❌ Component name validation failed
💡 Possible solutions:
1. Use lowercase letters and hyphens only (e.g., header-nav)
2. Examples: shopping-cart, product-list
📍 Context:
Current directory: /Users/dev/projectContext-Aware Command Suggestions
Running ollieshop without arguments shows context-aware suggestions:
$ ollieshop
📍 Current location:
You're in a component directory: header-nav
Common commands for this component:
validate Check component validity
build Build for production
dev Start development server
deploy Deploy to cloud
Recent commands:
ollieshop validate --fix (2 min ago)
ollieshop build --watch (1 hour ago)
💡 Tip: Run 'ollieshop dev' to start developing this componentInteractive Mode
Use --interactive flag for step-by-step guidance:
$ ollieshop component create --interactive
🎨 Component Creation Wizard
? Component name: shopping-cart
? Component slot: ›
❯ Header
Main (default)
Sidebar
Footer
? Language: ›
❯ TypeScript (recommended)
JavaScript
? Include test files? (Y/n)
[Preview] This will run: ollieshop component create --name shopping-cart --slot header
? Create this component? (Y/n)Rich Progress Visualization
Deployment now shows detailed progress:
$ ollieshop component deploy --wait
🚀 Starting deployment...
🔍 Validating ████████████████████ 100% | 0.3s ✓
🔨 Building ███████████░░░░░░░░░ 60% | 1.2s | Bundling dependencies...
⚡ Optimizing ░░░░░░░░░░░░░░░░░░░░ 0% | Queued
🚀 Deploying ░░░░░░░░░░░░░░░░░░░░ 0% | Queued
✅ Verifying ░░░░░░░░░░░░░░░░░░░░ 0% | Queued
📊 Build Stats:
• Bundle size: 127KB (gzipped: 42KB)
• Tree shaking: Removed 18 unused exports
• Dependencies: 12 (3 updated)
✅ Deployment completed successfully!
Deployment URL: https://cdn.ollie.shop/components/header-nav-v1.2.0.js
Total time: 3.4sFunction Validation
The function validate command checks for:
- Required files:
index.tsorindex.js,package.json,meta.json - Valid exports: Handler function properly exported
- Function metadata: Valid meta.json structure
- Package dependencies: All required dependencies present
# Validate a function (functions are created in ./functions/<name>/)
$ ollieshop function validate --path ./functions/my-function
# Or validate from within function directory
$ cd functions/my-function
$ ollieshop function validateCommand Implementation Status
✅ Fully Implemented & Delegated to Core
These commands are fully functional and delegate to @ollie-shop/core:
Store Version Management
store-version create- Create new store versionstore-version list- List all versions for a storestore-version get- Get version detailsstore-version activate/deactivate- Toggle version statusstore-version set-default- Set default versionstore-version clone- Clone existing versionstore-version delete- Delete version
Component Management
component create- Create new componentcomponent build- Build componentcomponent validate- Validate component structurecomponent deploy- Deploy component with build status pollingcomponent deploy-status- Check deployment status
Function Management
function create- Create new functionfunction build- Build functionfunction validate- Validate function structurefunction test- Test function locallyfunction deploy- Deploy function with build status pollingfunction deploy-status- Check deployment status
Template Management
template list- List available templatestemplate apply- Apply template to versiontemplate preview- Preview template
Project Management
project init- Initialize new projectproject info- Show project informationproject link- Link to existing project
🔄 Partially Implemented
These commands have basic functionality but need database integration:
component list- Shows empty list (needs Supabase)function list- Shows empty list (needs Supabase)
🔧 CLI-Only Features
login- OAuth flow with local serverwhoami- Shows current user (mocked)docs- Opens documentationhelp- Shows command helpversion- Shows CLI version
Testing the CLI Locally
Manual Testing Checklist
# 1. Test authentication
npm run cli -- login
npm run cli -- whoami
# 2. Test store version commands
npm run cli -- store-version create --store 123e4567-e89b-12d3-a456-426614174000 --name "Test Version"
npm run cli -- store-version list --store 123e4567-e89b-12d3-a456-426614174000
npm run cli -- store-version get <version-id>
npm run cli -- store-version activate <version-id>
npm run cli -- store-version set-default <version-id>
# 3. Test component commands
npm run cli -- component create --name test-component --slot header
# Note: Components are created in ./components/<name>/ directory
npm run cli -- component validate --path ./components/test-component
npm run cli -- component build --path ./components/test-component
npm run cli -- component deploy --path ./components/test-component
npm run cli -- component deploy-status <build-id>
npm run cli -- component list
# 4. Test function commands
npm run cli -- function create --name test-function --event cart --timing before
# Note: Functions are created in ./functions/<name>/ directory
npm run cli -- function validate --path ./functions/test-function
npm run cli -- function build --path ./functions/test-function
npm run cli -- function test --path ./functions/test-function
npm run cli -- function deploy --path ./functions/test-function
npm run cli -- function deploy-status <build-id>
npm run cli -- function list
# 5. Test template commands
npm run cli -- template list
npm run cli -- template preview default
npm run cli -- template apply minimal --version <version-id>
# 6. Test project commands
npm run cli -- project init test-project --template default
npm run cli -- project info
npm run cli -- project link <project-id>
# 7. Test error handling
npm run cli -- component create # Missing required --name
npm run cli -- invalid-command # Unknown command
# 8. Test global options
npm run cli -- --verbose component create --name test-component
npm run cli -- --quiet component build
npm run cli -- --no-color component listDebugging
# Enable debug output
DEBUG=ollie:* npm run cli -- component create --name Test
# Use Node debugger
node --inspect-brk ./dist/bin/cli.js component create --name Test
# Check generated files
ls -la ~/.ollie-shop/
cat ~/.ollie-shop/credentials.jsonAdding New Commands
- Create command file in
src/commands/:
// src/commands/newfeature.command.ts
export const newFeatureCommand = (program: Command) => {
const cmd = program
.command('newfeature')
.description('New feature description');
// Add subcommands
cmd.command('action')
.option('--param <value>', 'Parameter description')
.action(newFeatureActions.action);
};- Create action file in
src/actions/:
// src/actions/newfeature.actions.ts
export const action = async (options: NewFeatureOptions) => {
const spinner = cliConsole.spinner('Processing...');
try {
// Delegate to core
const { NewFeatureService } = await import('@ollie-shop/core');
const service = new NewFeatureService();
await service.process(options);
spinner.succeed('Complete');
} catch (error) {
spinner.fail('Failed');
throw error;
}
};- Register in main CLI:
// src/commands/index.ts
export * from './newfeature.command';Core Integration Pattern
The CLI follows a strict delegation pattern:
// CLI Layer - User interaction only
async function createComponent(options) {
// 1. Show spinner
const spinner = cliConsole.spinner('Creating...');
try {
// 2. Lazy load core service
const { ComponentService } = await import('@ollie-shop/core');
// 3. Delegate all logic to core
const service = new ComponentService();
const result = await service.create(options);
// 4. Show success and next steps
spinner.succeed('Created');
showNextSteps(result);
} catch (error) {
// 5. Handle errors with context
spinner.fail('Failed');
handleError(error);
}
}Configuration Files
ollie.json Structure
{
"storeId": "uuid",
"versionId": "uuid",
"platform": "vtex",
"platformStoreId": "string",
"sessionId": "uuid",
"props": {},
"theme": {}
}Credentials Storage
// ~/.ollie-shop/credentials.json
{
"accessToken": "jwt-token",
"refreshToken": "refresh-token",
"expiresAt": "2024-01-01T00:00:00Z"
}Development Workflow
# 1. Make changes to CLI
cd packages/cli
vim src/commands/component.command.ts
# 2. Build and test
pnpm build
npm run cli -- component create --name Test
# 3. Test with core changes
cd ../core
pnpm build
cd ../cli
npm run cli -- component create --name Test
# 4. Run tests (when implemented)
pnpm testKnown Issues & TODOs
High Priority
- Implement real authentication (currently returns mock user)
- Add Supabase integration for list commands
- Connect to AWS Builder service
- Implement dev server with Vite
Medium Priority
- Add comprehensive test suite
- Implement progress bars for long operations
- Add command aliases (e.g.,
ocforollieshop component) - Cache command results where appropriate
Low Priority
- Add shell completion
- Implement update notifications
- Add telemetry (with opt-out)
- Create interactive mode
Debugging Tips
- Check core delegation: Most issues are in
@ollie-shop/core, not CLI - Validate schemas: Zod errors show what's missing
- Use verbose mode:
--verboseshows additional output - Check credentials:
cat ~/.ollie-shop/credentials.json - Clear state:
rm -rf ~/.ollie-shopto start fresh
