command-executor

from front-depiction/claude-setup

Reusable Claude Code configuration for Effect TypeScript projects with specialized agents and skills

10 stars4 forksUpdated Jan 19, 2026
npx skills add https://github.com/front-depiction/claude-setup --skill command-executor

SKILL.md

Command Execution with @effect/platform

Overview

The Command module provides type-safe, testable process execution with automatic resource cleanup. Use this for spawning child processes, running shell commands, capturing output, and managing process lifecycles.

When to use this skill:

  • Running shell commands or external programs
  • Spawning child processes with controlled stdio
  • Capturing command output (string, lines, stream)
  • Managing long-running processes with cleanup
  • Setting environment variables or working directories
  • Piping commands together

Note: This skill covers the Command module for process execution, NOT @effect/cli for building CLI applications.

Import Pattern

import { Command, CommandExecutor } from "@effect/platform"

Creating Commands

Basic Command

import { Command } from "@effect/platform"
import { pipe } from "effect"

declare const PROJECT_ROOT: string

// Simple command with arguments
const command = Command.make("echo", "-n", "test")

// With working directory
const commandWithDir = pipe(
  Command.make("npm", "install"),
  Command.workingDirectory("/path/to/project")
)

// With environment variables
const commandWithEnv = pipe(
  Command.make("node", "script.js"),
  Command.env({ NODE_ENV: "production", API_KEY: "xyz" })
)

// Control stdio streams
const commandWithStdio = pipe(
  Command.make("hardhat", "node"),
  Command.stdout("inherit"),  // "inherit" | "pipe"
  Command.stderr("inherit"),
  Command.workingDirectory(PROJECT_ROOT)
)

Command Configuration Options

import { Command } from "@effect/platform"
import type { Stream } from "effect"

declare const stream: Stream.Stream<Uint8Array>
declare const stringInput: string

// stdout/stderr modes:
// - "inherit": Pass through to parent process
// - "pipe": Capture for programmatic access

const configuredCommand = pipe(
  Command.make("some-command"),
  Command.stdout("pipe"),    // Capture output
  Command.stderr("inherit"), // Show errors in console
  Command.stdin(stream),     // Pipe stream as stdin
  Command.feed(stringInput)  // Feed string as stdin
)

Executing Commands

Capture as String

import { Command } from "@effect/platform"
import { Effect } from "effect"

const result = Effect.gen(function* () {
  const command = Command.make("echo", "-n", "hello")
  const output = yield* Command.string(command)
  // output: "hello"
  return output
})

Capture as Lines

import { Command } from "@effect/platform"
import { Effect } from "effect"

const result = Effect.gen(function* () {
  const command = Command.make("ls", "-1")
  const lines = yield* Command.lines(command)
  // lines: string[]
  return lines
})

Stream Output

import { Command } from "@effect/platform"
import { Effect, Stream, Chunk, Console, pipe } from "effect"

declare const decoder: TextDecoder

const result = Effect.gen(function* () {
  const command = Command.make("tail", "-f", "app.log")

  // As line stream
  const lineStream = Command.streamLines(command)
  yield* Stream.runForEach(lineStream, (line) => Console.log(line))

  // As byte stream
  const byteStream = Command.stream(command)
  yield* pipe(
    byteStream,
    Stream.mapChunks(Chunk.map((bytes) => decoder.decode(bytes))),
    Stream.runCollect
  )
})

Get Exit Code

import { Command } from "@effect/platform"
import { Effect } from "effect"

const result = Effect.gen(function* () {
  const command = Command.make("test", "-f", "file.txt")
  const exitCode = yield* Command.exitCode(command)
  // exitCode: number (0 = success, non-zero = failure)
  return exitCode
})

Process Management

Start Process with Handle

import { Command, CommandExecutor } from "@effect/platform"
import { Effect, Stream, pipe } from "effect"

declare const PROJECT_ROOT: string
declare function handleOutput(chunk: Uint8Array): Effect.Effect<void>

const program = Effect.gen(function* () {
  // Get the executor service
  const executor = yield* CommandExecutor.CommandExecutor

  const command = pipe(
    Command.make("bunx", "hardhat", "node"),
    Command.workingDirectory(PROJECT_ROOT),
    Command.stdout("inherit"),
    Command.stderr("inherit")
  )

  // Start returns a process handle
  const process = yield* executor.start(command)

  // Check if running
  const isRunning = yield* process.isRunning

  // Kill the process
  yield* process.kill("SIGTERM")  // or "SIGKILL", "SIGINT", etc.

  // Access streams (when stdout/stderr are "pipe")
  yield* Stream.runForEach(process.stdout, handleOutput)
})

Automatic Cleanup with Finalizers

import { Command, CommandExecutor } from "@effect/platform"
import { Effect, pipe } from "effect"

declare const PROJECT_ROOT: string
declare const waitForHardhat: Effect.Effect<void>

const startHardhatNode = Effect.gen(function* () {
  const executor = yield* Com

...
Read full content

Repository Stats

Stars10
Forks4