Files
Codewalkers/.planning/research/STACK.md
Lukas May 0ff65b0b02 feat: Rename application from "Codewalk District" to "Codewalkers"
Update all user-facing strings (HTML title, manifest, header logo,
browser title updater), code comments, and documentation references.
Folder name retained as-is.
2026-03-05 12:05:08 +01:00

12 KiB

Stack Research: TypeScript CLI with Embedded Server and SQLite

Domain: Multi-agent orchestration / Developer tooling Researched: 2026-01-30 Confidence: HIGH (based on official docs, npm stats, GitHub activity, authoritative blog posts)


Core Technologies

Name Version Purpose Why Recommended
Node.js v22+ LTS Runtime Native ESM support, node:sqlite experimental, stable LTS
TypeScript 5.7+ Type safety Strict mode required for tRPC/Zod inference
Commander.js 14.x CLI framework Lightweight, TypeScript types included, mature (9+ years), subcommand support
tRPC 11.x API layer Type-safe RPC, standalone server adapter, SSE subscriptions, no codegen
Drizzle ORM 0.44+ Database ORM Lightweight, TypeScript-first, SQL-centric, faster than raw better-sqlite3 with prepared statements
better-sqlite3 11.x SQLite driver Synchronous API, fastest SQLite driver for Node.js, Drizzle's recommended driver
Zod 3.24+ Validation tRPC's default validator, runtime + compile-time safety, Standard Schema compliant

Supporting Libraries

Name Version Purpose Notes
execa 9.6+ Process spawning Promise-based, auto-cleanup, cross-platform, graceful shutdown
simple-git 3.27+ Git operations Wrapper around git CLI, worktree config support
cors 2.8+ CORS handling For tRPC standalone server in dev mode
nanoid 5.x ID generation URL-safe, small, fast
consola 3.x CLI logging Pretty console output, log levels
chalk 5.x Terminal styling ESM-native, color output

Development Tools

Tool Version Purpose
tsup 8.5+ Build/bundle
tsx 4.x Dev execution
vitest 3.x Testing
drizzle-kit 0.31+ Migrations
@types/better-sqlite3 latest Types
@types/node 22.x Types

Installation Commands

# Core dependencies
npm install commander @trpc/server drizzle-orm better-sqlite3 zod

# Supporting libraries
npm install execa simple-git nanoid consola chalk cors

# Development dependencies
npm install -D typescript tsup tsx vitest drizzle-kit @types/better-sqlite3 @types/node

Project Structure (Hexagonal Architecture)

src/
├── cli/                    # CLI entry points (commander)
│   ├── index.ts           # Main CLI binary
│   └── commands/          # Subcommand handlers
├── server/                # tRPC server
│   ├── router.ts          # tRPC router definitions
│   ├── context.ts         # Request context
│   └── adapters/          # HTTP adapter config
├── core/                  # Domain logic (hexagonal core)
│   ├── domain/            # Entities, value objects
│   ├── ports/             # Interfaces (inbound/outbound)
│   └── services/          # Application services
├── infrastructure/        # Adapters
│   ├── db/                # Drizzle schema, repositories
│   ├── git/               # Git worktree operations
│   └── agents/            # Claude Code process spawning
└── shared/                # Shared types, utils
    ├── schema.ts          # Zod schemas (shared contracts)
    └── types.ts           # Inferred types

Key Patterns

tRPC Standalone Server Setup

import { initTRPC } from '@trpc/server';
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import cors from 'cors';

const t = initTRPC.context<Context>().create();

const appRouter = t.router({
  // procedures
});

const server = createHTTPServer({
  middleware: cors(),
  router: appRouter,
  createContext,
});

server.listen(3000);

Drizzle + better-sqlite3 Setup

import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';

const sqlite = new Database('codewalk.db');
const db = drizzle(sqlite);

// Use prepared statements for performance
const prepared = db.select().from(tasks).where(eq(tasks.id, sql.placeholder('id'))).prepare();
const result = prepared.execute({ id: taskId });

Process Spawning with execa

import { execa } from 'execa';

// Spawn Claude Code agent
const subprocess = execa('claude', ['--worktree', worktreePath], {
  cwd: worktreePath,
  stdio: ['pipe', 'pipe', 'pipe'],
});

// Graceful cleanup
process.on('SIGTERM', () => subprocess.kill());

Commander CLI with Subcommands

import { Command } from 'commander';

const program = new Command();

program
  .name('cw')
  .description('Codewalkers - Multi-agent orchestration')
  .version('0.1.0');

program
  .command('serve')
  .description('Start the orchestration server')
  .option('-p, --port <number>', 'Port number', '3000')
  .action(async (options) => {
    // Start tRPC server
  });

program
  .command('task')
  .description('Manage tasks')
  .addCommand(new Command('create').action(createTask))
  .addCommand(new Command('list').action(listTasks));

Alternatives Considered

CLI Frameworks

Alternative Why Not Chosen
oclif Overkill for single developer, steeper learning curve, heavier
yargs Less TypeScript-native, callback-heavy syntax
clipanion Less ecosystem support than commander
Ink React overhead unnecessary for this use case

Database

Alternative Why Not Chosen
Prisma Heavy, slow cold starts, codegen complexity
TypeORM Complex queries lose type safety
node:sqlite Experimental, not production-ready
libsql Unnecessary Turso features for local-only use

HTTP Server

Alternative Why Not Chosen
Fastify tRPC standalone is simpler for this use case
Express Slower, less modern
Hono Better for edge, unnecessary complexity for Node CLI

Process Management

Alternative Why Not Chosen
child_process Lower-level, manual cleanup required
shelljs Less maintained, synchronous-focused
cross-spawn execa wraps this with better DX

What NOT to Use

Technology Reason
Express Slower than alternatives, less type-safe
node-sqlite3 Async API slower than better-sqlite3's sync API
Prisma Too heavy for embedded CLI database
GraphQL Overkill for internal API, tRPC is simpler
Sequelize TypeScript support inferior to Drizzle
npm link Use tsx for development instead
ts-node tsx is faster, better ESM support
Jest Vitest is faster, native ESM/TS support

Version Compatibility Notes

  • Node.js 22+ required for stable ESM, modern child_process features
  • TypeScript 5.7+ for satisfies, improved inference
  • tRPC v11 requires "strict": true in tsconfig
  • Drizzle 0.44+ for latest SQLite blob handling fixes
  • execa 9+ is ESM-only (no CommonJS)
  • chalk 5+ is ESM-only
  • better-sqlite3 requires native compilation (node-gyp)

tsconfig.json Recommendations

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "declaration": true,
    "composite": true
  }
}

package.json Type Configuration

{
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "bin": {
    "cw": "./dist/cli/index.js"
  }
}

Sources

CLI Frameworks

tRPC

SQLite / Drizzle

Process Spawning

Git Libraries

Build Tools

Validation


Confidence Assessment

Area Confidence Reasoning
CLI Framework HIGH Commander is battle-tested (13M weekly downloads), clear winner for simplicity
tRPC HIGH v11 officially released, standalone adapter well-documented
SQLite Stack HIGH better-sqlite3 + Drizzle is the recommended combination in official docs
Process Spawning HIGH execa is the de facto standard (150M+ weekly downloads)
Build Tooling HIGH tsup/tsx/vitest is the modern consensus for TypeScript libraries
Git Operations MEDIUM simple-git works but worktree support may need CLI fallback

Last updated: 2026-01-30