@llamaventures/cli 1.14.1 → 1.15.0
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 +15 -1
- package/CHANGELOG.md +19 -0
- package/README.md +15 -3
- package/README.zh-CN.md +14 -3
- package/bin/llama-mcp.mjs +79 -1
- package/bin/llama.mjs +100 -0
- 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:
|
|
@@ -259,7 +270,7 @@ Run `llama --help` for the full surface (~40 commands).
|
|
|
259
270
|
|
|
260
271
|
## MCP-native agents
|
|
261
272
|
|
|
262
|
-
If you support [MCP](https://modelcontextprotocol.io), **prefer the MCP server over parsing CLI output.** The same package ships `llama-mcp` (
|
|
273
|
+
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
274
|
|
|
264
275
|
Add to your MCP client config (Claude Desktop / Claude Code / Cursor / OpenClaw / Codex / etc.):
|
|
265
276
|
|
|
@@ -270,6 +281,9 @@ Add to your MCP client config (Claude Desktop / Claude Code / Cursor / OpenClaw
|
|
|
270
281
|
Tools available:
|
|
271
282
|
|
|
272
283
|
- `auth_status` — verify creds + identity (call first if anything 401s)
|
|
284
|
+
- `agent_bootstrap` — fetch the live Command + Llama OS runtime manifest
|
|
285
|
+
- `skills_search` / `skills_read` — discover and read authenticated runtime skills
|
|
286
|
+
- `object_inspect` — explain Command URLs, 404s, deleted objects, and lifecycle trail
|
|
273
287
|
- `deal_search` / `deal_show` / `deal_create` / `deal_update`
|
|
274
288
|
- `brief_blocks` / `brief_add_text` / `brief_add_link` / `brief_add_callout`
|
|
275
289
|
- `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,25 @@ this project adheres to [Semantic Versioning](https://semver.org).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.15.0] — 2026-06-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`llama agent bootstrap`** — fetches the live Llama Command + Llama OS
|
|
13
|
+
runtime manifest, including the authenticated skill bundle metadata and
|
|
14
|
+
object-inspection contract.
|
|
15
|
+
- **`llama skills search|show`** — discovers and reads runtime Llama OS skills
|
|
16
|
+
from Llama Command. The public npm package does not bundle private skill
|
|
17
|
+
content; Command returns only what the authenticated token may see.
|
|
18
|
+
- **`llama explain <url-or-object>`** — asks Llama Command to explain URLs,
|
|
19
|
+
deleted objects, 404s, and lifecycle history before an agent guesses that
|
|
20
|
+
the system is broken.
|
|
21
|
+
- MCP parity tools: **`agent_bootstrap`**, **`skills_search`**,
|
|
22
|
+
**`skills_read`**, and **`object_inspect`**.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- `AGENT_BRIEFING.md` now teaches agents to use the authenticated runtime
|
|
26
|
+
skill gateway instead of assuming a local private `llama-os` checkout.
|
|
27
|
+
|
|
9
28
|
## [1.14.1] — 2026-06-15
|
|
10
29
|
|
|
11
30
|
### 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
|
|
@@ -211,6 +211,12 @@ llama approvals decide <approvalId> approved --note "..."
|
|
|
211
211
|
llama timeline <dealId>
|
|
212
212
|
llama post <dealId> "message body" [--link url]
|
|
213
213
|
|
|
214
|
+
# Agent runtime — live Command + private Llama OS skill gateway
|
|
215
|
+
llama agent bootstrap
|
|
216
|
+
llama skills search "wiki delete tombstone"
|
|
217
|
+
llama skills show llama-command
|
|
218
|
+
llama explain https://command.llamaventures.vc/wiki/some-page
|
|
219
|
+
|
|
214
220
|
# Wiki
|
|
215
221
|
llama wiki search "<query>"
|
|
216
222
|
llama wiki read <slug> [--lang en|zh]
|
|
@@ -249,13 +255,14 @@ agents can pattern-match without parsing prose.
|
|
|
249
255
|
## MCP server
|
|
250
256
|
|
|
251
257
|
The bundled `llama-mcp` is a **stdio Model Context Protocol** server exposing
|
|
252
|
-
**
|
|
258
|
+
**56 typed tools** that mirror the most-used CLI surface. Every tool is named
|
|
253
259
|
and scoped — there is no generic API passthrough, by design (a public-package
|
|
254
260
|
escape hatch reachable from a prompt-injectable agent context is exactly the
|
|
255
261
|
shape we want to avoid).
|
|
256
262
|
|
|
257
263
|
Coverage is grouped around the workflows agents actually need: auth
|
|
258
|
-
diagnostics;
|
|
264
|
+
diagnostics; live agent bootstrap; authenticated Llama OS skill search/read;
|
|
265
|
+
Command URL/object inspection; deal search/show/create/update/feed; server-side deal agent runs;
|
|
259
266
|
deal enrichment harnesses;
|
|
260
267
|
trust-rated facts; brief blocks and version history; wiki read/write/delete/restore;
|
|
261
268
|
timeline posts and mentions; skill corrections; refresh triggers; external pitch intake;
|
|
@@ -277,6 +284,11 @@ The `agent_briefing` MCP **prompt** also returns
|
|
|
277
284
|
[`AGENT_BRIEFING.md`](AGENT_BRIEFING.md) verbatim, so any new agent loading the
|
|
278
285
|
server can self-onboard without leaving the protocol.
|
|
279
286
|
|
|
287
|
+
For current Llama OS skills, use the runtime tools instead of looking for a
|
|
288
|
+
local private repo: `agent_bootstrap`, `skills_search`, `skills_read`, and
|
|
289
|
+
`object_inspect`. The public npm package does not bundle private skill text;
|
|
290
|
+
Command returns only the content visible to the authenticated token.
|
|
291
|
+
|
|
280
292
|
### Wire into your agent
|
|
281
293
|
|
|
282
294
|
<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 客户端、
|
|
@@ -232,6 +232,12 @@ llama approvals decide <approvalId> approved --note "..."
|
|
|
232
232
|
llama timeline <dealId>
|
|
233
233
|
llama post <dealId> "消息内容" [--link url]
|
|
234
234
|
|
|
235
|
+
# Agent runtime——Command 上的 Llama OS skill gateway
|
|
236
|
+
llama agent bootstrap
|
|
237
|
+
llama skills search "wiki delete tombstone"
|
|
238
|
+
llama skills show llama-command
|
|
239
|
+
llama explain https://command.llamaventures.vc/wiki/some-page
|
|
240
|
+
|
|
235
241
|
# Wiki
|
|
236
242
|
llama wiki search "<query>"
|
|
237
243
|
llama wiki save <slug> --title "..." --content "..."
|
|
@@ -261,11 +267,12 @@ MCP server 在 `isError: true` 内容里返回相同的前缀,agent 不用解
|
|
|
261
267
|
## MCP server
|
|
262
268
|
|
|
263
269
|
随包发布的 `llama-mcp` 是一个 **stdio Model Context Protocol** server,
|
|
264
|
-
暴露 **
|
|
270
|
+
暴露 **56 个 typed tools**——基本镜像 CLI 最常用的命令。每个 tool 都是
|
|
265
271
|
具名、scoped 的;**没有**通用的 API passthrough,这是有意设计的(公开
|
|
266
272
|
package 里一个能被 prompt-injection 触达的逃生通道,正是我们要避开的形状)。
|
|
267
273
|
|
|
268
|
-
覆盖面按 agent 真正会用的工作流分组:auth 诊断;
|
|
274
|
+
覆盖面按 agent 真正会用的工作流分组:auth 诊断;live agent bootstrap;
|
|
275
|
+
授权后的 Llama OS skill search/read;Command URL/object inspect;deal search/show/create/
|
|
269
276
|
update/feed;服务端 deal agent run;deal enrichment harness;带 trust ladder 的事实;brief blocks
|
|
270
277
|
和版本历史;wiki 读写删恢复;timeline posts 和 mentions;skill corrections;
|
|
271
278
|
refresh triggers;外部 pitch intake;memo show/regenerate/save/reset;
|
|
@@ -286,6 +293,10 @@ printf '%s\n' \
|
|
|
286
293
|
[`AGENT_BRIEFING.md`](AGENT_BRIEFING.md)——刚装上 server 的 agent
|
|
287
294
|
不用离开协议就能给自己 onboard。
|
|
288
295
|
|
|
296
|
+
要读当前 Llama OS skills,用 runtime tools:`agent_bootstrap`、
|
|
297
|
+
`skills_search`、`skills_read`、`object_inspect`。公开 npm 包不打包私有
|
|
298
|
+
skill 正文;Command 只按当前 token 的权限返回可见内容。
|
|
299
|
+
|
|
289
300
|
### 接到你的 agent 上
|
|
290
301
|
|
|
291
302
|
<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
|
// ============================================================
|
|
@@ -1501,7 +1577,9 @@ server.registerPrompt(
|
|
|
1501
1577
|
"contract: identity, Pipeline First rule, content capture, autonomy " +
|
|
1502
1578
|
"levels (L0/L1/L2/L3), communication style, error recovery, CLI/MCP " +
|
|
1503
1579
|
"reference, and boundaries. Read this once, internalise it, operate " +
|
|
1504
|
-
"accordingly. Same content as `llama agent-onboard` from the CLI."
|
|
1580
|
+
"accordingly. Same content as `llama agent-onboard` from the CLI. " +
|
|
1581
|
+
"For the live private Llama OS skill library, call agent_bootstrap, " +
|
|
1582
|
+
"skills_search, and skills_read.",
|
|
1505
1583
|
},
|
|
1506
1584
|
async () => {
|
|
1507
1585
|
// Gate the briefing behind a /api/me check so unauthenticated MCP
|
package/bin/llama.mjs
CHANGED
|
@@ -273,6 +273,10 @@ const HELP_FULL = `Llama Command CLI
|
|
|
273
273
|
|
|
274
274
|
Agent onboarding (run once on first install):
|
|
275
275
|
llama agent-onboard # print AGENT_BRIEFING.md — the workflow contract for AI agents
|
|
276
|
+
llama agent bootstrap # fetch live Command + Llama OS skill manifest
|
|
277
|
+
llama skills search "pipeline update" # discover relevant runtime skills
|
|
278
|
+
llama skills show llama-pipeline # read a skill from Command
|
|
279
|
+
llama explain <url-or-object> # explain Command URL/object status + lifecycle
|
|
276
280
|
|
|
277
281
|
External pitch — talk to Llama Ventures' intake agent (no token required):
|
|
278
282
|
llama pitch start --name "Jane Doe" --email "jane@acme.ai"
|
|
@@ -512,6 +516,9 @@ Common:
|
|
|
512
516
|
llama deal feed <dealId> every contribution (facts + notes), newest first
|
|
513
517
|
llama post <dealId> "..." add a note to a deal
|
|
514
518
|
llama agent-onboard print the AI-agent workflow contract
|
|
519
|
+
llama agent bootstrap live Llama OS skill manifest from Command
|
|
520
|
+
llama skills search "<query>" discover which skill to read
|
|
521
|
+
llama explain <url-or-object> explain Command URLs, 404s, deleted objects
|
|
515
522
|
|
|
516
523
|
Command groups — run \`llama help <group>\` for that group's commands:
|
|
517
524
|
deal create · show · feed · update · enrich · search · collaborators · links · delete
|
|
@@ -524,6 +531,8 @@ Command groups — run \`llama help <group>\` for that group's commands:
|
|
|
524
531
|
pitch external founder intake (no token needed)
|
|
525
532
|
ownership claim · nominate · approvals
|
|
526
533
|
admin audit events (system admin only)
|
|
534
|
+
agent bootstrap · skills · explain for AI agents
|
|
535
|
+
skills search · show runtime Llama OS skills
|
|
527
536
|
auth setup · tokens · auth status
|
|
528
537
|
|
|
529
538
|
llama help all the full command reference (everything at once)
|
|
@@ -543,6 +552,8 @@ const HELP_AREA_MATCH = {
|
|
|
543
552
|
pitch: [/^External pitch/],
|
|
544
553
|
ownership: [/^Ownership/, /^Approvals/],
|
|
545
554
|
admin: [/^Admin/],
|
|
555
|
+
agent: [/^Agent onboarding/],
|
|
556
|
+
skills: [/^Agent onboarding/],
|
|
546
557
|
auth: [/^Setup/, /^Zero-config/, /^Token discovery/, /^Env/],
|
|
547
558
|
};
|
|
548
559
|
|
|
@@ -931,6 +942,95 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
931
942
|
return;
|
|
932
943
|
}
|
|
933
944
|
|
|
945
|
+
// Live runtime bootstrap from Llama Command. Unlike agent-onboard, this is
|
|
946
|
+
// not bundled in the public npm package; Command returns the current
|
|
947
|
+
// authenticated skill manifest and object-inspection contract.
|
|
948
|
+
if (area === "agent" && action === "bootstrap") {
|
|
949
|
+
const { flags } = parseFlags(rest, ["json", "limit"]);
|
|
950
|
+
const params = new URLSearchParams();
|
|
951
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
952
|
+
const manifest = await request("GET", `/api/agent/manifest${params.toString() ? `?${params}` : ""}`);
|
|
953
|
+
if (flags.json) {
|
|
954
|
+
print(manifest);
|
|
955
|
+
} else {
|
|
956
|
+
process.stdout.write(`${manifest.briefing || JSON.stringify(manifest, null, 2)}\n`);
|
|
957
|
+
}
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (area === "skills" || (area === "agent" && action === "skills")) {
|
|
962
|
+
const sub = area === "skills" ? action : rest[0];
|
|
963
|
+
const args = area === "skills" ? rest : rest.slice(1);
|
|
964
|
+
if (!sub || sub === "list") {
|
|
965
|
+
const { flags } = parseFlags(args, ["json", "limit"]);
|
|
966
|
+
const params = new URLSearchParams();
|
|
967
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
968
|
+
const result = await request("GET", `/api/agent/skills${params.toString() ? `?${params}` : ""}`);
|
|
969
|
+
print(result);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
if (sub === "search") {
|
|
973
|
+
const { flags, positional } = parseFlags(args, ["json", "limit"]);
|
|
974
|
+
const q = positional.join(" ").trim();
|
|
975
|
+
if (!q) throw new Error("Usage: llama skills search <query> [--limit 20]");
|
|
976
|
+
const params = new URLSearchParams({ q });
|
|
977
|
+
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
978
|
+
const result = await request("GET", `/api/agent/skills?${params}`);
|
|
979
|
+
print(result);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (sub === "show" || sub === "read") {
|
|
983
|
+
const { flags, positional } = parseFlags(args, ["json"]);
|
|
984
|
+
const slug = positional[0];
|
|
985
|
+
if (!slug) throw new Error("Usage: llama skills show <slug> [--json]");
|
|
986
|
+
const result = await request("GET", `/api/agent/skills/${encodeURIComponent(slug)}`);
|
|
987
|
+
if (flags.json) {
|
|
988
|
+
print(result);
|
|
989
|
+
} else {
|
|
990
|
+
process.stdout.write(`${result.skill?.content || JSON.stringify(result, null, 2)}\n`);
|
|
991
|
+
}
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
throw new Error(`Unknown skills subcommand "${sub}". Use: list / search / show.`);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (area === "explain" || (area === "agent" && action === "explain")) {
|
|
998
|
+
const args = area === "explain" ? [action, ...rest].filter(Boolean) : rest;
|
|
999
|
+
const { flags, positional } = parseFlags(args, ["json", "type", "id", "lang"]);
|
|
1000
|
+
const params = new URLSearchParams();
|
|
1001
|
+
const q = positional.join(" ").trim();
|
|
1002
|
+
if (q) params.set("q", q);
|
|
1003
|
+
if (flags.type && flags.type !== true) params.set("type", String(flags.type));
|
|
1004
|
+
if (flags.id && flags.id !== true) params.set("id", String(flags.id));
|
|
1005
|
+
if (flags.lang === "zh") params.set("lang", "zh");
|
|
1006
|
+
if (!params.has("q") && !(params.has("type") && params.has("id"))) {
|
|
1007
|
+
throw new Error("Usage: llama explain <url-or-object> OR llama explain --type <type> --id <id>");
|
|
1008
|
+
}
|
|
1009
|
+
const result = await request("GET", `/api/agent/explain?${params}`);
|
|
1010
|
+
if (flags.json) {
|
|
1011
|
+
print(result);
|
|
1012
|
+
} else {
|
|
1013
|
+
const target = result.result?.target;
|
|
1014
|
+
const lifecycle = result.result?.lifecycle || [];
|
|
1015
|
+
const lines = [
|
|
1016
|
+
`${target?.objectType || "object"} ${target?.objectId || ""}`,
|
|
1017
|
+
`Status: ${target?.status || "unknown"}`,
|
|
1018
|
+
`Title: ${target?.title || "Untitled"}`,
|
|
1019
|
+
target?.detail ? `Detail: ${target.detail}` : null,
|
|
1020
|
+
target?.url ? `URL: ${target.url}` : null,
|
|
1021
|
+
`Lifecycle events: ${lifecycle.length}`,
|
|
1022
|
+
].filter(Boolean);
|
|
1023
|
+
if (lifecycle[0]) {
|
|
1024
|
+
lines.push(
|
|
1025
|
+
`Latest lifecycle: ${lifecycle[0].action} by ${lifecycle[0].actor_label || "unknown"} at ${lifecycle[0].created_at}`,
|
|
1026
|
+
);
|
|
1027
|
+
if (lifecycle[0].reason) lines.push(`Reason: ${lifecycle[0].reason}`);
|
|
1028
|
+
}
|
|
1029
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
1030
|
+
}
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
934
1034
|
// `llama pitch ...` — external founder-pitch family. No Llama token
|
|
935
1035
|
// required; bootstraps a session against /api/external/* via PoW + cookie.
|
|
936
1036
|
// See lib/external.mjs and AGENT_BRIEFING.md for the full surface.
|
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);
|