npm package: @loombrain/cli
Description: CLI for LoomBrain — capture, search, and browse your personal knowledge graph
Latest version: 0.2.0

# @loombrain/cli

CLI for [LoomBrain](https://loombrain.com) — capture, search, and browse your personal knowledge graph from the terminal.

## Install

### Homebrew (macOS)

```bash
brew install amentistudio/tap/lb
```

### npm

```bash
npm install -g @loombrain/cli
```

## Authentication

### Interactive login (recommended)

```bash
lb login
```

Opens your browser for device code authentication. Follow the prompts to authorize the CLI.

### Token-based (CI / LLM agents)

Set the `LB_TOKEN` environment variable with an API key from your LoomBrain dashboard:

```bash
export LB_TOKEN=lb_sk_...
lb search "knowledge graphs"
```

## Commands

### `lb capture`

Capture a URL, piped content, or a quick note into your knowledge graph.

```bash
# Capture a URL
lb capture https://example.com/article --why "relevant to my research"

# Capture a note
lb capture --note "Meeting takeaway: switch to weekly releases" --why "process change"

# Pipe content from stdin
cat notes.md | lb capture --why "imported notes" --title "March notes"
```

**Options:**
- `--why` (required) — Why are you capturing this?
- `--note` — Quick note to capture (instead of URL/stdin)
- `--title` — Override the title
- `--type` — Content type: `article`, `tweet`, `repo`, `pdf`, `audio`, `note`, `video`, `image`
- `--json` — Output as JSON

### `lb search`

Search nodes in your knowledge graph.

```bash
lb search "distributed systems"
lb search --para "projects/my-project" --status active
lb search --tags "rust,wasm" --limit 5
```

**Options:**
- `--para` — Filter by PARA path
- `--status` — Filter by status
- `--tags` — Filter by tags (comma-separated)
- `--limit` — Results per page (default: 20)
- `--page` — Page number
- `--mode` — Search mode: `keyword`, `semantic`, or `hybrid`
- `--json` — Output as JSON

### `lb context`

Get session context nodes for a topic within a PARA path. Useful for loading relevant context into an LLM conversation.

```bash
lb context "API design" --para "projects/loombrain"
lb context "testing strategies" --para "areas/engineering" --limit 5
```

**Options:**
- `--para` (required) — PARA path to scope context
- `--limit` — Number of context nodes (default: 10)
- `--json` — Output as JSON

### `lb tree`

Browse your PARA tree.

```bash
lb tree
lb tree projects
lb tree areas --json
```

**Options:**
- Category filter: `projects`, `areas`, `resources`, `archive`
- `--status` — Filter by status: `active` or `archived`
- `--json` — Output as JSON

### `lb health`

Show graph health stats.

```bash
lb health
lb health --json
```

**Options:**
- `--json` — Output as JSON

### `lb add`

Add a node to your knowledge graph.

```bash
# Add with inline body
lb add "API Design Notes" --body "Key decisions from today's meeting" --tags "api,design" --why "architecture reference"

# Add from file
lb add "Weekly Report" --body-file report.md --para "projects/my-project"

# Pipe from stdin
cat notes.md | lb add "Meeting Notes" --why "team sync"
```

**Options:**
- `--body` — Node body content (markdown)
- `--body-file` — Read body from file path
- `--summary` — Node summary
- `--tags` — Tags (comma-separated)
- `--para` — PARA path (e.g. `projects/my-project`)
- `--why` — Why are you adding this node?
- `--json` — Output as JSON

### `lb related`

Show nodes related to a given node.

```bash
lb related abc123
lb related abc123 --limit 5 --json
```

**Options:**
- `--limit` — Max results to show (default: 20)
- `--json` — Output as JSON

### `lb para`

Manage PARA items (projects, areas, resources).

```bash
# Create a new project
lb para create --category projects --label "Kitchen Renovation"

# Archive a project
lb para archive <id>

# Restore an archived item
lb para unarchive <id>
```

**Subcommands:**
- `lb para create` — Create a PARA item
  - `--category` (required) — projects, areas, or resources
  - `--label` (required) — Item label
  - `--description` — Optional description
- `lb para archive <id>` — Archive a PARA item
- `lb para unarchive <id>` — Restore an archived item

All support `--json`.

### `lb retag`

Update PARA assignments for a node.

```bash
# Replace all PARA assignments
lb retag <node-id> --set "para-id-1,para-id-2"

# Add a PARA assignment (non-destructive)
lb retag <node-id> --add "para-id-3"

# Remove a PARA assignment (non-destructive)
lb retag <node-id> --remove "para-id-2"
```

**Options:**
- `--set` — Replace all PARA assignments (comma-separated IDs)
- `--add` — Add PARA assignments (comma-separated IDs)
- `--remove` — Remove PARA assignments (comma-separated IDs)
- `--json` — Output as JSON

Exactly one of `--set`, `--add`, or `--remove` must be provided. Use `--set ""` to clear all PARA assignments.

> **Note**: `--add` and `--remove` use a read-modify-write pattern and are not concurrency-safe. If multiple clients modify the same node simultaneously, the last write wins.

## JSON output

All commands support `--json` for machine-readable output — useful for piping into `jq`, scripting, or feeding context to LLMs:

```bash
lb search "rust" --json | jq '.[].title'
lb context "API design" --para "projects/lb" --json > context.json
```

## Global options

- `--api-url` — Override the API endpoint (default: `https://api.loombrain.com`)

## Requirements

- Node.js >= 18

## Development

### Architecture

The CLI is a **thin HTTP client over the API**. Each command validates arguments locally, calls the corresponding API endpoint via `ofetch`, and formats the response for the terminal. No business logic lives in the CLI — it's all in the API/services layer.

```
User → citty (arg parsing) → createApiClient (auth + HTTP) → API endpoint → format + print
```

Key libraries:

| Library | Purpose |
|---------|---------|
| [citty](https://github.com/unjs/citty) | Command definitions, arg parsing, subcommands |
| [ofetch](https://github.com/unjs/ofetch) | HTTP client with interceptors |
| [consola](https://github.com/unjs/consola) | Logging and user-facing messages |
| [@loombrain/shared](../../packages/shared/) | Shared types and Zod schemas |

### Adding a new command

1. Create `apps/cli/src/commands/<name>.ts`:

```ts
import { defineCommand } from "citty";
import { createApiClient } from "../lib/client";
import { handleError } from "../lib/output";

export const myCommand = defineCommand({
  meta: { name: "my-command", description: "What it does" },
  args: {
    json: { type: "boolean", description: "Output as JSON", default: false },
    "api-url": { type: "string", description: "API URL override" },
  },
  async run({ args }) {
    const jsonOutput = Boolean(args.json);
    let client;
    try {
      client = await createApiClient(args["api-url"] as string | undefined);
    } catch (err) {
      handleError(err, jsonOutput, "AUTH_REQUIRED");
      return;
    }
    const { fetch: apiFetch } = client;
    try {
      const result = await apiFetch<MyResponseType>("/your-endpoint");
      process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
    } catch (err) {
      handleError(err, jsonOutput, "API_ERROR");
    }
  },
});
```

2. Register in `apps/cli/src/main.ts`:

```ts
import { myCommand } from "./commands/my-command";
// Add to subCommands:
subCommands: { ..., myCommand },
```

3. Add a formatter in `apps/cli/src/lib/output.ts` if the command needs human-readable output.

4. Add tests in `apps/cli/src/__tests__/` — mock `createApiClient` at the boundary, verify the correct endpoint is called with correct params.

### Testing

```bash
bun run test --filter=cli     # Run CLI tests only
bun run test                  # Run all workspace tests
```

Tests mock `ofetch` and `createApiClient` at the module boundary. No real API calls are made. See `apps/cli/src/__tests__/commands.test.ts` for the pattern.

### Local development

```bash
# Start the API locally (required — CLI calls the API)
bun run dev --filter=api

# Point CLI at local API
LB_API_URL=http://localhost:8787 node apps/cli/dist/index.js search "test"

# Or build + run
bun run build --filter=cli
LB_API_URL=http://localhost:8787 node apps/cli/dist/index.js health
```

### Prerequisites

- [Bun](https://bun.sh) (package manager + runtime)
- Node.js >= 18