@lotics/cli 0.87.0 → 0.88.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 +14 -0
- package/dist/render_page.js +21 -19
- package/dist/src/cli.js +25 -23
- package/dist/starter_template.js +4 -4
- package/docs/document_templates.md +153 -0
- package/docs/knowledge_docs.md +112 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -10,6 +10,20 @@ Lotics is an AI-powered operations platform. Through this CLI you can:
|
|
|
10
10
|
- Administer the workspace — invite members, manage groups, share resources, transfer ownership, browse connected accounts (OAuth attach is web-only)
|
|
11
11
|
- Create and manage apps, knowledge docs, and files
|
|
12
12
|
|
|
13
|
+
## Capability guides
|
|
14
|
+
|
|
15
|
+
Two of the platform's primary surfaces have dedicated usage guides that ship inside this
|
|
16
|
+
package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
|
|
17
|
+
|
|
18
|
+
- [`docs/document_templates.md`](docs/document_templates.md) — generate finished documents
|
|
19
|
+
(PDF, Excel, Word, email) by filling reusable templates: the five template types, the
|
|
20
|
+
create → generate → chain lifecycle, and the marker capabilities.
|
|
21
|
+
- [`docs/knowledge_docs.md`](docs/knowledge_docs.md) — the AI's rulebook layer: authoring the
|
|
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.
|
|
24
|
+
|
|
25
|
+
Both point to `lotics tools <name>` for exact input schemas.
|
|
26
|
+
|
|
13
27
|
## Install
|
|
14
28
|
|
|
15
29
|
```bash
|
package/dist/render_page.js
CHANGED
|
@@ -58303,11 +58303,10 @@ ${e.toString()}`);
|
|
|
58303
58303
|
return val && typeof val === "object" ? val : {};
|
|
58304
58304
|
}
|
|
58305
58305
|
|
|
58306
|
-
// ../
|
|
58306
|
+
// ../shared/src/ole.ts
|
|
58307
58307
|
var OLE2_MAGIC2 = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
58308
58308
|
var ENDOFCHAIN = 4294967294;
|
|
58309
58309
|
var FREESECT = 4294967295;
|
|
58310
|
-
var DEFAULT_MAX_ROWS = 1e4;
|
|
58311
58310
|
function isOle2(bytes) {
|
|
58312
58311
|
return bytes.length >= 8 && OLE2_MAGIC2.every((b, i3) => bytes[i3] === b);
|
|
58313
58312
|
}
|
|
@@ -58322,7 +58321,7 @@ ${e.toString()}`);
|
|
|
58322
58321
|
const firstMiniFatSector = u32(60);
|
|
58323
58322
|
const firstDifatSector = u32(68);
|
|
58324
58323
|
if (sectorSize < 512 || sectorSize > 1 << 20) {
|
|
58325
|
-
throw new Error("Unsupported
|
|
58324
|
+
throw new Error("Unsupported OLE2 file: invalid sector size");
|
|
58326
58325
|
}
|
|
58327
58326
|
const sectorOffset = (n) => sectorSize * (n + 1);
|
|
58328
58327
|
const maxSectors = Math.ceil(bytes.length / sectorSize) + 1;
|
|
@@ -58384,6 +58383,25 @@ ${e.toString()}`);
|
|
|
58384
58383
|
const readStream = (e) => e.size >= miniCutoff ? readBig(e.start, e.size) : readMini(e.start, e.size);
|
|
58385
58384
|
return dir.filter((e) => e.type === 2).map((e) => ({ name: e.name, bytes: readStream(e) }));
|
|
58386
58385
|
}
|
|
58386
|
+
function utf16le(b) {
|
|
58387
|
+
let s = "";
|
|
58388
|
+
for (let i3 = 0; i3 + 1 < b.length; i3 += 2) s += String.fromCharCode(b[i3] | b[i3 + 1] << 8);
|
|
58389
|
+
return s;
|
|
58390
|
+
}
|
|
58391
|
+
function concat(parts) {
|
|
58392
|
+
let total = 0;
|
|
58393
|
+
for (const p of parts) total += p.length;
|
|
58394
|
+
const out = new Uint8Array(total);
|
|
58395
|
+
let o = 0;
|
|
58396
|
+
for (const p of parts) {
|
|
58397
|
+
out.set(p, o);
|
|
58398
|
+
o += p.length;
|
|
58399
|
+
}
|
|
58400
|
+
return out;
|
|
58401
|
+
}
|
|
58402
|
+
|
|
58403
|
+
// ../xlsx/src/biff8_reader.ts
|
|
58404
|
+
var DEFAULT_MAX_ROWS = 1e4;
|
|
58387
58405
|
var REC = {
|
|
58388
58406
|
FORMULA: 6,
|
|
58389
58407
|
EOF: 10,
|
|
@@ -58682,22 +58700,6 @@ ${e.toString()}`);
|
|
|
58682
58700
|
}
|
|
58683
58701
|
return s;
|
|
58684
58702
|
}
|
|
58685
|
-
function utf16le(b) {
|
|
58686
|
-
let s = "";
|
|
58687
|
-
for (let i3 = 0; i3 + 1 < b.length; i3 += 2) s += String.fromCharCode(b[i3] | b[i3 + 1] << 8);
|
|
58688
|
-
return s;
|
|
58689
|
-
}
|
|
58690
|
-
function concat(parts) {
|
|
58691
|
-
let total = 0;
|
|
58692
|
-
for (const p of parts) total += p.length;
|
|
58693
|
-
const out = new Uint8Array(total);
|
|
58694
|
-
let o = 0;
|
|
58695
|
-
for (const p of parts) {
|
|
58696
|
-
out.set(p, o);
|
|
58697
|
-
o += p.length;
|
|
58698
|
-
}
|
|
58699
|
-
return out;
|
|
58700
|
-
}
|
|
58701
58703
|
function builtinFormat(id) {
|
|
58702
58704
|
const DATES = {
|
|
58703
58705
|
14: "m/d/yyyy",
|
package/dist/src/cli.js
CHANGED
|
@@ -30830,12 +30830,12 @@ export default defineConfig({
|
|
|
30830
30830
|
// default export the dev optimizer won't synthesize unbundled ("does not
|
|
30831
30831
|
// provide an export named 'default'") \u2014 blanking the iframe the moment a
|
|
30832
30832
|
// markdown component mounts. Pre-bundling react-markdown folds the whole
|
|
30833
|
-
// subtree (incl. style-to-js) into one interop'd chunk
|
|
30834
|
-
//
|
|
30835
|
-
//
|
|
30833
|
+
// subtree (incl. style-to-js) into one interop'd chunk. It is a regular dep
|
|
30834
|
+
// of @lotics/ui (hoisted), so it needs no package.json entry here. (The GFM
|
|
30835
|
+
// plugin @lotics/ui composes is pure ESM \u2014 no CJS interop to force.)
|
|
30836
30836
|
include: [
|
|
30837
30837
|
"react-native-svg",
|
|
30838
|
-
"react-markdown",
|
|
30838
|
+
"react-markdown",
|
|
30839
30839
|
"react-native-web", "@react-native/normalize-colors",
|
|
30840
30840
|
"inline-style-prefixer/lib/createPrefixer",
|
|
30841
30841
|
"inline-style-prefixer/lib/plugins/crossFade",
|
|
@@ -43352,11 +43352,10 @@ function ensureVmlArray(val) {
|
|
|
43352
43352
|
return Array.isArray(val) ? val : [val];
|
|
43353
43353
|
}
|
|
43354
43354
|
|
|
43355
|
-
// ../
|
|
43355
|
+
// ../shared/src/ole.ts
|
|
43356
43356
|
var OLE2_MAGIC2 = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
43357
43357
|
var ENDOFCHAIN = 4294967294;
|
|
43358
43358
|
var FREESECT = 4294967295;
|
|
43359
|
-
var DEFAULT_MAX_ROWS = 1e4;
|
|
43360
43359
|
function isOle2(bytes) {
|
|
43361
43360
|
return bytes.length >= 8 && OLE2_MAGIC2.every((b, i2) => bytes[i2] === b);
|
|
43362
43361
|
}
|
|
@@ -43371,7 +43370,7 @@ function readCfbStreams(bytes) {
|
|
|
43371
43370
|
const firstMiniFatSector = u32(60);
|
|
43372
43371
|
const firstDifatSector = u32(68);
|
|
43373
43372
|
if (sectorSize < 512 || sectorSize > 1 << 20) {
|
|
43374
|
-
throw new Error("Unsupported
|
|
43373
|
+
throw new Error("Unsupported OLE2 file: invalid sector size");
|
|
43375
43374
|
}
|
|
43376
43375
|
const sectorOffset = (n) => sectorSize * (n + 1);
|
|
43377
43376
|
const maxSectors = Math.ceil(bytes.length / sectorSize) + 1;
|
|
@@ -43433,6 +43432,25 @@ function readCfbStreams(bytes) {
|
|
|
43433
43432
|
const readStream = (e) => e.size >= miniCutoff ? readBig(e.start, e.size) : readMini(e.start, e.size);
|
|
43434
43433
|
return dir.filter((e) => e.type === 2).map((e) => ({ name: e.name, bytes: readStream(e) }));
|
|
43435
43434
|
}
|
|
43435
|
+
function utf16le(b) {
|
|
43436
|
+
let s = "";
|
|
43437
|
+
for (let i2 = 0; i2 + 1 < b.length; i2 += 2) s += String.fromCharCode(b[i2] | b[i2 + 1] << 8);
|
|
43438
|
+
return s;
|
|
43439
|
+
}
|
|
43440
|
+
function concat(parts) {
|
|
43441
|
+
let total = 0;
|
|
43442
|
+
for (const p of parts) total += p.length;
|
|
43443
|
+
const out = new Uint8Array(total);
|
|
43444
|
+
let o = 0;
|
|
43445
|
+
for (const p of parts) {
|
|
43446
|
+
out.set(p, o);
|
|
43447
|
+
o += p.length;
|
|
43448
|
+
}
|
|
43449
|
+
return out;
|
|
43450
|
+
}
|
|
43451
|
+
|
|
43452
|
+
// ../xlsx/src/biff8_reader.ts
|
|
43453
|
+
var DEFAULT_MAX_ROWS = 1e4;
|
|
43436
43454
|
var REC = {
|
|
43437
43455
|
FORMULA: 6,
|
|
43438
43456
|
EOF: 10,
|
|
@@ -43731,22 +43749,6 @@ function readUnicodeShort(b, offset, lenBytes) {
|
|
|
43731
43749
|
}
|
|
43732
43750
|
return s;
|
|
43733
43751
|
}
|
|
43734
|
-
function utf16le(b) {
|
|
43735
|
-
let s = "";
|
|
43736
|
-
for (let i2 = 0; i2 + 1 < b.length; i2 += 2) s += String.fromCharCode(b[i2] | b[i2 + 1] << 8);
|
|
43737
|
-
return s;
|
|
43738
|
-
}
|
|
43739
|
-
function concat(parts) {
|
|
43740
|
-
let total = 0;
|
|
43741
|
-
for (const p of parts) total += p.length;
|
|
43742
|
-
const out = new Uint8Array(total);
|
|
43743
|
-
let o = 0;
|
|
43744
|
-
for (const p of parts) {
|
|
43745
|
-
out.set(p, o);
|
|
43746
|
-
o += p.length;
|
|
43747
|
-
}
|
|
43748
|
-
return out;
|
|
43749
|
-
}
|
|
43750
43752
|
function builtinFormat(id) {
|
|
43751
43753
|
const DATES = {
|
|
43752
43754
|
14: "m/d/yyyy",
|
package/dist/starter_template.js
CHANGED
|
@@ -235,12 +235,12 @@ export default defineConfig({
|
|
|
235
235
|
// default export the dev optimizer won't synthesize unbundled ("does not
|
|
236
236
|
// provide an export named 'default'") — blanking the iframe the moment a
|
|
237
237
|
// markdown component mounts. Pre-bundling react-markdown folds the whole
|
|
238
|
-
// subtree (incl. style-to-js) into one interop'd chunk
|
|
239
|
-
//
|
|
240
|
-
//
|
|
238
|
+
// subtree (incl. style-to-js) into one interop'd chunk. It is a regular dep
|
|
239
|
+
// of @lotics/ui (hoisted), so it needs no package.json entry here. (The GFM
|
|
240
|
+
// plugin @lotics/ui composes is pure ESM — no CJS interop to force.)
|
|
241
241
|
include: [
|
|
242
242
|
"react-native-svg",
|
|
243
|
-
"react-markdown",
|
|
243
|
+
"react-markdown",
|
|
244
244
|
"react-native-web", "@react-native/normalize-colors",
|
|
245
245
|
"inline-style-prefixer/lib/createPrefixer",
|
|
246
246
|
"inline-style-prefixer/lib/plugins/crossFade",
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Document Templates
|
|
2
|
+
|
|
3
|
+
Turn workspace data into finished documents — invoices, contracts, reports, letters,
|
|
4
|
+
labels, emails — by filling a reusable template instead of writing a file from scratch.
|
|
5
|
+
You register a template once, then generate a filled file (or a rendered email) from it as
|
|
6
|
+
many times as you like, feeding it data from records, a workflow, or an agent run.
|
|
7
|
+
|
|
8
|
+
This is a **capability + usage** guide. For the exact input schema of any tool named here,
|
|
9
|
+
run `lotics tools <tool_name>` — that is always the source of truth for arguments.
|
|
10
|
+
|
|
11
|
+
## The five template types
|
|
12
|
+
|
|
13
|
+
| Type | What it fills | Output | Create with |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `pdf-form` | fields overlaid on an **uploaded PDF** at fixed positions | PDF | `create_pdf_template` (mode `form`) |
|
|
16
|
+
| `html` (a PDF) | an **HTML + Handlebars** layout you write from scratch | PDF | `create_pdf_template` (mode `html`) |
|
|
17
|
+
| `excel` | an **uploaded `.xlsx`** with `{{marker}}` cells | `.xlsx` | `create_excel_template` |
|
|
18
|
+
| `word` | an **uploaded `.docx`** with `{{marker}}`s | `.docx` | `create_word_template` |
|
|
19
|
+
| `email` | an **inline HTML + Handlebars** body, rendered when the email is sent | email | `create_email_template` |
|
|
20
|
+
|
|
21
|
+
Two shapes underneath: **file-backed** (`pdf-form`, `excel`, `word`) clone an uploaded
|
|
22
|
+
office file and mark it up; **inline** (`html`, `email`) store the markup you author directly.
|
|
23
|
+
|
|
24
|
+
## The lifecycle: create → generate → chain
|
|
25
|
+
|
|
26
|
+
1. **Create** a template — register the file (or inline markup) and declare a `variables`
|
|
27
|
+
map (the named slots the template fills). This returns a template id (`tpl_…`).
|
|
28
|
+
2. **Generate** a filled file — call the matching `generate_*_from_template` with the
|
|
29
|
+
template id, a `filename` (no extension), and a `data` map keyed by your variable names.
|
|
30
|
+
It substitutes the markers and returns a **generated file** (with a `file_id`).
|
|
31
|
+
3. **Chain** the result — generation only *produces* the file. Attaching it to a record,
|
|
32
|
+
sending it, or saving it locally is a separate step: inside a workflow, pass the returned
|
|
33
|
+
`file_id` to the next step; from the CLI, `lotics download <file_id>` fetches it.
|
|
34
|
+
|
|
35
|
+
Email is the exception: there is **no** `generate_email_from_template`. An email template is
|
|
36
|
+
rendered to a subject + body **at send time** from the template and the data the send step
|
|
37
|
+
supplies — so you author it with `create_email_template` and a workflow/app send step (or the
|
|
38
|
+
web composer) does the rendering and delivery. External agents send their own email directly.
|
|
39
|
+
|
|
40
|
+
## Creating each type
|
|
41
|
+
|
|
42
|
+
### PDF from HTML (`create_pdf_template`, mode `html`)
|
|
43
|
+
|
|
44
|
+
Write a full HTML document with inline CSS and `{{variable}}` placeholders; it renders to PDF
|
|
45
|
+
via a headless browser. Good for invoices, reports, letters, certificates — anything whose
|
|
46
|
+
layout you control. Declare `variables` (plain text, rich `html`, an auto-rendered `table`,
|
|
47
|
+
or a `list`), and optionally `format` (page size) and `orientation`. See
|
|
48
|
+
`lotics tools create_pdf_template` for the exact variable shapes.
|
|
49
|
+
|
|
50
|
+
### PDF form (`create_pdf_template`, mode `form`)
|
|
51
|
+
|
|
52
|
+
For an existing PDF you must fill in place (a government form, a printed contract):
|
|
53
|
+
|
|
54
|
+
1. `lotics upload ./form.pdf` → a `file_id`.
|
|
55
|
+
2. `create_pdf_template` with mode `form` and that `file_id`.
|
|
56
|
+
3. `analyze_pdf_template` — extracts the labels and their positions off the page.
|
|
57
|
+
4. `update_pdf_template` — define the positioned fields (`text` / `checkbox` / `image`, each
|
|
58
|
+
with a page + x/y/size) in `variables`.
|
|
59
|
+
|
|
60
|
+
Form-mode data is scalar-only (text, numbers, checkboxes) — one value per positioned field.
|
|
61
|
+
|
|
62
|
+
### Excel (`create_excel_template`)
|
|
63
|
+
|
|
64
|
+
1. Build a `.xlsx` in Excel (or with `lotics xlsx`) and put `{{marker}}`s in the cells that
|
|
65
|
+
should be filled. `lotics upload ./template.xlsx` → a `file_id`.
|
|
66
|
+
2. `create_excel_template` with that `template_file_id` and a `variables` map. Markers are
|
|
67
|
+
**validated at create time** — a structural error (mismatched loop, unknown helper, bad
|
|
68
|
+
placement) blocks the save, and declared variables with no matching marker come back in
|
|
69
|
+
`unmarked_variables` (skipped, not filled).
|
|
70
|
+
|
|
71
|
+
To inspect or add markers on the uploaded file without leaving the CLI: `excel_get_range` /
|
|
72
|
+
`excel_find_cells` to look, `excel_update_range` to write markers, `validate_excel_template`
|
|
73
|
+
to re-check. Pass raw numbers / ISO dates / booleans at generate time — the cell's number
|
|
74
|
+
format handles display.
|
|
75
|
+
|
|
76
|
+
### Word (`create_word_template`)
|
|
77
|
+
|
|
78
|
+
1. Build a `.docx` with `{{marker}}`s where values go. `lotics upload ./template.docx`.
|
|
79
|
+
2. `create_word_template` with the `template_file_id` and `variables`. As with Excel, an
|
|
80
|
+
unmarked declared variable comes back in `unmarked_variables` and is skipped.
|
|
81
|
+
|
|
82
|
+
To add markers/loops/conditionals to the uploaded file from the CLI: `word_replace_text`,
|
|
83
|
+
`word_insert_loop`, `word_insert_conditional` (and their `remove_*` counterparts).
|
|
84
|
+
|
|
85
|
+
### Email (`create_email_template`)
|
|
86
|
+
|
|
87
|
+
Inline HTML + Handlebars, no uploaded file. Declare `variables`, and optionally a default
|
|
88
|
+
`subject` (which itself supports `{{variable}}` expressions) and default to/cc/bcc. The body
|
|
89
|
+
and subject render from the data at send time.
|
|
90
|
+
|
|
91
|
+
## Markers, at a usage level
|
|
92
|
+
|
|
93
|
+
Every type supports three shapes of substitution. The exact syntax differs per engine — read
|
|
94
|
+
the create-tool description for the reference; here is the capability:
|
|
95
|
+
|
|
96
|
+
- **Scalar** — a single value in one spot: `{{name}}`, `{{total}}`. Whole-cell scalars in
|
|
97
|
+
Excel preserve their type via the cell's number format.
|
|
98
|
+
- **Repeating rows / line items** — one template row rendered once per item in a list, for
|
|
99
|
+
invoice lines, order rows, tables. Excel and the HTML/email engines use a Handlebars-style
|
|
100
|
+
`{{#each items}}…{{/each}}`; Word uses `{{FOR item IN items}}…{{$item.field}}…{{END-FOR item}}`
|
|
101
|
+
(the `word_insert_loop` tool writes these command rows for you). The HTML and email types
|
|
102
|
+
also offer an auto-rendered `table` variable — declare its columns and pass an array, no
|
|
103
|
+
hand-written loop. (Excel always uses the `{{#each}}` marker rows — it has no auto-rendered
|
|
104
|
+
`table` type; its variable types are string/number/date/boolean/array.)
|
|
105
|
+
- **Conditional sections** — a block shown only when a condition holds (a "paid" stamp, an
|
|
106
|
+
optional notes block). Excel/HTML/email use `{{#if}}…{{else}}…{{/if}}`; Word uses
|
|
107
|
+
`word_insert_conditional`.
|
|
108
|
+
|
|
109
|
+
Run `lotics tools create_excel_template`, `create_word_template`, `create_pdf_template`, or
|
|
110
|
+
`create_email_template` for each engine's exact marker grammar and helper list — don't guess it.
|
|
111
|
+
|
|
112
|
+
## Generating a filled file
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# See what a template expects, then fill it
|
|
116
|
+
lotics tools generate_excel_from_template
|
|
117
|
+
lotics run get_template '{"template_id":"tpl_..."}' # its declared variables
|
|
118
|
+
lotics run generate_excel_from_template '{"document_template_id":"tpl_...","filename":"invoice-1042","data":{"company_name":"Acme","total":1042,"items":[...]}}'
|
|
119
|
+
lotics download <file_id> -o ./out/
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `data` is a map keyed by your variable names. Scalars for scalar/form fields; arrays for
|
|
123
|
+
`table`/`list`/loop variables. `filename` excludes the extension (the type sets it).
|
|
124
|
+
- Each `generate_*_from_template` returns a generated file object; take its `file_id` onward.
|
|
125
|
+
- In a workflow, a **generate step** calls the same tools and hands the `file_id` to the next
|
|
126
|
+
step (attach to a record, send as an attachment, etc.) — see `docs/workflow_engine.md` in
|
|
127
|
+
the platform docs.
|
|
128
|
+
|
|
129
|
+
## Discovering and managing templates
|
|
130
|
+
|
|
131
|
+
Four unified tools work across all five types:
|
|
132
|
+
|
|
133
|
+
| Tool | Does |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `query_templates` | list templates (optionally filter by type: excel/word/pdf/email) |
|
|
136
|
+
| `get_template` | one template's full definition, incl. the `variables` it expects |
|
|
137
|
+
| `clone_template` | copy a template to iterate on |
|
|
138
|
+
| `delete_template` | remove a template |
|
|
139
|
+
|
|
140
|
+
Call `get_template` before generating when you don't already know a template's variable names.
|
|
141
|
+
|
|
142
|
+
## Reaching the tools
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
lotics tools # all categories
|
|
146
|
+
lotics tools create_word_template # one tool: full description + input schema
|
|
147
|
+
lotics run <tool> '<json-args>' # execute (inline JSON, @file.json, or piped stdin)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The template tools live in the categories **PDF Templates**, **Excel Templates**,
|
|
151
|
+
**Word Templates**, **Email Templates**, and **Templates** (the unified query/get/clone/delete).
|
|
152
|
+
Uploading the source file for a file-backed template is `lotics upload <file>`; fetching a
|
|
153
|
+
generated file is `lotics download <file_id>`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Knowledge Docs
|
|
2
|
+
|
|
3
|
+
Knowledge docs are the AI's **rulebook layer** — the workspace-specific facts and rules an
|
|
4
|
+
agent can't know on its own: your price list, your product codes, your SOPs, your shipping
|
|
5
|
+
tariffs, your glossary of in-house terms. You write them once; the agent **searches and reads
|
|
6
|
+
them on demand** while it works, pulling in only the lines it needs.
|
|
7
|
+
|
|
8
|
+
They are deliberately **not** injected into the agent wholesale. Bulk-loading every doc into
|
|
9
|
+
every request would burn the context budget and drown the signal. Instead the agent retrieves
|
|
10
|
+
from them through a search funnel, so a 10,000-line tariff book costs nothing until a question
|
|
11
|
+
actually touches it.
|
|
12
|
+
|
|
13
|
+
This is a **capability + usage** guide. For the exact input schema of any tool named here, run
|
|
14
|
+
`lotics tools <tool_name>`.
|
|
15
|
+
|
|
16
|
+
## Rulebook, not methodology
|
|
17
|
+
|
|
18
|
+
Put in a knowledge doc the things the model **cannot guess**: your specific numbers, codes,
|
|
19
|
+
names, exceptions, and policies. Do **not** put in it general skills the model already has
|
|
20
|
+
("how to write a polite email", "how to summarize"). If the agent would get it right without
|
|
21
|
+
the doc, the doc is noise. A knowledge doc earns its place only by carrying external facts
|
|
22
|
+
tied to your workspace.
|
|
23
|
+
|
|
24
|
+
## Creating a doc — `create_knowledge`
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
lotics run create_knowledge @doc.json # content is large → read from a file or stdin
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`create_knowledge` takes three things:
|
|
31
|
+
|
|
32
|
+
- `name` — what it is.
|
|
33
|
+
- `description` — what the doc is **and how to retrieve from it**: its vocabulary, synonyms for
|
|
34
|
+
colloquial terms, and what to read for detail. This is surfaced when the doc is *found*,
|
|
35
|
+
before its content is read, so the agent knows whether to open it.
|
|
36
|
+
- `content` — Markdown.
|
|
37
|
+
|
|
38
|
+
A new doc is created **owned by you, active in your own agent context, and private** — no one
|
|
39
|
+
else can see it yet. (The tool itself takes only name/description/content; attaching files to a
|
|
40
|
+
doc and changing its default-active state are done through the web app / REST surface, not this
|
|
41
|
+
tool.)
|
|
42
|
+
|
|
43
|
+
## Access vs. activation — both are required
|
|
44
|
+
|
|
45
|
+
A knowledge doc reaches an agent only when **two** conditions hold for that member:
|
|
46
|
+
|
|
47
|
+
1. **Access** — the member can `use` the doc. New docs are private to the owner; share them
|
|
48
|
+
with other members or groups using the IAM tool `share_resource` (category **Admin**;
|
|
49
|
+
`unshare_resource` to revoke).
|
|
50
|
+
2. **Activation** — the doc is *active* in that member's agent context. Activation is
|
|
51
|
+
**separate from sharing**: a member can have access to a doc that is switched off in their
|
|
52
|
+
context, and it won't reach their agent. Each doc has a default-active state (on by default),
|
|
53
|
+
and each member can override it on or off for themselves. Activation is toggled in the web
|
|
54
|
+
app, per member — there is no CLI tool for it.
|
|
55
|
+
|
|
56
|
+
So: shared + active → the agent can find and read it. Shared but deactivated → invisible to
|
|
57
|
+
that member's agent. This is the token economy in action — activation is how a member curates
|
|
58
|
+
which rulebooks their agent carries.
|
|
59
|
+
|
|
60
|
+
## How an agent uses a doc — the retrieval funnel
|
|
61
|
+
|
|
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.
|
|
64
|
+
|
|
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.
|
|
74
|
+
|
|
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:
|
|
77
|
+
|
|
78
|
+
- Organize under clear Markdown headers, so the doc can be outlined and read by section.
|
|
79
|
+
- Keep each searchable unit self-contained — a section for prose, **one record per line** for
|
|
80
|
+
dense/tabular data (a price row, a code entry) — carrying both the terms someone would search
|
|
81
|
+
for and its answer.
|
|
82
|
+
- Lead with the most-queried fields.
|
|
83
|
+
- Note colloquial synonyms next to official terms, so an ambiguous query still matches.
|
|
84
|
+
|
|
85
|
+
## Updating a doc — `update_knowledge`
|
|
86
|
+
|
|
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). Refine structure as you
|
|
90
|
+
learn what users actually ask: add the synonym that failed to match, split the section that was
|
|
91
|
+
too coarse to grep. See `lotics tools update_knowledge` for the edit shape.
|
|
92
|
+
|
|
93
|
+
## Package-managed knowledge
|
|
94
|
+
|
|
95
|
+
A knowledge doc can also be delivered as part of a **content package** — a versioned corpus of
|
|
96
|
+
docs (and document templates) published from one workspace and installed into others. Install
|
|
97
|
+
delivers the docs; upgrade refreshes them in place. Authoring, sharing, and retrieval work the
|
|
98
|
+
same either way; the package layer just manages distribution and version pinning. See
|
|
99
|
+
`docs/packages.md` in the platform docs for the publish/install/upgrade lifecycle.
|
|
100
|
+
|
|
101
|
+
## Reaching the tools
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
lotics tools # all categories (knowledge tools are under "Knowledge")
|
|
105
|
+
lotics tools grep_knowledge # one tool: full description + input schema
|
|
106
|
+
lotics run create_knowledge @doc.json
|
|
107
|
+
echo '{"query":"tariff"}' | lotics run search_knowledge
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The Knowledge category covers `create_knowledge`, `update_knowledge`, `query_knowledge`,
|
|
111
|
+
`search_knowledge`, `grep_knowledge`, `read_knowledge`, and `delete_knowledge`. Sharing a doc
|
|
112
|
+
to other members is `share_resource` / `unshare_resource` (category **Admin**).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.0",
|
|
4
4
|
"description": "Lotics SDK and CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
-
"README.md"
|
|
14
|
+
"README.md",
|
|
15
|
+
"docs"
|
|
15
16
|
],
|
|
16
17
|
"scripts": {
|
|
17
18
|
"build": "tsgo && node scripts/build_cli.mjs",
|