@llamaventures/cli 1.15.0 → 1.16.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 +18 -6
- package/CHANGELOG.md +32 -0
- package/README.md +42 -7
- package/README.zh-CN.md +16 -5
- package/bin/llama-mcp.mjs +97 -15
- package/bin/llama.mjs +142 -38
- package/lib/client.mjs +332 -0
- package/package.json +1 -1
- package/scripts/verify-agent-routing.mjs +127 -16
package/bin/llama.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { createRequire } from "module";
|
|
3
4
|
import readline from "readline";
|
|
4
5
|
import {
|
|
5
6
|
DEFAULT_BASE_URL,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
TOKEN_FILE,
|
|
10
11
|
getAuthHeaders,
|
|
11
12
|
getBaseUrl,
|
|
13
|
+
getLastAgentEvent,
|
|
12
14
|
getToken,
|
|
13
15
|
print,
|
|
14
16
|
readBriefing,
|
|
@@ -33,6 +35,9 @@ import { LLAMA_CLI_CLIENT_ID, pkceLoopbackFlow, revokeToken as revokeOAuthToken
|
|
|
33
35
|
import { deleteBundle, detectBackend, readBundle, writeBundle } from "../lib/oauth-storage.mjs";
|
|
34
36
|
import { maybeNudgeUpdate, getUpdateNudge } from "../lib/version-check.mjs";
|
|
35
37
|
|
|
38
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
39
|
+
const { version: PKG_VERSION } = requireFromHere("../package.json");
|
|
40
|
+
|
|
36
41
|
function parseFlags(args, knownFlags = null) {
|
|
37
42
|
const flags = {};
|
|
38
43
|
const positional = [];
|
|
@@ -71,6 +76,34 @@ function parseFlags(args, knownFlags = null) {
|
|
|
71
76
|
return { flags, positional };
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
function agentOnboardNoAuthMessage() {
|
|
80
|
+
return `Llama Ventures team onboarding requires credentials.
|
|
81
|
+
|
|
82
|
+
Team member?
|
|
83
|
+
- Run \`gcloud auth login\` with your @llamaventures.vc account, OR
|
|
84
|
+
- Mint a token at https://command.llamaventures.vc/settings/tokens
|
|
85
|
+
then \`llama token set <llc_...>\`.
|
|
86
|
+
Re-run \`llama agent-onboard\` after — the workflow contract will print.
|
|
87
|
+
|
|
88
|
+
Founder or external visitor (no Llama account)?
|
|
89
|
+
Run \`llama pitch start --name "Your Name" --email "you@company.com"\`
|
|
90
|
+
to chat with our intake agent — no token required.`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function agentOnboardRejectedMessage() {
|
|
94
|
+
return `Llama Ventures team onboarding requires valid credentials.
|
|
95
|
+
|
|
96
|
+
Server rejected the credentials we sent. Re-mint at
|
|
97
|
+
https://command.llamaventures.vc/settings/tokens, run
|
|
98
|
+
\`llama token set <llc_...>\`, then re-run \`llama agent-onboard\`.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function fetchServerAgentBriefing() {
|
|
102
|
+
const params = new URLSearchParams({ clientVersion: PKG_VERSION });
|
|
103
|
+
const result = await request("GET", `/api/agent/briefing?${params}`);
|
|
104
|
+
return result?.briefing || "";
|
|
105
|
+
}
|
|
106
|
+
|
|
74
107
|
function closestKnownFlag(input, candidates) {
|
|
75
108
|
let best = null;
|
|
76
109
|
let bestScore = Infinity;
|
|
@@ -127,6 +160,45 @@ function slugifyTitle(title) {
|
|
|
127
160
|
return slug;
|
|
128
161
|
}
|
|
129
162
|
|
|
163
|
+
function parseExpectedIds(raw) {
|
|
164
|
+
const text = typeof raw === "string" ? raw : "";
|
|
165
|
+
const expected = { dealIds: [], wikiSlugs: [], raw: [] };
|
|
166
|
+
for (const item of text.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
167
|
+
const [kind, ...rest] = item.split(":");
|
|
168
|
+
const value = rest.join(":").trim();
|
|
169
|
+
if (kind === "deal" && value) expected.dealIds.push(value);
|
|
170
|
+
else if ((kind === "wiki" || kind === "slug") && value) expected.wikiSlugs.push(value);
|
|
171
|
+
else expected.raw.push(item);
|
|
172
|
+
}
|
|
173
|
+
return expected;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function submitEvalFeedback(action, flags, queryText = "") {
|
|
177
|
+
const useLast = flags.last !== false;
|
|
178
|
+
const last = useLast ? getLastAgentEvent() : null;
|
|
179
|
+
const eventId =
|
|
180
|
+
flags.event && flags.event !== true
|
|
181
|
+
? Number(flags.event)
|
|
182
|
+
: last?.lastEventId ?? null;
|
|
183
|
+
if ((action === "good" || action === "bad") && !eventId && !queryText) {
|
|
184
|
+
throw new Error(`Usage: llama eval ${action} [--last] [--reason "..."]`);
|
|
185
|
+
}
|
|
186
|
+
const body = {
|
|
187
|
+
action,
|
|
188
|
+
eventId: Number.isFinite(eventId) ? eventId : undefined,
|
|
189
|
+
query: queryText || undefined,
|
|
190
|
+
surface: flags.surface && flags.surface !== true ? String(flags.surface) : last?.lastSurface ?? undefined,
|
|
191
|
+
expected:
|
|
192
|
+
flags.expect && flags.expect !== true
|
|
193
|
+
? parseExpectedIds(String(flags.expect))
|
|
194
|
+
: {},
|
|
195
|
+
reason: flags.reason && flags.reason !== true ? String(flags.reason) : undefined,
|
|
196
|
+
privacyLevel:
|
|
197
|
+
flags.privacy && flags.privacy !== true ? String(flags.privacy) : "internal",
|
|
198
|
+
};
|
|
199
|
+
return request("POST", "/api/agent/eval-feedback", body);
|
|
200
|
+
}
|
|
201
|
+
|
|
130
202
|
// Client-side fuzzy match — used as a fallback when the server hasn't yet
|
|
131
203
|
// shipped the search/filter API (Fix B, 2026-04-25). Once the server
|
|
132
204
|
// returns the `{deals,total,limit,offset}` envelope, this path is never
|
|
@@ -141,7 +213,7 @@ function clientSideMatch(deal, filters) {
|
|
|
141
213
|
const fields = [
|
|
142
214
|
deal.companyName, deal.founders, deal.founderInfo,
|
|
143
215
|
deal.description, deal.notes, deal.dealOwner,
|
|
144
|
-
deal.source, deal.location,
|
|
216
|
+
deal.source, deal.sourceDirection, deal.location,
|
|
145
217
|
];
|
|
146
218
|
if (!fields.some((f) => incl(f, filters.q))) return false;
|
|
147
219
|
}
|
|
@@ -151,6 +223,7 @@ function clientSideMatch(deal, filters) {
|
|
|
151
223
|
if (filters.status && !eq(deal.status, filters.status)) return false;
|
|
152
224
|
if (filters.theirStage && !eq(deal.theirStage, filters.theirStage)) return false;
|
|
153
225
|
if (filters.stage && !eq(deal.stage, filters.stage)) return false;
|
|
226
|
+
if (filters.sourceDirection && !eq(deal.sourceDirection, filters.sourceDirection)) return false;
|
|
154
227
|
return true;
|
|
155
228
|
}
|
|
156
229
|
|
|
@@ -158,11 +231,14 @@ function clientSideMatch(deal, filters) {
|
|
|
158
231
|
function buildDealsQuery(q, flags) {
|
|
159
232
|
const params = new URLSearchParams();
|
|
160
233
|
if (q) params.set("q", q);
|
|
161
|
-
for (const key of ["companyName", "founder", "owner", "status", "theirStage", "stage", "limit", "offset"]) {
|
|
234
|
+
for (const key of ["companyName", "founder", "owner", "status", "theirStage", "stage", "sourceDirection", "limit", "offset"]) {
|
|
162
235
|
if (flags[key] !== undefined && flags[key] !== true) {
|
|
163
236
|
params.set(key, String(flags[key]));
|
|
164
237
|
}
|
|
165
238
|
}
|
|
239
|
+
if (flags["source-direction"] !== undefined && flags["source-direction"] !== true) {
|
|
240
|
+
params.set("sourceDirection", String(flags["source-direction"]));
|
|
241
|
+
}
|
|
166
242
|
return params;
|
|
167
243
|
}
|
|
168
244
|
|
|
@@ -185,6 +261,7 @@ async function searchDeals(q, flags) {
|
|
|
185
261
|
status: flags.status,
|
|
186
262
|
theirStage: flags.theirStage,
|
|
187
263
|
stage: flags.stage,
|
|
264
|
+
sourceDirection: flags.sourceDirection || flags["source-direction"],
|
|
188
265
|
};
|
|
189
266
|
const filtered = result.filter((d) => clientSideMatch(d, filters));
|
|
190
267
|
const limit = Number(flags.limit) > 0 ? Number(flags.limit) : 200;
|
|
@@ -277,6 +354,8 @@ Agent onboarding (run once on first install):
|
|
|
277
354
|
llama skills search "pipeline update" # discover relevant runtime skills
|
|
278
355
|
llama skills show llama-pipeline # read a skill from Command
|
|
279
356
|
llama explain <url-or-object> # explain Command URL/object status + lifecycle
|
|
357
|
+
llama eval good|bad --last # mark the latest CLI/MCP result for eval
|
|
358
|
+
llama eval add "<query>" --expect wiki:<slug>|deal:<uuid>
|
|
280
359
|
|
|
281
360
|
External pitch — talk to Llama Ventures' intake agent (no token required):
|
|
282
361
|
llama pitch start --name "Jane Doe" --email "jane@acme.ai"
|
|
@@ -297,15 +376,16 @@ auto-detects \`gcloud auth print-identity-token\` and uses Bearer auth.
|
|
|
297
376
|
Manually-set \`llc_\` tokens are used as a fallback.
|
|
298
377
|
|
|
299
378
|
Deals:
|
|
300
|
-
llama deal create "Company" --source <name> --description "..." --website https://...
|
|
379
|
+
llama deal create "Company" --source <name> --source-direction Inbound|Outbound --description "..." --status Outreached|Sourced --website https://...
|
|
301
380
|
llama deal show <dealId>
|
|
302
381
|
llama deal feed <dealId> # every contribution (facts + notes), human-typed or assistant-drafted, newest first
|
|
303
382
|
llama deal update <dealId> <field> <value>
|
|
304
|
-
Writable fields: status, theirStage, stage, notes, dealOwner, source,
|
|
383
|
+
Writable fields: status, theirStage, stage, notes, dealOwner, source, sourceDirection,
|
|
305
384
|
description, website, location, founders, founderInfo, proposedAmount,
|
|
306
385
|
roundSize, valuation, deckLink, folderUrl, sector, subsector,
|
|
307
386
|
foundedYear, leadInvestor, investors, agentActive.
|
|
308
387
|
e.g. llama deal update <dealId> website https://acme.ai
|
|
388
|
+
llama deal update <dealId> status Outreached
|
|
309
389
|
llama deal update <dealId> sector "Developer Tools"
|
|
310
390
|
llama deal update <dealId> foundedYear 2024
|
|
311
391
|
llama deal update <dealId> leadInvestor "Acme Capital"
|
|
@@ -320,7 +400,7 @@ Deals:
|
|
|
320
400
|
string. Audited to deal_events as field_change "extra.<key>".
|
|
321
401
|
llama deal extra unset <dealId> <key> # delete the key (admin)
|
|
322
402
|
llama deal search <query> [--founder name] [--owner <user-key>] [--status Diligence]
|
|
323
|
-
[--theirStage Raising] [--stage Seed]
|
|
403
|
+
[--theirStage Raising] [--stage Seed] [--source-direction Inbound]
|
|
324
404
|
[--limit 200] [--offset 0]
|
|
325
405
|
llama deal list [--owner ...] [--status ...] [...same flags as search]
|
|
326
406
|
|
|
@@ -519,6 +599,7 @@ Common:
|
|
|
519
599
|
llama agent bootstrap live Llama OS skill manifest from Command
|
|
520
600
|
llama skills search "<query>" discover which skill to read
|
|
521
601
|
llama explain <url-or-object> explain Command URLs, 404s, deleted objects
|
|
602
|
+
llama eval bad --last mark latest CLI/MCP result as an eval candidate
|
|
522
603
|
|
|
523
604
|
Command groups — run \`llama help <group>\` for that group's commands:
|
|
524
605
|
deal create · show · feed · update · enrich · search · collaborators · links · delete
|
|
@@ -526,6 +607,7 @@ Command groups — run \`llama help <group>\` for that group's commands:
|
|
|
526
607
|
facts deal facts + skill corrections (the sourced, trust-rated layer)
|
|
527
608
|
timeline timeline · posts · mentions
|
|
528
609
|
wiki cross-deal knowledge entries (markdown or HTML)
|
|
610
|
+
eval mark real CLI/MCP searches good/bad or add a golden-query candidate
|
|
529
611
|
memo long-form HTML investment memo
|
|
530
612
|
html deal-specific HTML artifacts (/deals/<id>/browse/<slug>)
|
|
531
613
|
pitch external founder intake (no token needed)
|
|
@@ -859,18 +941,15 @@ async function runPitchRepl() {
|
|
|
859
941
|
async function main() {
|
|
860
942
|
const [area, action, ...rest] = process.argv.slice(2);
|
|
861
943
|
if (area === "--version" || area === "-v" || area === "version") {
|
|
862
|
-
const { createRequire } = await import("module");
|
|
863
|
-
const requireFromHere = createRequire(import.meta.url);
|
|
864
|
-
const { version } = requireFromHere("../package.json");
|
|
865
944
|
// `llama version --check` — explicitly check npm for a newer release and
|
|
866
945
|
// print the upgrade line (or "up to date"). Lets an agent surface the
|
|
867
946
|
// nudge on demand, separate from the throttled, TTY-gated auto-nudge.
|
|
868
947
|
if (action === "--check" || action === "check") {
|
|
869
948
|
const nudge = await getUpdateNudge();
|
|
870
|
-
console.log(nudge || `llama CLI ${
|
|
949
|
+
console.log(nudge || `llama CLI ${PKG_VERSION} — up to date`);
|
|
871
950
|
return;
|
|
872
951
|
}
|
|
873
|
-
console.log(
|
|
952
|
+
console.log(PKG_VERSION);
|
|
874
953
|
return;
|
|
875
954
|
}
|
|
876
955
|
if (!area || area === "help" || area === "--help" || area === "-h") {
|
|
@@ -891,12 +970,13 @@ async function main() {
|
|
|
891
970
|
return;
|
|
892
971
|
}
|
|
893
972
|
|
|
894
|
-
// `llama agent-onboard` —
|
|
895
|
-
//
|
|
896
|
-
//
|
|
973
|
+
// `llama agent-onboard` — fetch the server-owned Agent Runtime Contract
|
|
974
|
+
// so an AI agent reads the current Llama Ventures workflow contract. The
|
|
975
|
+
// bundled AGENT_BRIEFING.md is now only a fallback when the server route
|
|
976
|
+
// is unavailable during rollout.
|
|
897
977
|
// Also: `llama agent onboard` (two-word form) for symmetry.
|
|
898
978
|
//
|
|
899
|
-
// Gated behind
|
|
979
|
+
// Gated behind Command auth — without valid credentials we print a short
|
|
900
980
|
// bootstrap stub instead. Stops unauthenticated callers from harvesting
|
|
901
981
|
// internal command surface / workflow conventions just by running the
|
|
902
982
|
// public CLI.
|
|
@@ -906,39 +986,24 @@ async function main() {
|
|
|
906
986
|
) {
|
|
907
987
|
const headers = await getAuthHeaders();
|
|
908
988
|
if (Object.keys(headers).length === 0) {
|
|
909
|
-
console.log(
|
|
910
|
-
`Llama Ventures team onboarding requires credentials.
|
|
911
|
-
|
|
912
|
-
Team member?
|
|
913
|
-
- Run \`gcloud auth login\` with your @llamaventures.vc account, OR
|
|
914
|
-
- Mint a token at https://command.llamaventures.vc/settings/tokens
|
|
915
|
-
then \`llama token set <llc_...>\`.
|
|
916
|
-
Re-run \`llama agent-onboard\` after — the workflow contract will print.
|
|
917
|
-
|
|
918
|
-
Founder or external visitor (no Llama account)?
|
|
919
|
-
Run \`llama pitch start --name "Your Name" --email "you@company.com"\`
|
|
920
|
-
to chat with our intake agent — no token required.`
|
|
921
|
-
);
|
|
989
|
+
console.log(agentOnboardNoAuthMessage());
|
|
922
990
|
return;
|
|
923
991
|
}
|
|
924
992
|
try {
|
|
925
|
-
await
|
|
993
|
+
const briefing = await fetchServerAgentBriefing();
|
|
994
|
+
process.stdout.write(briefing || readBriefing());
|
|
926
995
|
} catch (e) {
|
|
927
996
|
const msg = e?.message || "";
|
|
928
997
|
if (msg.includes("Error[UNAUTHORIZED]") || msg.includes("Error[NO_AUTH]")) {
|
|
929
|
-
console.log(
|
|
930
|
-
`Llama Ventures team onboarding requires valid credentials.
|
|
931
|
-
|
|
932
|
-
Server rejected the credentials we sent. Re-mint at
|
|
933
|
-
https://command.llamaventures.vc/settings/tokens, run
|
|
934
|
-
\`llama token set <llc_...>\`, then re-run \`llama agent-onboard\`.`
|
|
935
|
-
);
|
|
998
|
+
console.log(agentOnboardRejectedMessage());
|
|
936
999
|
process.exitCode = 1;
|
|
937
1000
|
return;
|
|
938
1001
|
}
|
|
939
|
-
|
|
1002
|
+
process.stderr.write(
|
|
1003
|
+
`warning: server agent briefing unavailable (${msg}); using bundled fallback.\n`,
|
|
1004
|
+
);
|
|
1005
|
+
process.stdout.write(readBriefing());
|
|
940
1006
|
}
|
|
941
|
-
process.stdout.write(readBriefing());
|
|
942
1007
|
return;
|
|
943
1008
|
}
|
|
944
1009
|
|
|
@@ -948,6 +1013,7 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
948
1013
|
if (area === "agent" && action === "bootstrap") {
|
|
949
1014
|
const { flags } = parseFlags(rest, ["json", "limit"]);
|
|
950
1015
|
const params = new URLSearchParams();
|
|
1016
|
+
params.set("clientVersion", PKG_VERSION);
|
|
951
1017
|
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
952
1018
|
const manifest = await request("GET", `/api/agent/manifest${params.toString() ? `?${params}` : ""}`);
|
|
953
1019
|
if (flags.json) {
|
|
@@ -1031,6 +1097,43 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1031
1097
|
return;
|
|
1032
1098
|
}
|
|
1033
1099
|
|
|
1100
|
+
if (area === "eval") {
|
|
1101
|
+
const sub = action;
|
|
1102
|
+
if (sub === "good" || sub === "bad") {
|
|
1103
|
+
const { flags, positional } = parseFlags(rest, [
|
|
1104
|
+
"last",
|
|
1105
|
+
"event",
|
|
1106
|
+
"reason",
|
|
1107
|
+
"expect",
|
|
1108
|
+
"surface",
|
|
1109
|
+
"privacy",
|
|
1110
|
+
]);
|
|
1111
|
+
const q = positional.join(" ").trim();
|
|
1112
|
+
print(await submitEvalFeedback(sub, flags, q));
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
if (sub === "add") {
|
|
1116
|
+
const { flags, positional } = parseFlags(rest, [
|
|
1117
|
+
"event",
|
|
1118
|
+
"expect",
|
|
1119
|
+
"reason",
|
|
1120
|
+
"surface",
|
|
1121
|
+
"privacy",
|
|
1122
|
+
]);
|
|
1123
|
+
const q = positional.join(" ").trim();
|
|
1124
|
+
if (!q && !flags.event) {
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
`Usage: llama eval add "<query>" --surface deal|wiki --expect wiki:<slug>|deal:<uuid>`,
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
print(await submitEvalFeedback("add", flags, q));
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
"Usage: llama eval good|bad [--last] [--reason ...] OR llama eval add \"<query>\" --expect wiki:<slug>|deal:<uuid>",
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1034
1137
|
// `llama pitch ...` — external founder-pitch family. No Llama token
|
|
1035
1138
|
// required; bootstraps a session against /api/external/* via PoW + cookie.
|
|
1036
1139
|
// See lib/external.mjs and AGENT_BRIEFING.md for the full surface.
|
|
@@ -1243,6 +1346,7 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1243
1346
|
const body = {
|
|
1244
1347
|
companyName,
|
|
1245
1348
|
source: flags.source,
|
|
1349
|
+
sourceDirection: flags.sourceDirection || flags["source-direction"],
|
|
1246
1350
|
description: flags.description,
|
|
1247
1351
|
website: flags.website,
|
|
1248
1352
|
notes: flags.notes,
|
|
@@ -1319,7 +1423,7 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1319
1423
|
const q = positional.join(" ").trim();
|
|
1320
1424
|
if (!q && Object.keys(flags).length === 0) {
|
|
1321
1425
|
throw new Error(
|
|
1322
|
-
`Usage: llama deal search <query> [--founder ...] [--owner ...] [--status ...] [--stage ...] [--limit N]`
|
|
1426
|
+
`Usage: llama deal search <query> [--founder ...] [--owner ...] [--status ...] [--stage ...] [--source-direction Inbound|Outbound] [--limit N]`
|
|
1323
1427
|
);
|
|
1324
1428
|
}
|
|
1325
1429
|
print(await searchDeals(q, flags));
|