Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/node_modules
/bun.lockb
.DS_Store
.context/
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ bee mcp serve-http [--port N] # local HTTP, 127.0.0.1, bearer-token auth
Every read, search, and manage capability below is exposed as an MCP tool (`bee_search`, `bee_list_facts`, `bee_get_daily_summary`, …), so the CLI and your assistant work from the same data.

## Use Bee with AI Agents (CLI)

If your prefer CLI for your Agent over MCP, the CLI provides structured JSON output, typed exit codes, command discovery, and pre-validation:

```bash
export BEE_OUTPUT_FORMAT=json
bee --describe # discover all commands + parameters as JSON
bee validate facts list # pre-check before executing
bee facts list # structured JSON on stdout, errors on stderr
```

Exit codes: `0` success, `2` auth, `3` bad args, `4` network, `5` rate-limited.

See **[docs/AGENT_USAGE.md](docs/AGENT_USAGE.md)** for the full agent protocol, command reference, and copy-paste prompts.

## Installation

Install from npm:
Expand Down
13 changes: 13 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
95 changes: 49 additions & 46 deletions bun.lock

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions docs/AGENT_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Bee CLI — Agent Instructions

Instructions for AI agents to use bee-cli autonomously via shell.

## Quick Start (copy-paste to any agent)

```
You have access to the Bee CLI for managing personal AI data.

Environment:
export BEE_OUTPUT_FORMAT=json

Discover all commands:
bee --describe

Pre-validate before writes:
bee validate <command>

Execute:
bee <command> [subcommand] [--flags]

Exit codes: 0=success, 2=auth, 3=bad args, 4=network, 5=rate-limit
On error, stderr contains: {"error":"...","code":N,"recoverable":bool,"suggestion":"..."}
```

## Installation

```bash
npm install -g @beeai/cli
bee login
```

Or from source:

```bash
git clone https://github.com/bee-computer/bee-cli.git
cd bee-cli
bun install
bun run build
./dist/bee login
```

## Protocol

1. Run `bee --describe` once to learn available commands and their parameters
2. Run `bee validate <command>` before any create/update/delete
3. Parse stdout as JSON on exit code 0
4. On non-zero exit, read stderr for structured error JSON

## Exit Codes

| Code | Meaning | Agent Action |
|------|---------|-------------|
| 0 | Success | Parse stdout JSON |
| 1 | General error | Report to user |
| 2 | Auth error | Run `bee login` |
| 3 | Invalid arguments | Fix flags, retry |
| 4 | Network/API error | Retry with backoff |
| 5 | Rate limited | Wait 60s, retry |

## Commands

### Read (no side effects)

| Command | Description |
|---------|-------------|
| `bee facts list [--limit N] [--cursor C]` | List personal facts |
| `bee facts get <id>` | Get a single fact |
| `bee todos list [--limit N] [--cursor C]` | List todos |
| `bee todos get <id>` | Get a single todo |
| `bee conversations list [--limit N] [--cursor C]` | List conversations |
| `bee conversations get <id>` | Get conversation detail |
| `bee conversations transcript <id>` | Get full transcript |
| `bee daily list [--limit N] [--cursor C]` | List daily summaries |
| `bee daily get <id>` | Get daily summary detail |
| `bee journals list [--limit N] [--cursor C]` | List journal entries |
| `bee journals get <id>` | Get a journal entry |
| `bee search --query <text>` | Search across data |
| `bee today` | Today's brief |
| `bee now` | Recent conversations (last 10 hours) |
| `bee changed [--cursor C]` | Changes since last check |
| `bee activity [--limit N]` | Recent activity |
| `bee insights list [--limit N]` | AI insights |
| `bee locations [--limit N]` | Location history |
| `bee photos [--limit N]` | Photos with descriptions |
| `bee me` | User profile |
| `bee status --json` | Auth and connection status |

### Write (validate first)

| Command | Description |
|---------|-------------|
| `bee facts create --text <text>` | Create a fact |
| `bee facts update <id> --text <text>` | Update a fact |
| `bee facts delete <id>` | Delete a fact |
| `bee todos create --text <text>` | Create a todo |
| `bee todos update <id> [--text T] [--completed true\|false]` | Update a todo |
| `bee todos delete <id>` | Delete a todo |

### Utility

| Command | Description |
|---------|-------------|
| `bee --describe` | Full command schema with parameters (JSON) |
| `bee validate <command>` | Pre-validate without executing |
| `bee sync --output <dir>` | Export to markdown files |
| `bee stream [--json]` | Real-time event stream (SSE) |
| `bee mcp` | Start MCP server (alternative agent interface) |
| `bee dashboard` | Interactive TUI (humans only) |

## Pagination

List commands return `next_cursor` in the response. Pass it back:
```bash
bee facts list --cursor "cursor_value_here"
```

## Error Recovery

```
Exit 2 (auth) → run bee login (needs human), then retry
Exit 3 (args) → fix command flags, retry immediately
Exit 4 (network) → exponential backoff: 1s, 2s, 4s, max 3 retries
Exit 5 (rate) → wait 60s, then retry
Exit 1 (general) → log and report to user
```

## Alternative: MCP Protocol

For agents that support MCP natively, use `bee mcp` to start an MCP server instead of shell commands. MCP provides richer tool schemas and structured responses at the protocol level.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@
"prepack": "bun run build:lib && bun run build:all",
"postinstall": "node ./scripts/postinstall.js",
"release": "bash ./scripts/release.sh",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"lint": "biome check sources/",
"format": "biome format --write sources/"
},
"dependencies": {
"date-fns": "^4.1.0",
"handlebars": "^4.7.8",
"qrcode": "^1.5.4",
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/qrcode": "^1.5.5",
"bun-types": "^1.3.8",
"typescript": "^5.7.3"
Expand Down
7 changes: 4 additions & 3 deletions sources/commands/changed/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Command, CommandContext } from "@/commands/types";
import { ValidationError } from "@/errors";
import { printJson, requestClientJson } from "@/client/clientApi";
import {
formatDateValue,
Expand Down Expand Up @@ -103,22 +104,22 @@ function parseChangedArgs(args: readonly string[]): ChangedOptions {
if (arg === "--cursor") {
const value = args[i + 1];
if (value === undefined) {
throw new Error("--cursor requires a value");
throw new ValidationError("--cursor requires a value");
}
cursor = value;
i += 1;
continue;
}

if (arg.startsWith("-")) {
throw new Error(`Unknown option: ${arg}`);
throw new ValidationError(`Unknown option: ${arg}`);
}

positionals.push(arg);
}

if (positionals.length > 0) {
throw new Error(`Unexpected arguments: ${positionals.join(" ")}`);
throw new ValidationError(`Unexpected arguments: ${positionals.join(" ")}`);
}

const options: ChangedOptions = {};
Expand Down
13 changes: 7 additions & 6 deletions sources/commands/login/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Command, CommandContext } from "@/commands/types";
import { ValidationError } from "@/errors";
import type { Environment } from "@/environment";
import { createDeveloperClient, createProxyClient } from "@/client";
import {
Expand Down Expand Up @@ -125,7 +126,7 @@ async function handleLogin(
}

if (!token) {
throw new Error("Missing token.");
throw new ValidationError("Missing token.");
}

token = token.trim();
Expand Down Expand Up @@ -189,7 +190,7 @@ export function parseLoginArgs(args: readonly string[]): LoginOptions {
if (arg === "--token") {
const value = args[i + 1];
if (value === undefined) {
throw new Error("--token requires a value");
throw new ValidationError("--token requires a value");
}
token = value;
i += 1;
Expand All @@ -204,7 +205,7 @@ export function parseLoginArgs(args: readonly string[]): LoginOptions {
if (arg === "--proxy") {
const value = args[i + 1];
if (value === undefined) {
throw new Error("--proxy requires a value");
throw new ValidationError("--proxy requires a value");
}
proxy = value;
i += 1;
Expand All @@ -222,14 +223,14 @@ export function parseLoginArgs(args: readonly string[]): LoginOptions {
}

if (arg.startsWith("-")) {
throw new Error(`Unknown option: ${arg}`);
throw new ValidationError(`Unknown option: ${arg}`);
}

positionals.push(arg);
}

if (positionals.length > 0) {
throw new Error(`Unexpected arguments: ${positionals.join(" ")}`);
throw new ValidationError(`Unexpected arguments: ${positionals.join(" ")}`);
}

if (token && tokenStdin) {
Expand Down Expand Up @@ -335,7 +336,7 @@ export async function validateProxyConnection(

async function readTokenFromStdin(): Promise<string> {
if (process.stdin.isTTY) {
throw new Error("--token-stdin requires input via stdin.");
throw new ValidationError("--token-stdin requires input via stdin.");
}

const chunks: string[] = [];
Expand Down
3 changes: 2 additions & 1 deletion sources/commands/mcp/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Command } from "@/commands/types";
import { ValidationError } from "@/errors";
import { serveMcpHttp, type McpHttpOptions } from "@/mcp/httpServer";
import { serveMcp } from "@/mcp/server";
import {
Expand Down Expand Up @@ -31,7 +32,7 @@ export const mcpCommand: Command = {
run: async (args, context) => {
const [subcommand, ...remaining] = args;
if (!subcommand) {
throw new Error("Missing MCP subcommand.");
throw new ValidationError("Missing MCP subcommand.");
}

if (subcommand === "serve") {
Expand Down
31 changes: 16 additions & 15 deletions sources/commands/stream/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Command, CommandContext } from "@/commands/types";
import { requireClientToken } from "@/client/clientApi";
import Handlebars from "handlebars";

const SUPPORTED_EVENT_TYPES = [
// Conversations
Expand Down Expand Up @@ -291,16 +290,19 @@ type WebhookPayload = {

type WebhookConfig = {
endpoint: string;
template: Handlebars.TemplateDelegate<WebhookPayload>;
bodyTemplate: string;
};

function renderTemplate(template: string, vars: Record<string, string>): string {
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
}

function buildWebhook(options: StreamOptions): WebhookConfig | null {
if (!options.webhookEndpoint || !options.webhookBody) {
return null;
}

const template = Handlebars.compile(options.webhookBody, { noEscape: true });
return { endpoint: options.webhookEndpoint, template };
return { endpoint: options.webhookEndpoint, bodyTemplate: options.webhookBody };
}

async function handleEvent(
Expand Down Expand Up @@ -385,21 +387,19 @@ async function sendWebhook(
webhook: WebhookConfig,
payload: WebhookPayload
): Promise<void> {
let body: string;
try {
body = webhook.template(payload);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`Webhook template failed: ${message}`);
return;
}
const vars: Record<string, string> = {
message: payload.message,
agentMessage: payload.agentMessage,
event: payload.event,
timestamp: payload.timestamp,
raw: payload.raw,
};
const body = renderTemplate(webhook.bodyTemplate, vars);

try {
const response = await fetch(webhook.endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
headers: { "Content-Type": "application/json" },
body,
});

Expand All @@ -413,6 +413,7 @@ async function sendWebhook(
}
}


function formatEvent(
eventType: string,
data: Record<string, unknown>,
Expand Down
Loading