npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@knpkv/effect-ai-claude-code-cli

v0.2.0

Published

Effect-TS wrapper for Claude Code CLI with @effect/ai integration

Readme

@knpkv/effect-ai-claude-code-cli

Warning This package is experimental and in early development. Code is primarily AI-generated and not yet publicly published. For preview, use snapshot releases.

Effect-TS wrapper for Claude Code CLI with @effect/ai integration.

Provides a type-safe, functional interface to programmatically interact with Claude Code CLI, including:

  • Non-blocking query execution
  • Streaming responses with full event support
  • Tool call visibility (Read, Write, Bash, etc.)
  • Comprehensive error handling
  • @effect/ai LanguageModel integration
  • Type-safe tool names with IDE autocomplete

Installation

npm install @knpkv/effect-ai-claude-code-cli effect

Prerequisites:

npm install -g @anthropics/claude-code

Quick Start

Basic Query

import { ClaudeCodeCliClient } from "@knpkv/effect-ai-claude-code-cli"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const client = yield* ClaudeCodeCliClient.ClaudeCodeCliClient
  const response = yield* client.query("What is Effect-TS?")
  console.log(response)
})

Effect.runPromise(program.pipe(Effect.provide(ClaudeCodeCliClient.layer())))

Streaming Responses

import { ClaudeCodeCliClient } from "@knpkv/effect-ai-claude-code-cli"
import { Effect, Stream } from "effect"

const program = Effect.gen(function* () {
  const client = yield* ClaudeCodeCliClient.ClaudeCodeCliClient
  const stream = client.queryStream("Write a haiku about TypeScript")

  yield* stream.pipe(
    Stream.runForEach((chunk) =>
      Effect.sync(() => {
        if (chunk.type === "text") {
          process.stdout.write(chunk.text)
        }
      })
    )
  )
})

Effect.runPromise(program.pipe(Effect.provide(ClaudeCodeCliClient.layer())))

@effect/ai Integration

import { LanguageModel } from "@effect/ai"
import { ClaudeCodeCliClient, ClaudeCodeCliLanguageModel } from "@knpkv/effect-ai-claude-code-cli"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const model = yield* LanguageModel.LanguageModel
  const response = yield* model.generateText({
    prompt: [{ role: "user", content: [{ type: "text", text: "Explain monads" }] }]
  })

  console.log(response.text)
  console.log("Usage:", response.usage)
})

Effect.runPromise(
  program.pipe(Effect.provide(ClaudeCodeCliLanguageModel.layer()), Effect.provide(ClaudeCodeCliClient.layer()))
)

Configuration

Tool Permissions

Control which tools Claude can use:

import { ClaudeCodeCliConfig } from "@knpkv/effect-ai-claude-code-cli"
import { Layer } from "effect"

const config = Layer.succeed(
  ClaudeCodeCliConfig.ClaudeCodeCliConfig,
  ClaudeCodeCliConfig.ClaudeCodeCliConfig.of({
    allowedTools: ["Read", "Glob", "Bash"], // IDE autocomplete for known tools
    disallowedTools: ["Write", "Edit"]
  })
)

Effect.runPromise(program.pipe(Effect.provide(ClaudeCodeCliClient.layer()), Effect.provide(config)))

Model Selection

const config = ClaudeCodeCliConfig.ClaudeCodeCliConfig.of({
  model: "claude-sonnet-4-5"
})

Streaming Events

The client emits detailed chunk types for comprehensive event handling:

Chunk Types

  • TextChunk - Text content deltas
  • ToolUseStartChunk - Tool invocation begins (includes tool name and ID)
  • ToolInputChunk - Tool input JSON streaming
  • ContentBlockStartChunk - Content block boundaries
  • ContentBlockStopChunk - Content block completion
  • MessageStartChunk - Message metadata (model, usage)
  • MessageDeltaChunk - Updates (stop_reason, usage)
  • MessageStopChunk - Stream completion

Example: Logging All Events

const stream = client.queryStream("Read package.json and summarize it")

yield *
  stream.pipe(
    Stream.runForEach((chunk) =>
      Effect.gen(function* () {
        switch (chunk.type) {
          case "text":
            yield* Console.log("Text:", chunk.text)
            break
          case "tool_use_start":
            yield* Console.log(`Tool: ${chunk.name} (${chunk.id})`)
            break
          case "tool_input":
            yield* Console.log("Input:", chunk.partialJson)
            break
          case "message_delta":
            yield* Console.log("Usage:", chunk.usage)
            break
        }
      })
    )
  )

Type Safety Features

Branded Types

The package provides branded types for critical identifiers to prevent string confusion:

import { Brand, Validation } from "@knpkv/effect-ai-claude-code-cli"

// Validate and construct branded types
const model = yield * Validation.validateModel("claude-4-sonnet-20250514") // ModelId
const prompt = yield * Validation.validatePrompt("Your prompt here") // PromptText
const tool = yield * Validation.validateToolName("Read") // ToolName

Input Validation

Comprehensive validation functions ensure inputs meet requirements:

import { Validation } from "@knpkv/effect-ai-claude-code-cli"

// Validate prompt (non-empty, length limits)
const prompt = yield * Validation.validatePrompt("Explain TypeScript")

// Validate model ID (starts with "claude-")
const model = yield * Validation.validateModel("claude-4-sonnet")

// Validate tool name (PascalCase format)
const tool = yield * Validation.validateToolName("Read", false) // strict mode optional

// Validate file path (no null bytes, no path traversal)
const path = yield * Validation.validateFilePath("/home/user/file.txt")

// Validate timeout (1s to 10min)
const timeout = yield * Validation.validateTimeout(30000)

// Validate multiple tools
const tools = yield * Validation.validateTools(["Read", "Write", "Bash"])

Type Guards

Helper functions for working with stream chunks:

import { TypeGuards } from "@knpkv/effect-ai-claude-code-cli"
import { Stream } from "effect"

// Filter to only text chunks
const textStream = stream.pipe(Stream.filter(TypeGuards.isTextChunk))

// Extract usage information
const usage = stream.pipe(
  Stream.filterMap((chunk) => TypeGuards.extractUsage(chunk)),
  Stream.runLast
)

CLI Version Checking

Ensure CLI compatibility:

import { CliVersion } from "@knpkv/effect-ai-claude-code-cli"

const program = Effect.gen(function* () {
  // Check CLI version on startup
  yield* CliVersion.checkCliVersion() // Validates minimum version

  const client = yield* ClaudeCodeCliClient.ClaudeCodeCliClient
  // ... use client
})

Error Handling

The package provides typed error handling with specific error types:

import { ClaudeCodeCliError } from "@knpkv/effect-ai-claude-code-cli"
import { Match } from "effect"

const handleError = Match.type<ClaudeCodeCliError.ClaudeCodeCliError>().pipe(
  Match.tag("CliNotFoundError", () => Console.error("Claude CLI not found. Install: npm i -g @anthropics/claude-code")),
  Match.tag("RateLimitError", (error) => Console.error(`Rate limited. Retry after ${error.retryAfter}s`)),
  Match.tag("InvalidApiKeyError", (error) => Console.error("Invalid API key:", error.stderr)),
  Match.tag("ValidationError", (error) => Console.error("Validation failed:", error.message)),
  Match.tag("CliVersionMismatchError", (error) =>
    Console.error(`Version ${error.installed} < required ${error.required}`)
  ),
  Match.tag("StreamParsingError", (error) => Console.error(`Parse error at line: ${error.line}`)),
  Match.orElse((error) => Console.error("Error:", error))
)

Effect.runPromise(program.pipe(Effect.catchAll(handleError), Effect.provide(ClaudeCodeCliClient.layer())))

API Reference

ClaudeCodeCliClient

Service Tag: ClaudeCodeCliClient.ClaudeCodeCliClient

Methods

  • query(prompt: string): Effect<string, ClaudeCodeCliError> - Execute non-streaming query
  • queryStream(prompt: string): Stream<MessageChunk, ClaudeCodeCliError> - Stream response with full event visibility

Layers

  • layer(): Layer<ClaudeCodeCliClient, never, ClaudeCodeCliConfig> - Default layer
  • layerConfig(config): Layer<ClaudeCodeCliClient> - Layer with inline config

ClaudeCodeCliConfig

Service Tag: ClaudeCodeCliConfig.ClaudeCodeCliConfig

Configuration

interface ClaudeCodeCliConfig {
  model?: string // Model name (default: from CLI config)
  allowedTools?: ReadonlyArray<ToolNameOrString> // Allowed tools with autocomplete
  disallowedTools?: ReadonlyArray<ToolNameOrString> // Disallowed tools with autocomplete
}

Layers

  • ClaudeCodeCliConfig.default - Empty configuration (uses CLI defaults)

ClaudeCodeCliLanguageModel

Service Tag: LanguageModel.LanguageModel (from @effect/ai)

Layers

  • layer(config?): Layer<LanguageModel, never, ClaudeCodeCliClient> - @effect/ai integration

Model Constructor

  • model(config?): AiModel<"claude-code-cli", LanguageModel, ClaudeCodeCliClient>

Examples

See the examples directory for complete working examples:

Development

# Install dependencies
pnpm install

# Run tests
pnpm test

# Type check
pnpm check

# Lint
pnpm lint

# Build
pnpm build

Architecture

This package follows Effect-TS patterns:

  • Services - Context.Tag-based dependency injection
  • Layers - Composable service providers
  • Effects - Typed, referentially transparent computations
  • Streams - Incremental, backpressure-aware processing
  • Errors - Typed error channel with discriminated unions

License

MIT

Links