Back to Blog
Open SourceCLINode.jsDeveloper ToolsTypeScript

Building Re-Shell CLI: My Open Source Journey

Umut Korkmaz2025-03-208 min read

Every developer eventually builds their own tools. For me, that tool became Re-Shell CLI—a comprehensive platform for building modern distributed applications that unites microservices and microfrontends under a single powerful CLI.

The Problem

Working on multiple projects with microservices and microfrontends, I noticed the same patterns emerging:

  • Repetitive boilerplate for each new service
  • Inconsistent project structures across teams
  • Complex deployment orchestration
  • Difficulty maintaining shared configurations

The existing tools either did too much (vendor lock-in) or too little (just scaffolding).

The Vision

Re-Shell aims to be the middle ground—opinionated enough to provide real productivity gains, but flexible enough to adapt to different tech stacks:

bash
# Create a new distributed application
reshell init my-platform

# Add a new microservice
reshell add service user-service --template node-fastify

# Add a microfrontend
reshell add frontend dashboard --template react-vite

# Run everything locally
reshell dev

# Deploy to production
reshell deploy --env production

Technical Architecture

The CLI is built with TypeScript and follows a plugin-based architecture:

typescript
// Core CLI structure
import { Command } from 'commander';
import { loadPlugins } from './plugin-loader';

const program = new Command();

program
  .name('reshell')
  .description('Unified CLI for distributed applications')
  .version('1.0.0');

// Dynamic command loading from plugins
const plugins = await loadPlugins();
plugins.forEach(plugin => {
  plugin.registerCommands(program);
});

program.parse();

Plugin System

Each capability is a separate plugin:

typescript
// Plugin interface
export interface ReShellPlugin {
  name: string;
  version: string;
  registerCommands: (program: Command) => void;
  hooks?: {
    preInit?: () => Promise<void>;
    postInit?: () => Promise<void>;
    preBuild?: () => Promise<void>;
    postBuild?: () => Promise<void>;
  };
}

// Example: Service Generator Plugin
export const serviceGeneratorPlugin: ReShellPlugin = {
  name: 'service-generator',
  version: '1.0.0',
  registerCommands: (program) => {
    program
      .command('add service <name>')
      .description('Add a new microservice')
      .option('-t, --template <template>', 'Service template', 'node-express')
      .option('-p, --port <port>', 'Default port')
      .action(async (name, options) => {
        await generateService(name, options);
      });
  }
};

Template Engine

Services and frontends are generated from customizable templates:

typescript
// Template processing
export const processTemplate = async (
  templatePath: string,
  outputPath: string,
  variables: Record<string, string>
) => {
  const files = await glob(`${templatePath}/**/*`, { nodir: true });
  
  for (const file of files) {
    let content = await fs.readFile(file, 'utf-8');
    
    // Replace template variables
    for (const [key, value] of Object.entries(variables)) {
      content = content.replace(
        new RegExp(`\\{\\{${key}\\}\\}`, 'g'),
        value
      );
    }
    
    // Process filename variables
    const relativePath = path.relative(templatePath, file);
    const processedPath = relativePath.replace(
      /\{\{(\w+)\}\}/g,
      (_, key) => variables[key] || ''
    );
    
    const outputFile = path.join(outputPath, processedPath);
    await fs.mkdir(path.dirname(outputFile), { recursive: true });
    await fs.writeFile(outputFile, content);
  }
};

Workspace Configuration

A central configuration file manages all services:

yaml
# reshell.yaml
name: my-platform
version: 1.0.0

services:
  user-service:
    type: microservice
    template: node-fastify
    port: 3001
    dependencies:
      - database
      - cache
    
  product-service:
    type: microservice
    template: node-express
    port: 3002
    dependencies:
      - database

frontends:
  dashboard:
    type: microfrontend
    template: react-vite
    port: 5173
    remotes:
      - shared-ui
      
  customer-portal:
    type: microfrontend
    template: react-vite
    port: 5174

shared:
  database:
    type: postgres
    version: 15
    
  cache:
    type: redis
    version: 7

environments:
  development:
    compose: true
    
  production:
    provider: kubernetes
    namespace: my-platform

Lessons from Open Source

1. Documentation is Everything

No matter how good your code is, poor documentation kills adoption. I spent as much time on docs as on code.

2. Start Small

The first version only had init and dev commands. Features grew based on actual usage feedback.

3. Dogfood Your Own Tool

I use Re-Shell for my own projects. Nothing finds bugs faster than real usage.

4. Community Matters

Even with a small user base, the feedback and contributions have been invaluable.

What's Next

Current development focuses on:

  • Cloud provider plugins - AWS, GCP, Azure deployment automation
  • Monitoring integration - Built-in observability setup
  • AI assistance - Natural language service generation

Contributing

Re-Shell is open source and welcomes contributions:

bash
# Clone the repository
git clone https://github.com/Re-Shell/cli.git

# Install dependencies
npm install

# Run in development mode
npm run dev

# Run tests
npm test

Whether you're building a small side project or a complex enterprise system, Re-Shell aims to make distributed application development more accessible. Check it out at github.com/Re-Shell/cli and let me know what you think!