@llamaventures/cli 1.6.1 → 1.8.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 +17 -0
- package/bin/llama-mcp.mjs +299 -1
- package/bin/llama.mjs +110 -8
- package/package.json +1 -1
package/AGENT_BRIEFING.md
CHANGED
|
@@ -29,6 +29,19 @@ Don't:
|
|
|
29
29
|
|
|
30
30
|
Conversation produces value → that value flows somewhere. This is not optional.
|
|
31
31
|
|
|
32
|
+
### When someone gives you info about a deal (the most common case)
|
|
33
|
+
|
|
34
|
+
A teammate says "I just met them and heard…" or pastes a chunk of notes. Your job: get it into the right deal, in the right layer, and confirm it's right. Three steps:
|
|
35
|
+
|
|
36
|
+
1. **Find the deal** — `llama deal search "<name>"` (Pipeline First). New name → offer to create it.
|
|
37
|
+
2. **Split what they gave you into two kinds** — this is the whole data model:
|
|
38
|
+
- **Verifiable claims → facts.** `llama deal fact add <dealId> --category <cat> --claim "…" --source "<where it came from>"`. A claim someone *relayed* ("their ARR is $3M", "raised from a16z") is a fact at **unverified** trust — it's hearsay until checked. Pass `--attested` ONLY if you actually verified it against a source yourself.
|
|
39
|
+
- **Their judgment / impression → a note.** `llama post <dealId> "…"`. "Founder seemed evasive", "I'd lean pass", "worth a second meeting" — opinion, not fact. Attributed, never "verified".
|
|
40
|
+
- A pasted blob → pull the verifiable claims out as facts, capture their take as a note.
|
|
41
|
+
3. **Confirm accuracy.** After filing, tell them in plain language what you recorded and where, and ask if it's right. Facts you add stay **unverified** until a human confirms them (then they rise to human-vouched) — the confirmation IS the trust step. Never silently mark something verified.
|
|
42
|
+
|
|
43
|
+
Why split it: facts and opinions live in different layers so the deal keeps one clean **source of truth** (facts, sourced + trust-rated) separate from people's **takes** (notes). The four layers — facts / notes / brief (AI's synthesis) / timeline — are documented in Llama Command's `docs/SCHEMA.md`.
|
|
44
|
+
|
|
32
45
|
### Where does this HTML / thesis / artifact go? (decision tree)
|
|
33
46
|
|
|
34
47
|
When the user hands you an HTML page, thesis write-up, market map, dashboard, IC memo, sector landscape — anything that isn't a one-off note — pick the destination in this order. **Llama Command native (the workbench) outranks Netlify for everything internal.** Only escape to Netlify when the page is truly going to a public / founder-facing URL.
|
|
@@ -157,6 +170,9 @@ llama deal list [--owner ...] [--status ...]
|
|
|
157
170
|
# Pipeline — write
|
|
158
171
|
llama deal create "Company" --description "..."
|
|
159
172
|
llama deal update <dealId> <field> <value>
|
|
173
|
+
# writable: status theirStage stage notes dealOwner source description website
|
|
174
|
+
# location founders proposedAmount roundSize valuation sector subsector
|
|
175
|
+
# foundedYear leadInvestor investors (each write logs a deal_events row)
|
|
160
176
|
|
|
161
177
|
# Brief blocks
|
|
162
178
|
llama brief blocks <dealId>
|
|
@@ -244,6 +260,7 @@ You can also fetch this exact briefing as an MCP prompt named `agent_briefing`.
|
|
|
244
260
|
## Boundaries (what NOT to do)
|
|
245
261
|
|
|
246
262
|
- **Don't impersonate a human's opinion.** Tag AI-generated content as `[AI · …]`.
|
|
263
|
+
- **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
264
|
- **Don't use absolute language** ("only", "all", "best", "no one", "极") unless verifiable.
|
|
248
265
|
- **Don't bypass `llama` CLI / MCP for pipeline writes.** CSRF defence, rate limits, audit logs all flow through it.
|
|
249
266
|
- **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
|
// ============================================================
|
|
@@ -226,6 +303,22 @@ server.registerTool(
|
|
|
226
303
|
callApi("GET", `/api/deals/${encodeURIComponent(dealId)}/blocks`)
|
|
227
304
|
);
|
|
228
305
|
|
|
306
|
+
server.registerTool(
|
|
307
|
+
"deal_feed",
|
|
308
|
+
{
|
|
309
|
+
description:
|
|
310
|
+
"The unified, time-sorted stream of everything a HUMAN has added to a deal — " +
|
|
311
|
+
"facts + notes/discussion + legacy posts, merged at query time, newest first. " +
|
|
312
|
+
"Excludes AI-generated content. Each item: kind (fact|note), ts, who, text, " +
|
|
313
|
+
"and for facts: source + trust rung + category.",
|
|
314
|
+
inputSchema: {
|
|
315
|
+
dealId: z.string(),
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
async ({ dealId }) =>
|
|
319
|
+
callApi("GET", `/api/deals/${encodeURIComponent(dealId)}/feed`)
|
|
320
|
+
);
|
|
321
|
+
|
|
229
322
|
server.registerTool(
|
|
230
323
|
"brief_add_text",
|
|
231
324
|
{
|
|
@@ -272,6 +365,108 @@ server.registerTool(
|
|
|
272
365
|
addBriefBlock(dealId, { type: "callout", tone, heading: heading ?? "", body })
|
|
273
366
|
);
|
|
274
367
|
|
|
368
|
+
server.registerTool(
|
|
369
|
+
"brief_edit",
|
|
370
|
+
{
|
|
371
|
+
description:
|
|
372
|
+
"Edit an existing brief block in place. Pass only the fields you want to change " +
|
|
373
|
+
"(heading/body/url/label/description/tone). Meta toggles: locked (protect from bulk " +
|
|
374
|
+
"overwrite), hidden (fold), sourceSection (route watcher writes). Snapshots the prior " +
|
|
375
|
+
"version to history (reversible via brief_restore_version).",
|
|
376
|
+
inputSchema: {
|
|
377
|
+
dealId: z.string(),
|
|
378
|
+
blockId: z.string(),
|
|
379
|
+
heading: z.string().optional(),
|
|
380
|
+
body: z.string().optional(),
|
|
381
|
+
url: z.string().optional(),
|
|
382
|
+
label: z.string().optional(),
|
|
383
|
+
description: z.string().optional(),
|
|
384
|
+
tone: z.string().optional(),
|
|
385
|
+
locked: z.boolean().optional(),
|
|
386
|
+
hidden: z.boolean().optional(),
|
|
387
|
+
sourceSection: z.string().optional(),
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
async ({ dealId, blockId, heading, body, url, label, description, tone, locked, hidden, sourceSection }) => {
|
|
391
|
+
const patch = {};
|
|
392
|
+
for (const [k, v] of Object.entries({ heading, body, url, label, description, tone })) {
|
|
393
|
+
if (v !== undefined) patch[k] = v;
|
|
394
|
+
}
|
|
395
|
+
const meta = {};
|
|
396
|
+
if (locked !== undefined) meta.locked = locked;
|
|
397
|
+
if (hidden !== undefined) meta.hidden = hidden;
|
|
398
|
+
if (sourceSection !== undefined) meta.sourceSection = sourceSection;
|
|
399
|
+
if (Object.keys(meta).length > 0) patch.meta = meta;
|
|
400
|
+
return callApi(
|
|
401
|
+
"PATCH",
|
|
402
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}`,
|
|
403
|
+
patch
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
server.registerTool(
|
|
409
|
+
"brief_delete",
|
|
410
|
+
{
|
|
411
|
+
description:
|
|
412
|
+
"Soft-delete a brief block (reversible via brief_restore). Locked blocks are refused.",
|
|
413
|
+
inputSchema: { dealId: z.string(), blockId: z.string() },
|
|
414
|
+
},
|
|
415
|
+
async ({ dealId, blockId }) =>
|
|
416
|
+
callApi("DELETE", `/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}`)
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
server.registerTool(
|
|
420
|
+
"brief_restore",
|
|
421
|
+
{
|
|
422
|
+
description: "Restore a soft-deleted brief block.",
|
|
423
|
+
inputSchema: { dealId: z.string(), blockId: z.string() },
|
|
424
|
+
},
|
|
425
|
+
async ({ dealId, blockId }) =>
|
|
426
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/restore`)
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
server.registerTool(
|
|
430
|
+
"brief_history",
|
|
431
|
+
{
|
|
432
|
+
description:
|
|
433
|
+
"List the content-version history of a brief block (every overwrite is snapshotted). " +
|
|
434
|
+
"Use the returned history id with brief_restore_version.",
|
|
435
|
+
inputSchema: {
|
|
436
|
+
dealId: z.string(),
|
|
437
|
+
blockId: z.string(),
|
|
438
|
+
limit: z.number().optional(),
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
async ({ dealId, blockId, limit }) => {
|
|
442
|
+
const qs = limit ? `?limit=${encodeURIComponent(String(limit))}` : "";
|
|
443
|
+
return callApi(
|
|
444
|
+
"GET",
|
|
445
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/history${qs}`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
server.registerTool(
|
|
451
|
+
"brief_restore_version",
|
|
452
|
+
{
|
|
453
|
+
description:
|
|
454
|
+
"Restore a brief block to a specific historical version (find historyId via brief_history). " +
|
|
455
|
+
"Itself reversible — the outgoing version is snapshotted before replacement.",
|
|
456
|
+
inputSchema: {
|
|
457
|
+
dealId: z.string(),
|
|
458
|
+
blockId: z.string(),
|
|
459
|
+
historyId: z.number(),
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
async ({ dealId, blockId, historyId }) =>
|
|
463
|
+
callApi(
|
|
464
|
+
"POST",
|
|
465
|
+
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}/history`,
|
|
466
|
+
{ history_id: historyId }
|
|
467
|
+
)
|
|
468
|
+
);
|
|
469
|
+
|
|
275
470
|
// ============================================================
|
|
276
471
|
// Wiki (knowledge base)
|
|
277
472
|
// ============================================================
|
|
@@ -482,6 +677,109 @@ server.registerTool(
|
|
|
482
677
|
}
|
|
483
678
|
);
|
|
484
679
|
|
|
680
|
+
server.registerTool(
|
|
681
|
+
"mentions_resolve",
|
|
682
|
+
{
|
|
683
|
+
description: "Mark an @-mention as resolved (clears it from the recipient's open cues).",
|
|
684
|
+
inputSchema: { mentionId: z.union([z.string(), z.number()]) },
|
|
685
|
+
},
|
|
686
|
+
async ({ mentionId }) =>
|
|
687
|
+
callApi("POST", `/api/mentions/${encodeURIComponent(String(mentionId))}/resolve`)
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
// ============================================================
|
|
691
|
+
// Skill corrections (persona-owner pushback workflow)
|
|
692
|
+
// ============================================================
|
|
693
|
+
|
|
694
|
+
server.registerTool(
|
|
695
|
+
"skill_correction_list",
|
|
696
|
+
{
|
|
697
|
+
description:
|
|
698
|
+
"List the recorded corrections (long-term rules) for a persona/skill. These shape how " +
|
|
699
|
+
"that persona's analysis is generated.",
|
|
700
|
+
inputSchema: {
|
|
701
|
+
skillSlug: z.string(),
|
|
702
|
+
includeDeleted: z.boolean().optional(),
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
async ({ skillSlug, includeDeleted }) => {
|
|
706
|
+
const params = new URLSearchParams({ skill: skillSlug });
|
|
707
|
+
if (includeDeleted) params.set("include_deleted", "1");
|
|
708
|
+
return callApi("GET", `/api/skill-corrections?${params}`);
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
server.registerTool(
|
|
713
|
+
"skill_correction_add",
|
|
714
|
+
{
|
|
715
|
+
description:
|
|
716
|
+
"Record a long-term correction rule for a persona/skill (e.g. 'always check burn multiple " +
|
|
717
|
+
"before commenting on efficiency'). ALWAYS reconfirm the distilled rule with the user before " +
|
|
718
|
+
"calling — this changes how the persona behaves going forward. Optionally tie it to the deal/" +
|
|
719
|
+
"block where it came up.",
|
|
720
|
+
inputSchema: {
|
|
721
|
+
skillSlug: z.string(),
|
|
722
|
+
correctionText: z.string(),
|
|
723
|
+
dealUuid: z.string().optional(),
|
|
724
|
+
blockId: z.string().optional(),
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
async ({ skillSlug, correctionText, dealUuid, blockId }) =>
|
|
728
|
+
callApi("POST", "/api/skill-corrections", {
|
|
729
|
+
skill_slug: skillSlug,
|
|
730
|
+
correction_text: correctionText,
|
|
731
|
+
triggered_in_deal_uuid: dealUuid ?? null,
|
|
732
|
+
triggered_in_block_id: blockId ?? null,
|
|
733
|
+
})
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
server.registerTool(
|
|
737
|
+
"skill_correction_delete",
|
|
738
|
+
{
|
|
739
|
+
description: "Soft-delete a recorded skill correction by id.",
|
|
740
|
+
inputSchema: { id: z.union([z.string(), z.number()]) },
|
|
741
|
+
},
|
|
742
|
+
async ({ id }) =>
|
|
743
|
+
callApi("DELETE", `/api/skill-corrections/${encodeURIComponent(String(id))}`)
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
// ============================================================
|
|
747
|
+
// Brief / persona refresh (signal-driven re-evaluation)
|
|
748
|
+
// ============================================================
|
|
749
|
+
|
|
750
|
+
server.registerTool(
|
|
751
|
+
"deal_refresh_brief",
|
|
752
|
+
{
|
|
753
|
+
description:
|
|
754
|
+
"Trigger a stale-section re-evaluation of a deal's brief. Pass force=true to bypass the " +
|
|
755
|
+
"debounce. Returns a runId (or null if debounced / deal inactive).",
|
|
756
|
+
inputSchema: {
|
|
757
|
+
dealId: z.string(),
|
|
758
|
+
force: z.boolean().optional(),
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
async ({ dealId, force }) =>
|
|
762
|
+
callApi(
|
|
763
|
+
"POST",
|
|
764
|
+
`/api/deals/${encodeURIComponent(dealId)}/refresh-brief${force ? "?force=1" : ""}`
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
server.registerTool(
|
|
769
|
+
"deal_refresh_persona",
|
|
770
|
+
{
|
|
771
|
+
description:
|
|
772
|
+
"Regenerate one persona's analysis section for a deal. persona ∈ gavin | kyle | jack | " +
|
|
773
|
+
"david | bryan | herman | hongjiang | liuyi | kevin. Returns a runId (or null if debounced).",
|
|
774
|
+
inputSchema: {
|
|
775
|
+
dealId: z.string(),
|
|
776
|
+
persona: z.string(),
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
async ({ dealId, persona }) =>
|
|
780
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/refresh-persona`, { persona })
|
|
781
|
+
);
|
|
782
|
+
|
|
485
783
|
// ============================================================
|
|
486
784
|
// External pitch (founder intake) — no Llama Command token required
|
|
487
785
|
// ============================================================
|
package/bin/llama.mjs
CHANGED
|
@@ -198,8 +198,7 @@ async function searchDeals(q, flags) {
|
|
|
198
198
|
return result;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
console.log(`Llama Command CLI
|
|
201
|
+
const HELP_FULL = `Llama Command CLI
|
|
203
202
|
|
|
204
203
|
Agent onboarding (run once on first install):
|
|
205
204
|
llama agent-onboard # print AGENT_BRIEFING.md — the workflow contract for AI agents
|
|
@@ -225,7 +224,15 @@ Manually-set \`llc_\` tokens are used as a fallback.
|
|
|
225
224
|
Deals:
|
|
226
225
|
llama deal create "Company" --source <name> --description "..." --website https://...
|
|
227
226
|
llama deal show <dealId>
|
|
227
|
+
llama deal feed <dealId> # everything humans added (facts + notes), newest first
|
|
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):
|
|
@@ -403,7 +410,86 @@ Token discovery (in order):
|
|
|
403
410
|
Env:
|
|
404
411
|
LLAMA_TOKEN token override
|
|
405
412
|
LLAMA_API_URL API base URL override
|
|
406
|
-
|
|
413
|
+
`;
|
|
414
|
+
|
|
415
|
+
// ── Progressive help (Constitution §1) ──
|
|
416
|
+
// Default `llama` / `llama --help` prints a SHORT root: the command groups +
|
|
417
|
+
// a few starters. Drill into one group with `llama help <area>` (or
|
|
418
|
+
// `llama <area> --help`); `llama help all` prints the full reference above.
|
|
419
|
+
const HELP_ROOT = `Llama Command CLI — the \`llama\` command for the Llama Ventures workbench.
|
|
420
|
+
|
|
421
|
+
Common:
|
|
422
|
+
llama deal search "<name>" find a deal in the pipeline
|
|
423
|
+
llama deal show <dealId> full deal record
|
|
424
|
+
llama deal feed <dealId> everything humans added, newest first
|
|
425
|
+
llama post <dealId> "..." add a note to a deal
|
|
426
|
+
llama agent-onboard print the AI-agent workflow contract
|
|
427
|
+
|
|
428
|
+
Command groups — run \`llama help <group>\` for that group's commands:
|
|
429
|
+
deal create · show · feed · update · search · collaborators · links · delete
|
|
430
|
+
brief brief blocks: list · add · edit · history · refresh
|
|
431
|
+
facts deal facts + skill corrections (the sourced, trust-rated layer)
|
|
432
|
+
timeline timeline · posts · mentions
|
|
433
|
+
wiki cross-deal knowledge entries (markdown or HTML)
|
|
434
|
+
memo long-form HTML investment memo
|
|
435
|
+
html deal-specific HTML artifacts (/deals/<id>/browse/<slug>)
|
|
436
|
+
pitch external founder intake (no token needed)
|
|
437
|
+
ownership claim · nominate · approvals
|
|
438
|
+
admin audit events (system admin only)
|
|
439
|
+
auth setup · tokens · auth status
|
|
440
|
+
|
|
441
|
+
llama help all the full command reference (everything at once)
|
|
442
|
+
|
|
443
|
+
Auth: if you've run \`gcloud auth login\` with your @llamaventures.vc account,
|
|
444
|
+
the CLI auto-detects it — no token needed (\`llc_\` tokens are a fallback).`;
|
|
445
|
+
|
|
446
|
+
// Area → which top-level sections of HELP_FULL belong to it.
|
|
447
|
+
const HELP_AREA_MATCH = {
|
|
448
|
+
deal: [/^Deals/, /^Collaborators/, /^Soft-delete/, /^Deal links/, /^Deal soft-delete/],
|
|
449
|
+
brief: [/^Brief blocks/, /^Brief \/ persona/],
|
|
450
|
+
facts: [/^Deal facts/, /^Skill corrections/],
|
|
451
|
+
timeline: [/^Timeline/, /^Mentions/],
|
|
452
|
+
wiki: [/^Wiki/, /^Where does this HTML/],
|
|
453
|
+
memo: [/^Memo/],
|
|
454
|
+
html: [/^Deal page HTML/],
|
|
455
|
+
pitch: [/^External pitch/],
|
|
456
|
+
ownership: [/^Ownership/, /^Approvals/],
|
|
457
|
+
admin: [/^Admin/],
|
|
458
|
+
auth: [/^Setup/, /^Zero-config/, /^Token discovery/, /^Env/],
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
// Slice HELP_FULL into sections: a top-level (non-indented) header line plus
|
|
462
|
+
// the indented/blank lines that follow it, until the next header.
|
|
463
|
+
function helpSections() {
|
|
464
|
+
const out = [];
|
|
465
|
+
let cur = null;
|
|
466
|
+
for (const line of HELP_FULL.split("\n")) {
|
|
467
|
+
if (/^[A-Za-z]/.test(line)) {
|
|
468
|
+
cur = { head: line, lines: [line] };
|
|
469
|
+
out.push(cur);
|
|
470
|
+
} else if (cur) {
|
|
471
|
+
cur.lines.push(line);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function usage(area) {
|
|
478
|
+
if (area === "all") {
|
|
479
|
+
console.log(HELP_FULL);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const matchers = area && HELP_AREA_MATCH[area];
|
|
483
|
+
if (matchers) {
|
|
484
|
+
const blocks = helpSections()
|
|
485
|
+
.filter((s) => matchers.some((re) => re.test(s.head)))
|
|
486
|
+
.map((s) => s.lines.join("\n").replace(/\s+$/, ""));
|
|
487
|
+
if (blocks.length) {
|
|
488
|
+
console.log(blocks.join("\n\n"));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
console.log(HELP_ROOT);
|
|
407
493
|
}
|
|
408
494
|
|
|
409
495
|
// ============================================================
|
|
@@ -681,7 +767,12 @@ async function main() {
|
|
|
681
767
|
return;
|
|
682
768
|
}
|
|
683
769
|
if (!area || area === "help" || area === "--help" || area === "-h") {
|
|
684
|
-
usage();
|
|
770
|
+
usage(area === "help" ? action : undefined);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
// `llama <area> --help` / `-h` → just that group's commands
|
|
774
|
+
if (action === "--help" || action === "-h") {
|
|
775
|
+
usage(area);
|
|
685
776
|
return;
|
|
686
777
|
}
|
|
687
778
|
|
|
@@ -971,6 +1062,13 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
971
1062
|
return;
|
|
972
1063
|
}
|
|
973
1064
|
|
|
1065
|
+
if (area === "deal" && action === "feed") {
|
|
1066
|
+
const dealId = rest[0];
|
|
1067
|
+
if (!dealId) throw new Error("Usage: llama deal feed <dealId>");
|
|
1068
|
+
print(await request("GET", `/api/deals/${encodeURIComponent(dealId)}/feed`));
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
974
1072
|
if (area === "deal" && action === "update") {
|
|
975
1073
|
const [dealId, field, ...valueParts] = rest;
|
|
976
1074
|
const value = valueParts.join(" ");
|
|
@@ -1147,14 +1245,18 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1147
1245
|
if (!flags.category || !flags.claim) {
|
|
1148
1246
|
throw new Error(
|
|
1149
1247
|
`Usage: llama deal fact add <dealId> --category <cat> --claim "<text>" ` +
|
|
1150
|
-
`[--source <url>] [--confidence high|medium|low]`
|
|
1248
|
+
`[--source <url>] [--confidence high|medium|low] [--attested]`
|
|
1151
1249
|
);
|
|
1152
1250
|
}
|
|
1251
|
+
// --attested: the caller takes responsibility that this is accurate
|
|
1252
|
+
// (verified against the source). With it, the fact is recorded as
|
|
1253
|
+
// vouched; without it, it stays unverified. Declare honestly.
|
|
1153
1254
|
print(await request("POST", `/api/deals/${encodeURIComponent(dealId)}/facts`, {
|
|
1154
1255
|
category: String(flags.category),
|
|
1155
1256
|
claim: String(flags.claim),
|
|
1156
1257
|
source: flags.source ? String(flags.source) : "",
|
|
1157
1258
|
confidence: flags.confidence ? String(flags.confidence) : "medium",
|
|
1259
|
+
attested: flags.attested === true,
|
|
1158
1260
|
}));
|
|
1159
1261
|
return;
|
|
1160
1262
|
}
|
|
@@ -2160,8 +2262,8 @@ Routing — is this the right command?
|
|
|
2160
2262
|
const { flags } = parseFlags(rest.slice(1), knownFlags);
|
|
2161
2263
|
|
|
2162
2264
|
// --slug is the natural agent guess (DB column is `document_slug`).
|
|
2163
|
-
// Accept it as an alias for --doc so the failure mode
|
|
2164
|
-
//
|
|
2265
|
+
// Accept it as an alias for --doc so the earlier failure mode
|
|
2266
|
+
// (silent fall-through to 'main') can't happen again.
|
|
2165
2267
|
if (flags.slug && !flags.doc) {
|
|
2166
2268
|
process.stderr.write("note: --slug accepted as alias for --doc.\n");
|
|
2167
2269
|
flags.doc = flags.slug;
|