@llamaventures/cli 1.14.1 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_BRIEFING.md +28 -3
- package/CHANGELOG.md +30 -0
- package/README.md +26 -4
- package/README.zh-CN.md +25 -4
- package/bin/llama-mcp.mjs +97 -6
- package/bin/llama.mjs +113 -6
- package/package.json +1 -1
- package/scripts/verify-agent-routing.mjs +142 -1
package/AGENT_BRIEFING.md
CHANGED
|
@@ -20,6 +20,17 @@ Most teammates don't know everything this CLI can do. Part of your job is to sur
|
|
|
20
20
|
- **Point at `llama --help`** for the full surface rather than reciting it. The CLI uses progressive help: `llama --help` is a short overview, `llama <area> --help` drills in.
|
|
21
21
|
- **Stay current.** If you suspect the CLI is stale, run `llama version --check`; if it reports an upgrade, tell the user the one-line `npm i -g @llamaventures/cli@latest` command. Don't nag repeatedly.
|
|
22
22
|
|
|
23
|
+
## Runtime skill library
|
|
24
|
+
|
|
25
|
+
This npm package is public, but Llama OS skills are private. Do not assume the skill text is bundled locally. For team-token sessions, discover the live runtime library through Llama Command:
|
|
26
|
+
|
|
27
|
+
- Start with `llama agent bootstrap` or MCP `agent_bootstrap` when you need the current Command + Llama OS contract.
|
|
28
|
+
- Use `llama skills search "<task>"` or MCP `skills_search` before choosing a Llama workflow.
|
|
29
|
+
- Use `llama skills show <slug>` or MCP `skills_read` only for the relevant skill.
|
|
30
|
+
- Use `llama explain <command-url-or-object>` or MCP `object_inspect` for 404s, deleted wiki pages, notifier links, deal URLs, and unknown Command objects before telling the user "the system is broken."
|
|
31
|
+
|
|
32
|
+
The boundary matters: public CLI/MCP discovers skills, but authenticated Command decides which skill content the token may read.
|
|
33
|
+
|
|
23
34
|
## Pipeline First (hard rule)
|
|
24
35
|
|
|
25
36
|
Any time the user mentions a company name or founder name:
|
|
@@ -27,6 +38,9 @@ Any time the user mentions a company name or founder name:
|
|
|
27
38
|
1. **Run `llama deal search "<name>"` BEFORE web search.** Always. No exceptions.
|
|
28
39
|
2. If pipeline has it → pull the data, integrate into your reply silently.
|
|
29
40
|
3. If pipeline doesn't have it → ask once: "New name. Add to pipeline? (Y/n)". On yes, `llama deal create`.
|
|
41
|
+
- Use `--status Outreached` when we only contacted/logged the company and have no response or effective relationship yet.
|
|
42
|
+
- Use `--status Sourced` only once there is a response, intro, meeting, or another real relationship signal.
|
|
43
|
+
- Also set `--source-direction Inbound` if the deal came into the firm; set `--source-direction Outbound` if Llama found/listed/reached out first.
|
|
30
44
|
4. If user gives you new facts (status / valuation / founder note) → `llama deal update` immediately, tell the user **one line** afterward.
|
|
31
45
|
|
|
32
46
|
Don't:
|
|
@@ -190,12 +204,20 @@ llama deal show <dealId>
|
|
|
190
204
|
llama deal list [--owner ...] [--status ...]
|
|
191
205
|
|
|
192
206
|
# Pipeline — write
|
|
193
|
-
llama deal create "Company" --description "..."
|
|
207
|
+
llama deal create "Company" --description "..." --source-direction Outbound --status Outreached
|
|
208
|
+
llama deal create "Company" --description "..." --source-direction Inbound --status Sourced
|
|
194
209
|
llama deal update <dealId> <field> <value>
|
|
195
|
-
# writable: status theirStage stage notes dealOwner source description website
|
|
210
|
+
# writable: status theirStage stage notes dealOwner source sourceDirection description website
|
|
196
211
|
# location founders proposedAmount roundSize valuation sector subsector
|
|
197
212
|
# foundedYear leadInvestor investors (each write logs a deal_events row)
|
|
198
213
|
|
|
214
|
+
# Our Stage vocabulary starts with:
|
|
215
|
+
# Outreached → Sourced → First Meeting → Diligence → Partner Meeting → Term Sheet → Invested
|
|
216
|
+
# `Outreached` is relationship memory only. Do not inflate it to `Sourced`
|
|
217
|
+
# unless a real relationship signal exists.
|
|
218
|
+
# `sourceDirection` is separate: Inbound = came into the firm; Outbound =
|
|
219
|
+
# we found/listed/reached out first.
|
|
220
|
+
|
|
199
221
|
# Brief blocks
|
|
200
222
|
llama brief blocks <dealId>
|
|
201
223
|
llama brief add-text <dealId> --heading "..." --body "..."
|
|
@@ -259,7 +281,7 @@ Run `llama --help` for the full surface (~40 commands).
|
|
|
259
281
|
|
|
260
282
|
## MCP-native agents
|
|
261
283
|
|
|
262
|
-
If you support [MCP](https://modelcontextprotocol.io), **prefer the MCP server over parsing CLI output.** The same package ships `llama-mcp` (
|
|
284
|
+
If you support [MCP](https://modelcontextprotocol.io), **prefer the MCP server over parsing CLI output.** The same package ships `llama-mcp` (56 typed tools, identical auth chain).
|
|
263
285
|
|
|
264
286
|
Add to your MCP client config (Claude Desktop / Claude Code / Cursor / OpenClaw / Codex / etc.):
|
|
265
287
|
|
|
@@ -270,6 +292,9 @@ Add to your MCP client config (Claude Desktop / Claude Code / Cursor / OpenClaw
|
|
|
270
292
|
Tools available:
|
|
271
293
|
|
|
272
294
|
- `auth_status` — verify creds + identity (call first if anything 401s)
|
|
295
|
+
- `agent_bootstrap` — fetch the live Command + Llama OS runtime manifest
|
|
296
|
+
- `skills_search` / `skills_read` — discover and read authenticated runtime skills
|
|
297
|
+
- `object_inspect` — explain Command URLs, 404s, deleted objects, and lifecycle trail
|
|
273
298
|
- `deal_search` / `deal_show` / `deal_create` / `deal_update`
|
|
274
299
|
- `brief_blocks` / `brief_add_text` / `brief_add_link` / `brief_add_callout`
|
|
275
300
|
- `wiki_search` / `wiki_save` (accepts `content_type: 'markdown' | 'html'` — HTML entries render as full-viewport sandboxed iframe at `/wiki/<slug>`) / `wiki_delete` / `wiki_restore` (soft-delete, reversible)
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,36 @@ this project adheres to [Semantic Versioning](https://semver.org).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.15.1] — 2026-06-16
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Documented the new `Outreached` Our Stage across CLI, MCP, and agent
|
|
13
|
+
briefing surfaces. Agents should use `Outreached` for logged outreach
|
|
14
|
+
without a response/effective relationship, and reserve `Sourced` for real
|
|
15
|
+
relationship signals.
|
|
16
|
+
- Added `sourceDirection` / `--source-direction` guidance across CLI, MCP, and
|
|
17
|
+
agent briefing surfaces. Use `Inbound` for deals that came into the firm and
|
|
18
|
+
`Outbound` for companies Llama found/listed/reached out to first.
|
|
19
|
+
|
|
20
|
+
## [1.15.0] — 2026-06-15
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **`llama agent bootstrap`** — fetches the live Llama Command + Llama OS
|
|
24
|
+
runtime manifest, including the authenticated skill bundle metadata and
|
|
25
|
+
object-inspection contract.
|
|
26
|
+
- **`llama skills search|show`** — discovers and reads runtime Llama OS skills
|
|
27
|
+
from Llama Command. The public npm package does not bundle private skill
|
|
28
|
+
content; Command returns only what the authenticated token may see.
|
|
29
|
+
- **`llama explain <url-or-object>`** — asks Llama Command to explain URLs,
|
|
30
|
+
deleted objects, 404s, and lifecycle history before an agent guesses that
|
|
31
|
+
the system is broken.
|
|
32
|
+
- MCP parity tools: **`agent_bootstrap`**, **`skills_search`**,
|
|
33
|
+
**`skills_read`**, and **`object_inspect`**.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- `AGENT_BRIEFING.md` now teaches agents to use the authenticated runtime
|
|
37
|
+
skill gateway instead of assuming a local private `llama-os` checkout.
|
|
38
|
+
|
|
9
39
|
## [1.14.1] — 2026-06-15
|
|
10
40
|
|
|
11
41
|
### Added
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
```
|
|
49
49
|
@llamaventures/cli
|
|
50
50
|
├── bin/llama interactive CLI for humans + bash
|
|
51
|
-
└── bin/llama-mcp stdio MCP server,
|
|
51
|
+
└── bin/llama-mcp stdio MCP server, 56 typed tools — for any MCP-native agent
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Both binaries share `lib/client.mjs` — the **same** auth chain, **same** HTTP
|
|
@@ -180,11 +180,14 @@ llama token show
|
|
|
180
180
|
|
|
181
181
|
# Pipeline — read
|
|
182
182
|
llama deal search "acme ai"
|
|
183
|
+
llama deal list --owner alex --status Outreached
|
|
184
|
+
llama deal list --source-direction Outbound --status Outreached
|
|
183
185
|
llama deal list --owner alex --status Diligence
|
|
184
186
|
llama deal show <dealId>
|
|
185
187
|
|
|
186
188
|
# Pipeline — write
|
|
187
|
-
llama deal create "Acme AI" --description "..." --source Gavin
|
|
189
|
+
llama deal create "Acme AI" --description "..." --source Gavin --source-direction Outbound --status Outreached
|
|
190
|
+
llama deal create "Acme AI" --description "..." --source Gavin --source-direction Inbound --status Sourced
|
|
188
191
|
llama deal update <dealId> status Diligence
|
|
189
192
|
llama deal enrich <dealId> --dry-run
|
|
190
193
|
llama deal enrich <dealId> --apply --executor server_agent
|
|
@@ -193,6 +196,13 @@ llama deal agent run <dealId> --message "collect founder evidence and update typ
|
|
|
193
196
|
llama deal delete <dealId> # soft (audit-logged)
|
|
194
197
|
llama deal restore <dealId>
|
|
195
198
|
|
|
199
|
+
# Status semantics
|
|
200
|
+
# Outreached = contact/logged, but no response or effective relationship yet.
|
|
201
|
+
# Sourced = response, intro, meeting, or another real relationship signal exists.
|
|
202
|
+
# sourceDirection is separate:
|
|
203
|
+
# Inbound = came into the firm.
|
|
204
|
+
# Outbound = we found/listed/reached out first.
|
|
205
|
+
|
|
196
206
|
# Deal Brief — ordered, typed blocks (text · link · embed · callout)
|
|
197
207
|
llama brief blocks <dealId>
|
|
198
208
|
llama brief add-text <dealId> --heading "..." --body "..."
|
|
@@ -211,6 +221,12 @@ llama approvals decide <approvalId> approved --note "..."
|
|
|
211
221
|
llama timeline <dealId>
|
|
212
222
|
llama post <dealId> "message body" [--link url]
|
|
213
223
|
|
|
224
|
+
# Agent runtime — live Command + private Llama OS skill gateway
|
|
225
|
+
llama agent bootstrap
|
|
226
|
+
llama skills search "wiki delete tombstone"
|
|
227
|
+
llama skills show llama-command
|
|
228
|
+
llama explain https://command.llamaventures.vc/wiki/some-page
|
|
229
|
+
|
|
214
230
|
# Wiki
|
|
215
231
|
llama wiki search "<query>"
|
|
216
232
|
llama wiki read <slug> [--lang en|zh]
|
|
@@ -249,13 +265,14 @@ agents can pattern-match without parsing prose.
|
|
|
249
265
|
## MCP server
|
|
250
266
|
|
|
251
267
|
The bundled `llama-mcp` is a **stdio Model Context Protocol** server exposing
|
|
252
|
-
**
|
|
268
|
+
**56 typed tools** that mirror the most-used CLI surface. Every tool is named
|
|
253
269
|
and scoped — there is no generic API passthrough, by design (a public-package
|
|
254
270
|
escape hatch reachable from a prompt-injectable agent context is exactly the
|
|
255
271
|
shape we want to avoid).
|
|
256
272
|
|
|
257
273
|
Coverage is grouped around the workflows agents actually need: auth
|
|
258
|
-
diagnostics;
|
|
274
|
+
diagnostics; live agent bootstrap; authenticated Llama OS skill search/read;
|
|
275
|
+
Command URL/object inspection; deal search/show/create/update/feed; server-side deal agent runs;
|
|
259
276
|
deal enrichment harnesses;
|
|
260
277
|
trust-rated facts; brief blocks and version history; wiki read/write/delete/restore;
|
|
261
278
|
timeline posts and mentions; skill corrections; refresh triggers; external pitch intake;
|
|
@@ -277,6 +294,11 @@ The `agent_briefing` MCP **prompt** also returns
|
|
|
277
294
|
[`AGENT_BRIEFING.md`](AGENT_BRIEFING.md) verbatim, so any new agent loading the
|
|
278
295
|
server can self-onboard without leaving the protocol.
|
|
279
296
|
|
|
297
|
+
For current Llama OS skills, use the runtime tools instead of looking for a
|
|
298
|
+
local private repo: `agent_bootstrap`, `skills_search`, `skills_read`, and
|
|
299
|
+
`object_inspect`. The public npm package does not bundle private skill text;
|
|
300
|
+
Command returns only the content visible to the authenticated token.
|
|
301
|
+
|
|
280
302
|
### Wire into your agent
|
|
281
303
|
|
|
282
304
|
<details open>
|
package/README.zh-CN.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
```
|
|
49
49
|
@llamaventures/cli
|
|
50
50
|
├── bin/llama 给人 + bash 用的交互式 CLI
|
|
51
|
-
└── bin/llama-mcp 给 MCP 原生 agent 用的 stdio MCP server,
|
|
51
|
+
└── bin/llama-mcp 给 MCP 原生 agent 用的 stdio MCP server,56 个工具
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
两个 binary 共享 `lib/client.mjs`——**同一**认证链、**同一** HTTP 客户端、
|
|
@@ -201,11 +201,14 @@ llama token show
|
|
|
201
201
|
|
|
202
202
|
# Pipeline——读
|
|
203
203
|
llama deal search "acme ai"
|
|
204
|
+
llama deal list --owner alex --status Outreached
|
|
205
|
+
llama deal list --source-direction Outbound --status Outreached
|
|
204
206
|
llama deal list --owner alex --status Diligence
|
|
205
207
|
llama deal show <dealId>
|
|
206
208
|
|
|
207
209
|
# Pipeline——写
|
|
208
|
-
llama deal create "Acme AI" --description "..." --source Gavin
|
|
210
|
+
llama deal create "Acme AI" --description "..." --source Gavin --source-direction Outbound --status Outreached
|
|
211
|
+
llama deal create "Acme AI" --description "..." --source Gavin --source-direction Inbound --status Sourced
|
|
209
212
|
llama deal update <dealId> status Diligence
|
|
210
213
|
llama deal enrich <dealId> --dry-run
|
|
211
214
|
llama deal enrich <dealId> --apply --executor server_agent
|
|
@@ -214,6 +217,13 @@ llama deal agent run <dealId> --message "collect founder evidence and update typ
|
|
|
214
217
|
llama deal delete <dealId> # 软删除(审计日志记录)
|
|
215
218
|
llama deal restore <dealId>
|
|
216
219
|
|
|
220
|
+
# Status 语义
|
|
221
|
+
# Outreached = 只是联系/记录了,还没有回复或有效关系。
|
|
222
|
+
# Sourced = 已有回复、intro、会议,或其它真实关系信号。
|
|
223
|
+
# sourceDirection 是单独维度:
|
|
224
|
+
# Inbound = 自然流入公司。
|
|
225
|
+
# Outbound = 我们主动发现/建名单/触达。
|
|
226
|
+
|
|
217
227
|
# Deal Brief——有序的、有类型的 block(text · link · embed · callout)
|
|
218
228
|
llama brief blocks <dealId>
|
|
219
229
|
llama brief add-text <dealId> --heading "..." --body "..."
|
|
@@ -232,6 +242,12 @@ llama approvals decide <approvalId> approved --note "..."
|
|
|
232
242
|
llama timeline <dealId>
|
|
233
243
|
llama post <dealId> "消息内容" [--link url]
|
|
234
244
|
|
|
245
|
+
# Agent runtime——Command 上的 Llama OS skill gateway
|
|
246
|
+
llama agent bootstrap
|
|
247
|
+
llama skills search "wiki delete tombstone"
|
|
248
|
+
llama skills show llama-command
|
|
249
|
+
llama explain https://command.llamaventures.vc/wiki/some-page
|
|
250
|
+
|
|
235
251
|
# Wiki
|
|
236
252
|
llama wiki search "<query>"
|
|
237
253
|
llama wiki save <slug> --title "..." --content "..."
|
|
@@ -261,11 +277,12 @@ MCP server 在 `isError: true` 内容里返回相同的前缀,agent 不用解
|
|
|
261
277
|
## MCP server
|
|
262
278
|
|
|
263
279
|
随包发布的 `llama-mcp` 是一个 **stdio Model Context Protocol** server,
|
|
264
|
-
暴露 **
|
|
280
|
+
暴露 **56 个 typed tools**——基本镜像 CLI 最常用的命令。每个 tool 都是
|
|
265
281
|
具名、scoped 的;**没有**通用的 API passthrough,这是有意设计的(公开
|
|
266
282
|
package 里一个能被 prompt-injection 触达的逃生通道,正是我们要避开的形状)。
|
|
267
283
|
|
|
268
|
-
覆盖面按 agent 真正会用的工作流分组:auth 诊断;
|
|
284
|
+
覆盖面按 agent 真正会用的工作流分组:auth 诊断;live agent bootstrap;
|
|
285
|
+
授权后的 Llama OS skill search/read;Command URL/object inspect;deal search/show/create/
|
|
269
286
|
update/feed;服务端 deal agent run;deal enrichment harness;带 trust ladder 的事实;brief blocks
|
|
270
287
|
和版本历史;wiki 读写删恢复;timeline posts 和 mentions;skill corrections;
|
|
271
288
|
refresh triggers;外部 pitch intake;memo show/regenerate/save/reset;
|
|
@@ -286,6 +303,10 @@ printf '%s\n' \
|
|
|
286
303
|
[`AGENT_BRIEFING.md`](AGENT_BRIEFING.md)——刚装上 server 的 agent
|
|
287
304
|
不用离开协议就能给自己 onboard。
|
|
288
305
|
|
|
306
|
+
要读当前 Llama OS skills,用 runtime tools:`agent_bootstrap`、
|
|
307
|
+
`skills_search`、`skills_read`、`object_inspect`。公开 npm 包不打包私有
|
|
308
|
+
skill 正文;Command 只按当前 token 的权限返回可见内容。
|
|
309
|
+
|
|
289
310
|
### 接到你的 agent 上
|
|
290
311
|
|
|
291
312
|
<details open>
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -195,6 +195,82 @@ server.registerTool(
|
|
|
195
195
|
}
|
|
196
196
|
);
|
|
197
197
|
|
|
198
|
+
server.registerTool(
|
|
199
|
+
"agent_bootstrap",
|
|
200
|
+
{
|
|
201
|
+
description:
|
|
202
|
+
"Fetch the live Llama Command + Llama OS runtime manifest. Use this at " +
|
|
203
|
+
"the start of an agent session to discover current skills, the skill " +
|
|
204
|
+
"bundle version, and the object-inspection contract. Unlike the bundled " +
|
|
205
|
+
"agent_briefing prompt, this comes from authenticated Command runtime.",
|
|
206
|
+
inputSchema: {
|
|
207
|
+
limit: z.number().optional().describe("number of skill summaries to include; default 25"),
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
async ({ limit } = {}) => {
|
|
211
|
+
const params = new URLSearchParams();
|
|
212
|
+
if (limit) params.set("limit", String(limit));
|
|
213
|
+
return callApi("GET", `/api/agent/manifest${params.toString() ? `?${params}` : ""}`);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
server.registerTool(
|
|
218
|
+
"skills_search",
|
|
219
|
+
{
|
|
220
|
+
description:
|
|
221
|
+
"Search the authenticated Llama OS runtime skill library. Call this " +
|
|
222
|
+
"before choosing a workflow for Llama pipeline/wiki/DD/research/ops tasks. " +
|
|
223
|
+
"Returns summaries only; call skills_read for the exact SKILL.md.",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
q: z.string().describe("workflow/task query, e.g. 'wiki delete tombstone' or 'deal DD memo'"),
|
|
226
|
+
limit: z.number().optional().describe("default 20"),
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
async ({ q, limit }) => {
|
|
230
|
+
const params = new URLSearchParams({ q });
|
|
231
|
+
if (limit) params.set("limit", String(limit));
|
|
232
|
+
return callApi("GET", `/api/agent/skills?${params}`);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
server.registerTool(
|
|
237
|
+
"skills_read",
|
|
238
|
+
{
|
|
239
|
+
description:
|
|
240
|
+
"Read one runtime Llama OS skill by slug. Use after skills_search. " +
|
|
241
|
+
"Returns the full SKILL.md content from Llama Command; public npm does " +
|
|
242
|
+
"not bundle private skill text.",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
slug: z.string().describe("skill slug, e.g. llama-command or llama-wiki"),
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
async ({ slug }) => callApi("GET", `/api/agent/skills/${encodeURIComponent(slug)}`)
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
server.registerTool(
|
|
251
|
+
"object_inspect",
|
|
252
|
+
{
|
|
253
|
+
description:
|
|
254
|
+
"Explain a Llama Command URL or object id. Use for 404s, deleted wiki " +
|
|
255
|
+
"pages, notifier links, deal URLs, brief blocks, HTML docs, and unknown " +
|
|
256
|
+
"Command objects before guessing that the system is broken.",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
q: z.string().optional().describe("URL or compact query, e.g. wiki:my-slug or a Command URL"),
|
|
259
|
+
type: z.string().optional().describe("explicit object type if not using q"),
|
|
260
|
+
id: z.string().optional().describe("explicit object id if not using q"),
|
|
261
|
+
lang: z.enum(["en", "zh"]).optional().describe("wiki language; default en"),
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
async ({ q, type, id, lang } = {}) => {
|
|
265
|
+
const params = new URLSearchParams();
|
|
266
|
+
if (q) params.set("q", q);
|
|
267
|
+
if (type) params.set("type", type);
|
|
268
|
+
if (id) params.set("id", id);
|
|
269
|
+
if (lang) params.set("lang", lang);
|
|
270
|
+
return callApi("GET", `/api/agent/explain?${params}`);
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
|
|
198
274
|
// ============================================================
|
|
199
275
|
// Deals — read
|
|
200
276
|
// ============================================================
|
|
@@ -204,7 +280,7 @@ server.registerTool(
|
|
|
204
280
|
{
|
|
205
281
|
description:
|
|
206
282
|
"Search the Llama Ventures deal pipeline. Fuzzy match on company name, " +
|
|
207
|
-
"founders, description, founder info, notes, deal owner, source, and location. " +
|
|
283
|
+
"founders, description, founder info, notes, deal owner, source, source direction, and location. " +
|
|
208
284
|
"Returns up to `limit` deals (default 200, cap 1000).",
|
|
209
285
|
inputSchema: {
|
|
210
286
|
q: z.string().optional().describe("fuzzy search query across all text fields"),
|
|
@@ -215,7 +291,7 @@ server.registerTool(
|
|
|
215
291
|
.string()
|
|
216
292
|
.optional()
|
|
217
293
|
.describe(
|
|
218
|
-
"exact match on 'Our Stage' (Sourced, First Meeting, Diligence, Partner Meeting, Term Sheet, Invested, Passed, Stalled, Future, Unknown)"
|
|
294
|
+
"exact match on 'Our Stage' (Outreached, Sourced, First Meeting, Diligence, Partner Meeting, Term Sheet, Invested, Passed, Stalled, Future, Unknown). Outreached means contact was logged but no effective relationship/response exists yet."
|
|
219
295
|
),
|
|
220
296
|
theirStage: z.string().optional().describe("exact match on 'Their Stage'"),
|
|
221
297
|
stage: z
|
|
@@ -224,6 +300,10 @@ server.registerTool(
|
|
|
224
300
|
.describe(
|
|
225
301
|
"exact match on Round (Pre-Seed, Seed, Series A, Series B, Series C+, Stealth)"
|
|
226
302
|
),
|
|
303
|
+
sourceDirection: z
|
|
304
|
+
.string()
|
|
305
|
+
.optional()
|
|
306
|
+
.describe("exact match on source direction: Inbound, Outbound, or Unknown"),
|
|
227
307
|
limit: z.number().optional().describe("max results (default 200, cap 1000)"),
|
|
228
308
|
offset: z.number().optional(),
|
|
229
309
|
},
|
|
@@ -242,7 +322,7 @@ server.registerTool(
|
|
|
242
322
|
{
|
|
243
323
|
description:
|
|
244
324
|
"Get the full canonical record for one deal by uuid. Includes status, " +
|
|
245
|
-
"stage, founders, owner, source, valuation, all whitelisted writable fields, " +
|
|
325
|
+
"stage, founders, owner, source, sourceDirection, valuation, all whitelisted writable fields, " +
|
|
246
326
|
"and the `extra` JSONB blob.",
|
|
247
327
|
inputSchema: {
|
|
248
328
|
dealId: z.string().describe("deal uuid"),
|
|
@@ -272,8 +352,17 @@ server.registerTool(
|
|
|
272
352
|
.string()
|
|
273
353
|
.optional()
|
|
274
354
|
.describe("Pre-Seed | Seed | Series A | Series B | Series C+ | Stealth"),
|
|
275
|
-
status: z
|
|
355
|
+
status: z
|
|
356
|
+
.string()
|
|
357
|
+
.optional()
|
|
358
|
+
.describe(
|
|
359
|
+
"Our Stage workflow position. Use Outreached when we only contacted/logged them and have no response/effective relationship; use Sourced only once there is a response, intro, meeting, or other real relationship signal."
|
|
360
|
+
),
|
|
276
361
|
source: z.string().optional().describe("free-form sourced-by; recommend nominating a user"),
|
|
362
|
+
sourceDirection: z
|
|
363
|
+
.string()
|
|
364
|
+
.optional()
|
|
365
|
+
.describe("Separate direction tag: Inbound if the deal came into the firm; Outbound if Llama found/listed/reached out first; Unknown if unclear."),
|
|
277
366
|
notes: z.string().optional(),
|
|
278
367
|
location: z.string().optional(),
|
|
279
368
|
},
|
|
@@ -286,7 +375,7 @@ server.registerTool(
|
|
|
286
375
|
{
|
|
287
376
|
description:
|
|
288
377
|
"Update a single whitelisted field on a deal. Writable fields: status, theirStage, " +
|
|
289
|
-
"notes, stage, dealOwner, source, description, website, location, founders, " +
|
|
378
|
+
"notes, stage, dealOwner, source, sourceDirection, description, website, location, founders, " +
|
|
290
379
|
"proposedAmount, roundSize, valuation, sector, subsector, foundedYear, leadInvestor, " +
|
|
291
380
|
"investors. Logs a field_change event in deal_events.",
|
|
292
381
|
inputSchema: {
|
|
@@ -1501,7 +1590,9 @@ server.registerPrompt(
|
|
|
1501
1590
|
"contract: identity, Pipeline First rule, content capture, autonomy " +
|
|
1502
1591
|
"levels (L0/L1/L2/L3), communication style, error recovery, CLI/MCP " +
|
|
1503
1592
|
"reference, and boundaries. Read this once, internalise it, operate " +
|
|
1504
|
-
"accordingly. Same content as `llama agent-onboard` from the CLI."
|
|
1593
|
+
"accordingly. Same content as `llama agent-onboard` from the CLI. " +
|
|
1594
|
+
"For the live private Llama OS skill library, call agent_bootstrap, " +
|
|
1595
|
+
"skills_search, and skills_read.",
|
|
1505
1596
|
},
|
|
1506
1597
|
async () => {
|
|
1507
1598
|
// Gate the briefing behind a /api/me check so unauthenticated MCP
|
package/bin/llama.mjs
CHANGED
|
@@ -141,7 +141,7 @@ function clientSideMatch(deal, filters) {
|
|
|
141
141
|
const fields = [
|
|
142
142
|
deal.companyName, deal.founders, deal.founderInfo,
|
|
143
143
|
deal.description, deal.notes, deal.dealOwner,
|
|
144
|
-
deal.source, deal.location,
|
|
144
|
+
deal.source, deal.sourceDirection, deal.location,
|
|
145
145
|
];
|
|
146
146
|
if (!fields.some((f) => incl(f, filters.q))) return false;
|
|
147
147
|
}
|
|
@@ -151,6 +151,7 @@ function clientSideMatch(deal, filters) {
|
|
|
151
151
|
if (filters.status && !eq(deal.status, filters.status)) return false;
|
|
152
152
|
if (filters.theirStage && !eq(deal.theirStage, filters.theirStage)) return false;
|
|
153
153
|
if (filters.stage && !eq(deal.stage, filters.stage)) return false;
|
|
154
|
+
if (filters.sourceDirection && !eq(deal.sourceDirection, filters.sourceDirection)) return false;
|
|
154
155
|
return true;
|
|
155
156
|
}
|
|
156
157
|
|
|
@@ -158,11 +159,14 @@ function clientSideMatch(deal, filters) {
|
|
|
158
159
|
function buildDealsQuery(q, flags) {
|
|
159
160
|
const params = new URLSearchParams();
|
|
160
161
|
if (q) params.set("q", q);
|
|
161
|
-
for (const key of ["companyName", "founder", "owner", "status", "theirStage", "stage", "limit", "offset"]) {
|
|
162
|
+
for (const key of ["companyName", "founder", "owner", "status", "theirStage", "stage", "sourceDirection", "limit", "offset"]) {
|
|
162
163
|
if (flags[key] !== undefined && flags[key] !== true) {
|
|
163
164
|
params.set(key, String(flags[key]));
|
|
164
165
|
}
|
|
165
166
|
}
|
|
167
|
+
if (flags["source-direction"] !== undefined && flags["source-direction"] !== true) {
|
|
168
|
+
params.set("sourceDirection", String(flags["source-direction"]));
|
|
169
|
+
}
|
|
166
170
|
return params;
|
|
167
171
|
}
|
|
168
172
|
|
|
@@ -185,6 +189,7 @@ async function searchDeals(q, flags) {
|
|
|
185
189
|
status: flags.status,
|
|
186
190
|
theirStage: flags.theirStage,
|
|
187
191
|
stage: flags.stage,
|
|
192
|
+
sourceDirection: flags.sourceDirection || flags["source-direction"],
|
|
188
193
|
};
|
|
189
194
|
const filtered = result.filter((d) => clientSideMatch(d, filters));
|
|
190
195
|
const limit = Number(flags.limit) > 0 ? Number(flags.limit) : 200;
|
|
@@ -273,6 +278,10 @@ const HELP_FULL = `Llama Command CLI
|
|
|
273
278
|
|
|
274
279
|
Agent onboarding (run once on first install):
|
|
275
280
|
llama agent-onboard # print AGENT_BRIEFING.md — the workflow contract for AI agents
|
|
281
|
+
llama agent bootstrap # fetch live Command + Llama OS skill manifest
|
|
282
|
+
llama skills search "pipeline update" # discover relevant runtime skills
|
|
283
|
+
llama skills show llama-pipeline # read a skill from Command
|
|
284
|
+
llama explain <url-or-object> # explain Command URL/object status + lifecycle
|
|
276
285
|
|
|
277
286
|
External pitch — talk to Llama Ventures' intake agent (no token required):
|
|
278
287
|
llama pitch start --name "Jane Doe" --email "jane@acme.ai"
|
|
@@ -293,15 +302,16 @@ auto-detects \`gcloud auth print-identity-token\` and uses Bearer auth.
|
|
|
293
302
|
Manually-set \`llc_\` tokens are used as a fallback.
|
|
294
303
|
|
|
295
304
|
Deals:
|
|
296
|
-
llama deal create "Company" --source <name> --description "..." --website https://...
|
|
305
|
+
llama deal create "Company" --source <name> --source-direction Inbound|Outbound --description "..." --status Outreached|Sourced --website https://...
|
|
297
306
|
llama deal show <dealId>
|
|
298
307
|
llama deal feed <dealId> # every contribution (facts + notes), human-typed or assistant-drafted, newest first
|
|
299
308
|
llama deal update <dealId> <field> <value>
|
|
300
|
-
Writable fields: status, theirStage, stage, notes, dealOwner, source,
|
|
309
|
+
Writable fields: status, theirStage, stage, notes, dealOwner, source, sourceDirection,
|
|
301
310
|
description, website, location, founders, founderInfo, proposedAmount,
|
|
302
311
|
roundSize, valuation, deckLink, folderUrl, sector, subsector,
|
|
303
312
|
foundedYear, leadInvestor, investors, agentActive.
|
|
304
313
|
e.g. llama deal update <dealId> website https://acme.ai
|
|
314
|
+
llama deal update <dealId> status Outreached
|
|
305
315
|
llama deal update <dealId> sector "Developer Tools"
|
|
306
316
|
llama deal update <dealId> foundedYear 2024
|
|
307
317
|
llama deal update <dealId> leadInvestor "Acme Capital"
|
|
@@ -316,7 +326,7 @@ Deals:
|
|
|
316
326
|
string. Audited to deal_events as field_change "extra.<key>".
|
|
317
327
|
llama deal extra unset <dealId> <key> # delete the key (admin)
|
|
318
328
|
llama deal search <query> [--founder name] [--owner <user-key>] [--status Diligence]
|
|
319
|
-
[--theirStage Raising] [--stage Seed]
|
|
329
|
+
[--theirStage Raising] [--stage Seed] [--source-direction Inbound]
|
|
320
330
|
[--limit 200] [--offset 0]
|
|
321
331
|
llama deal list [--owner ...] [--status ...] [...same flags as search]
|
|
322
332
|
|
|
@@ -512,6 +522,9 @@ Common:
|
|
|
512
522
|
llama deal feed <dealId> every contribution (facts + notes), newest first
|
|
513
523
|
llama post <dealId> "..." add a note to a deal
|
|
514
524
|
llama agent-onboard print the AI-agent workflow contract
|
|
525
|
+
llama agent bootstrap live Llama OS skill manifest from Command
|
|
526
|
+
llama skills search "<query>" discover which skill to read
|
|
527
|
+
llama explain <url-or-object> explain Command URLs, 404s, deleted objects
|
|
515
528
|
|
|
516
529
|
Command groups — run \`llama help <group>\` for that group's commands:
|
|
517
530
|
deal create · show · feed · update · enrich · search · collaborators · links · delete
|
|
@@ -524,6 +537,8 @@ Command groups — run \`llama help <group>\` for that group's commands:
|
|
|
524
537
|
pitch external founder intake (no token needed)
|
|
525
538
|
ownership claim · nominate · approvals
|
|
526
539
|
admin audit events (system admin only)
|
|
540
|
+
agent bootstrap · skills · explain for AI agents
|
|
541
|
+
skills search · show runtime Llama OS skills
|
|
527
542
|
auth setup · tokens · auth status
|
|
528
543
|
|
|
529
544
|
llama help all the full command reference (everything at once)
|
|
@@ -543,6 +558,8 @@ const HELP_AREA_MATCH = {
|
|
|
543
558
|
pitch: [/^External pitch/],
|
|
544
559
|
ownership: [/^Ownership/, /^Approvals/],
|
|
545
560
|
admin: [/^Admin/],
|
|
561
|
+
agent: [/^Agent onboarding/],
|
|
562
|
+
skills: [/^Agent onboarding/],
|
|
546
563
|
auth: [/^Setup/, /^Zero-config/, /^Token discovery/, /^Env/],
|
|
547
564
|
};
|
|
548
565
|
|
|
@@ -931,6 +948,95 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
931
948
|
return;
|
|
932
949
|
}
|
|
933
950
|
|
|
951
|
+
// Live runtime bootstrap from Llama Command. Unlike agent-onboard, this is
|
|
952
|
+
// not bundled in the public npm package; Command returns the current
|
|
953
|
+
// authenticated skill manifest and object-inspection contract.
|
|
954
|
+
if (area === "agent" && action === "bootstrap") {
|
|
955
|
+
const { flags } = parseFlags(rest, ["json", "limit"]);
|
|
956
|
+
const params = new URLSearchParams();
|
|
957
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
958
|
+
const manifest = await request("GET", `/api/agent/manifest${params.toString() ? `?${params}` : ""}`);
|
|
959
|
+
if (flags.json) {
|
|
960
|
+
print(manifest);
|
|
961
|
+
} else {
|
|
962
|
+
process.stdout.write(`${manifest.briefing || JSON.stringify(manifest, null, 2)}\n`);
|
|
963
|
+
}
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (area === "skills" || (area === "agent" && action === "skills")) {
|
|
968
|
+
const sub = area === "skills" ? action : rest[0];
|
|
969
|
+
const args = area === "skills" ? rest : rest.slice(1);
|
|
970
|
+
if (!sub || sub === "list") {
|
|
971
|
+
const { flags } = parseFlags(args, ["json", "limit"]);
|
|
972
|
+
const params = new URLSearchParams();
|
|
973
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
974
|
+
const result = await request("GET", `/api/agent/skills${params.toString() ? `?${params}` : ""}`);
|
|
975
|
+
print(result);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (sub === "search") {
|
|
979
|
+
const { flags, positional } = parseFlags(args, ["json", "limit"]);
|
|
980
|
+
const q = positional.join(" ").trim();
|
|
981
|
+
if (!q) throw new Error("Usage: llama skills search <query> [--limit 20]");
|
|
982
|
+
const params = new URLSearchParams({ q });
|
|
983
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
984
|
+
const result = await request("GET", `/api/agent/skills?${params}`);
|
|
985
|
+
print(result);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (sub === "show" || sub === "read") {
|
|
989
|
+
const { flags, positional } = parseFlags(args, ["json"]);
|
|
990
|
+
const slug = positional[0];
|
|
991
|
+
if (!slug) throw new Error("Usage: llama skills show <slug> [--json]");
|
|
992
|
+
const result = await request("GET", `/api/agent/skills/${encodeURIComponent(slug)}`);
|
|
993
|
+
if (flags.json) {
|
|
994
|
+
print(result);
|
|
995
|
+
} else {
|
|
996
|
+
process.stdout.write(`${result.skill?.content || JSON.stringify(result, null, 2)}\n`);
|
|
997
|
+
}
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
throw new Error(`Unknown skills subcommand "${sub}". Use: list / search / show.`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (area === "explain" || (area === "agent" && action === "explain")) {
|
|
1004
|
+
const args = area === "explain" ? [action, ...rest].filter(Boolean) : rest;
|
|
1005
|
+
const { flags, positional } = parseFlags(args, ["json", "type", "id", "lang"]);
|
|
1006
|
+
const params = new URLSearchParams();
|
|
1007
|
+
const q = positional.join(" ").trim();
|
|
1008
|
+
if (q) params.set("q", q);
|
|
1009
|
+
if (flags.type && flags.type !== true) params.set("type", String(flags.type));
|
|
1010
|
+
if (flags.id && flags.id !== true) params.set("id", String(flags.id));
|
|
1011
|
+
if (flags.lang === "zh") params.set("lang", "zh");
|
|
1012
|
+
if (!params.has("q") && !(params.has("type") && params.has("id"))) {
|
|
1013
|
+
throw new Error("Usage: llama explain <url-or-object> OR llama explain --type <type> --id <id>");
|
|
1014
|
+
}
|
|
1015
|
+
const result = await request("GET", `/api/agent/explain?${params}`);
|
|
1016
|
+
if (flags.json) {
|
|
1017
|
+
print(result);
|
|
1018
|
+
} else {
|
|
1019
|
+
const target = result.result?.target;
|
|
1020
|
+
const lifecycle = result.result?.lifecycle || [];
|
|
1021
|
+
const lines = [
|
|
1022
|
+
`${target?.objectType || "object"} ${target?.objectId || ""}`,
|
|
1023
|
+
`Status: ${target?.status || "unknown"}`,
|
|
1024
|
+
`Title: ${target?.title || "Untitled"}`,
|
|
1025
|
+
target?.detail ? `Detail: ${target.detail}` : null,
|
|
1026
|
+
target?.url ? `URL: ${target.url}` : null,
|
|
1027
|
+
`Lifecycle events: ${lifecycle.length}`,
|
|
1028
|
+
].filter(Boolean);
|
|
1029
|
+
if (lifecycle[0]) {
|
|
1030
|
+
lines.push(
|
|
1031
|
+
`Latest lifecycle: ${lifecycle[0].action} by ${lifecycle[0].actor_label || "unknown"} at ${lifecycle[0].created_at}`,
|
|
1032
|
+
);
|
|
1033
|
+
if (lifecycle[0].reason) lines.push(`Reason: ${lifecycle[0].reason}`);
|
|
1034
|
+
}
|
|
1035
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
1036
|
+
}
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
934
1040
|
// `llama pitch ...` — external founder-pitch family. No Llama token
|
|
935
1041
|
// required; bootstraps a session against /api/external/* via PoW + cookie.
|
|
936
1042
|
// See lib/external.mjs and AGENT_BRIEFING.md for the full surface.
|
|
@@ -1143,6 +1249,7 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1143
1249
|
const body = {
|
|
1144
1250
|
companyName,
|
|
1145
1251
|
source: flags.source,
|
|
1252
|
+
sourceDirection: flags.sourceDirection || flags["source-direction"],
|
|
1146
1253
|
description: flags.description,
|
|
1147
1254
|
website: flags.website,
|
|
1148
1255
|
notes: flags.notes,
|
|
@@ -1219,7 +1326,7 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1219
1326
|
const q = positional.join(" ").trim();
|
|
1220
1327
|
if (!q && Object.keys(flags).length === 0) {
|
|
1221
1328
|
throw new Error(
|
|
1222
|
-
`Usage: llama deal search <query> [--founder ...] [--owner ...] [--status ...] [--stage ...] [--limit N]`
|
|
1329
|
+
`Usage: llama deal search <query> [--founder ...] [--owner ...] [--status ...] [--stage ...] [--source-direction Inbound|Outbound] [--limit N]`
|
|
1223
1330
|
);
|
|
1224
1331
|
}
|
|
1225
1332
|
print(await searchDeals(q, flags));
|
package/package.json
CHANGED
|
@@ -3,12 +3,23 @@
|
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
7
8
|
import os from "node:os";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
|
|
11
12
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
assert.equal(
|
|
14
|
+
existsSync(path.join(repoRoot, "docs/agent-skills.bundle.json")),
|
|
15
|
+
false,
|
|
16
|
+
"public llama-cli must not bundle private Llama OS skill content",
|
|
17
|
+
);
|
|
18
|
+
assert.equal(
|
|
19
|
+
existsSync(path.join(repoRoot, "src/data/llama-os-skills.bundle.json")),
|
|
20
|
+
false,
|
|
21
|
+
"public llama-cli must not copy the Command-side skill mirror",
|
|
22
|
+
);
|
|
12
23
|
const calls = [];
|
|
13
24
|
let threadSeq = 0;
|
|
14
25
|
|
|
@@ -49,7 +60,81 @@ const server = createServer(async (req, res) => {
|
|
|
49
60
|
try {
|
|
50
61
|
const body = await readJson(req);
|
|
51
62
|
const url = new URL(req.url, "http://localhost");
|
|
52
|
-
calls.push({
|
|
63
|
+
calls.push({
|
|
64
|
+
method: req.method,
|
|
65
|
+
path: url.pathname,
|
|
66
|
+
query: Object.fromEntries(url.searchParams.entries()),
|
|
67
|
+
body,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (req.method === "GET" && url.pathname === "/api/agent/manifest") {
|
|
71
|
+
writeJson(res, {
|
|
72
|
+
ok: true,
|
|
73
|
+
briefing: "runtime briefing: use skills_search, skills_read, and object_inspect",
|
|
74
|
+
llama_os: {
|
|
75
|
+
visible_skill_count: 49,
|
|
76
|
+
included_skill_count: Number(url.searchParams.get("limit") || 25),
|
|
77
|
+
},
|
|
78
|
+
skills: [
|
|
79
|
+
{
|
|
80
|
+
slug: "llama-command",
|
|
81
|
+
description: "Llama Command runtime skill",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (req.method === "GET" && url.pathname === "/api/agent/skills") {
|
|
89
|
+
writeJson(res, {
|
|
90
|
+
ok: true,
|
|
91
|
+
q: url.searchParams.get("q"),
|
|
92
|
+
count: 1,
|
|
93
|
+
skills: [
|
|
94
|
+
{
|
|
95
|
+
slug: "llama-command",
|
|
96
|
+
description: "Llama Command runtime skill",
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (req.method === "GET" && url.pathname === "/api/agent/skills/llama-command") {
|
|
104
|
+
writeJson(res, {
|
|
105
|
+
ok: true,
|
|
106
|
+
skill: {
|
|
107
|
+
slug: "llama-command",
|
|
108
|
+
content: "---\nname: llama-command\n---\n# Llama Command runtime skill\n",
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (req.method === "GET" && url.pathname === "/api/agent/explain") {
|
|
115
|
+
writeJson(res, {
|
|
116
|
+
ok: true,
|
|
117
|
+
result: {
|
|
118
|
+
target: {
|
|
119
|
+
objectType: "wiki_article",
|
|
120
|
+
objectId: "missing-page",
|
|
121
|
+
status: "deleted",
|
|
122
|
+
title: "Missing Page",
|
|
123
|
+
detail: "Deleted by Kevin Yu",
|
|
124
|
+
url: "https://command.llamaventures.vc/wiki/missing-page",
|
|
125
|
+
},
|
|
126
|
+
lifecycle: [
|
|
127
|
+
{
|
|
128
|
+
action: "deleted",
|
|
129
|
+
actor_label: "Kevin Yu",
|
|
130
|
+
created_at: "2026-06-15T19:02:00Z",
|
|
131
|
+
reason: "user_deleted",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
53
138
|
|
|
54
139
|
if (req.method === "POST" && /^\/api\/deals\/[^/]+\/threads$/.test(url.pathname)) {
|
|
55
140
|
threadSeq += 1;
|
|
@@ -228,6 +313,31 @@ const baseUrl = `http://${address.address}:${address.port}`;
|
|
|
228
313
|
const homeDir = await mkdtemp(path.join(os.tmpdir(), "llama-cli-routing-"));
|
|
229
314
|
|
|
230
315
|
try {
|
|
316
|
+
resetCalls();
|
|
317
|
+
const bootstrapRun = await runCli(["agent", "bootstrap", "--limit", "3"], baseUrl, homeDir);
|
|
318
|
+
assert.match(bootstrapRun.stdout, /runtime briefing/);
|
|
319
|
+
assert.deepEqual(paths(), ["GET /api/agent/manifest"]);
|
|
320
|
+
assert.equal(calls[0].query.limit, "3");
|
|
321
|
+
|
|
322
|
+
resetCalls();
|
|
323
|
+
const skillSearchRun = await runCli(["skills", "search", "pipeline", "--limit", "5"], baseUrl, homeDir);
|
|
324
|
+
assert.match(skillSearchRun.stdout, /llama-command/);
|
|
325
|
+
assert.deepEqual(paths(), ["GET /api/agent/skills"]);
|
|
326
|
+
assert.equal(calls[0].query.q, "pipeline");
|
|
327
|
+
assert.equal(calls[0].query.limit, "5");
|
|
328
|
+
|
|
329
|
+
resetCalls();
|
|
330
|
+
const skillShowRun = await runCli(["skills", "show", "llama-command"], baseUrl, homeDir);
|
|
331
|
+
assert.match(skillShowRun.stdout, /# Llama Command runtime skill/);
|
|
332
|
+
assert.deepEqual(paths(), ["GET /api/agent/skills/llama-command"]);
|
|
333
|
+
|
|
334
|
+
resetCalls();
|
|
335
|
+
const explainRun = await runCli(["explain", "https://command.llamaventures.vc/wiki/missing-page"], baseUrl, homeDir);
|
|
336
|
+
assert.match(explainRun.stdout, /Status: deleted/);
|
|
337
|
+
assert.match(explainRun.stdout, /Deleted by Kevin Yu/);
|
|
338
|
+
assert.deepEqual(paths(), ["GET /api/agent/explain"]);
|
|
339
|
+
assert.equal(calls[0].query.q, "https://command.llamaventures.vc/wiki/missing-page");
|
|
340
|
+
|
|
231
341
|
resetCalls();
|
|
232
342
|
const enrichRun = await runCli(
|
|
233
343
|
[
|
|
@@ -299,6 +409,37 @@ try {
|
|
|
299
409
|
assert.equal(payload.threadId, "thread-1");
|
|
300
410
|
assert.equal(payload.text, "agent done");
|
|
301
411
|
|
|
412
|
+
resetCalls();
|
|
413
|
+
const mcpBootstrap = await callMcpTool("agent_bootstrap", { limit: 2 }, baseUrl, homeDir);
|
|
414
|
+
const bootstrapPayload = JSON.parse(mcpBootstrap.content?.[0]?.text ?? "{}");
|
|
415
|
+
assert.equal(bootstrapPayload.ok, true);
|
|
416
|
+
assert.deepEqual(paths(), ["GET /api/agent/manifest"]);
|
|
417
|
+
assert.equal(calls[0].query.limit, "2");
|
|
418
|
+
|
|
419
|
+
resetCalls();
|
|
420
|
+
const mcpSkills = await callMcpTool("skills_search", { q: "command", limit: 4 }, baseUrl, homeDir);
|
|
421
|
+
const skillsPayload = JSON.parse(mcpSkills.content?.[0]?.text ?? "{}");
|
|
422
|
+
assert.equal(skillsPayload.skills?.[0]?.slug, "llama-command");
|
|
423
|
+
assert.deepEqual(paths(), ["GET /api/agent/skills"]);
|
|
424
|
+
assert.equal(calls[0].query.q, "command");
|
|
425
|
+
|
|
426
|
+
resetCalls();
|
|
427
|
+
const mcpSkillRead = await callMcpTool("skills_read", { slug: "llama-command" }, baseUrl, homeDir);
|
|
428
|
+
const skillPayload = JSON.parse(mcpSkillRead.content?.[0]?.text ?? "{}");
|
|
429
|
+
assert.match(skillPayload.skill?.content ?? "", /# Llama Command runtime skill/);
|
|
430
|
+
assert.deepEqual(paths(), ["GET /api/agent/skills/llama-command"]);
|
|
431
|
+
|
|
432
|
+
resetCalls();
|
|
433
|
+
const mcpInspect = await callMcpTool(
|
|
434
|
+
"object_inspect",
|
|
435
|
+
{ q: "https://command.llamaventures.vc/wiki/missing-page" },
|
|
436
|
+
baseUrl,
|
|
437
|
+
homeDir,
|
|
438
|
+
);
|
|
439
|
+
const inspectPayload = JSON.parse(mcpInspect.content?.[0]?.text ?? "{}");
|
|
440
|
+
assert.equal(inspectPayload.result?.target?.status, "deleted");
|
|
441
|
+
assert.deepEqual(paths(), ["GET /api/agent/explain"]);
|
|
442
|
+
|
|
302
443
|
console.log("agent routing verification passed");
|
|
303
444
|
} finally {
|
|
304
445
|
await close(server);
|