This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is an unofficial SimpleLogin CLI (sl) built with the Oclif framework. It's a command-line tool for managing SimpleLogin aliases, mailboxes, and account settings, designed for scripting and automation.
Key Technologies:
- TypeScript (ES2022, Node16 modules)
- Oclif v4 framework for CLI structure
- simplelogin-client SDK for API interactions
- Mocha + Chai for testing
- ESLint for linting
- Package manager: pnpm
# Install dependencies
pnpm install
# Build the project (cleans dist/ and compiles TypeScript)
pnpm run build
# Run tests (includes linting post-test)
pnpm run test
# Run linting only
pnpm run lint
# Run a single test file
pnpm run test -- test/commands/hello/index.test.ts
# Package for distribution
pnpm run prepack # Generates oclif.manifest.json and updates README
pnpm run pack:tarballs # Creates distribution tarballs
# Test CLI locally
./bin/run.js <command>
# or after npm link:
sl <command>Important: Run pnpm prepack when creating new Oclif commands so that metadata is updated and the command can be executed
src/
commands/ # Oclif command files (one file per command/subcommand)
hello/
index.ts # Example: 'sl hello' command
world.ts # Example: 'sl hello world' subcommand
index.ts # Entry point (re-exports @oclif/core)
test/
commands/ # Mirrors src/commands structure
hello/
index.test.ts # Tests for hello command
bin/
run.js # CLI entry point
This project uses Oclif's class-based command structure. Each command is a TypeScript class that:
- Extends
Commandfrom@oclif/core - Defines static properties for configuration:
description: Command descriptionexamples: Usage examplesflags: Command-line flags usingFlags.*args: Positional arguments usingArgs.*
- Implements
run()method: Main command logic
Example structure:
import {Command, Flags, Args} from '@oclif/core'
export default class MyCommand extends Command {
static description = 'Command description'
static flags = {
myFlag: Flags.string({description: 'Flag description'}),
}
static args = {
myArg: Args.string({description: 'Argument description', required: true}),
}
async run(): Promise<void> {
const {args, flags} = await this.parse(MyCommand)
// Command logic here
}
}- Commands are organized in
src/commands/with directory structure defining the command hierarchy src/commands/hello/index.ts→sl hellosrc/commands/hello/world.ts→sl hello world- Oclif automatically discovers commands based on file structure
Tests use @oclif/test and Chai for assertions:
import {runCommand} from '@oclif/test'
import {expect} from 'chai'
describe('command-name', () => {
it('runs command', async () => {
const {stdout} = await runCommand('command-name args --flags')
expect(stdout).to.contain('expected output')
})
})The CLI_DESIGN.md file contains the complete specification for all commands to be implemented. Key architectural decisions from the design:
- Resource-Action Pattern: Commands follow
sl <resource> <action>structure (e.g.,sl alias list,sl mailbox create) - Docker-style aliases: Support short aliases like
lsforlist,rmfordelete - Global Flags: All commands support
--format(plain/json/yaml) and--config(path to config file)
- Default config location:
~/.config/simplelogin-cli/config.yaml - Config format: YAML with
urlandapiKeyfields - Security: Config file must have 600 permissions, directory 700 permissions
- API Key storage: Plaintext in config, secured by file permissions
All commands interact with the SimpleLogin API via the simplelogin-client package:
AccountApi- User account operationsAliasApi- Alias managementMailboxApi- Mailbox operationsCustomDomainApi- Domain management
Commands must support three output formats via --format flag:
- plain (default): Human-readable tables and key-value pairs
- json: Structured JSON for scripting
- yaml: YAML format for readability and scripting
Standard exit codes:
- 0: Success
- 1: General error
- 2: Invalid arguments
- 3: Authentication required
- 4: API error
- 5: Network error
- Create file in
src/commands/following the directory structure for your command path - Extend
Commandclass from@oclif/core - Define
static description,static flags,static args, andstatic examples - Implement
async run()method - Use
await this.parse(ClassName)to get parsed args and flags - Refer to
CLI_DESIGN.mdfor the command's specification - Create corresponding test file in
test/commands/with same structure - After implementation, run
pnpm run prepackto update README with command documentation
- Use
this.log()for output (notconsole.log) - Use
this.error()for errors (automatically sets exit code) - Use
this.warn()for warnings - Flags are defined with
Flags.string(),Flags.boolean(), etc. - Args are defined with
Args.string(),Args.integer(), etc. - For required flags/args, use
{required: true} - For flag aliases, use
{char: 'f'}for short form
- Test files mirror command structure in
test/commands/ - Use
runCommand()from@oclif/testto execute commands - Tests run with Mocha (
pnpm run test) - Linting runs automatically after tests via
posttestscript
- Module system: ES modules (Node16 module resolution)
- Target: ES2022
- Strict mode: Enabled
- Output:
dist/directory - Use
.jsextensions in imports when referencing other project files (Node16 modules requirement)