diff --git a/Makefile b/Makefile index 01c6968a9..cc750c0bc 100644 --- a/Makefile +++ b/Makefile @@ -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: @@ -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= - Create a paired worktree at .claude/worktrees/ (lobu + submodule on real branch, .env copied, ports auto-assigned, Lobu context registered)" @echo " make task-clean NAME= [FORCE=1] - Remove the worktree, both branches, and the Lobu context (refuses if there's uncommitted/unpushed work unless FORCE=1)" @@ -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 ...HEAD` (BASE defaults to main; override with BASE= diff --git a/packages/cli/src/commands/_lib/apply/desired-state.ts b/packages/cli/src/commands/_lib/apply/desired-state.ts index 22f55ab61..a695cc4e2 100644 --- a/packages/cli/src/commands/_lib/apply/desired-state.ts +++ b/packages/cli/src/commands/_lib/apply/desired-state.ts @@ -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, @@ -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 = {}; + 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 ( diff --git a/packages/cli/src/templates/AGENTS.md.tmpl b/packages/cli/src/templates/AGENTS.md.tmpl index 1938ac98b..a9de3cfb9 100644 --- a/packages/cli/src/templates/AGENTS.md.tmpl +++ b/packages/cli/src/templates/AGENTS.md.tmpl @@ -64,7 +64,8 @@ const agent = defineAgent({ name: "{{PROJECT_NAME}}", description: "", 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/")], // network: { allowed: ["api.example.com"] }, // egress allowlist diff --git a/skills/lobu/SKILL.md b/skills/lobu/SKILL.md index 30fbc5ca4..2672b2d86 100644 --- a/skills/lobu/SKILL.md +++ b/skills/lobu/SKILL.md @@ -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 -cd -``` - -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. -- **`.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("./.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. -- **`.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: "./.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": ""}' - ``` -4. Show the user the memory event row from step 3, plus the admin UI at `http://localhost:8787//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