@llamaventures/cli 2.6.0 → 2.7.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 +13 -0
- package/CHANGELOG.md +27 -0
- package/README.md +30 -0
- package/README.zh-CN.md +28 -0
- package/bin/llama-mcp.mjs +23 -13
- package/bin/llama.mjs +53 -7
- package/contracts/core-api.json +2 -2
- package/lib/build-manifest.json +4 -4
- package/lib/deal-actions.mjs +76 -2
- package/package.json +2 -2
package/AGENT_BRIEFING.md
CHANGED
|
@@ -93,6 +93,8 @@ Before researching or creating a company:
|
|
|
93
93
|
1. `llama deal search "<company, founder or topic>"`. Core API 5.41.0+ also searches descriptions and sector/subsector in either stored language. Use `--sector "robotics"` alone or combine it with a query and `--state`/`--limit`; filters apply before the limit. These are literal substrings, not semantic synonyms. A sector filter requires server confirmation; never treat an older unfiltered response as sector-filtered results.
|
|
94
94
|
Use short keywords. Inspect `searchMatch` fields and `search.has_more`; truncated results are not exhaustive. On zero matches, broaden the sector or wording. Current Page updates are immediately searchable without manual index or tag maintenance.
|
|
95
95
|
2. Read the best candidate with `--detail overview` and verify its specific category; a broad sector match does not establish a narrower classification.
|
|
96
|
+
For "which Deals are invested / in diligence / latest investments", never guess from names: run `llama deal search --status Invested --limit 200` (Core API 5.42.0+) and order by `investmentDate`/`markedInvestedAt`, stating how many Deals you checked.
|
|
97
|
+
To answer from Information, prefer a targeted read over `--detail memory` (Core API 5.42.0+): `--information-q "<text>"` for snippets, `--information-type <type>` for one kind of record, and `--information-id <id>` for full values. Values over 8,000 characters in text/type reads are omitted; read that id. Never treat an unconfirmed (older server) response as filtered.
|
|
96
98
|
3. Expand only the resource needed for the task.
|
|
97
99
|
4. Create only when no existing company matches.
|
|
98
100
|
|
|
@@ -252,6 +254,17 @@ request, and verify the exact Artifact version and hash through Command.
|
|
|
252
254
|
separate domains. Deal Memory is also separate. None expands the four-action
|
|
253
255
|
Occam Deal space.
|
|
254
256
|
|
|
257
|
+
## Wiki publishing
|
|
258
|
+
|
|
259
|
+
Read `contract.wiki_publishing` from authenticated bootstrap and use
|
|
260
|
+
`llama help wiki` for the save commands. New authorized publications default
|
|
261
|
+
to complete self-contained HTML. Preserve full report content, charts,
|
|
262
|
+
interactions and existing language/version markers, with rendering assets
|
|
263
|
+
embedded. Read the exact slug and language before edits and preserve its
|
|
264
|
+
format. Honor explicit Markdown requests. For MCP, set `content_type: html`
|
|
265
|
+
and the intended `lang`. Verify the save receipt and canonical page before
|
|
266
|
+
reporting completion.
|
|
267
|
+
|
|
255
268
|
## Report UX friction
|
|
256
269
|
|
|
257
270
|
When a user, agent or both encounter a concrete obstacle (including a task that
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,33 @@ this project adheres to [Semantic Versioning](https://semver.org).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.7.1] - 2026-11-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Add `llama help wiki`, save usage guidance, and MCP publishing descriptions
|
|
14
|
+
that lead agents to the current authenticated Wiki publishing contract and
|
|
15
|
+
complete self-contained HTML. Existing Markdown save behavior is preserved.
|
|
16
|
+
- Allow MCP `wiki_save` to send `lang`, matching CLI and Core support for
|
|
17
|
+
language-specific publications and updates.
|
|
18
|
+
|
|
19
|
+
## [2.7.0] - 2026-11-05
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Read Deal Information selectively with `llama deal read <dealId>
|
|
24
|
+
--information-id <ids> | --information-type <types> | --information-q <text>`
|
|
25
|
+
or MCP `read_deal.informationIds/informationTypes/informationQuery`, instead
|
|
26
|
+
of loading all memory with `--detail memory`. Core API 5.42.0+ returns full
|
|
27
|
+
values for id reads and snippets for text reads, omits values over 8,000
|
|
28
|
+
characters in text/type reads, and echoes the applied filter; responses from
|
|
29
|
+
an older Core that ignored the filter are rejected.
|
|
30
|
+
- Filter Deal search by pipeline status with `llama deal search --status
|
|
31
|
+
Invested[,Diligence]` or MCP `search_deals.status`, alone or combined with a
|
|
32
|
+
query, sector, state and limit, so "every invested Deal" is one bounded query
|
|
33
|
+
instead of a guess. Core API 5.42.0+ echoes the applied statuses; an older
|
|
34
|
+
Core is rejected.
|
|
35
|
+
|
|
9
36
|
## [2.6.0] - 2026-11-02
|
|
10
37
|
|
|
11
38
|
### Added
|
package/README.md
CHANGED
|
@@ -68,6 +68,15 @@ llama deal search "humanoid" --sector "robotics" --state active
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
`--sector` searches sector and subsector and can be used without a query.
|
|
71
|
+
`--status` matches the current pipeline status exactly (any of a comma list)
|
|
72
|
+
and also works alone; with Core API 5.42.0+ it enumerates, for example, every
|
|
73
|
+
invested Deal in one bounded query:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
llama deal search --status Invested --limit 200
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Results carry `investmentDate` and `markedInvestedAt` for ordering.
|
|
71
80
|
MCP `search_deals` accepts the same optional `q`, `sector`, `state` and `limit`.
|
|
72
81
|
If Core does not confirm the sector filter in its response, the client reports
|
|
73
82
|
an unsupported-server error instead of returning an unfiltered pipeline.
|
|
@@ -80,6 +89,27 @@ wording, then read candidate Deals to verify specific categories and claims.
|
|
|
80
89
|
Search reads current Page data directly, so updates and clears are reflected
|
|
81
90
|
immediately without a separate indexing job or manual tag maintenance.
|
|
82
91
|
|
|
92
|
+
|
|
93
|
+
### Read Information selectively
|
|
94
|
+
|
|
95
|
+
With Core API 5.42.0 or newer, read only the Information you need instead of
|
|
96
|
+
`--detail memory` (which can return megabytes on a large Deal):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
llama deal read <dealId> --information-q "ARR"
|
|
100
|
+
llama deal read <dealId> --information-type founder_claim,team
|
|
101
|
+
llama deal read <dealId> --information-id <recordId>,<recordId>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`--information-q` matches literal text in a record's type, subject, labels and
|
|
105
|
+
value and returns `match.snippets` around each hit. In text and type reads, a
|
|
106
|
+
value over 8,000 characters comes back as `{ omitted, chars }`; read that record
|
|
107
|
+
with `--information-id` for its full value. Filters combine with AND, return at
|
|
108
|
+
most 50 records, and `informationFilter` echoes what Core applied
|
|
109
|
+
(`matched > returned` means narrow the filter). MCP `read_deal` accepts the same
|
|
110
|
+
filters as `informationIds`, `informationTypes` and `informationQuery`. If Core
|
|
111
|
+
does not confirm the filter, the client reports an unsupported-server error
|
|
112
|
+
instead of returning unfiltered memory.
|
|
83
113
|
### Create
|
|
84
114
|
|
|
85
115
|
```json
|
package/README.zh-CN.md
CHANGED
|
@@ -70,6 +70,34 @@ llama deal search "humanoid" --sector "robotics" --state active
|
|
|
70
70
|
更宽的行业或其他措辞,再读取候选 Deal 核实具体分类和结论。搜索直接读取当前
|
|
71
71
|
Page 数据,更新和清空立即生效,无需另外维护索引或标签。
|
|
72
72
|
|
|
73
|
+
|
|
74
|
+
`--status` 按当前 pipeline 状态精确匹配(可用逗号给多个),也可以单独使用。
|
|
75
|
+
Core API 5.42.0 及以上版本可以一次列出所有已投 Deal:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
llama deal search --status Invested --limit 200
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
结果里带 `investmentDate` 和 `markedInvestedAt`,可以按投资时间排序。
|
|
82
|
+
|
|
83
|
+
### 按需读取 Information
|
|
84
|
+
|
|
85
|
+
Core API 5.42.0 及以上版本可以只读需要的 Information,不必用 `--detail memory`
|
|
86
|
+
(大 Deal 可能一次返回数 MB):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
llama deal read <dealId> --information-q "ARR"
|
|
90
|
+
llama deal read <dealId> --information-type founder_claim,team
|
|
91
|
+
llama deal read <dealId> --information-id <recordId>,<recordId>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`--information-q` 在记录的 type、subject、labels 和 value 中做字面匹配,并在
|
|
95
|
+
`match.snippets` 里返回命中处的上下文。按文本或类型读取时,超过 8,000 字符的
|
|
96
|
+
value 会以 `{ omitted, chars }` 返回,需要全文时用 `--information-id` 读取该记录。
|
|
97
|
+
多个条件按 AND 组合,最多返回 50 条;`informationFilter` 回显 Core 实际应用的
|
|
98
|
+
条件(`matched > returned` 表示需要再收窄)。MCP `read_deal` 用
|
|
99
|
+
`informationIds`、`informationTypes`、`informationQuery` 接受同样的条件。如果
|
|
100
|
+
Core 没有确认过滤条件,客户端会报"服务端不支持",而不是返回未过滤的全部内容。
|
|
73
101
|
### 创建 Deal
|
|
74
102
|
|
|
75
103
|
```json
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
buildDealReadPath,
|
|
25
25
|
buildDealSearchPath,
|
|
26
26
|
verifyDealSearchResult,
|
|
27
|
+
informationReadFilter,
|
|
28
|
+
verifyInformationReadResult,
|
|
27
29
|
compactDealWriteResult,
|
|
28
30
|
prepareDealCommand,
|
|
29
31
|
} from "../lib/deal-actions.mjs";
|
|
@@ -143,25 +145,26 @@ const writeDealCommandSchema = z.discriminatedUnion("operation", [
|
|
|
143
145
|
server.registerTool(
|
|
144
146
|
"search_deals",
|
|
145
147
|
{
|
|
146
|
-
description: "Search current Deal metadata by company name/key, description, sector/subsector or founder name in either stored language. Use short keywords; matching is a case-insensitive literal substring, not semantic search. Combine q with sector/state/limit, or use sector alone for category discovery. Sector filters before limit and requires a supporting Core server. Exact names rank first. Inspect searchMatch fields and search.has_more; truncated results are not exhaustive. On zero matches, try broader or alternative wording before concluding no Deal exists. Read candidates to verify specific categories and claims. Always search before creating a company.",
|
|
148
|
+
description: "Search current Deal metadata by company name/key, description, sector/subsector or founder name in either stored language. Use short keywords; matching is a case-insensitive literal substring, not semantic search. Combine q with sector/status/state/limit, or use sector alone for category discovery. status filters by exact pipeline status (e.g. [\"Invested\"]); to list every invested Deal use status [\"Invested\"] with limit 200 and order by investmentDate/markedInvestedAt, instead of guessing from names. Sector filters before limit and requires a supporting Core server. Exact names rank first. Inspect searchMatch fields and search.has_more; truncated results are not exhaustive. On zero matches, try broader or alternative wording before concluding no Deal exists. Read candidates to verify specific categories and claims. Always search before creating a company.",
|
|
147
149
|
inputSchema: {
|
|
148
150
|
q: z.string().optional(),
|
|
149
151
|
sector: z.string().trim().min(1).max(200).optional(),
|
|
152
|
+
status: z.array(z.string().min(1).max(60)).min(1).max(10).optional(),
|
|
150
153
|
state: z.enum(["active", "archived", "trashed"]).optional(),
|
|
151
154
|
limit: z.number().int().min(1).max(2000).optional(),
|
|
152
155
|
},
|
|
153
156
|
},
|
|
154
157
|
// @core-api-operation GET /api/occam/deals
|
|
155
|
-
async ({ q, sector, state, limit } = {}) => callApi(
|
|
156
|
-
"GET", buildDealSearchPath(q, { sector, state, limit }), undefined,
|
|
157
|
-
(result) => verifyDealSearchResult(result, { sector }),
|
|
158
|
+
async ({ q, sector, status, state, limit } = {}) => callApi(
|
|
159
|
+
"GET", buildDealSearchPath(q, { sector, status, state, limit }), undefined,
|
|
160
|
+
(result) => verifyDealSearchResult(result, { sector, status }),
|
|
158
161
|
),
|
|
159
162
|
);
|
|
160
163
|
|
|
161
164
|
server.registerTool(
|
|
162
165
|
"read_deal",
|
|
163
166
|
{
|
|
164
|
-
description: "Read one Deal. Live Page is current human-visible state; Information is memory and never updates Page automatically. Use artifactId to read original source text with page/paragraph anchors. Continue with nextOffset and source.sha256. Page field semantics come from agent_bootstrap, not every Deal read.",
|
|
167
|
+
description: "Read one Deal. Live Page is current human-visible state; Information is memory and never updates Page automatically. Read Information selectively with informationIds (full values, up to 50), informationTypes, and/or informationQuery (literal text; returns match.snippets, and values over 8,000 characters as { omitted, chars }: then read that id). Prefer these over detail=memory, which returns up to ~2 MB. Use artifactId to read original source text with page/paragraph anchors. Continue with nextOffset and source.sha256. Page field semantics come from agent_bootstrap, not every Deal read.",
|
|
165
168
|
inputSchema: {
|
|
166
169
|
dealId: z.string().min(1),
|
|
167
170
|
detail: z.enum(["overview", "memory", "files", "conversation", "history", "all"]).optional(),
|
|
@@ -170,17 +173,23 @@ server.registerTool(
|
|
|
170
173
|
offset: z.number().int().min(0).optional(),
|
|
171
174
|
limit: z.number().int().min(1).max(50000).optional(),
|
|
172
175
|
sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
176
|
+
informationIds: z.array(z.string().uuid()).min(1).max(50).optional(),
|
|
177
|
+
informationTypes: z.array(z.string().min(1).max(120)).min(1).max(20).optional(),
|
|
178
|
+
informationQuery: z.string().min(2).max(200).optional(),
|
|
173
179
|
},
|
|
174
180
|
},
|
|
175
|
-
async ({ dealId, detail, artifactId, ...options }) => {
|
|
181
|
+
async ({ dealId, detail, artifactId, informationIds, informationTypes, informationQuery, ...options }) => {
|
|
182
|
+
const filter = informationReadFilter({ ids: informationIds, types: informationTypes, q: informationQuery });
|
|
176
183
|
if (artifactId) {
|
|
177
184
|
if (detail) throw new Error("Use artifactId or detail, not both");
|
|
185
|
+
if (filter) throw new Error("Use artifactId or information filters, not both");
|
|
178
186
|
// @core-api-operation GET /api/occam/deals/{dealId}/artifacts/{artifactId}
|
|
179
187
|
return callApi("GET", buildArtifactReadPath(dealId, artifactId, options));
|
|
180
188
|
}
|
|
181
189
|
if (Object.values(options).some(value => value !== undefined)) throw new Error("Content options require artifactId");
|
|
182
190
|
// @core-api-operation GET /api/occam/deals/{dealId}
|
|
183
|
-
return callApi("GET", buildDealReadPath(dealId, detail || "overview")
|
|
191
|
+
return callApi("GET", buildDealReadPath(dealId, detail || "overview", filter), undefined,
|
|
192
|
+
(result) => verifyInformationReadResult(result, filter));
|
|
184
193
|
},
|
|
185
194
|
);
|
|
186
195
|
|
|
@@ -268,7 +277,7 @@ server.registerTool(
|
|
|
268
277
|
server.registerTool(
|
|
269
278
|
"agent_bootstrap",
|
|
270
279
|
{
|
|
271
|
-
description: "Mandatory before Llama work: fetch the authenticated two-part private Brain (Investment Framework V3 + Llama Command operating skill), a compact Live Deal Page schema index, runtime contract, and visible Llama OS skills. Read exact Page fields separately with get_live_deal_page_schema.",
|
|
280
|
+
description: "Mandatory before Llama work: fetch the authenticated two-part private Brain (Investment Framework V3 + Llama Command operating skill), a compact Live Deal Page schema index, runtime contract including Wiki publishing guidance, and visible Llama OS skills. Read exact Page fields separately with get_live_deal_page_schema.",
|
|
272
281
|
inputSchema: { limit: z.number().int().min(1).max(100).optional() },
|
|
273
282
|
},
|
|
274
283
|
async ({ limit } = {}) => {
|
|
@@ -410,17 +419,18 @@ server.registerTool(
|
|
|
410
419
|
server.registerTool(
|
|
411
420
|
"wiki_save",
|
|
412
421
|
{
|
|
413
|
-
description: "
|
|
422
|
+
description: "Publish institutional knowledge when authorized. Follow agent_bootstrap contract.wiki_publishing. New pages default to complete self-contained HTML with embedded rendering assets and full report content/interactions; set content_type=html. Honor explicit Markdown requests. Read the same slug/lang before edits and preserve its format unless conversion is authorized. Verify the receipt, canonical page and interactions before reporting completion.",
|
|
414
423
|
inputSchema: {
|
|
415
424
|
slug: z.string().min(1),
|
|
416
425
|
title: z.string().min(1),
|
|
417
|
-
content: z.string(),
|
|
426
|
+
content: z.string().describe("Complete page source. For HTML, include the full document and embedded rendering dependencies."),
|
|
418
427
|
sources: z.array(z.string()).min(1),
|
|
419
|
-
content_type: z.enum(["markdown", "html"]).optional(),
|
|
428
|
+
content_type: z.enum(["markdown", "html"]).optional().describe("Set html for HTML publications. Use markdown for an explicit Markdown request or an existing Markdown article."),
|
|
429
|
+
lang: z.enum(["en", "zh"]).optional().describe("Article language; Core defaults to en when omitted."),
|
|
420
430
|
},
|
|
421
431
|
},
|
|
422
|
-
async ({ slug, title, content, sources, content_type }) =>
|
|
423
|
-
callApi("POST", "/api/wiki/save", { slug, title, content, sources, content_type }),
|
|
432
|
+
async ({ slug, title, content, sources, content_type, lang }) =>
|
|
433
|
+
callApi("POST", "/api/wiki/save", { slug, title, content, sources, content_type, lang }),
|
|
424
434
|
);
|
|
425
435
|
|
|
426
436
|
server.registerTool(
|
package/bin/llama.mjs
CHANGED
|
@@ -36,7 +36,9 @@ import {
|
|
|
36
36
|
DEAL_ACTIONS,
|
|
37
37
|
buildDealReadPath,
|
|
38
38
|
buildDealSearchPath,
|
|
39
|
+
informationReadFilter,
|
|
39
40
|
verifyDealSearchResult,
|
|
41
|
+
verifyInformationReadResult,
|
|
40
42
|
compactDealWriteResult,
|
|
41
43
|
prepareDealCommand,
|
|
42
44
|
readJsonInput,
|
|
@@ -54,7 +56,7 @@ const { version: PKG_VERSION } = requireFromHere("../package.json");
|
|
|
54
56
|
const HELP_ROOT = `Llama Command CLI 2 — small authenticated tools for agents.
|
|
55
57
|
|
|
56
58
|
Deal has exactly four actions:
|
|
57
|
-
llama deal search [query] [--sector <text>] [--state active|archived|trashed] [--limit 10]
|
|
59
|
+
llama deal search [query] [--sector <text>] [--status <status>] [--state active|archived|trashed] [--limit 10]
|
|
58
60
|
llama deal read <dealId> [--detail overview|memory|files|conversation|history|all]
|
|
59
61
|
llama deal create --json <file|->
|
|
60
62
|
llama deal write --json <file|->
|
|
@@ -82,12 +84,39 @@ Separate preserved domains:
|
|
|
82
84
|
llama admin auth-events|deal-events|agent-events|feedback
|
|
83
85
|
llama pitch start|say|upload|status|finalize|end
|
|
84
86
|
|
|
85
|
-
Run \`llama help deal\` for the mutation contract.`;
|
|
87
|
+
Run \`llama help deal\` for the mutation contract and \`llama help wiki\` for publishing.`;
|
|
88
|
+
|
|
89
|
+
const HELP_WIKI = `Llama Wiki — publish complete institutional knowledge pages
|
|
90
|
+
|
|
91
|
+
llama agent bootstrap
|
|
92
|
+
llama wiki search <query>
|
|
93
|
+
llama wiki read <slug> [--lang en|zh]
|
|
94
|
+
llama wiki save <slug> --title "..." --file page.html --content-type html --lang en --sources "url1;url2"
|
|
95
|
+
llama wiki delete <slug> [--lang en|zh]
|
|
96
|
+
llama wiki restore <slug> [--lang en|zh]
|
|
97
|
+
|
|
98
|
+
Follow the current authenticated contract.wiki_publishing guidance. New
|
|
99
|
+
publications default to complete self-contained HTML. Preserve the report's
|
|
100
|
+
full content, charts, interactions and existing language/version controls.
|
|
101
|
+
Embed CSS, JavaScript, chart data and rendering assets in the HTML.
|
|
102
|
+
|
|
103
|
+
--file reads the full file; .html/.htm selects HTML automatically. When sending
|
|
104
|
+
HTML through --content, explicitly set --content-type html. Use --lang zh for
|
|
105
|
+
Chinese content. Sources are required; separate them with semicolons or pipes.
|
|
106
|
+
|
|
107
|
+
Honor an explicit Markdown request with --content-type markdown. Read an
|
|
108
|
+
existing slug and language before editing and preserve its content_type.
|
|
109
|
+
Format conversion or replacement needs authorization for that change.
|
|
110
|
+
|
|
111
|
+
Read the receipt's canonical_url, content_type, langs_available, verified and
|
|
112
|
+
warnings, then read back the article and check the rendered page and its
|
|
113
|
+
interactions before reporting completion. Report any checks still pending.
|
|
114
|
+
Wiki publication requires the user's request or authorization.`;
|
|
86
115
|
|
|
87
116
|
const HELP_DEAL = `Llama Command CLI 2 — Deal contract
|
|
88
117
|
|
|
89
118
|
Exactly four actions:
|
|
90
|
-
llama deal search [query] [--sector <text>] [--state active|archived|trashed] [--limit 10]
|
|
119
|
+
llama deal search [query] [--sector <text>] [--status <status>] [--state active|archived|trashed] [--limit 10]
|
|
91
120
|
llama deal read <dealId> [--detail overview|memory|files|conversation|history|all]
|
|
92
121
|
llama deal create --json <file|->
|
|
93
122
|
llama deal write --json <file|->
|
|
@@ -99,10 +128,23 @@ Combine --sector with a query/state/limit, or search a sector without a query:
|
|
|
99
128
|
llama deal search "humanoid" --sector "robotics"
|
|
100
129
|
Metadata search and --sector require Core API 5.41.0 or newer. A sector filter
|
|
101
130
|
must be confirmed by Core; old unfiltered responses are rejected.
|
|
131
|
+
--status matches the current pipeline status exactly (any of a comma list) and
|
|
132
|
+
works alone; it requires Core API 5.42.0+. To list every invested Deal:
|
|
133
|
+
llama deal search --status Invested --limit 200
|
|
134
|
+
then order by investmentDate / markedInvestedAt.
|
|
102
135
|
Use short keywords. searchMatch lists matched fields; search.has_more means
|
|
103
136
|
results are truncated. On zero matches, try broader or alternative wording.
|
|
104
137
|
Read candidate Deals to verify categories and substantive claims.
|
|
105
138
|
|
|
139
|
+
Read Information selectively (Core API 5.42.0+), instead of --detail memory:
|
|
140
|
+
llama deal read <dealId> --information-q "ARR" literal text; returns snippets
|
|
141
|
+
llama deal read <dealId> --information-type founder_claim exact types, comma-separated
|
|
142
|
+
llama deal read <dealId> --information-id <id>[,<id>] full values, up to 50
|
|
143
|
+
Filters combine with AND. Values over 8,000 characters in a text or type read
|
|
144
|
+
come back as { omitted, chars }; read that record by id. informationFilter
|
|
145
|
+
echoes what Core applied; matched > returned means narrow the filter. An older
|
|
146
|
+
Core that ignores the filter is rejected rather than shown as filtered.
|
|
147
|
+
|
|
106
148
|
Read original source content:
|
|
107
149
|
llama deal read <dealId> --artifact <artifactId> [--version N] [--offset N --sha256 HASH] [--limit N] [--output <file>]
|
|
108
150
|
llama wiki read <slug> --format text [--attachment <referenceId>] [--offset N --sha256 HASH] [--output <file>]
|
|
@@ -268,6 +310,7 @@ function usage(area) {
|
|
|
268
310
|
area === "memory" ? HELP_MEMORY :
|
|
269
311
|
area === "page-schema" ? HELP_PAGE_SCHEMA :
|
|
270
312
|
area === "feedback" ? HELP_FEEDBACK :
|
|
313
|
+
area === "wiki" ? HELP_WIKI :
|
|
271
314
|
HELP_ROOT,
|
|
272
315
|
);
|
|
273
316
|
}
|
|
@@ -540,7 +583,7 @@ async function handleWiki(action, rest) {
|
|
|
540
583
|
}
|
|
541
584
|
if (action === "save") {
|
|
542
585
|
if (!slug || typeof flags.title !== "string" || typeof flags.sources !== "string") {
|
|
543
|
-
throw new Error('Usage: llama wiki save <slug> --title "..." --
|
|
586
|
+
throw new Error('Usage: llama wiki save <slug> --title "..." --file page.html --content-type html --sources "url1;url2". See llama help wiki for HTML and Markdown publishing.');
|
|
544
587
|
}
|
|
545
588
|
if (flags.content && flags.file) throw new Error("Use either --content or --file, not both.");
|
|
546
589
|
const content = flags.file ? await readFile(String(flags.file), "utf8") : flags.content;
|
|
@@ -749,7 +792,7 @@ async function main() {
|
|
|
749
792
|
}
|
|
750
793
|
|
|
751
794
|
if (area === "deal" && action === "search") {
|
|
752
|
-
const { flags, positional } = parseFlags(rest, ["state", "limit", "sector"]);
|
|
795
|
+
const { flags, positional } = parseFlags(rest, ["state", "limit", "sector", "status"]);
|
|
753
796
|
// @core-api-operation GET /api/occam/deals
|
|
754
797
|
const result = await request("GET", buildDealSearchPath(positional.join(" ").trim(), flags));
|
|
755
798
|
print(verifyDealSearchResult(result, flags));
|
|
@@ -757,16 +800,19 @@ async function main() {
|
|
|
757
800
|
}
|
|
758
801
|
if (area === "deal" && action === "read") {
|
|
759
802
|
const dealId = rest[0];
|
|
760
|
-
const { flags } = parseFlags(rest.slice(1), ["detail", "artifact", "version", "format", "offset", "limit", "sha256", "output"]);
|
|
803
|
+
const { flags } = parseFlags(rest.slice(1), ["detail", "artifact", "version", "format", "offset", "limit", "sha256", "output", "information-id", "information-type", "information-q"]);
|
|
804
|
+
const filter = informationReadFilter({ ids: flags["information-id"], types: flags["information-type"], q: flags["information-q"] });
|
|
761
805
|
if (flags.artifact !== undefined) {
|
|
762
806
|
if (flags.detail !== undefined) throw new Error("Use --artifact or --detail, not both");
|
|
807
|
+
if (filter) throw new Error("Use --artifact or --information-*, not both");
|
|
763
808
|
// @core-api-operation GET /api/occam/deals/{dealId}/artifacts/{artifactId}
|
|
764
809
|
const result = await request("GET", buildArtifactReadPath(dealId, flags.artifact, flags));
|
|
765
810
|
print(await saveContentSource(result, flags.output));
|
|
766
811
|
} else {
|
|
767
812
|
if (["version", "format", "offset", "limit", "sha256", "output"].some(key => flags[key] !== undefined)) throw new Error("Content options require --artifact");
|
|
768
813
|
// @core-api-operation GET /api/occam/deals/{dealId}
|
|
769
|
-
|
|
814
|
+
const result = await request("GET", buildDealReadPath(dealId, flags.detail === true ? "overview" : flags.detail || "overview", filter));
|
|
815
|
+
print(verifyInformationReadResult(result, filter));
|
|
770
816
|
}
|
|
771
817
|
return;
|
|
772
818
|
}
|
package/contracts/core-api.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "llama.core-api-contract.v1",
|
|
3
3
|
"name": "llama-core-api",
|
|
4
|
-
"apiVersion": "5.
|
|
4
|
+
"apiVersion": "5.42.0",
|
|
5
5
|
"openapiVersion": "3.0.3",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "c244351c5d6b4f9f917a234229a3e7044558e8c4870decf0e8b1518a166f8799",
|
|
7
7
|
"pathCount": 133,
|
|
8
8
|
"operationCount": 164
|
|
9
9
|
}
|
package/lib/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "llama.cli-build.v1",
|
|
3
3
|
"packageName": "@llamaventures/cli",
|
|
4
|
-
"packageVersion": "2.
|
|
5
|
-
"sourceSha": "
|
|
4
|
+
"packageVersion": "2.7.1",
|
|
5
|
+
"sourceSha": "18bbea099ee97465f3c0ba4cbc01ff7c0c8a12bf",
|
|
6
6
|
"sourceKind": "github",
|
|
7
7
|
"sourceDirty": false,
|
|
8
8
|
"coreApiContract": {
|
|
9
9
|
"format": "llama.core-api-contract.v1",
|
|
10
10
|
"name": "llama-core-api",
|
|
11
|
-
"apiVersion": "5.
|
|
11
|
+
"apiVersion": "5.42.0",
|
|
12
12
|
"openapiVersion": "3.0.3",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "c244351c5d6b4f9f917a234229a3e7044558e8c4870decf0e8b1518a166f8799"
|
|
14
14
|
}
|
|
15
15
|
}
|
package/lib/deal-actions.mjs
CHANGED
|
@@ -169,11 +169,27 @@ export function buildDealSearchPath(query, flags = {}) {
|
|
|
169
169
|
if (query) params.set("q", query);
|
|
170
170
|
const sector = dealSearchSector(flags);
|
|
171
171
|
if (sector !== undefined) params.set("sector", sector);
|
|
172
|
+
const status = dealSearchStatus(flags);
|
|
173
|
+
if (status !== undefined) params.set("status", status.join(","));
|
|
172
174
|
if (flags.state && flags.state !== true) params.set("state", String(flags.state));
|
|
173
175
|
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
174
176
|
return `/api/occam/deals${params.size ? `?${params}` : ""}`;
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
/** Exact pipeline statuses (Core API 5.42.0+), e.g. "Invested" or ["Invested", "Diligence"]. */
|
|
180
|
+
function dealSearchStatus(flags) {
|
|
181
|
+
if (flags.status === undefined) return undefined;
|
|
182
|
+
if (flags.status === true) throw new Error("--status requires a pipeline status, e.g. Invested");
|
|
183
|
+
const values = (Array.isArray(flags.status) ? flags.status : String(flags.status).split(","))
|
|
184
|
+
.map((value) => String(value).trim())
|
|
185
|
+
.filter(Boolean);
|
|
186
|
+
if (!values.length) throw new Error("--status requires a pipeline status, e.g. Invested");
|
|
187
|
+
if (values.length > 10 || values.some((value) => value.length > 60)) {
|
|
188
|
+
throw new Error("--status accepts at most 10 statuses of at most 60 characters");
|
|
189
|
+
}
|
|
190
|
+
return [...new Set(values)];
|
|
191
|
+
}
|
|
192
|
+
|
|
177
193
|
function dealSearchSector(flags) {
|
|
178
194
|
if (flags.sector === undefined) return undefined;
|
|
179
195
|
if (typeof flags.sector !== "string" || !flags.sector.trim() || flags.sector.trim().length > 200) {
|
|
@@ -183,6 +199,10 @@ function dealSearchSector(flags) {
|
|
|
183
199
|
}
|
|
184
200
|
|
|
185
201
|
export function verifyDealSearchResult(result, flags = {}) {
|
|
202
|
+
const status = dealSearchStatus(flags);
|
|
203
|
+
if (status !== undefined && JSON.stringify(result?.search?.filters?.status ?? null) !== JSON.stringify(status)) {
|
|
204
|
+
throw new Error("Core did not confirm the requested status filter. Filtering by pipeline status requires Core API 5.42.0 or newer; this response was not accepted as filtered results.");
|
|
205
|
+
}
|
|
186
206
|
const sector = dealSearchSector(flags);
|
|
187
207
|
if (sector !== undefined && result?.search?.filters?.sector !== sector) {
|
|
188
208
|
throw new Error("Core did not confirm the requested sector filter. Upgrade the Llama Command server before retrying --sector; this response was not accepted as filtered results.");
|
|
@@ -190,7 +210,55 @@ export function verifyDealSearchResult(result, flags = {}) {
|
|
|
190
210
|
return result;
|
|
191
211
|
}
|
|
192
212
|
|
|
193
|
-
|
|
213
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
214
|
+
|
|
215
|
+
function listInput(value) {
|
|
216
|
+
if (value === undefined || value === null) return undefined;
|
|
217
|
+
const values = (Array.isArray(value) ? value : String(value).split(","))
|
|
218
|
+
.map((item) => String(item).trim())
|
|
219
|
+
.filter(Boolean);
|
|
220
|
+
return values.length ? [...new Set(values)] : undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Targeted Information read: ids (full values), exact types, and/or literal
|
|
225
|
+
* text. Returns undefined when no filter was requested. Core API 5.42.0+.
|
|
226
|
+
*/
|
|
227
|
+
export function informationReadFilter({ ids, types, q } = {}) {
|
|
228
|
+
const filter = {};
|
|
229
|
+
const idList = listInput(ids);
|
|
230
|
+
const typeList = listInput(types);
|
|
231
|
+
if (idList) {
|
|
232
|
+
if (idList.length > 50) throw new Error("--information-id accepts at most 50 ids");
|
|
233
|
+
if (idList.some((id) => !UUID_RE.test(id))) throw new Error("--information-id must be Information record UUIDs");
|
|
234
|
+
filter.ids = idList.map((id) => id.toLowerCase());
|
|
235
|
+
}
|
|
236
|
+
if (typeList) {
|
|
237
|
+
if (typeList.length > 20) throw new Error("--information-type accepts at most 20 types");
|
|
238
|
+
if (typeList.some((type) => type.length > 120)) throw new Error("--information-type values must be at most 120 characters");
|
|
239
|
+
filter.types = typeList;
|
|
240
|
+
}
|
|
241
|
+
if (q !== undefined && q !== null) {
|
|
242
|
+
if (q === true) throw new Error("--information-q requires text");
|
|
243
|
+
const text = String(q).trim();
|
|
244
|
+
if (text.length < 2 || text.length > 200) throw new Error("--information-q must be 2-200 characters");
|
|
245
|
+
filter.q = text;
|
|
246
|
+
}
|
|
247
|
+
return Object.keys(filter).length ? filter : undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Reject an unfiltered response from a Core that predates targeted reads. */
|
|
251
|
+
export function verifyInformationReadResult(result, filter) {
|
|
252
|
+
if (!filter) return result;
|
|
253
|
+
const applied = result?.informationFilter;
|
|
254
|
+
const same = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
255
|
+
if (!applied || !same(applied.ids, filter.ids) || !same(applied.types, filter.types) || !same(applied.q, filter.q)) {
|
|
256
|
+
throw new Error("Core did not confirm the requested Information filter. Targeted Information reads require Core API 5.42.0 or newer; this response was not accepted as filtered results. Use --detail memory on an older server.");
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function buildDealReadPath(dealId, detail = "overview", filter) {
|
|
194
262
|
if (!dealId || dealId.startsWith("--")) {
|
|
195
263
|
throw new Error("Usage: llama deal read <dealId> [--detail overview|memory|files|conversation|history|all]");
|
|
196
264
|
}
|
|
@@ -203,7 +271,13 @@ export function buildDealReadPath(dealId, detail = "overview") {
|
|
|
203
271
|
all: "information,artifacts,chat,feed",
|
|
204
272
|
}[detail];
|
|
205
273
|
if (expansion === undefined) throw new Error(`Unknown detail: ${detail}`);
|
|
206
|
-
|
|
274
|
+
const params = new URLSearchParams();
|
|
275
|
+
if (expansion) params.set("expand", expansion);
|
|
276
|
+
if (filter?.ids) params.set("information_id", filter.ids.join(","));
|
|
277
|
+
if (filter?.types) params.set("information_type", filter.types.join(","));
|
|
278
|
+
if (filter?.q !== undefined) params.set("information_q", filter.q);
|
|
279
|
+
const query = params.toString().replace(/%2C/g, ",");
|
|
280
|
+
return `/api/occam/deals/${encodeURIComponent(dealId)}${query ? `?${query}` : ""}`;
|
|
207
281
|
}
|
|
208
282
|
|
|
209
283
|
/**
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llamaventures/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "CLI + MCP server for the Llama Ventures investment workbench (command.llamaventures.vc).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "npm run test:auth && npm run test:agent-routing && npm run test:contract",
|
|
8
8
|
"test:agent-routing": "node scripts/verify-agent-routing.mjs",
|
|
9
|
-
"test:contract": "node --test scripts/feedback.test.mjs scripts/content-read.test.mjs scripts/build-manifest.test.mjs scripts/core-api-call-sites.test.mjs scripts/deal-actions-cli.test.mjs scripts/deal-actions.test.mjs scripts/deal-memory-actions-cli.test.mjs scripts/deal-memory-actions.test.mjs scripts/mcp-four-action-surface.test.mjs scripts/page-schema-cli.test.mjs scripts/page-schema.test.mjs scripts/server-compatibility.test.mjs scripts/verify-core-api-contract.test.mjs && node scripts/verify-core-api-contract.mjs",
|
|
9
|
+
"test:contract": "node --test scripts/wiki-publishing.test.mjs scripts/feedback.test.mjs scripts/content-read.test.mjs scripts/build-manifest.test.mjs scripts/core-api-call-sites.test.mjs scripts/deal-actions-cli.test.mjs scripts/deal-actions.test.mjs scripts/deal-memory-actions-cli.test.mjs scripts/deal-memory-actions.test.mjs scripts/mcp-four-action-surface.test.mjs scripts/page-schema-cli.test.mjs scripts/page-schema.test.mjs scripts/server-compatibility.test.mjs scripts/verify-core-api-contract.test.mjs && node scripts/verify-core-api-contract.mjs",
|
|
10
10
|
"verify:artifact": "node scripts/verify-release-artifact.mjs",
|
|
11
11
|
"verify:release": "npm test && npm run verify:artifact && node scripts/verify-tarball-clean.mjs",
|
|
12
12
|
"prepack": "node scripts/prepare-build-manifest.mjs",
|