@llamaventures/cli 1.5.0 → 1.6.1
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 +72 -3
- package/README.md +9 -1
- package/bin/llama-mcp.mjs +87 -8
- package/bin/llama.mjs +104 -6
- package/package.json +1 -1
package/AGENT_BRIEFING.md
CHANGED
|
@@ -29,13 +29,69 @@ Don't:
|
|
|
29
29
|
|
|
30
30
|
Conversation produces value → that value flows somewhere. This is not optional.
|
|
31
31
|
|
|
32
|
+
### Where does this HTML / thesis / artifact go? (decision tree)
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
HTML / thesis / artifact in hand
|
|
38
|
+
│
|
|
39
|
+
▼
|
|
40
|
+
┌─────────────────────────────────────────────────┐
|
|
41
|
+
│ Is it about ONE specific company or deal? │
|
|
42
|
+
│ (deal IC memo · dashboard for X · X 的 thesis │
|
|
43
|
+
│ · founder briefing for X · X 的 2×2 …) │
|
|
44
|
+
└──────────────┬──────────────────────────────────┘
|
|
45
|
+
│
|
|
46
|
+
yes ────►│ → Llama Command DEAL page
|
|
47
|
+
│ `llama html upload <dealId> --new --title "..." --file <path>`
|
|
48
|
+
│ Renders at /deals/<id>/browse/<slug>.
|
|
49
|
+
│ Use --doc <slug> + --file to update an existing one.
|
|
50
|
+
│
|
|
51
|
+
no ────►│ Is it cross-deal / institutional knowledge?
|
|
52
|
+
│ (sector landscape · market map · framework · firm-level
|
|
53
|
+
│ thesis · methodology · "AI infra in 2026" …)
|
|
54
|
+
│
|
|
55
|
+
│ yes ──► → Llama Command WIKI entry
|
|
56
|
+
│ Markdown body:
|
|
57
|
+
│ `llama wiki save <slug> --title "..." \`
|
|
58
|
+
│ ` --content "..." --sources "..."`
|
|
59
|
+
│ Standalone HTML page (full-viewport iframe):
|
|
60
|
+
│ `llama wiki save <slug> --title "..." \`
|
|
61
|
+
│ ` --file <path>.html --sources "..."`
|
|
62
|
+
│ Renders at /wiki/<slug>. Sources mandatory.
|
|
63
|
+
│
|
|
64
|
+
│ no ──► Likely doesn't need to live anywhere
|
|
65
|
+
│ durable — confirm with the user before
|
|
66
|
+
│ inventing a destination.
|
|
67
|
+
▼
|
|
68
|
+
┌─────────────────────────────────────────────────┐
|
|
69
|
+
│ Does the user EXPLICITLY say "share with │
|
|
70
|
+
│ founder" / "public share link" / "give it to │
|
|
71
|
+
│ <external person>" / "publish publicly"? │
|
|
72
|
+
└──────────────┬──────────────────────────────────┘
|
|
73
|
+
│
|
|
74
|
+
no ────►│ → STAY on Llama Command. Don't reach for Netlify.
|
|
75
|
+
│
|
|
76
|
+
yes ────►│ → Netlify (only this case).
|
|
77
|
+
│ Use the `netlify-access-guard` workflow:
|
|
78
|
+
│ server-side password + edge 401 verification.
|
|
79
|
+
│ Verify enforcement is at the Netlify edge, not a
|
|
80
|
+
│ browser-side JS fake.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Default bias:** when in doubt, route to Llama Command. It has auth, audit, search, backlinks, and lives next to the rest of the team's context. Netlify is the escape hatch for genuinely-external surfaces — not "where pretty HTML goes."
|
|
84
|
+
|
|
85
|
+
The table below details the exact CLI for each destination.
|
|
86
|
+
|
|
32
87
|
| Type | Destination | How |
|
|
33
88
|
|---|---|---|
|
|
34
89
|
| Deal metadata (status, stage, valuation, founders, notes, etc.) | Pipeline (Postgres) | `llama deal create` / `llama deal update` |
|
|
35
90
|
| Brief blocks (text / link / embed / callout) | Pipeline | `llama brief add-text` / `add-link` / `add-callout` |
|
|
36
91
|
| **HTML artifact, internal — IC report, dashboard, market map, 2×2, any hand-authored page** | **Llama Command native** (Postgres + sandboxed iframe at `/deals/<id>/browse/<slug>`) | Default path when the user says "deploy to llama", "deploy to llama command", "部署到 llama command", "put this HTML on the deal page", "在 deal 里看这个". **You MUST declare intent — "new artifact" vs "update existing":**<br><br>**New artifact:** `llama html upload <dealId> --new --title "<artifact name>" --file <path>` (CLI slugifies the title; pass `--doc <slug>` to override).<br>**Update existing:** `llama html upload <dealId> --doc <slug> --file <path>` (slug must already exist — run `llama html docs <dealId>` first to see what's there).<br><br>The bare form `llama html upload <id> --file <path>` REFUSES if `main` already has content. Do NOT default to Netlify for internal pages. |
|
|
37
92
|
| HTML artifact, external — founder-facing share link | Netlify | Only when the user explicitly says "share link", "give it to the founder", "publish publicly". Use the `netlify-access-guard` workflow (server-side password + edge 401 verification). |
|
|
38
|
-
| Insights, decisions, framework improvements | Wiki | `llama wiki save` (with attribution — see below) |
|
|
93
|
+
| Insights, decisions, framework improvements | Wiki (markdown) | `llama wiki save <slug> --content "..."` (with attribution — see below) |
|
|
94
|
+
| **HTML wiki entry — standalone HTML page hosted at `/wiki/<slug>`** (sector landscape, market map, dashboard, hand-styled thesis page) | **Wiki (HTML)** | `llama wiki save <slug> --title "..." --file <path.html> --sources "..."`. Auto-detects content_type=html from extension. Public page is full-viewport sandboxed iframe takeover (no wiki chrome). Sources/status/title still required; appears in `wiki search` + backlinks. Use when the user says "deploy this HTML to wiki", "wiki 词条", "make this page a wiki entry". HTML must be self-contained (inline CSS/JS, image data URIs or external URLs) — asset bundles aren't supported on wiki yet. |
|
|
39
95
|
| Large files (deck / PDF / transcript) | Drive deal folder | the deal's `folder_url` (from `llama deal show`) → upload via your filesystem / Drive tool |
|
|
40
96
|
| Cross-team mentions | Inbox + email | `llama post <dealId> "@<teammate> ..."` — server fires email + UI badge to the recipient |
|
|
41
97
|
|
|
@@ -138,7 +194,20 @@ llama html reset <dealId> [--doc <slug>] # soft-
|
|
|
138
194
|
|
|
139
195
|
# Wiki (knowledge base)
|
|
140
196
|
llama wiki search "<query>"
|
|
141
|
-
llama wiki
|
|
197
|
+
llama wiki read <slug> [--lang en|zh]
|
|
198
|
+
|
|
199
|
+
# Markdown entry (default):
|
|
200
|
+
llama wiki save <slug> --title "..." --content "..." --sources "url1;url2"
|
|
201
|
+
|
|
202
|
+
# HTML entry — standalone page at /wiki/<slug>, full-viewport sandboxed iframe:
|
|
203
|
+
llama wiki save <slug> --title "..." --file path.html --sources "..." [--content-type html]
|
|
204
|
+
# .html / .htm extension auto-implies content_type=html.
|
|
205
|
+
# --content-type html (or markdown) overrides the inference.
|
|
206
|
+
# Refuses to switch content_type on an existing slug; delete + re-create
|
|
207
|
+
# if you really mean to change format.
|
|
208
|
+
# Delete / restore (soft, reversible — CONSTITUTION §8):
|
|
209
|
+
llama wiki delete <slug> [--lang en|zh]
|
|
210
|
+
llama wiki restore <slug> [--lang en|zh]
|
|
142
211
|
|
|
143
212
|
# Timeline + posts
|
|
144
213
|
llama timeline <dealId>
|
|
@@ -165,7 +234,7 @@ Tools available:
|
|
|
165
234
|
- `auth_status` — verify creds + identity (call first if anything 401s)
|
|
166
235
|
- `deal_search` / `deal_show` / `deal_create` / `deal_update`
|
|
167
236
|
- `brief_blocks` / `brief_add_text` / `brief_add_link` / `brief_add_callout`
|
|
168
|
-
- `wiki_search` / `wiki_save`
|
|
237
|
+
- `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)
|
|
169
238
|
- `timeline` / `post`
|
|
170
239
|
- `mentions_list`
|
|
171
240
|
- `pitch_start` / `pitch_send_message` / `pitch_upload_file` / `pitch_status` / `pitch_finalize` — public intake (no Llama token needed; for founders / EAs / external agents)
|
package/README.md
CHANGED
|
@@ -209,7 +209,14 @@ llama post <dealId> "message body" [--link url]
|
|
|
209
209
|
|
|
210
210
|
# Wiki
|
|
211
211
|
llama wiki search "<query>"
|
|
212
|
-
llama wiki
|
|
212
|
+
llama wiki read <slug> [--lang en|zh]
|
|
213
|
+
# Markdown entry:
|
|
214
|
+
llama wiki save <slug> --title "..." --content "..." --sources "url1;url2"
|
|
215
|
+
# HTML entry — standalone page at /wiki/<slug> (full-viewport sandboxed iframe):
|
|
216
|
+
llama wiki save <slug> --title "..." --file page.html --sources "..." [--content-type html]
|
|
217
|
+
# Delete / restore (soft, reversible):
|
|
218
|
+
llama wiki delete <slug> [--lang en|zh]
|
|
219
|
+
llama wiki restore <slug> [--lang en|zh]
|
|
213
220
|
|
|
214
221
|
# Mentions inbox
|
|
215
222
|
llama mentions
|
|
@@ -255,6 +262,7 @@ brief_add_link brief_add_callout
|
|
|
255
262
|
timeline post
|
|
256
263
|
|
|
257
264
|
wiki_search wiki_save
|
|
265
|
+
wiki_delete wiki_restore
|
|
258
266
|
|
|
259
267
|
mentions_list
|
|
260
268
|
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -328,24 +328,94 @@ server.registerTool(
|
|
|
328
328
|
"wiki_save",
|
|
329
329
|
{
|
|
330
330
|
description:
|
|
331
|
-
"Create or update a wiki page
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
"
|
|
331
|
+
"Create or update a wiki page — Llama's CROSS-DEAL / institutional " +
|
|
332
|
+
"knowledge surface (sector landscape · market map · thesis · framework · " +
|
|
333
|
+
"methodology · anything not tied to ONE specific deal). Renders at " +
|
|
334
|
+
"/wiki/<slug>. " +
|
|
335
|
+
"**Routing — decide BEFORE calling:** " +
|
|
336
|
+
"(a) Deal-specific HTML (IC memo for X, dashboard for X) → use " +
|
|
337
|
+
"`html_upload` instead, NOT this. " +
|
|
338
|
+
"(b) Cross-deal / institutional (this tool) → /wiki/<slug>. " +
|
|
339
|
+
"(c) Founder-facing public share → Netlify only when user explicitly " +
|
|
340
|
+
"says so; Llama Command outranks Netlify for everything internal. " +
|
|
341
|
+
"By default `content` is markdown with attribution blocks " +
|
|
342
|
+
"(**[Name · YYYY-MM-DD · source · fact|opinion]**) for traceability. " +
|
|
343
|
+
"Set `content_type: 'html'` to deploy a standalone HTML page as the " +
|
|
344
|
+
"wiki entry (full-viewport sandboxed iframe takeover on /wiki/<slug>; " +
|
|
345
|
+
"the HTML itself is the page — no wiki chrome). `sources` is a " +
|
|
346
|
+
"separate citation list (URLs, doc names, or meeting references) — " +
|
|
347
|
+
"at least one required; URLs inside `content` do not count. For HTML " +
|
|
348
|
+
"asset bundles use the `llama wiki save --file ... --assets ...` CLI " +
|
|
349
|
+
"path; MCP only supports single-file HTML.",
|
|
335
350
|
inputSchema: {
|
|
336
351
|
slug: z.string().describe("kebab-case slug"),
|
|
337
352
|
title: z.string(),
|
|
338
|
-
content: z
|
|
353
|
+
content: z
|
|
354
|
+
.string()
|
|
355
|
+
.describe(
|
|
356
|
+
"body — markdown source by default, or raw HTML when content_type='html'"
|
|
357
|
+
),
|
|
339
358
|
sources: z
|
|
340
359
|
.array(z.string())
|
|
341
360
|
.min(1)
|
|
342
361
|
.describe(
|
|
343
362
|
"citation list — URLs, doc names, or meeting references. At least one required."
|
|
344
363
|
),
|
|
364
|
+
content_type: z
|
|
365
|
+
.enum(["markdown", "html"])
|
|
366
|
+
.optional()
|
|
367
|
+
.describe(
|
|
368
|
+
"'markdown' (default) renders via the wiki markdown pipeline. " +
|
|
369
|
+
"'html' stores the body as a standalone HTML page (sandboxed iframe)."
|
|
370
|
+
),
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
async ({ slug, title, content, sources, content_type }) =>
|
|
374
|
+
callApi("POST", "/api/wiki/save", {
|
|
375
|
+
slug,
|
|
376
|
+
title,
|
|
377
|
+
content,
|
|
378
|
+
sources,
|
|
379
|
+
...(content_type ? { content_type } : {}),
|
|
380
|
+
})
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
server.registerTool(
|
|
384
|
+
"wiki_delete",
|
|
385
|
+
{
|
|
386
|
+
description:
|
|
387
|
+
"Soft-delete a wiki page (reversible). The entry stops appearing in " +
|
|
388
|
+
"reads / search / backlinks; for HTML entries the standalone page + " +
|
|
389
|
+
"assets stop resolving too. Restore with wiki_restore. Use when the " +
|
|
390
|
+
"user asks to remove / delete / retire a wiki entry.",
|
|
391
|
+
inputSchema: {
|
|
392
|
+
slug: z.string().describe("kebab-case slug"),
|
|
393
|
+
lang: z.enum(["en", "zh"]).optional().describe("default: en"),
|
|
345
394
|
},
|
|
346
395
|
},
|
|
347
|
-
async ({ slug,
|
|
348
|
-
callApi(
|
|
396
|
+
async ({ slug, lang }) =>
|
|
397
|
+
callApi(
|
|
398
|
+
"DELETE",
|
|
399
|
+
`/api/wiki/${encodeURIComponent(slug)}?lang=${lang === "zh" ? "zh" : "en"}`
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
server.registerTool(
|
|
404
|
+
"wiki_restore",
|
|
405
|
+
{
|
|
406
|
+
description:
|
|
407
|
+
"Restore a soft-deleted wiki page (undo wiki_delete). Brings back the " +
|
|
408
|
+
"entry + (for HTML entries) its standalone page and assets.",
|
|
409
|
+
inputSchema: {
|
|
410
|
+
slug: z.string().describe("kebab-case slug"),
|
|
411
|
+
lang: z.enum(["en", "zh"]).optional().describe("default: en"),
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
async ({ slug, lang }) =>
|
|
415
|
+
callApi(
|
|
416
|
+
"POST",
|
|
417
|
+
`/api/wiki/${encodeURIComponent(slug)}/restore?lang=${lang === "zh" ? "zh" : "en"}`
|
|
418
|
+
)
|
|
349
419
|
);
|
|
350
420
|
|
|
351
421
|
// ============================================================
|
|
@@ -707,7 +777,16 @@ server.registerTool(
|
|
|
707
777
|
"html_upload",
|
|
708
778
|
{
|
|
709
779
|
description:
|
|
710
|
-
"Upload (PUT) a new HTML version for a
|
|
780
|
+
"Upload (PUT) a new HTML version for a SPECIFIC DEAL's /browse page " +
|
|
781
|
+
"(deal-scoped artifact: IC memo for X · dashboard for X · 2×2 for X). " +
|
|
782
|
+
"Renders at /deals/<id>/browse/<slug>. " +
|
|
783
|
+
"**Routing — pick the right destination BEFORE calling this:** " +
|
|
784
|
+
"(a) Deal-specific HTML (this tool) → /deals/<id>/browse/<slug>. " +
|
|
785
|
+
"(b) Cross-deal / institutional / thesis / sector landscape → use " +
|
|
786
|
+
"`wiki_save` with content_type='html' instead (/wiki/<slug>). " +
|
|
787
|
+
"(c) Founder-facing public share link → escape to Netlify only when " +
|
|
788
|
+
"the user explicitly says 'share with founder' / 'publish publicly'; " +
|
|
789
|
+
"Llama Command outranks Netlify for everything internal. " +
|
|
711
790
|
"Creates a NEW version row — the previous version is retained " +
|
|
712
791
|
"and restorable. Triggers SSE push so any open viewer auto- " +
|
|
713
792
|
"refreshes. Constraints: HTML body MUST start with " +
|
package/bin/llama.mjs
CHANGED
|
@@ -324,10 +324,28 @@ Mentions / Inbox:
|
|
|
324
324
|
llama mentions resolve <mentionId> # mark thread resolved (idempotent)
|
|
325
325
|
llama mentions unread # just the badge count
|
|
326
326
|
|
|
327
|
+
Where does this HTML / thesis / artifact go?
|
|
328
|
+
About ONE specific deal? ........ llama html upload <dealId> --new --title "..." --file <path>
|
|
329
|
+
(renders at /deals/<id>/browse/<slug>; see "Deal page HTML" below)
|
|
330
|
+
Cross-deal / institutional? ..... llama wiki save <slug> --title "..." --file <path>.html --sources "..."
|
|
331
|
+
(renders at /wiki/<slug>; see "Wiki" below)
|
|
332
|
+
Founder-facing public share? .... Netlify (with netlify-access-guard skill), only when user explicitly
|
|
333
|
+
says "share publicly". Llama Command outranks Netlify for everything
|
|
334
|
+
internal — don't reach for Netlify by default.
|
|
335
|
+
|
|
327
336
|
Wiki:
|
|
328
337
|
llama wiki search <query>
|
|
329
338
|
llama wiki read <slug>
|
|
330
|
-
|
|
339
|
+
Markdown entry (default):
|
|
340
|
+
llama wiki save <slug> --title "..." --content "..." --sources "url1;url2" [--type company] [--related "A;B"]
|
|
341
|
+
HTML entry — standalone HTML page at /wiki/<slug> (full-viewport sandboxed iframe):
|
|
342
|
+
llama wiki save <slug> --title "..." --file path.html --sources "..." [--content-type html]
|
|
343
|
+
(.html / .htm extension auto-implies content_type=html)
|
|
344
|
+
➜ Use Wiki when the artifact is NOT tied to one specific deal — sector landscape, market map,
|
|
345
|
+
thesis, framework, methodology. For deal-specific HTML use "llama html upload <dealId>" instead.
|
|
346
|
+
Delete / restore (soft — reversible):
|
|
347
|
+
llama wiki delete <slug> [--lang en|zh]
|
|
348
|
+
llama wiki restore <slug> [--lang en|zh]
|
|
331
349
|
|
|
332
350
|
Memo (long-form HTML investment memo — Memo tab in the UI):
|
|
333
351
|
llama memo show <dealId> [--out <path>] [--json] # default: html → stdout (pipeable to file / browser)
|
|
@@ -336,6 +354,9 @@ Memo (long-form HTML investment memo — Memo tab in the UI):
|
|
|
336
354
|
llama memo reset <dealId> [--all] # default drops manual override; --all drops every version
|
|
337
355
|
|
|
338
356
|
Deal page HTML (hand-authored sandboxed pages on /deals/<id>/browse/<slug>):
|
|
357
|
+
➜ Use this for DEAL-SPECIFIC artifacts: IC memo for X, dashboard for X, 2×2 for X.
|
|
358
|
+
For cross-deal / institutional pages (sector landscape, market map, thesis) use
|
|
359
|
+
"llama wiki save <slug> --file ..." instead — see "Wiki" above.
|
|
339
360
|
Each deal can host many HTML artifacts (IC report, dashboard, market map, …).
|
|
340
361
|
Each one has a stable slug. UPLOAD must declare intent — update an existing
|
|
341
362
|
artifact or add a new one — to avoid silent overwrites.
|
|
@@ -1337,32 +1358,98 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
1337
1358
|
}
|
|
1338
1359
|
|
|
1339
1360
|
// ----- Wiki: save (create or update) -----
|
|
1361
|
+
// Two body modes:
|
|
1362
|
+
// --content "..." inline markdown OR raw HTML (string)
|
|
1363
|
+
// --file <path> read body from a file; if .html/.htm,
|
|
1364
|
+
// content_type auto-detects to 'html'
|
|
1365
|
+
// --content-type <markdown|html> overrides auto-detect.
|
|
1366
|
+
// Refuses content_type mismatch on existing entries (server-side check;
|
|
1367
|
+
// CLI surfaces the server error verbatim).
|
|
1340
1368
|
if (area === "wiki" && action === "save") {
|
|
1341
1369
|
const { flags, positional } = parseFlags(rest);
|
|
1342
1370
|
const slug = positional[0];
|
|
1343
1371
|
const title = flags.title;
|
|
1344
|
-
const
|
|
1372
|
+
const inlineContent = flags.content;
|
|
1373
|
+
const filePath = flags.file;
|
|
1345
1374
|
const sourcesRaw = flags.sources;
|
|
1346
|
-
if (!slug || !title || !
|
|
1375
|
+
if (!slug || !title || !sourcesRaw || (!inlineContent && !filePath)) {
|
|
1347
1376
|
throw new Error(
|
|
1348
|
-
`Usage:
|
|
1377
|
+
`Usage:
|
|
1378
|
+
llama wiki save <slug> --title "..." --content "..." --sources "url1;url2" [--type company] [--related "A;B"] [--lang en|zh] [--content-type markdown|html]
|
|
1379
|
+
or
|
|
1380
|
+
llama wiki save <slug> --title "..." --file path/to/article.{md,html} --sources "url1;url2" [--type company] [--related "A;B"] [--lang en|zh] [--content-type markdown|html]
|
|
1381
|
+
|
|
1382
|
+
Pass either --content (inline) or --file (read from disk). With --file, content_type auto-detects from extension (.html/.htm → html, else markdown). Use --content-type to override.
|
|
1383
|
+
|
|
1384
|
+
Routing — is this the right command?
|
|
1385
|
+
✓ Cross-deal / institutional knowledge (sector landscape, market map, thesis, framework, methodology)
|
|
1386
|
+
→ YES, you're in the right place.
|
|
1387
|
+
✗ Deal-specific HTML (IC memo for X, dashboard for X, 2×2 for one company)
|
|
1388
|
+
→ use \`llama html upload <dealId> --new --title "..." --file <path>\` instead.
|
|
1389
|
+
✗ Founder-facing public share link
|
|
1390
|
+
→ escape to Netlify only when the user explicitly says "share publicly" / "give it to the founder";
|
|
1391
|
+
Llama Command outranks Netlify for everything internal.`
|
|
1349
1392
|
);
|
|
1350
1393
|
}
|
|
1394
|
+
if (inlineContent && filePath) {
|
|
1395
|
+
throw new Error("Pass either --content OR --file, not both.");
|
|
1396
|
+
}
|
|
1397
|
+
// Read body — either inline or from file.
|
|
1398
|
+
let body;
|
|
1399
|
+
let inferredType = "markdown";
|
|
1400
|
+
if (filePath) {
|
|
1401
|
+
const { readFileSync } = await import("fs");
|
|
1402
|
+
body = readFileSync(String(filePath), "utf-8");
|
|
1403
|
+
const lower = String(filePath).toLowerCase();
|
|
1404
|
+
if (lower.endsWith(".html") || lower.endsWith(".htm")) {
|
|
1405
|
+
inferredType = "html";
|
|
1406
|
+
}
|
|
1407
|
+
} else {
|
|
1408
|
+
body = String(inlineContent);
|
|
1409
|
+
}
|
|
1410
|
+
// Determine content_type: explicit flag wins over file-extension inference.
|
|
1411
|
+
let contentType = inferredType;
|
|
1412
|
+
if (flags["content-type"]) {
|
|
1413
|
+
const v = String(flags["content-type"]).toLowerCase();
|
|
1414
|
+
if (v !== "markdown" && v !== "html") {
|
|
1415
|
+
throw new Error(`--content-type must be 'markdown' or 'html', got "${v}"`);
|
|
1416
|
+
}
|
|
1417
|
+
contentType = v;
|
|
1418
|
+
}
|
|
1351
1419
|
const splitCsv = (v) => String(v).split(/[;|]/).map((s) => s.trim()).filter(Boolean);
|
|
1352
1420
|
const payload = {
|
|
1353
1421
|
slug,
|
|
1354
1422
|
title: String(title),
|
|
1355
|
-
content:
|
|
1423
|
+
content: body,
|
|
1356
1424
|
sources: splitCsv(sourcesRaw),
|
|
1357
1425
|
type: flags.type ? String(flags.type) : undefined,
|
|
1358
1426
|
related: flags.related ? splitCsv(flags.related) : undefined,
|
|
1359
1427
|
lang: flags.lang === "zh" ? "zh" : "en",
|
|
1360
1428
|
status: flags.status ? String(flags.status) : undefined,
|
|
1429
|
+
content_type: contentType,
|
|
1361
1430
|
};
|
|
1362
1431
|
print(await request("POST", "/api/wiki/save", payload));
|
|
1363
1432
|
return;
|
|
1364
1433
|
}
|
|
1365
1434
|
|
|
1435
|
+
// ----- Wiki: delete (soft) / restore -----
|
|
1436
|
+
// Soft-delete (CONSTITUTION §8 reversible). For HTML entries the
|
|
1437
|
+
// sentinel deal_browse_html body + assets are soft-deleted too;
|
|
1438
|
+
// `llama wiki restore <slug>` brings it all back.
|
|
1439
|
+
if (area === "wiki" && (action === "delete" || action === "restore")) {
|
|
1440
|
+
const { flags, positional } = parseFlags(rest);
|
|
1441
|
+
const slug = positional[0];
|
|
1442
|
+
if (!slug) throw new Error(`Usage: llama wiki ${action} <slug> [--lang en|zh]`);
|
|
1443
|
+
const lang = flags.lang === "zh" ? "zh" : "en";
|
|
1444
|
+
const qs = `?lang=${lang}`;
|
|
1445
|
+
if (action === "delete") {
|
|
1446
|
+
print(await request("DELETE", `/api/wiki/${encodeURIComponent(slug)}${qs}`));
|
|
1447
|
+
} else {
|
|
1448
|
+
print(await request("POST", `/api/wiki/${encodeURIComponent(slug)}/restore${qs}`));
|
|
1449
|
+
}
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1366
1453
|
// ----- Brief blocks: list / add-* / edit / delete -----
|
|
1367
1454
|
// The block-based deal brief stores an ordered array of typed blocks
|
|
1368
1455
|
// (text / link / embed / callout) per deal. These commands wrap the
|
|
@@ -2052,7 +2139,18 @@ https://command.llamaventures.vc/settings/tokens, run
|
|
|
2052
2139
|
"\n" +
|
|
2053
2140
|
"Default (no --doc, no --new) targets slug 'main' but REFUSES if 'main'\n" +
|
|
2054
2141
|
"already has content — pass --doc main to update it explicitly, or\n" +
|
|
2055
|
-
"--new --title \"...\" to add a NEW artifact alongside
|
|
2142
|
+
"--new --title \"...\" to add a NEW artifact alongside.\n" +
|
|
2143
|
+
"\n" +
|
|
2144
|
+
"Routing — is this the right command?\n" +
|
|
2145
|
+
" ✓ DEAL-specific HTML (IC memo for X, dashboard for X, 2×2 for X)\n" +
|
|
2146
|
+
" → YES, you're in the right place. Pass <dealId> + --new / --doc.\n" +
|
|
2147
|
+
" ✗ Cross-deal / institutional knowledge (sector landscape, market map,\n" +
|
|
2148
|
+
" thesis, framework, methodology, anything not tied to one company)\n" +
|
|
2149
|
+
" → use `llama wiki save <slug> --title \"...\" --file <path>.html --sources \"...\"`\n" +
|
|
2150
|
+
" instead (renders at /wiki/<slug>).\n" +
|
|
2151
|
+
" ✗ Founder-facing public share link\n" +
|
|
2152
|
+
" → escape to Netlify only when the user explicitly says \"share publicly\";\n" +
|
|
2153
|
+
" Llama Command outranks Netlify for everything internal.",
|
|
2056
2154
|
);
|
|
2057
2155
|
}
|
|
2058
2156
|
const knownFlags = [
|