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

@scottwalker/kraube-konnektor

v0.6.6

Published

Programmatic Node.js interface for Claude Code CLI

Readme

Kraube Konnektor

Programmatic Node.js interface for Claude Code CLI.

Use Claude Code from your application code — no terminal required. Works with your existing Max/Team/Enterprise subscription.

Website | Examples | API Reference | Architecture


Why

Claude Code is a powerful AI coding agent, but it only runs in a terminal. kraube-konnektor turns it into a programmable API — so you can embed it into CI pipelines, build custom tools, orchestrate multi-agent workflows, or integrate it with any Node.js application.

Key design decisions:

  • CLI wrapper, not API client — uses your local claude binary and subscription, not the Anthropic HTTP API
  • Two execution modes — persistent SDK session (fast, default) or CLI process spawning (simple)
  • Executor abstraction — swap CLI for SDK or HTTP backend without changing your code
  • Full CLI parity — exposes all 45+ Claude Code flags through typed options
  • Typed handlesStreamHandle (fluent .on().done() + for-await) and ChatHandle (multi-turn conversations)

Requirements

  • Node.js >= 18.0.0
  • Claude Code CLI installed and authenticated

Install

npm install @scottwalker/kraube-konnektor

CLI Setup

Bootstrap Claude Code on a fresh server with a single command:

npx @scottwalker/kraube-konnektor setup

The setup wizard will:

  1. Check Node.js version
  2. Install Claude Code globally (if not installed)
  3. Ask for a config directory (default: ~/.claude) — use different paths for isolated instances
  4. Ask for an HTTP proxy (optional) — for servers behind a proxy
  5. Run claude login for authentication
  6. Print a ready-to-use code example with your settings

Use --proxy to skip the interactive proxy prompt:

npx @scottwalker/kraube-konnektor setup --proxy "http://user:pass@host:port"

Each instance can have its own config directory and proxy — no global environment variables needed:

const claude = new Claude({
  model: 'sonnet',
  env: {
    CLAUDE_CONFIG_DIR: '/opt/my-project/.claude',
    HTTPS_PROXY: 'http://user:pass@host:port',
  },
})

Quick Start

import { Claude, PERMISSION_ACCEPT_EDITS } from '@scottwalker/kraube-konnektor'

const claude = new Claude({ permissionMode: PERMISSION_ACCEPT_EDITS })

// Simple query
const result = await claude.query('Find and fix bugs in auth.ts')
console.log(result.text)
console.log(result.sessionId)   // resume later
console.log(result.usage)       // { inputTokens, outputTokens }

Features

Custom CLI Path

Point to a specific Claude Code installation when multiple versions coexist:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  executable: '/opt/claude-code/v2/bin/claude',
  cwd: '/path/to/project',
})

Streaming

Real-time output as Claude works. stream() returns a StreamHandle — use the fluent .on().done() API or classic for-await:

import {
  Claude,
  EVENT_TEXT, EVENT_TOOL_USE, EVENT_RESULT, EVENT_ERROR,
} from '@scottwalker/kraube-konnektor'

const claude = new Claude()

// Fluent API (.on / .done)
const result = await claude
  .stream('Rewrite the auth module')
  .on(EVENT_TEXT, (e) => process.stdout.write(e.text))
  .on(EVENT_TOOL_USE, (e) => console.log(`[Tool] ${e.toolName}`))
  .on(EVENT_ERROR, (e) => console.error(e.message))
  .done()

console.log(`Done in ${result.durationMs}ms`)

// Classic for-await
const handle = claude.stream('Rewrite the auth module')

for await (const event of handle) {
  switch (event.type) {
    case EVENT_TEXT:
      process.stdout.write(event.text)
      break
    case EVENT_TOOL_USE:
      console.log(`[Tool] ${event.toolName}`)
      break
    case EVENT_RESULT:
      console.log(`\nDone in ${event.durationMs}ms`)
      break
    case EVENT_ERROR:
      console.error(event.message)
      break
  }
}

Multi-turn Sessions

Maintain conversation context across queries:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude()
const session = claude.session()
await session.query('Analyze the architecture of this project')
await session.query('Now refactor the auth module based on your analysis')
// ^ Claude remembers the previous context

// Resume a session later (even across process restarts)
const s2 = claude.session({ resume: session.sessionId! })
await s2.query('Continue where we left off')

// Fork a session (branch without modifying the original)
const s3 = claude.session({ resume: session.sessionId!, fork: true })

Structured Output

Get typed JSON responses via JSON Schema:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude()
const result = await claude.query('Extract all API endpoints from the codebase', {
  schema: {
    type: 'object',
    properties: {
      endpoints: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            method: { type: 'string' },
            path: { type: 'string' },
            handler: { type: 'string' },
          },
        },
      },
    },
  },
})
console.log(result.structured)
// { endpoints: [{ method: 'GET', path: '/api/users', handler: 'getUsers' }, ...] }

Parallel Execution

Run independent queries concurrently (each spawns a separate CLI process):

import { Claude, PERMISSION_PLAN } from '@scottwalker/kraube-konnektor'

const claude = new Claude()

const [bugs, tests, docs] = await claude.parallel([
  { prompt: 'Find bugs in src/', options: { cwd: './src' } },
  { prompt: 'Run the test suite', options: { allowedTools: ['Bash'] } },
  { prompt: 'Review documentation', options: { permissionMode: PERMISSION_PLAN } },
])

Recurring Tasks

Node.js-level equivalent of the /loop CLI command:

import { Claude, SCHED_RESULT, SCHED_ERROR } from '@scottwalker/kraube-konnektor'

const claude = new Claude()
const job = claude.loop('5m', 'Check CI pipeline status and report failures')

job.on(SCHED_RESULT, (result) => {
  console.log(`[${new Date().toISOString()}] ${result.text}`)
})

job.on(SCHED_ERROR, (err) => {
  console.error('Check failed:', err.message)
})

// Stop when no longer needed
job.stop()

Supported intervals: '30s', '5m', '2h', '1d', or raw milliseconds.

MCP Servers

Connect Model Context Protocol servers:

import { Claude } from '@scottwalker/kraube-konnektor'

// SDK mode (default) — inline definitions
const claude = new Claude({
  mcpServers: {
    playwright: {
      command: 'npx',
      args: ['@playwright/mcp@latest'],
    },
    database: {
      type: 'http',
      url: 'http://localhost:3001/mcp',
    },
  },
})

// CLI mode — config file path
const cliClaude = new Claude({
  useSdk: false,
  mcpConfig: './mcp.json',
})

Custom Subagents

Define specialized agents:

import { Claude, PERMISSION_ACCEPT_EDITS } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  agents: {
    reviewer: {
      description: 'Code review expert',
      model: 'haiku',
      tools: ['Read', 'Glob', 'Grep'],
      prompt: 'Review code for bugs, security issues, and style',
    },
    deployer: {
      description: 'Deployment automation agent',
      tools: ['Bash', 'Read'],
      permissionMode: PERMISSION_ACCEPT_EDITS,
    },
  },
})

Git Worktree Isolation

Run operations in an isolated copy of the repository:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude()
const result = await claude.query('Refactor the entire auth module', {
  worktree: 'refactor-auth',  // or `true` for auto-generated name
})

Piped Input

Pass data alongside the prompt (like echo data | claude -p "prompt"):

import { readFileSync } from 'node:fs'
import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude()
const result = await claude.query('Analyze this error log and suggest fixes', {
  input: readFileSync('./error.log', 'utf-8'),
})

Lifecycle Hooks

Attach hooks to tool execution:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  hooks: {
    PostToolUse: [
      {
        matcher: 'Edit|Write',
        hooks: [{ command: 'prettier --write ${file_path}' }],
      },
    ],
    PreToolUse: [
      {
        matcher: 'Bash',
        hooks: [{ command: './scripts/validate-command.sh', timeout: 5 }],
      },
    ],
  },
})

Programmatic Permissions

Control tool approval with a callback instead of static permission modes:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  canUseTool: async (toolName, input, { signal }) => {
    if (toolName === 'Bash' && String(input.command).includes('rm -rf'))
      return { behavior: 'deny', message: 'Dangerous command blocked' }
    return { behavior: 'allow' }
  },
})

In-Process MCP Tools

Define custom tools that run in-process — no external MCP server required:

import { Claude, createSdkMcpServer, sdkTool } from '@scottwalker/kraube-konnektor'
import { z } from 'zod/v4'

const server = await createSdkMcpServer({
  name: 'my-tools',
  tools: [
    await sdkTool('getUser', 'Get user by ID', { id: z.string() },
      async ({ id }) => ({
        content: [{ type: 'text', text: JSON.stringify({ name: 'Alice', role: 'admin' }) }],
      })
    ),
  ],
})
const claude = new Claude({ mcpServers: { myTools: server } })

JS Hook Callbacks

Subscribe to all 21 hook events with native JS callbacks (no shell commands):

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  hookCallbacks: {
    PreToolUse: [{
      matcher: 'Bash',
      hooks: [async (input) => {
        if (String(input.tool_input?.command).includes('sudo'))
          return { decision: 'block', reason: 'sudo not allowed' }
        return { continue: true }
      }],
    }],
    Notification: [{
      hooks: [async (input) => {
        console.log('Notification:', input.message)
        return {}
      }],
    }],
  },
})

Thinking Config

Control Claude's reasoning behavior:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({ thinking: { type: 'enabled' } })
// 'adaptive' — Claude decides | 'enabled' — always think | 'disabled' — no thinking

Runtime Control

Change model or permission mode during a session:

import { Claude, PERMISSION_PLAN } from '@scottwalker/kraube-konnektor'

const claude = new Claude({ model: 'sonnet' })
claude.setModel('opus')                       // switch to a more capable model
claude.setPermissionMode(PERMISSION_PLAN)     // tighten permissions mid-session

Dynamic MCP

Add, reconnect, or toggle MCP servers at runtime:

const claude = new Claude()
claude.setMcpServers({ db: { command: 'npx', args: ['@db/mcp'] } })
await claude.reconnectMcpServer('db')   // restart after config change
claude.toggleMcpServer('db', false)     // temporarily disable

File Checkpointing

Snapshot and restore files modified by Claude:

const claude = new Claude({ enableFileCheckpointing: true })
const result = await claude.query('Refactor the auth module')
// Something went wrong? Roll back all file changes:
await claude.rewindFiles(result.sessionId!)

Account & Model Info

Query your subscription info and available models:

const claude = new Claude()
const account = await claude.accountInfo()       // { plan, usage, limits }
const models = await claude.supportedModels()    // ['opus', 'sonnet', ...]
const agents = await claude.supportedAgents()    // available agent types

Per-Query Abort

Cancel individual queries with standard AbortSignal:

const controller = new AbortController()
setTimeout(() => controller.abort(), 30_000)     // 30s timeout

const result = await claude.query('Long analysis task', {
  signal: controller.signal,
})

Subagent Control

Monitor and stop spawned subagent tasks:

const handle = claude.stream('Run a full analysis')
  .on('task_started', (e) => console.log(`Subagent: ${e.taskId}`))
  .on('task_progress', (e) => console.log(`Progress: ${e.status}`))
  .on('task_notification', (e) => console.log(e.message))

// Stop a specific subagent
await claude.stopTask(taskId)

Settings & Plugins

Provide CLAUDE.md instructions, settings overrides, and plugins programmatically:

const claude = new Claude({
  settingSources: ['user', 'project'],
  settings: {
    permissions: { allow: ['Bash(npm test)', 'Read(*)'] },
  },
  plugins: [
    { type: 'local', path: './my-plugin' },
  ],
})

Custom Process Spawn

Override how Claude Code processes are created — useful for VMs, containers, or remote execution:

import { Claude } from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  spawnClaudeCodeProcess: (options) => {
    // options: { command, args, cwd, env, signal }
    // Run inside a Docker container instead of locally
    return spawn('docker', ['exec', 'my-sandbox', options.command, ...options.args], {
      env: options.env,
      cwd: options.cwd,
    })
  },
})

Session Utilities

List and inspect past sessions:

import { listSessions, getSessionMessages } from '@scottwalker/kraube-konnektor'

const sessions = await listSessions({ limit: 10 })                  // all session IDs + metadata
const messages = await getSessionMessages(sessions[0].sessionId)    // full message history

Full Configuration

All Claude Code CLI capabilities in one place:

import {
  Claude,
  EFFORT_HIGH, PERMISSION_ACCEPT_EDITS, PERMISSION_PLAN,
} from '@scottwalker/kraube-konnektor'

const claude = new Claude({
  // CLI binary
  executable: '/usr/local/bin/claude',
  cwd: '/path/to/project',

  // Model
  model: 'opus',                      // 'opus' | 'sonnet' | 'haiku' | full model ID
  effortLevel: EFFORT_HIGH,           // EFFORT_LOW | EFFORT_MEDIUM | EFFORT_HIGH | EFFORT_MAX
  fallbackModel: 'sonnet',            // auto-fallback on failure

  // Permissions
  permissionMode: PERMISSION_ACCEPT_EDITS,  // PERMISSION_DEFAULT | PERMISSION_ACCEPT_EDITS | PERMISSION_PLAN | PERMISSION_AUTO | PERMISSION_DONT_ASK | PERMISSION_BYPASS
  allowedTools: ['Read', 'Edit', 'Bash(npm run *)'],
  disallowedTools: ['WebFetch'],

  // Prompts
  systemPrompt: 'You are a senior TypeScript developer',
  appendSystemPrompt: 'Always write tests for new code',

  // Limits
  maxTurns: 10,                       // max agentic turns per query
  maxBudget: 5.0,                     // max USD per query

  // Directories
  additionalDirs: ['../shared-lib', '../proto'],

  // MCP (inline definitions for SDK mode)
  mcpServers: { /* ... */ },

  // Agents
  agents: { /* ... */ },

  // Hooks
  hooks: { /* ... */ },

  // Environment
  env: {
    MAX_THINKING_TOKENS: '50000',
    CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1',
  },

  // Session
  noSessionPersistence: true,         // for CI/automation
})

// Override any option per query
const result = await claude.query('Analyze this module', {
  model: 'haiku',                     // cheaper model for this query
  permissionMode: PERMISSION_PLAN,    // read-only
  maxTurns: 3,
})

Error Handling

All errors extend KraubeKonnektorError for uniform catching:

import {
  Claude,
  KraubeKonnektorError,
  CliNotFoundError,
  CliExecutionError,
  CliTimeoutError,
  ParseError,
  ValidationError,
} from '@scottwalker/kraube-konnektor'

const claude = new Claude()

try {
  const result = await claude.query('Fix the bug')
} catch (err) {
  if (err instanceof CliNotFoundError) {
    // Claude Code CLI not installed or wrong path
    console.error(`CLI not found: ${err.executable}`)
  } else if (err instanceof CliExecutionError) {
    // Non-zero exit code
    console.error(`Exit ${err.exitCode}: ${err.stderr}`)
  } else if (err instanceof CliTimeoutError) {
    // Exceeded timeout
    console.error(`Timed out after ${err.timeoutMs}ms`)
  } else if (err instanceof ParseError) {
    // Unexpected CLI output
    console.error(`Parse failed: ${err.rawOutput.slice(0, 100)}`)
  } else if (err instanceof KraubeKonnektorError) {
    // Any other library error
    console.error(err.message)
  }
}

Custom Executor

The IExecutor abstraction lets you swap the CLI backend for testing, mocking, or alternative transports:

import {
  Claude, EVENT_TEXT, EVENT_RESULT,
  type IExecutor, type ExecuteOptions, type QueryResult, type StreamEvent,
} from '@scottwalker/kraube-konnektor'

class MockExecutor implements IExecutor {
  async execute(args: readonly string[], options: ExecuteOptions): Promise<QueryResult> {
    return {
      text: 'Mocked response',
      sessionId: 'mock-session',
      usage: { inputTokens: 0, outputTokens: 0 },
      cost: null,
      durationMs: 0,
      messages: [],
      structured: null,
      raw: {},
    }
  }

  async *stream(args: readonly string[], options: ExecuteOptions): AsyncIterable<StreamEvent> {
    yield { type: EVENT_TEXT, text: 'Mocked stream' }
    yield {
      type: EVENT_RESULT,
      text: 'Mocked stream',
      sessionId: 'mock-session',
      usage: { inputTokens: 0, outputTokens: 0 },
      cost: null,
      durationMs: 0,
    }
  }
}

// Use in tests or with future backends
const claude = new Claude({ model: 'opus' }, new MockExecutor())

Architecture

┌──────────┐     ┌─────────────┐     ┌─────────────┐     ┌───────────────┐
│  Claude  │────>│ ArgsBuilder │────>│  IExecutor  │────>│ CLI Process   │
│ (facade) │     │             │     │ (abstract)  │     │ (claude -p)   │
└──────────┘     └─────────────┘     └─────────────┘     └───────────────┘
     │                                    ^
     v                                    |
  Session                          SdkExecutor (default, persistent session)
  Scheduler                        CliExecutor (useSdk: false, process-per-query)
  • Two modes — SDK (persistent session, fast) or CLI (process-per-query, simple)
  • Executor pattern — swap CLI for SDK/HTTP without touching consumer code
  • Immutable config — client options frozen at construction, per-query overrides are non-destructive

See docs/ARCHITECTURE.md for detailed design documentation.

Examples

| Example | Description | |---------|-------------| | examples/interactive-chat | Terminal chat — ask questions, get answers in real time | | examples/integration-test | Package integration test (mock executor) |

# Try the interactive chat:
cd examples/interactive-chat
npm install
npm start          # standard mode
npm run stream     # streaming mode (word by word)

Documentation

| Document | Description | |----------|-------------| | Architecture | Design principles, SOLID breakdown, data flow diagrams | | API Reference | Complete reference for all classes, methods, types, and options | | Examples | Comprehensive cookbook covering every feature with code snippets | | Changelog | Version history | | Contributing | Development setup and guidelines |

Development

git clone [email protected]:scott-walker/kraube-konnektor.git
cd kraube-konnektor
npm install

npm run build              # compile TypeScript
npm test                   # run unit tests
npm run test:integration   # build + run integration test
npm run typecheck           # type-check without emitting

License

MIT