@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/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 ${version} — up to date`);
949
+ console.log(nudge || `llama CLI ${PKG_VERSION} — up to date`);
871
950
  return;
872
951
  }
873
- console.log(version);
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` — print the bundled AGENT_BRIEFING.md so an AI
895
- // agent reads it once and internalises the Llama Ventures workflow
896
- // contract. Same content the `agent_briefing` MCP prompt returns.
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 /api/me — without valid credentials we print a short
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 request("GET", "/api/me");
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
- throw e;
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));