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

@factory/cli

v0.55.2

Published

Factory Droid CLI - AI-powered software engineering agent

Downloads

167

Readme

Factory CLI - Demo Edit

A hybrid command-line interface that runs either:

  1. Interactive TUI – a full-screen React/Ink terminal app
  2. Headless commands – traditional droid headless <command> sub-commands powered by Commander

The entry-point (src/index.ts) detects how it was invoked and chooses the right mode automatically.


1. Overview of the Hybrid Architecture

src/
├── index.ts          # Hybrid entry – mode detection
│
├── app.tsx           # React/Ink TUI (interactive mode)
│
└── commands/         # Commander commands (headless mode)
    ├── droid.ts
    └── login.ts

No positional args ➜ Interactive TUI
headless subcommand ➜ Headless mode


2 · Local Development

All dev tasks are exposed as npm scripts – never run compiled .js files directly.

| Purpose | Command | | -------------------------------- | --------------------- | | Start CLI (auto mode) | npm start | | Start with Node inspector | npm run debug | | Set up shell alias for local dev | npm run setup:alias | | Build SEA for current platform | npm run build | | Build and run the binary | npm run build:run | | Lint source | npm run lint | | Type-check | npm run typecheck | | Run tests | npm test | | Produce executable bundle | npm run bundle | | Clean build artifacts | npm run clean |

The start/debug scripts use tsx so you can edit TypeScript and restart instantly.

Shell Alias Setup

For easier local development, you can set up a droid alias that runs the CLI from your current directory:

# Add alias to ~/.zshrc (checks if already exists)
npm run setup:alias

# Start a new terminal or source the file
source ~/.zshrc

# Now you can run 'droid' from anywhere
droid

Building Standalone Executables (SEA)

The CLI can be compiled into Single Executable Applications (SEA) for different platforms:

# Build for your current platform automatically
npm run build

# Build and immediately run the binary
npm run build:run

# Build for specific platforms
npm run build:sea:mac:arm64      # macOS Apple Silicon
npm run build:sea:mac:x64        # macOS Intel
npm run build:sea:linux:arm64    # Linux ARM64
npm run build:sea:linux:x64      # Linux x64
npm run build:sea:windows:x64    # Windows x64

The build script automatically detects your OS and architecture, then builds the appropriate binary to dist/{platform}/{arch}/droid (or droid.exe on Windows).


3 · Testing Both Modes Locally

Interactive TUI

# Launch interactive UI
npm start

You'll see a colourful Ink interface; quit with Ctrl-C.

Running in VSCode

Factory CLI can also be run inside VSCode using the Factory extension:

  1. First, install the Factory VSCode extension (see VSCode Extension README for installation instructions)
  2. Click the Run Factory button (🤖) in the editor toolbar to launch Factory CLI in a dedicated terminal
  3. The extension provides full VSCode context (open files, selections, diagnostics) to Factory via MCP

Headless Commands

# Show global help
npm start -- --help

# Show headless subcommands
npm start -- headless --help

# Run login interactively (headless)
npm start -- headless login

# Send message to a droid
npm start -- headless droid "Hello, Droid!" --session-id <sessionId>

The extra -- after npm start passes subsequent flags to the CLI.


4 · Development vs Production

| Phase | Command(s) | Result | | ----------- | ------------------------------------ | ---------------------------------------------- | | Dev | npm start / npm run debug | Runs from TS sources with tsx, fast reload. | | Bundle | npm run bundle (calls build) | Generates single executable bundle/droid.js. | | Publish | npm publish (bundled in prepare) | Users install droid binary from npm. |

During CI the prepare script produces the bundle automatically.


5 · Examples

Headless examples

# Show authentication status
droid headless status

# Authenticate (opens browser)
droid headless login

# Talk to Droid
droid headless "Hello" --session-id dOLpXUI8ux6YdZrg3kCs

# Streaming input mode
# Send multiple messages over stdin, maintaining conversation state
echo '{"role":"user","content":"hello"}' | droid exec --input-format stream-json --output-format stream-json

Streaming Input Mode (Droid Manager Integration)

The CLI supports a streaming input mode designed for Droid Manager integration, allowing a single process to handle multiple user messages while maintaining conversation state.

Usage Pattern:

// Spawn process once
const droid = spawn('droid', [
  'exec',
  '--input-format',
  'stream-json',
  '--output-format',
  'stream-json',
  '--auto',
  'low',
]);

// Send messages over time (JSONL format via stdin)
droid.stdin.write('{"role":"user","content":"hello"}\n');

// Read events from stdout (JSONL format)
droid.stdout.on('data', (chunk) => {
  const events = chunk.toString().split('\n').filter(Boolean);
  events.forEach((line) => {
    const event = JSON.parse(line);
    // Handle: user messages, assistant responses, tool calls, etc.
  });
});

// Send another message later
droid.stdin.write('{"role":"user","content":"what can you do?"}\n');

// Close stdin when done to trigger graceful shutdown
droid.stdin.end();

Key Points:

  • Input: JSONL (newline-delimited JSON) via stdin, each line is Anthropic.MessageParam with role: "user"
  • Output: JSONL events via stdout (same format as --output-format stream-json)
  • stdin stays open until explicitly closed by the caller
  • Messages processed sequentially - one at a time
  • Session persists across all messages in the process lifetime
  • Graceful shutdown when stdin closes

Interactive example

# Simply run with no args
droid

6 · Testing the Production droid Command

Sometimes you need to test the exact binary users will get from npm install -g factory-cli.
Follow this workflow:

# 1. Build optimised bundle (also compiles TS → JS)
npm run bundle

# 2. Link globally so `droid` is on your PATH
npm link

# 3. Use it anywhere
droid --help
droid headless status
droid headless "Hello" --session-id <sessionId>

# 4. (Optional) Un-link when finished
npm unlink -g factory-cli

| Situation | Command to use | | --------------------------- | --------------------------------------------------------- | | Fast iteration / TypeScript | npm start -- <args> | | Debug with inspector | npm run debug -- <args> | | Validate production bundle | npm run bundle && npm link then droid headless <args> |

ℹ️ Tip: The extra -- after npm start or npm run debug passes the remaining flags directly to the CLI.


7 · ESM & Imports

The package is "type": "module"; all runtime imports use .js extensions even though the source is TypeScript. The build pipeline rewrites them automatically.


8 · Troubleshooting

| Problem | Fix | | --------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | EACCES when running droid | Ensure the bundle is executable (chmod +x bundle/droid.js). npm run bundle handles this automatically. | | module not found after rename | Run npm run clean && npm run bundle to rebuild from scratch. | | Global command still points to old code | Run npm unlink -g factory-cli && npm link to refresh the symlink. |


9 · Logging

Factory CLI has two different logging behaviors depending on the execution mode:

Interactive Mode Logging

When running in interactive TUI mode (droid with no arguments), all logging output is redirected to files to avoid interfering with the clean React/Ink interface:

  • Log Directory: ~/.factory/logs/
  • Log Files: droid-log-<timestamp>.log (e.g., droid-log-2025-01-15T10-30-45-123Z.log)
  • Content: All logInfo, logException, and logWarn calls are written to the timestamped log file
  • Format: [timestamp] LEVEL: message | Context: {...}

Example log file location:

~/.factory/logs/droid-log-2025-01-15T10-30-45-123Z.log

Example log entry:

[2025-01-15T10:30:45.123Z] INFO: User started interactive session
[2025-01-15T10:30:47.456Z] ERROR: Failed to initialize MCP client: Connection refused | Context: {"retry": 1}

Headless Mode Logging

When running headless commands (droid headless <command>), logging follows standard console output patterns:

  • Log Output: Directly to stdout/stderr using the standard @factory/logging package
  • Integration: Works with existing telemetry, Sentry, and monitoring systems
  • Format: Standard Factory logging format with metadata support

Accessing Logs

Interactive Mode Logs:

# View the most recent log file
ls -la ~/.factory/logs/

# Tail the logs in real-time (find the most recent file)
tail -f ~/.factory/logs/droid-log-*.log

# View logs from a specific session
cat ~/.factory/logs/droid-log-2025-01-15T10-30-45-123Z.log

Headless Mode Logs:

# Logs appear directly in terminal output
droid headless "test message" --session-id abc123

# Redirect to file if needed
droid headless "test" --session-id abc123 2>&1 | tee my-session.log

Log Cleanup

Interactive mode creates a new log file for each session. To manage disk space:

# Remove logs older than 7 days
find ~/.factory/logs -name "droid-log-*.log" -mtime +7 -delete

# View total log directory size
du -sh ~/.factory/logs

10 · Tool Registry & Executors Design

🔄 What Changed

After: Dynamic mapping automatically discovers tools from TUI registry

// Tools are automatically discovered from TUI registry
const toolMapping = buildToolMapping();

🛠 How to Add New Tools

  1. Create executor in src/tools/executors/client/ (implement ClientToolExecutor)
  2. Register in TUI registry in src/tools/tui.ts:
    getTUIToolRegistry().register({
      tool: myNewCliTool, // from @factory/droid-core/tools/definitions
      executorFactory: () => new MyNewExecutor(),
    });
  3. That's it! Tool is automatically available in executeTool() using its llmId

11 · E2E Testing of the TUI

The Factory CLI includes end-to-end tests for the terminal UI using Microsoft's @microsoft/tui-test.

Testing Framework

  • Framework: @microsoft/tui-test
  • Shell: bash
  • Config: apps/cli/tui-test.config.ts

Prerequisites

The E2E tests require a valid FACTORY_API_KEY environment variable to authenticate with Factory services. You can generate an API key from https://dev.app.factory.ai/settings/api-keys.

export FACTORY_API_KEY="your-api-key-here"
npm run test:e2e

The tests launch the CLI via a wrapper script that configures the environment:

  • program: ./e2e-tests/test-workspace/run-droid.sh
  • The wrapper exports all required env vars (e.g., FACTORY_HOME_OVERRIDE) and ensures correct execution context for tests.
  • rows/columns: 30x80

Test Layout

apps/cli/
├── e2e-tests/
│   ├── chat-input.test.ts        # Text entry, deletion, Ctrl+C, file suggestions, cursor nav
│   ├── settings.template.json    # Copied to each test's .factory-dev
│   ├── templates/                # Blueprint copied to each test workspace
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── src/
│   └── test-workspace/           # Contains wrapper scripts only
│       ├── run-droid.sh          # Creates unique workspace per invocation
│       ├── run-droid.ps1         # Windows version
│       └── run-droid.cmd         # Windows CMD wrapper
└── tui-test.config.ts            # TUI test config using the wrapper

Running Tests

# From apps/cli
npm run test:e2e                 # bundles CLI then runs all e2e tests

# Run a specific test file (or append :line)
npm run test:e2e e2e-tests/chat-input.test.ts

# Tests automatically create isolated workspaces - no setup needed!
# Each test invocation gets its own /tmp/droid-test-* directory

Debugging

At any point of time, you can access the serialized terminal view using:

const terminalView = terminal.serialize().view;

Tracing is enabled in config (trace: true). Traces are saved under tui-traces/.

# Replay a trace
npm run show-trace tui-traces/<trace-file>
# Or directly
npx @microsoft/tui-test show-trace tui-traces/<trace-file>

Test Environment Isolation

Each test case runs in complete isolation with its own workspace and .factory-dev directory:

  • Per-test workspaces: The wrapper scripts (run-droid.sh, run-droid.ps1) automatically create a unique temporary workspace for each test invocation
  • Unique identifiers: Workspaces are named with PID and timestamp: /tmp/droid-test-{pid}-{timestamp}-XXXXXX
  • Template copying: Files from e2e-tests/templates/ (package.json, tsconfig.json, src/) are copied to each test workspace
  • Automatic cleanup: Temporary workspaces are cleaned up when tests complete (via trap on Unix, try/finally on Windows)
  • Parallel execution: Tests can run in parallel without state interference since each has its own .factory-dev

Settings template (copied to each test's .factory-dev/settings.json):

{
  "model": "claude-sonnet-4-20250514",
  "cloudSessionSync": false,
  "diffMode": "github"
}

What’s Covered

Current suite validates:

  • Text entry visibility
  • Backspace/Delete behavior (including forward delete)
  • Ctrl+C warning and double Ctrl+C exit
  • File suggestions via @: show, filter, navigate, select, dismiss
  • Cursor navigation and in-line editing

Writing New Tests

Global config already provides the program and env. You can optionally tweak terminal size per test file:

import { test, expect } from '@microsoft/tui-test';

test.use({ rows: 24, columns: 100 });

test('example', async ({ terminal }) => {
  await expect(
    terminal.getByText('standing in an open terminal', {
      full: false,
      strict: false,
    })
  ).toBeVisible({ timeout: 10000 });
  terminal.write('Hello');
  await expect(terminal.getByText('Hello')).toBeVisible();
});

12 · Contributing

  1. pnpm install (or npm install) at repo root
  2. cd apps/cli
  3. Implement feature / fix
  4. Ensure npm run lint && npm run typecheck && npm test pass
  5. Run E2E tests with npm run test:e2e
  6. Commit & open PR 🚀