@llamaventures/cli 1.6.1 → 1.7.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 +4 -0
- package/bin/llama-mcp.mjs +283 -1
- package/bin/llama.mjs +15 -4
- package/package.json +1 -1
package/AGENT_BRIEFING.md
CHANGED
|
@@ -157,6 +157,9 @@ llama deal list [--owner ...] [--status ...]
|
|
|
157
157
|
# Pipeline — write
|
|
158
158
|
llama deal create "Company" --description "..."
|
|
159
159
|
llama deal update <dealId> <field> <value>
|
|
160
|
+
# writable: status theirStage stage notes dealOwner source description website
|
|
161
|
+
# location founders proposedAmount roundSize valuation sector subsector
|
|
162
|
+
# foundedYear leadInvestor investors (each write logs a deal_events row)
|
|
160
163
|
|
|
161
164
|
# Brief blocks
|
|
162
165
|
llama brief blocks <dealId>
|
|
@@ -244,6 +247,7 @@ You can also fetch this exact briefing as an MCP prompt named `agent_briefing`.
|
|
|
244
247
|
## Boundaries (what NOT to do)
|
|
245
248
|
|
|
246
249
|
- **Don't impersonate a human's opinion.** Tag AI-generated content as `[AI · …]`.
|
|
250
|
+
- **Don't vouch for facts you haven't checked.** When you `add fact`, pass `--attested` only if you actually verified the claim against its source. Without it the fact is stored as *unverified* — that's the honest default, not a failure. You cannot mark a fact as human-confirmed; only a person can raise it there.
|
|
247
251
|
- **Don't use absolute language** ("only", "all", "best", "no one", "极") unless verifiable.
|
|
248
252
|
- **Don't bypass `llama` CLI / MCP for pipeline writes.** CSRF defence, rate limits, audit logs all flow through it.
|
|
249
253
|
- **Don't write to retired surfaces.** Google Sheet is read-only archive. Legacy `~/.llama-command/config.json` auto-migrates.
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -197,7 +197,8 @@ server.registerTool(
|
|
|
197
197
|
description:
|
|
198
198
|
"Update a single whitelisted field on a deal. Writable fields: status, theirStage, " +
|
|
199
199
|
"notes, stage, dealOwner, source, description, website, location, founders, " +
|
|
200
|
-
"proposedAmount, roundSize, valuation
|
|
200
|
+
"proposedAmount, roundSize, valuation, sector, subsector, foundedYear, leadInvestor, " +
|
|
201
|
+
"investors. Logs a field_change event in deal_events.",
|
|
201
202
|
inputSchema: {
|
|
202
203
|
dealId: z.string(),
|
|
203
204
|
field: z.string().describe("camelCase field name (see description for whitelist)"),
|
|
@@ -208,6 +209,82 @@ server.registerTool(
|
|
|
208
209
|
callApi("POST", "/api/deals/update", { dealId, field, value })
|
|
209
210
|
);
|
|
210
211
|
|
|
212
|
+
// ============================================================
|
|
213
|
+
// Deal facts (research substrate + trust ladder)
|
|
214
|
+
// ============================================================
|
|
215
|
+
|
|
216
|
+
server.registerTool(
|
|
217
|
+
"deal_fact_list",
|
|
218
|
+
{
|
|
219
|
+
description:
|
|
220
|
+
"List a deal's recorded facts (the research substrate). Each fact carries a " +
|
|
221
|
+
"category, a claim, a source, a confidence, and a trust rung (unverified → " +
|
|
222
|
+
"agent-verified → human-vouched → endorsed) plus who/what recorded it.",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
dealId: z.string(),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
async ({ dealId }) =>
|
|
228
|
+
callApi("GET", `/api/deals/${encodeURIComponent(dealId)}/facts`)
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
server.registerTool(
|
|
232
|
+
"deal_fact_add",
|
|
233
|
+
{
|
|
234
|
+
description:
|
|
235
|
+
"Record a factual claim about a deal. RESPONSIBILITY: set `attested` honestly — " +
|
|
236
|
+
"true ONLY if you actually verified the claim against its cited source (the fact " +
|
|
237
|
+
"is then stored at trust level 'agent-verified'); false/omitted if you are relaying " +
|
|
238
|
+
"something unconfirmed (stored 'unverified', which is the honest default). You CANNOT " +
|
|
239
|
+
"mark a fact as human-confirmed — only a person can raise it to 'human-vouched'. " +
|
|
240
|
+
"`confidence` is how certain the claim is; `attested` is whether YOU take responsibility " +
|
|
241
|
+
"for having checked it. category ∈ founders | financials | product | market | team | " +
|
|
242
|
+
"company_basics | risk | fundraise | milestone | meta.",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
dealId: z.string(),
|
|
245
|
+
category: z.string(),
|
|
246
|
+
claim: z.string(),
|
|
247
|
+
source: z.string().optional().describe("where you found this (URL, 'deck p3', 'LinkedIn')"),
|
|
248
|
+
confidence: z.enum(["high", "medium", "low"]).optional(),
|
|
249
|
+
attested: z
|
|
250
|
+
.boolean()
|
|
251
|
+
.optional()
|
|
252
|
+
.describe("true → stored 'agent-verified'; false/omitted → 'unverified'. Answer honestly."),
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
async ({ dealId, category, claim, source, confidence, attested }) =>
|
|
256
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/facts`, {
|
|
257
|
+
category,
|
|
258
|
+
claim,
|
|
259
|
+
source: source ?? "",
|
|
260
|
+
confidence: confidence ?? "medium",
|
|
261
|
+
attested: attested === true,
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
server.registerTool(
|
|
266
|
+
"deal_fact_verify",
|
|
267
|
+
{
|
|
268
|
+
description:
|
|
269
|
+
"Verify a recorded fact. status='confirmed' vouches for it (raises to 'human-vouched'); " +
|
|
270
|
+
"status='disputed' marks it contradicted. Trust-ladder guardrails apply server-side " +
|
|
271
|
+
"(external-org callers are capped at 'unverified'; only Partners reach 'endorsed'). " +
|
|
272
|
+
"Optionally pass correctedValue when disputing.",
|
|
273
|
+
inputSchema: {
|
|
274
|
+
dealId: z.string(),
|
|
275
|
+
factId: z.union([z.string(), z.number()]),
|
|
276
|
+
status: z.enum(["confirmed", "disputed"]),
|
|
277
|
+
correctedValue: z.string().optional(),
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
async ({ dealId, factId, status, correctedValue }) =>
|
|
281
|
+
callApi(
|
|
282
|
+
"PATCH",
|
|
283
|
+
`/api/deals/${encodeURIComponent(dealId)}/facts/${encodeURIComponent(String(factId))}`,
|
|
284
|
+
{ status, ...(correctedValue !== undefined ? { correctedValue } : {}) }
|
|
285
|
+
)
|
|
286
|
+
);
|
|
287
|
+
|
|
211
288
|
// ============================================================
|
|
212
289
|
// Brief blocks
|
|
213
290
|
// ============================================================
|
|
@@ -272,6 +349,108 @@ server.registerTool(
|
|
|
272
349
|
addBriefBlock(dealId, { type: "callout", tone, heading: heading ?? "", body })
|
|
273
350
|
);
|
|
274
351
|
|
|
352
|
+
server.registerTool(
|
|
353
|
+
"brief_edit",
|
|
354
|
+
{
|
|
355
|
+
description:
|
|
356
|
+
"Edit an existing brief block in place. Pass only the fields you want to change " +
|
|
357
|
+
"(heading/body/url/label/description/tone). Meta toggles: locked (protect from bulk " +
|
|
358
|
+
"overwrite), hidden (fold), sourceSection (route watcher writes). Snapshots the prior " +
|
|
359
|
+
"version to history (reversible via brief_restore_version).",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
dealId: z.string(),
|
|
362
|
+
blockId: z.string(),
|
|
363
|
+
heading: z.string().optional(),
|
|
364
|
+
body: z.string().optional(),
|
|
365
|
+
url: z.string().optional(),
|
|
366
|
+
label: z.string().optional(),
|
|
367
|
+
description: z.string().optional(),
|
|
368
|
+
tone: z.string().optional(),
|
|
369
|
+
locked: z.boolean().optional(),
|
|
370
|
+
hidden: z.boolean().optional(),
|
|
371
|
+
sourceSection: z.string().optional(),
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
async ({ dealId, blockId, heading, body, url, label, description, tone, locked, hidden, sourceSection }) => {
|
|
375
|
+
const patch = {};
|
|
376
|
+
for (const [k, v] of Object.entries({ heading, body, url, label, description, tone })) {
|
|
377
|
+
if (v !== undefined) patch[k] = v;
|
|
378
|
+
}
|
|
379
|
+
const meta = {};
|
|
380
|
+
if (locked !== undefined) meta.locked = locked;
|
|
381
|
+
if (hidden !== undefined) meta.hidden = hidden;
|
|
382
|
+
if (sourceSection !== undefined) meta.sourceSection = sourceSection;
|
|
383
|
+
if (Object.keys(meta).length > 0) patch.meta = meta;
|
|
384
|
+
return callApi(
|
|
385
|
+
"PATCH",
|
|
386
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}`,
|
|
387
|
+
patch
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
server.registerTool(
|
|
393
|
+
"brief_delete",
|
|
394
|
+
{
|
|
395
|
+
description:
|
|
396
|
+
"Soft-delete a brief block (reversible via brief_restore). Locked blocks are refused.",
|
|
397
|
+
inputSchema: { dealId: z.string(), blockId: z.string() },
|
|
398
|
+
},
|
|
399
|
+
async ({ dealId, blockId }) =>
|
|
400
|
+
callApi("DELETE", `/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}`)
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
server.registerTool(
|
|
404
|
+
"brief_restore",
|
|
405
|
+
{
|
|
406
|
+
description: "Restore a soft-deleted brief block.",
|
|
407
|
+
inputSchema: { dealId: z.string(), blockId: z.string() },
|
|
408
|
+
},
|
|
409
|
+
async ({ dealId, blockId }) =>
|
|
410
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/restore`)
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
server.registerTool(
|
|
414
|
+
"brief_history",
|
|
415
|
+
{
|
|
416
|
+
description:
|
|
417
|
+
"List the content-version history of a brief block (every overwrite is snapshotted). " +
|
|
418
|
+
"Use the returned history id with brief_restore_version.",
|
|
419
|
+
inputSchema: {
|
|
420
|
+
dealId: z.string(),
|
|
421
|
+
blockId: z.string(),
|
|
422
|
+
limit: z.number().optional(),
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
async ({ dealId, blockId, limit }) => {
|
|
426
|
+
const qs = limit ? `?limit=${encodeURIComponent(String(limit))}` : "";
|
|
427
|
+
return callApi(
|
|
428
|
+
"GET",
|
|
429
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/history${qs}`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
server.registerTool(
|
|
435
|
+
"brief_restore_version",
|
|
436
|
+
{
|
|
437
|
+
description:
|
|
438
|
+
"Restore a brief block to a specific historical version (find historyId via brief_history). " +
|
|
439
|
+
"Itself reversible — the outgoing version is snapshotted before replacement.",
|
|
440
|
+
inputSchema: {
|
|
441
|
+
dealId: z.string(),
|
|
442
|
+
blockId: z.string(),
|
|
443
|
+
historyId: z.number(),
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
async ({ dealId, blockId, historyId }) =>
|
|
447
|
+
callApi(
|
|
448
|
+
"POST",
|
|
449
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/history`,
|
|
450
|
+
{ history_id: historyId }
|
|
451
|
+
)
|
|
452
|
+
);
|
|
453
|
+
|
|
275
454
|
// ============================================================
|
|
276
455
|
// Wiki (knowledge base)
|
|
277
456
|
// ============================================================
|
|
@@ -482,6 +661,109 @@ server.registerTool(
|
|
|
482
661
|
}
|
|
483
662
|
);
|
|
484
663
|
|
|
664
|
+
server.registerTool(
|
|
665
|
+
"mentions_resolve",
|
|
666
|
+
{
|
|
667
|
+
description: "Mark an @-mention as resolved (clears it from the recipient's open cues).",
|
|
668
|
+
inputSchema: { mentionId: z.union([z.string(), z.number()]) },
|
|
669
|
+
},
|
|
670
|
+
async ({ mentionId }) =>
|
|
671
|
+
callApi("POST", `/api/mentions/${encodeURIComponent(String(mentionId))}/resolve`)
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
// ============================================================
|
|
675
|
+
// Skill corrections (persona-owner pushback workflow)
|
|
676
|
+
// ============================================================
|
|
677
|
+
|
|
678
|
+
server.registerTool(
|
|
679
|
+
"skill_correction_list",
|
|
680
|
+
{
|
|
681
|
+
description:
|
|
682
|
+
"List the recorded corrections (long-term rules) for a persona/skill. These shape how " +
|
|
683
|
+
"that persona's analysis is generated.",
|
|
684
|
+
inputSchema: {
|
|
685
|
+
skillSlug: z.string(),
|
|
686
|
+
includeDeleted: z.boolean().optional(),
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
async ({ skillSlug, includeDeleted }) => {
|
|
690
|
+
const params = new URLSearchParams({ skill: skillSlug });
|
|
691
|
+
if (includeDeleted) params.set("include_deleted", "1");
|
|
692
|
+
return callApi("GET", `/api/skill-corrections?${params}`);
|
|
693
|
+
}
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
server.registerTool(
|
|
697
|
+
"skill_correction_add",
|
|
698
|
+
{
|
|
699
|
+
description:
|
|
700
|
+
"Record a long-term correction rule for a persona/skill (e.g. 'always check burn multiple " +
|
|
701
|
+
"before commenting on efficiency'). ALWAYS reconfirm the distilled rule with the user before " +
|
|
702
|
+
"calling — this changes how the persona behaves going forward. Optionally tie it to the deal/" +
|
|
703
|
+
"block where it came up.",
|
|
704
|
+
inputSchema: {
|
|
705
|
+
skillSlug: z.string(),
|
|
706
|
+
correctionText: z.string(),
|
|
707
|
+
dealUuid: z.string().optional(),
|
|
708
|
+
blockId: z.string().optional(),
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
async ({ skillSlug, correctionText, dealUuid, blockId }) =>
|
|
712
|
+
callApi("POST", "/api/skill-corrections", {
|
|
713
|
+
skill_slug: skillSlug,
|
|
714
|
+
correction_text: correctionText,
|
|
715
|
+
triggered_in_deal_uuid: dealUuid ?? null,
|
|
716
|
+
triggered_in_block_id: blockId ?? null,
|
|
717
|
+
})
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
server.registerTool(
|
|
721
|
+
"skill_correction_delete",
|
|
722
|
+
{
|
|
723
|
+
description: "Soft-delete a recorded skill correction by id.",
|
|
724
|
+
inputSchema: { id: z.union([z.string(), z.number()]) },
|
|
725
|
+
},
|
|
726
|
+
async ({ id }) =>
|
|
727
|
+
callApi("DELETE", `/api/skill-corrections/${encodeURIComponent(String(id))}`)
|
|
728
|
+
);
|
|
729
|
+
|
|
730
|
+
// ============================================================
|
|
731
|
+
// Brief / persona refresh (signal-driven re-evaluation)
|
|
732
|
+
// ============================================================
|
|
733
|
+
|
|
734
|
+
server.registerTool(
|
|
735
|
+
"deal_refresh_brief",
|
|
736
|
+
{
|
|
737
|
+
description:
|
|
738
|
+
"Trigger a stale-section re-evaluation of a deal's brief. Pass force=true to bypass the " +
|
|
739
|
+
"debounce. Returns a runId (or null if debounced / deal inactive).",
|
|
740
|
+
inputSchema: {
|
|
741
|
+
dealId: z.string(),
|
|
742
|
+
force: z.boolean().optional(),
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
async ({ dealId, force }) =>
|
|
746
|
+
callApi(
|
|
747
|
+
"POST",
|
|
748
|
+
`/api/deals/${encodeURIComponent(dealId)}/refresh-brief${force ? "?force=1" : ""}`
|
|
749
|
+
)
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
server.registerTool(
|
|
753
|
+
"deal_refresh_persona",
|
|
754
|
+
{
|
|
755
|
+
description:
|
|
756
|
+
"Regenerate one persona's analysis section for a deal. persona ∈ gavin | kyle | jack | " +
|
|
757
|
+
"david | bryan | herman | hongjiang | liuyi | kevin. Returns a runId (or null if debounced).",
|
|
758
|
+
inputSchema: {
|
|
759
|
+
dealId: z.string(),
|
|
760
|
+
persona: z.string(),
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
async ({ dealId, persona }) =>
|
|
764
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/refresh-persona`, { persona })
|
|
765
|
+
);
|
|
766
|
+
|
|
485
767
|
// ============================================================
|
|
486
768
|
// External pitch (founder intake) — no Llama Command token required
|
|
487
769
|
// ============================================================
|
package/bin/llama.mjs
CHANGED
|
@@ -226,6 +226,13 @@ Deals:
|
|
|
226
226
|
llama deal create "Company" --source <name> --description "..." --website https://...
|
|
227
227
|
llama deal show <dealId>
|
|
228
228
|
llama deal update <dealId> <field> <value>
|
|
229
|
+
Writable fields: status, theirStage, stage, notes, dealOwner, source,
|
|
230
|
+
description, website, location, founders, proposedAmount, roundSize,
|
|
231
|
+
valuation, sector, subsector, foundedYear, leadInvestor, investors.
|
|
232
|
+
e.g. llama deal update <dealId> website https://acme.ai
|
|
233
|
+
llama deal update <dealId> sector "Developer Tools"
|
|
234
|
+
llama deal update <dealId> foundedYear 2024
|
|
235
|
+
llama deal update <dealId> leadInvestor "Acme Capital"
|
|
229
236
|
llama deal search <query> [--founder name] [--owner <user-key>] [--status Diligence]
|
|
230
237
|
[--theirStage Raising] [--stage Seed]
|
|
231
238
|
[--limit 200] [--offset 0]
|
|
@@ -307,7 +314,7 @@ Deal soft-delete / restore / trash list:
|
|
|
307
314
|
|
|
308
315
|
Deal facts (AI-extracted or human-asserted, with verification):
|
|
309
316
|
llama deal fact list <dealId> # ⚠ session-only on server today
|
|
310
|
-
llama deal fact add <dealId> --category <cat> --claim "<text>" [--source <url>] [--confidence high|medium|low]
|
|
317
|
+
llama deal fact add <dealId> --category <cat> --claim "<text>" [--source <url>] [--confidence high|medium|low] [--attested]
|
|
311
318
|
llama deal fact verify <dealId> <factId> --status confirmed|disputed [--corrected-value "..."]
|
|
312
319
|
|
|
313
320
|
Skill corrections (persona-owner pushback — read by persona-watcher):
|
|
@@ -1147,14 +1154,18 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1147
1154
|
if (!flags.category || !flags.claim) {
|
|
1148
1155
|
throw new Error(
|
|
1149
1156
|
`Usage: llama deal fact add <dealId> --category <cat> --claim "<text>" ` +
|
|
1150
|
-
`[--source <url>] [--confidence high|medium|low]`
|
|
1157
|
+
`[--source <url>] [--confidence high|medium|low] [--attested]`
|
|
1151
1158
|
);
|
|
1152
1159
|
}
|
|
1160
|
+
// --attested: the caller takes responsibility that this is accurate
|
|
1161
|
+
// (verified against the source). With it, the fact is recorded as
|
|
1162
|
+
// vouched; without it, it stays unverified. Declare honestly.
|
|
1153
1163
|
print(await request("POST", `/api/deals/${encodeURIComponent(dealId)}/facts`, {
|
|
1154
1164
|
category: String(flags.category),
|
|
1155
1165
|
claim: String(flags.claim),
|
|
1156
1166
|
source: flags.source ? String(flags.source) : "",
|
|
1157
1167
|
confidence: flags.confidence ? String(flags.confidence) : "medium",
|
|
1168
|
+
attested: flags.attested === true,
|
|
1158
1169
|
}));
|
|
1159
1170
|
return;
|
|
1160
1171
|
}
|
|
@@ -2160,8 +2171,8 @@ Routing — is this the right command?
|
|
|
2160
2171
|
const { flags } = parseFlags(rest.slice(1), knownFlags);
|
|
2161
2172
|
|
|
2162
2173
|
// --slug is the natural agent guess (DB column is `document_slug`).
|
|
2163
|
-
// Accept it as an alias for --doc so the failure mode
|
|
2164
|
-
//
|
|
2174
|
+
// Accept it as an alias for --doc so the earlier failure mode
|
|
2175
|
+
// (silent fall-through to 'main') can't happen again.
|
|
2165
2176
|
if (flags.slug && !flags.doc) {
|
|
2166
2177
|
process.stderr.write("note: --slug accepted as alias for --doc.\n");
|
|
2167
2178
|
flags.doc = flags.slug;
|