@lotics/cli 0.91.2 → 0.92.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/README.md CHANGED
@@ -20,7 +20,7 @@ package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
20
20
  create → generate → chain lifecycle, and the marker capabilities.
21
21
  - [`docs/knowledge_docs.md`](docs/knowledge_docs.md) — the AI's rulebook layer: authoring the
22
22
  workspace facts an agent can't guess, the access-vs-activation model, and the
23
- search → grep → read retrieval funnel agents use to pull only the lines they need.
23
+ catalog-then-stage retrieval model agents use to pull only the lines they need.
24
24
 
25
25
  Both point to `lotics tools <name>` for exact input schemas.
26
26
 
@@ -164,6 +164,22 @@ Edits mutate the file in place via an atomic temp-file + rename. Unknown OOXML c
164
164
 
165
165
  Run `lotics xlsx` or `lotics docx` with no subcommand for the full list.
166
166
 
167
+ ## Knowledge docs
168
+
169
+ A file-native surface over the workspace's knowledge docs — the AI's rulebook layer. The body is a Markdown file: `create`/`update` read it from your filesystem, `get` writes it back. See [`docs/knowledge_docs.md`](docs/knowledge_docs.md) for the model.
170
+
171
+ ```bash
172
+ lotics knowledge list # catalog: id, name, description (--json)
173
+ lotics knowledge create --name "Shipping tariffs" \
174
+ --description "HS-coded rates; searchable by lane and code" \
175
+ --from ./tariffs.md # or --content '<inline>'; prints the new id
176
+ lotics knowledge get kdc_... -o ./tariffs.md # body → file (omit -o for stdout; --json = full doc)
177
+ lotics knowledge update kdc_... --from ./tariffs.md # send only what changed (--name / --description too)
178
+ lotics knowledge rm kdc_... # archive
179
+ ```
180
+
181
+ `create` / `update` send the body inline; the server mints and version-chains the content file (and `update` diffs + resolves concurrency internally — no token to pass). `get` is the one content-read path, hydrating the body server-side.
182
+
167
183
  ## Custom-code apps
168
184
 
169
185
  ```bash
package/dist/src/cli.js CHANGED
@@ -22135,7 +22135,7 @@ var require_lib2 = __commonJS({
22135
22135
  }
22136
22136
  if (this.state !== PENDING) {
22137
22137
  var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
22138
- unwrap(promise2, resolver, this.outcome);
22138
+ unwrap2(promise2, resolver, this.outcome);
22139
22139
  } else {
22140
22140
  this.queue.push(new QueueItem(promise2, onFulfilled, onRejected));
22141
22141
  }
@@ -22156,15 +22156,15 @@ var require_lib2 = __commonJS({
22156
22156
  handlers.resolve(this.promise, value);
22157
22157
  };
22158
22158
  QueueItem.prototype.otherCallFulfilled = function(value) {
22159
- unwrap(this.promise, this.onFulfilled, value);
22159
+ unwrap2(this.promise, this.onFulfilled, value);
22160
22160
  };
22161
22161
  QueueItem.prototype.callRejected = function(value) {
22162
22162
  handlers.reject(this.promise, value);
22163
22163
  };
22164
22164
  QueueItem.prototype.otherCallRejected = function(value) {
22165
- unwrap(this.promise, this.onRejected, value);
22165
+ unwrap2(this.promise, this.onRejected, value);
22166
22166
  };
22167
- function unwrap(promise2, func, value) {
22167
+ function unwrap2(promise2, func, value) {
22168
22168
  immediate(function() {
22169
22169
  var returnValue;
22170
22170
  try {
@@ -29585,7 +29585,7 @@ var require_lib3 = __commonJS({
29585
29585
  // src/cli.ts
29586
29586
  import dns from "node:dns";
29587
29587
  import net2 from "node:net";
29588
- import fs8 from "node:fs";
29588
+ import fs9 from "node:fs";
29589
29589
  import path7 from "node:path";
29590
29590
  import readline from "node:readline";
29591
29591
 
@@ -29750,6 +29750,16 @@ var LoticsClient = class {
29750
29750
  }
29751
29751
  return this.request("POST", "/v1/tools/execute", body);
29752
29752
  }
29753
+ // --- Knowledge docs ---
29754
+ /**
29755
+ * Fetch one knowledge doc with its hydrated `content` — the single content-read
29756
+ * path for a non-sandbox client (the `list_knowledge` tool returns metadata
29757
+ * only, and the sandbox-staging read path is unavailable here). Works for both
29758
+ * the file-model and legacy parked-column rows. Mirrors GET /v1/knowledge_docs/{id}.
29759
+ */
29760
+ async getKnowledgeDoc(knowledge_doc_id) {
29761
+ return this.request("GET", `/v1/knowledge_docs/${encodeURIComponent(knowledge_doc_id)}`);
29762
+ }
29753
29763
  // --- Apps ---
29754
29764
  async getApp(app_id) {
29755
29765
  return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}`);
@@ -33876,6 +33886,9 @@ function parseArgs(argv) {
33876
33886
  viewAs: void 0,
33877
33887
  uiSrc: void 0,
33878
33888
  name: void 0,
33889
+ description: void 0,
33890
+ from: void 0,
33891
+ content: void 0,
33879
33892
  timezone: void 0,
33880
33893
  message: void 0,
33881
33894
  local: false,
@@ -33923,6 +33936,15 @@ function parseArgs(argv) {
33923
33936
  case "--name":
33924
33937
  flags.name = argv[++i2];
33925
33938
  break;
33939
+ case "--description":
33940
+ flags.description = argv[++i2];
33941
+ break;
33942
+ case "--from":
33943
+ flags.from = argv[++i2];
33944
+ break;
33945
+ case "--content":
33946
+ flags.content = argv[++i2];
33947
+ break;
33926
33948
  case "--timezone":
33927
33949
  flags.timezone = argv[++i2];
33928
33950
  break;
@@ -67828,6 +67850,148 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
67828
67850
  }
67829
67851
  }
67830
67852
 
67853
+ // src/knowledge.ts
67854
+ import fs8 from "node:fs";
67855
+ function readBodyFile(filePath) {
67856
+ if (!fs8.existsSync(filePath)) fail(`File not found: ${filePath}`);
67857
+ return fs8.readFileSync(filePath, "utf-8");
67858
+ }
67859
+ function resolveBody(flags, required2) {
67860
+ if (flags.from !== void 0 && flags.content !== void 0) {
67861
+ fail("Pass either --from <file> or --content <str>, not both.");
67862
+ }
67863
+ if (flags.from !== void 0) return readBodyFile(flags.from);
67864
+ if (flags.content !== void 0) return flags.content;
67865
+ if (required2) fail("A body is required \u2014 pass --from <file.md> or --content <str>.");
67866
+ return void 0;
67867
+ }
67868
+ function unwrap(res) {
67869
+ if (res.error) fail(res.error);
67870
+ return res.result;
67871
+ }
67872
+ async function knowledgeList(client, flags) {
67873
+ const result = unwrap(await client.execute("list_knowledge", {}));
67874
+ const payload = result;
67875
+ const results = Array.isArray(payload?.results) ? payload.results : [];
67876
+ if (flags.json) {
67877
+ console.log(JSON.stringify(results, null, 2));
67878
+ return;
67879
+ }
67880
+ if (results.length === 0) {
67881
+ console.error("No knowledge docs in this workspace.");
67882
+ return;
67883
+ }
67884
+ const rows = results.map((r) => {
67885
+ const doc = r;
67886
+ return {
67887
+ id: typeof doc.id === "string" ? doc.id : "",
67888
+ name: typeof doc.name === "string" ? doc.name : "",
67889
+ // Collapse whitespace so a multi-line description never breaks the row.
67890
+ description: typeof doc.description === "string" ? doc.description.replace(/\s+/g, " ").trim() : ""
67891
+ };
67892
+ });
67893
+ const wId = Math.max("ID".length, ...rows.map((r) => r.id.length));
67894
+ const wName = Math.max("NAME".length, ...rows.map((r) => r.name.length));
67895
+ const pad = (s, n) => s.padEnd(n);
67896
+ console.log(`${pad("ID", wId)} ${pad("NAME", wName)} DESCRIPTION`);
67897
+ for (const r of rows) {
67898
+ console.log(`${pad(r.id, wId)} ${pad(r.name, wName)} ${r.description}`.trimEnd());
67899
+ }
67900
+ }
67901
+ async function knowledgeCreate(client, flags) {
67902
+ const name = flags.name;
67903
+ if (!name) fail("Usage: lotics knowledge create --name <name> [--description <d>] (--from <file.md> | --content <str>)");
67904
+ const content = resolveBody(flags, true);
67905
+ const result = unwrap(
67906
+ await client.execute("create_knowledge", {
67907
+ name,
67908
+ // The tool requires a description (empty string is accepted); the CLI
67909
+ // treats it as optional and defaults to "".
67910
+ description: flags.description ?? "",
67911
+ content
67912
+ })
67913
+ );
67914
+ if (flags.json) {
67915
+ console.log(JSON.stringify(result, null, 2));
67916
+ return;
67917
+ }
67918
+ const doc = result;
67919
+ const id = typeof doc.id === "string" ? doc.id : "";
67920
+ console.error(`Created knowledge doc: ${typeof doc.name === "string" ? doc.name : name} (${id})`);
67921
+ console.log(id);
67922
+ }
67923
+ async function knowledgeGet(client, id, flags) {
67924
+ if (!id) fail("Usage: lotics knowledge get <id> [-o <file.md>]");
67925
+ const doc = await client.getKnowledgeDoc(id);
67926
+ if (flags.output) {
67927
+ writeFileAtomic(flags.output, Buffer.from(doc.content, "utf-8"));
67928
+ return;
67929
+ }
67930
+ if (flags.json) {
67931
+ console.log(JSON.stringify(doc, null, 2));
67932
+ return;
67933
+ }
67934
+ console.log(doc.content);
67935
+ }
67936
+ async function knowledgeUpdate(client, id, flags) {
67937
+ if (!id) fail("Usage: lotics knowledge update <id> [--from <file.md> | --content <str>] [--name <n>] [--description <d>]");
67938
+ const content = resolveBody(flags, false);
67939
+ if (content === void 0 && flags.name === void 0 && flags.description === void 0) {
67940
+ fail("Nothing to update. Pass at least one of --from/--content, --name, or --description.");
67941
+ }
67942
+ const args = { knowledge_doc_id: id };
67943
+ if (content !== void 0) args.content = content;
67944
+ if (flags.name !== void 0) args.name = flags.name;
67945
+ if (flags.description !== void 0) args.description = flags.description;
67946
+ const result = unwrap(await client.execute("update_knowledge", args));
67947
+ if (flags.json) {
67948
+ console.log(JSON.stringify(result, null, 2));
67949
+ return;
67950
+ }
67951
+ console.error(`Updated knowledge doc: ${id}`);
67952
+ }
67953
+ async function knowledgeRm(client, id, flags) {
67954
+ if (!id) fail("Usage: lotics knowledge rm <id>");
67955
+ const result = unwrap(await client.execute("delete_knowledge", { knowledge_doc_id: id }));
67956
+ if (flags.json) {
67957
+ console.log(JSON.stringify(result, null, 2));
67958
+ return;
67959
+ }
67960
+ console.error(`Deleted knowledge doc: ${id}`);
67961
+ }
67962
+ function printKnowledgeHelp() {
67963
+ console.error(`Lotics knowledge commands \u2014 manage the AI's rulebook docs.
67964
+
67965
+ lotics knowledge list [--json]
67966
+ Catalog every doc you can use (id, name, description).
67967
+ lotics knowledge create --name <n> [--description <d>] (--from <file.md> | --content <str>) [--json]
67968
+ Create a doc; the body comes from a file or an inline string. Prints the new id.
67969
+ lotics knowledge get <id> [-o <file.md>] [--json]
67970
+ Fetch a doc's content \u2014 to <file.md>, or stdout. --json prints the full doc.
67971
+ lotics knowledge update <id> [--from <file.md> | --content <str>] [--name <n>] [--description <d>] [--json]
67972
+ Update a doc; sends only the fields you pass.
67973
+ lotics knowledge rm <id> [--json]
67974
+ Archive a doc.`);
67975
+ }
67976
+ async function runKnowledgeCommand(client, subcommand, toolArgs, flags) {
67977
+ switch (subcommand) {
67978
+ case "list":
67979
+ return knowledgeList(client, flags);
67980
+ case "create":
67981
+ return knowledgeCreate(client, flags);
67982
+ case "get":
67983
+ return knowledgeGet(client, toolArgs, flags);
67984
+ case "update":
67985
+ return knowledgeUpdate(client, toolArgs, flags);
67986
+ case "rm":
67987
+ return knowledgeRm(client, toolArgs, flags);
67988
+ default:
67989
+ if (subcommand) fail(`Unknown knowledge subcommand: ${subcommand}`);
67990
+ printKnowledgeHelp();
67991
+ return;
67992
+ }
67993
+ }
67994
+
67831
67995
  // src/preview.ts
67832
67996
  import { spawn as spawn3 } from "node:child_process";
67833
67997
  import { createServer } from "node:http";
@@ -68099,6 +68263,16 @@ COMMANDS
68099
68263
  lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
68100
68264
  lotics app rename "<new name>" Rename the app's display name (launcher title)
68101
68265
  lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
68266
+ lotics knowledge list List knowledge docs (id, name, description)
68267
+ lotics knowledge create --name <n> [--description <d>] --from <file.md>
68268
+ Create a doc from a file (or --content <str>);
68269
+ prints the new id
68270
+ lotics knowledge get <id> [-o <file.md>]
68271
+ Fetch a doc's content (to a file, or stdout;
68272
+ --json for the full doc)
68273
+ lotics knowledge update <id> [--from <file.md> | --content <str>] [--name <n>] [--description <d>]
68274
+ Update a doc \u2014 sends only the fields you pass
68275
+ lotics knowledge rm <id> Archive a doc
68102
68276
  lotics ui link <component> [--ui-src <path>] [--remove]
68103
68277
  Dev-link @lotics/ui to packages/ui/src (Vite alias
68104
68278
  + tsc paths) for live HMR + typecheck. Monorepo apps
@@ -68359,9 +68533,9 @@ function resolveUploadPaths(rawPaths) {
68359
68533
  const result = [];
68360
68534
  for (const p of rawPaths) {
68361
68535
  const resolved = path7.resolve(p);
68362
- const stat = fs8.statSync(resolved);
68536
+ const stat = fs9.statSync(resolved);
68363
68537
  if (stat.isDirectory()) {
68364
- const entries = fs8.readdirSync(resolved, { withFileTypes: true });
68538
+ const entries = fs9.readdirSync(resolved, { withFileTypes: true });
68365
68539
  for (const entry of entries) {
68366
68540
  if (entry.isFile()) {
68367
68541
  result.push(path7.join(resolved, entry.name));
@@ -68573,7 +68747,7 @@ async function main() {
68573
68747
  }
68574
68748
  return;
68575
68749
  }
68576
- if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app") {
68750
+ if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app" && command !== "knowledge") {
68577
68751
  console.error(`Unknown command: ${command}`);
68578
68752
  console.error('Run "lotics --help" for usage.');
68579
68753
  process.exit(1);
@@ -68734,6 +68908,10 @@ Available workspaces:`);
68734
68908
  return;
68735
68909
  }
68736
68910
  await resolveWorkspace(client, ctx);
68911
+ if (command === "knowledge") {
68912
+ await runKnowledgeCommand(client, subcommand, toolArgs, flags);
68913
+ return;
68914
+ }
68737
68915
  if (command === "app") {
68738
68916
  if (subcommand === "create") {
68739
68917
  const name = toolArgs;
@@ -68813,7 +68991,7 @@ Available workspaces:`);
68813
68991
  const ingested = await ingestJsonArgs({
68814
68992
  rawArg: restArgs[1],
68815
68993
  stdinIsTTY: process.stdin.isTTY ?? false,
68816
- readFile: (p) => fs8.readFileSync(p, "utf-8"),
68994
+ readFile: (p) => fs9.readFileSync(p, "utf-8"),
68817
68995
  readStdin
68818
68996
  });
68819
68997
  if (ingested.kind === "error") {
@@ -68965,7 +69143,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
68965
69143
  const ingested = await ingestJsonArgs({
68966
69144
  rawArg: toolArgs,
68967
69145
  stdinIsTTY: process.stdin.isTTY ?? false,
68968
- readFile: (p) => fs8.readFileSync(p, "utf-8"),
69146
+ readFile: (p) => fs9.readFileSync(p, "utf-8"),
68969
69147
  readStdin
68970
69148
  });
68971
69149
  if (ingested.kind === "error") {
@@ -55,6 +55,25 @@ export interface ToolInfo {
55
55
  description: string;
56
56
  input_schema: unknown;
57
57
  }
58
+ /**
59
+ * A single knowledge doc with its HYDRATED body — the shape of
60
+ * `GET /v1/knowledge_docs/{id}`. `content` is resolved server-side from the
61
+ * doc's content file (or the parked column for a legacy row), so this is the
62
+ * one content-read path a non-sandbox client has. `content_file_id` is the
63
+ * concurrency token the REST PATCH echoes; the `update_knowledge` TOOL CASes
64
+ * internally, so a CLI caller never needs to pass it.
65
+ */
66
+ export interface KnowledgeDocDetail {
67
+ id: string;
68
+ workspace_id: string;
69
+ name: string;
70
+ description: string;
71
+ content: string;
72
+ content_file_id: string | null;
73
+ files: unknown[];
74
+ created_at: string;
75
+ updated_at: string;
76
+ }
58
77
  export interface FileUploadResult {
59
78
  files: Array<{
60
79
  id: string;
@@ -142,6 +161,13 @@ export declare class LoticsClient {
142
161
  format?: "json" | "text";
143
162
  timeoutMs?: number;
144
163
  }): Promise<ToolExecuteResult>;
164
+ /**
165
+ * Fetch one knowledge doc with its hydrated `content` — the single content-read
166
+ * path for a non-sandbox client (the `list_knowledge` tool returns metadata
167
+ * only, and the sandbox-staging read path is unavailable here). Works for both
168
+ * the file-model and legacy parked-column rows. Mirrors GET /v1/knowledge_docs/{id}.
169
+ */
170
+ getKnowledgeDoc(knowledge_doc_id: string): Promise<KnowledgeDocDetail>;
145
171
  getApp(app_id: string): Promise<{
146
172
  id: string;
147
173
  name: string;
@@ -187,6 +187,16 @@ export class LoticsClient {
187
187
  }
188
188
  return this.request("POST", "/v1/tools/execute", body);
189
189
  }
190
+ // --- Knowledge docs ---
191
+ /**
192
+ * Fetch one knowledge doc with its hydrated `content` — the single content-read
193
+ * path for a non-sandbox client (the `list_knowledge` tool returns metadata
194
+ * only, and the sandbox-staging read path is unavailable here). Works for both
195
+ * the file-model and legacy parked-column rows. Mirrors GET /v1/knowledge_docs/{id}.
196
+ */
197
+ async getKnowledgeDoc(knowledge_doc_id) {
198
+ return this.request("GET", `/v1/knowledge_docs/${encodeURIComponent(knowledge_doc_id)}`);
199
+ }
190
200
  // --- Apps ---
191
201
  async getApp(app_id) {
192
202
  return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}`);
@@ -21,13 +21,17 @@ names, exceptions, and policies. Do **not** put in it general skills the model a
21
21
  the doc, the doc is noise. A knowledge doc earns its place only by carrying external facts
22
22
  tied to your workspace.
23
23
 
24
- ## Creating a doc — `create_knowledge`
24
+ ## Creating a doc — `lotics knowledge create`
25
25
 
26
26
  ```bash
27
- lotics run create_knowledge @doc.json # content is large → read from a file or stdin
27
+ lotics knowledge create --name "Shipping tariffs" \
28
+ --description "HS-coded rates; searchable by lane and code" \
29
+ --from ./tariffs.md # the body is a Markdown file; or --content '<inline>'
28
30
  ```
29
31
 
30
- `create_knowledge` takes three things:
32
+ This reads the body from your filesystem and creates the doc through `create_knowledge`
33
+ (prints the new id). A raw `lotics run create_knowledge @doc.json` works too — read a large
34
+ `content` from a file or stdin. Either way, `create_knowledge` takes three things:
31
35
 
32
36
  - `name` — what it is.
33
37
  - `description` — what the doc is **and how to retrieve from it**: its vocabulary, synonyms for
@@ -57,40 +61,42 @@ So: shared + active → the agent can find and read it. Shared but deactivated
57
61
  that member's agent. This is the token economy in action — activation is how a member curates
58
62
  which rulebooks their agent carries.
59
63
 
60
- ## How an agent uses a doc — the retrieval funnel
64
+ ## How an agent uses a doc — catalog, then read
61
65
 
62
- The agent narrows from "which doc" to "which lines" in three steps. All four tools respect
63
- access + activation, so they only ever surface docs the caller may use.
66
+ Docs are not injected wholesale. The agent finds the right doc from a **catalog**, then reads
67
+ only what it needs. Both steps respect access + activation, so only docs the caller may use ever
68
+ surface.
64
69
 
65
- 1. **Find the doc** — `search_knowledge` (keyword query matching docs + a snippet) or
66
- `query_knowledge` (list every active doc). Returns *docs*, not the lines inside them.
67
- 2. **Locate lines within one doc**`grep_knowledge` (regex over the lines of a single doc,
68
- returns matches with line numbers). Matching is **diacritics-insensitive by default** — a
69
- plain-ASCII query matches accented Vietnamese text and can be case-insensitive too.
70
- 3. **Read the section** `read_knowledge`. Pass `outline: true` first to get the Markdown
71
- header map (a table of contents with line numbers), then read a section with `offset` (the
72
- 1-based first line) + `limit` (line count). A large doc never returns whole; reads are
73
- line-numbered and paged.
70
+ 1. **Catalog** — `list_knowledge` returns every usable doc as `{ id, name, description }` — no
71
+ bodies. This is why the *description* carries the weight: it is what the agent reads before
72
+ deciding to open a doc. Write it to sell the doc — its vocabulary, the colloquial synonyms an
73
+ ambiguous query would use, and what it covers.
74
+ 2. **Read** the chat agent stages the chosen doc's content file into a code run and greps it
75
+ there (the body arrives as a file to `cat`/`grep`). Over this CLI you read a body directly
76
+ with `lotics knowledge get <id>`.
74
77
 
75
- The whole point of the funnel is that the agent lands on the exact section that answers the
76
- question without ever loading the rest. Structure your content so it works:
78
+ Structure your content so a reader lands on the answer without loading the rest:
77
79
 
78
- - Organize under clear Markdown headers, so the doc can be outlined and read by section.
80
+ - Organize under clear Markdown headers.
79
81
  - Keep each searchable unit self-contained — a section for prose, **one record per line** for
80
82
  dense/tabular data (a price row, a code entry) — carrying both the terms someone would search
81
83
  for and its answer.
82
84
  - Lead with the most-queried fields.
83
85
  - Note colloquial synonyms next to official terms, so an ambiguous query still matches.
84
86
 
85
- ## Updating a doc — `update_knowledge`
87
+ ## Updating a doc — `lotics knowledge update`
86
88
 
87
- Send only the fields you're changing. Content edits are **diffs**, not a full rewrite: pass an
88
- `edits` array (replace / insert / append operations) plus the `expected_version` you got from
89
- `read_knowledge` (optimistic concurrency — a stale version is rejected). Over the CLI, the default
90
- `read_knowledge` text output omits the version — read `current_version` from `lotics run read_knowledge … --json`.
91
- Refine structure as you
92
- learn what users actually ask: add the synonym that failed to match, split the section that was
93
- too coarse to grep. See `lotics tools update_knowledge` for the edit shape.
89
+ ```bash
90
+ lotics knowledge update kdc_... --from ./tariffs.md # replace the body (--name / --description too)
91
+ ```
92
+
93
+ Send only the fields you're changing. `--from` / `--content` replaces the body; `--name` /
94
+ `--description` change metadata. `update_knowledge` resolves concurrency **internally** it
95
+ re-reads the current content pointer and version-chains the new body so there is no version
96
+ token to pass from the CLI. (The chat agent may instead send an `edits` array — anchored
97
+ replace / insert / append — for a surgical change; see `lotics tools update_knowledge`.) Refine
98
+ structure as you learn what users actually ask: add the synonym that failed to match, split the
99
+ section that was too coarse to read.
94
100
 
95
101
  ## Package-managed knowledge
96
102
 
@@ -103,12 +109,12 @@ same either way; the package layer just manages distribution and version pinning
103
109
  ## Reaching the tools
104
110
 
105
111
  ```bash
106
- lotics tools # all categories (knowledge tools are under "Knowledge")
107
- lotics tools grep_knowledge # one tool: full description + input schema
108
- lotics run create_knowledge @doc.json
109
- echo '{"query":"tariff"}' | lotics run search_knowledge
112
+ lotics knowledge list # catalog: id, name, description
113
+ lotics knowledge get kdc_... -o doc.md # read a body to a file (omit -o for stdout)
114
+ lotics tools update_knowledge # full input schema for any knowledge tool
110
115
  ```
111
116
 
112
- The Knowledge category covers `create_knowledge`, `update_knowledge`, `query_knowledge`,
113
- `search_knowledge`, `grep_knowledge`, `read_knowledge`, and `delete_knowledge`. Sharing a doc
114
- to other members is `share_resource` / `unshare_resource` (category **Admin**).
117
+ The Knowledge category covers `list_knowledge`, `create_knowledge`, `update_knowledge`, and
118
+ `delete_knowledge` fronted by the `lotics knowledge list | create | get | update | rm`
119
+ commands. Sharing a doc to other members is `share_resource` / `unshare_resource` (category
120
+ **Admin**).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.91.2",
3
+ "version": "0.92.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {