Skip to content
Merged
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
15 changes: 14 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Development Makefile for Lobu

.PHONY: help setup build test clean dev build-packages ensure-submodule clean-workers test-unit test-integration test-e2e test-e2e-sdk test-e2e-cli typecheck task-setup task-clean e2e-browser bump review
.PHONY: help setup build test clean dev build-packages ensure-submodule clean-workers clean-test-pg test-unit test-integration test-e2e test-e2e-sdk test-e2e-cli typecheck task-setup task-clean e2e-browser bump review

# Default target
help:
Expand All @@ -14,6 +14,7 @@ help:
@echo " make test-e2e - Boot the dev server + run openclaw-plugin e2e against it"
@echo " make test-e2e-cli - Boot lobu run + walk every CLI command (the CI cli-smoke gate)"
@echo " make clean-workers - Stop any running embedded worker subprocesses"
@echo " make clean-test-pg - Reap orphaned lobu-test-pg embedded-Postgres clusters (frees macOS shm slots)"
@echo " make typecheck - Strict typecheck (same as Dockerfile) for server + owletto"
@echo " make task-setup NAME=<name> - Create a paired worktree at .claude/worktrees/<name> (lobu + submodule on real branch, .env copied, ports auto-assigned, Lobu context registered)"
@echo " make task-clean NAME=<name> [FORCE=1] - Remove the worktree, both branches, and the Lobu context (refuses if there's uncommitted/unpushed work unless FORCE=1)"
Expand Down Expand Up @@ -165,6 +166,18 @@ clean-workers:
@pkill -f '@lobu/worker' 2>/dev/null || true
@echo "✅ Worker subprocesses stopped"

# Orphaned `lobu-test-pg-*` embedded-Postgres clusters from other worktrees'
# integration runs eat macOS shared-memory slots (SHMMNI=32), and `lobu run` /
# `make review`'s integration suite then fail with "could not create shared
# memory segment: No space left on device" (shmget). Reap them.
clean-test-pg:
@echo "🧹 Reaping orphaned lobu-test-pg embedded-Postgres clusters..."
@pkill -f 'lobu-test-pg' 2>/dev/null || true
@pkill -f '@embedded-postgres' 2>/dev/null || true
@sleep 1
@echo "shm segments now: $$(ipcs -m 2>/dev/null | awk '/^m/{c++} END{print c+0}') / 32"
@echo "✅ Test-PG clusters reaped"

# --- Local AI review gate ---------------------------------------------------
# Local-only: runs the deterministic suites in cwd, then invokes pi against
# `git diff <BASE>...HEAD` (BASE defaults to main; override with BASE=<branch>
Expand Down
32 changes: 19 additions & 13 deletions packages/cli/src/commands/_lib/apply/desired-state.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, readFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
import { pathToFileURL } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
import type {
ConnectorAuthSchema,
ConnectorDefinition,
Expand Down Expand Up @@ -871,23 +871,29 @@ export async function loadProjectConfig(
throw new ValidationError(`No lobu.config.ts found in ${cwd}`);
}
const { createJiti } = await import("jiti");
const jiti = createJiti(pathToFileURL(configPath).href);
// Resolve the SDK imports the config will reference (`@lobu/cli/config`,
// `@lobu/connector-sdk`) against the running CLI's own copies — not the
// project's `node_modules`. This lets a freshly-scaffolded project
// `validate`/`run` with zero install: the user has the CLI, that's enough.
// Falls through silently if a symbol can't be resolved from here (the
// catch-all error below still surfaces real problems).
const alias: Record<string, string> = {};
for (const spec of ["@lobu/cli/config", "@lobu/connector-sdk"]) {
try {
alias[spec] = fileURLToPath(import.meta.resolve(spec));
} catch {
// CLI dist may not expose this symbol in some packaging layouts; skip.
}
}
const jiti = createJiti(
pathToFileURL(configPath).href,
Object.keys(alias).length > 0 ? { alias } : undefined
);
let project: unknown;
try {
project = await jiti.import(configPath, { default: true });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
// A fresh `lobu init` writes package.json declaring @lobu/cli but doesn't
// install — jiti then can't resolve the import. Point the user at the fix
// instead of surfacing a raw module-resolution error.
if (
/@lobu\/(cli|connector-sdk)/.test(message) &&
!existsSync(resolve(cwd, "node_modules"))
) {
throw new ValidationError(
`Failed to load lobu.config.ts — its @lobu/cli/config import can't be resolved because dependencies aren't installed. Run \`bun install\` (or npm/pnpm install) in ${cwd} first.`
);
}
throw new ValidationError(`Failed to load lobu.config.ts — ${message}`);
}
if (
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/templates/AGENTS.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ const agent = defineAgent({
name: "{{PROJECT_NAME}}",
description: "<one sentence: what it does>",
providers: [
{ id: "anthropic", model: "anthropic/claude-...", key: secret("ANTHROPIC_API_KEY") },
// init prewrites this from --provider; canonical ids include `claude`, `openai`, `z-ai`.
{ id: "claude", model: "claude-sonnet-4-20250514", key: secret("ANTHROPIC_API_KEY") },
],
// skills: [skillFromFile("./skills/<name>")],
// network: { allowed: ["api.example.com"] }, // egress allowlist
Expand Down
72 changes: 3 additions & 69 deletions skills/lobu/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,13 @@
---
name: lobu
description: Scaffold a new Lobu agent project from a user interview, then build, run, and maintain it, including lobu.config.ts, prompt files, local skills, evals, providers, connections, and Lobu memory workflows.
description: Work with an existing Lobu project (run, validate, evaluate, connect) and with Lobu memory from your coding agent. Covers MCP client setup, OpenClaw memory plugin, knowledge search/save, watchers, and browser-authenticated connectors. To scaffold a NEW project run "npx @lobu/cli@latest init"; the AGENTS.md it generates is the config-API guide.
---

# Lobu

Use this skill when the user wants to scaffold a new Lobu agent (no existing project), or when they're working on an existing Lobu project — running, validating, evaluating, or connecting one. Also use it for persistent Lobu memory, MCP client setup, OpenClaw memory plugin configuration, knowledge search/save workflows, watchers, and browser-authenticated connectors.
Use this skill when working with an existing Lobu project — running, validating, evaluating, or connecting one — or with Lobu memory from a coding agent: MCP client setup, OpenClaw memory plugin configuration, knowledge search/save workflows, watchers, and browser-authenticated connectors.

If no `lobu.config.ts` exists in the current working directory, treat the user as a first-time user and run the "First-Time Setup" flow below. Otherwise jump straight to "Core Model" + the relevant reference section.

## First-Time Setup

The user has installed this skill and wants a working Lobu agent end-to-end. Run the four phases below in order. Pause at every real decision and ask the user — do not fake credentials, do not guess. Cite specific docs/files when you do not know something.

### Phase 1 — Environment

Verify the host can run Lobu before writing any code:

- Node.js 22.x–24.x. Reject Node 25+ (it breaks `isolated-vm`, which the worker depends on). If missing, instruct the user to install via `nvm`, `fnm`, or `mise`.
- Bun (used to run the CLI from npm). Install via `curl -fsSL https://bun.sh/install | bash` if missing.
- A reachable Postgres with the `pgvector` extension. If the user does not have one, point them at `docker run -d --name lobu-pg -p 5432:5432 -e POSTGRES_PASSWORD=lobu pgvector/pgvector:pg16` or Neon/Supabase.
- One LLM provider API key. Anthropic (`ANTHROPIC_API_KEY`), OpenAI (`OPENAI_API_KEY`), or Z.ai (`ZAI_API_KEY`). Ask the user which they have; do not pick for them.

Capture `DATABASE_URL` and the provider key in the user's response — they will go into `.env` in Phase 3.

### Phase 2 — Interview

Ask short, concrete questions one at a time. Wait for the answer before asking the next. Do not batch questions.

1. **What is the agent for?** One sentence. (Example: "Triage support emails and draft replies for the on-call engineer.")
2. **Who uses it?** Just you, your team, or each of your customers (multi-tenant)?
3. **What does it need to remember?** Translate the answer into 1–3 entity types. (Example: "support tickets, customers, recurring issues.")
4. **Where does the data come from?** One source to start. (Slack, Gmail, GitHub, Linear, Stripe, a custom webhook, a CSV — pick one.) If the user names a source Lobu does not ship a bundled connector for, plan to write a custom `connector.ts`.
5. **Where do people talk to it?** Slack, Telegram, Discord, MS Teams, WhatsApp, web (HTTP API), or MCP-only?
6. **What should it do on a schedule, if anything?** For v1, propose at most one "dreaming" watcher (e.g. "every morning at 8am, cluster yesterday's tickets by root cause"). Skip if the user does not need one.

### Phase 3 — Scaffold

Based on the interview answers, run:

```bash
npx @lobu/cli@latest init <agent-name>
cd <agent-name>
```

The CLI generates the directory layout, including `lobu.config.ts`, `package.json`, and `tsconfig.json`. All authoring is TypeScript: import `defineConfig`, `defineAgent`, `defineEntityType`, `defineRelationshipType`, `defineWatcher`, `defineConnection`, `defineAuthProfile`, and `secret` from `@lobu/cli/config`. Read `examples/lobu-crm/lobu.config.ts` in the lobu repo for a complete, working reference before editing. Then edit:

- **`lobu.config.ts`** — set the agent name + description from question 1 on `defineAgent`; add the chosen provider with `providers: [{ id, model, key: secret("X_API_KEY") }]`; set `org` / `orgName` in `defineConfig` from a slug of the user's choice.
- **`.env`** — fill in `DATABASE_URL` and the provider API key from Phase 1.
- **Entity types** — declare the entity types from question 3 with `defineEntityType({ key, name, properties })` and list them in `defineConfig({ entities: [...] })`. Each property is a JSON Schema fragment; add `"x-table-label"` / `"x-table-column": true` to surface a column in the admin UI.
- **`<name>.connector.ts`** — only if the source from question 4 is not a bundled connector. Model it on `examples/lobu-crm/funnel-form.connector.ts` in the lobu repo, then list it with `connectorFromFile("./<name>.connector.ts")` in `defineConfig({ connectors: [...] })`.
- **Watchers** — add one watcher with `defineWatcher({ agent, slug, prompt, extractionSchema, schedule? })` and list it in `defineConfig({ watchers: [...] })`. Use the cron `schedule` from question 6 if the user wants one.
- **`<name>.reaction.ts`** — only if the watcher needs to call actions after extracting (post to Slack, update an entity, etc.). Point the watcher at it with `reaction: "./<name>.reaction.ts"`. Default (no `reaction`) just writes the extracted data to memory.

Then boot:

```bash
npx @lobu/cli@latest run
```

The CLI starts the embedded gateway + worker on `http://localhost:8787`.

### Phase 4 — Verify

Confirm the agent works end-to-end before declaring done:

1. Send a test message via the chosen channel from question 5 (or the local web UI at `http://localhost:8787` if MCP-only).
2. Confirm the agent replies.
3. If you wired a connector, trigger a real event (or post a synthetic one) and confirm the watcher fired:
```bash
npx @lobu/cli@latest memory run search_memory '{"query": "<something the watcher would have extracted>"}'
```
4. Show the user the memory event row from step 3, plus the admin UI at `http://localhost:8787/<org>/events` so they can see the structured record.

If anything fails, do not silently move on — surface the error, propose a fix, and only continue once the user confirms.
To scaffold a NEW project, run `npx @lobu/cli@latest init`. The `AGENTS.md` it writes into the project is the source of truth for the config API (the `define*` helpers, connectors, auth, watchers, memory) — this skill does not duplicate it. For an existing project, jump to "Core Model" + the relevant reference section below.

## Core Model

Expand Down
Loading