@lumoai/cli 1.41.0 → 1.42.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/assets/skill/SKILL.md +47 -41
- package/assets/skill/references/criteria.md +97 -252
- package/assets/skill/references/doc-editing.md +146 -0
- package/assets/skill/references/docs.md +49 -278
- package/assets/skill/references/memory.md +29 -0
- package/assets/skill/references/onboarding.md +4 -4
- package/assets/skill/references/sessions.md +84 -97
- package/assets/skill/references/task-context.md +65 -149
- package/assets/skill/references/task-deps.md +113 -0
- package/assets/skill/references/tasks.md +2 -133
- package/assets/skill/references/verify.md +121 -201
- package/assets/skill/references/worktree.md +21 -36
- package/dist/cli/src/commands/memory-fold.js +76 -0
- package/dist/cli/src/index.js +6 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Document Management
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Document **CRUD, listing, tree/move, sharing, and Google import/sync**. For the surgical live-doc edit path — `doc show --raw/--section`, `doc patch`, `doc append`, `doc diff`, `doc rebuild-source` — see [doc-editing.md](doc-editing.md). The content-channels and table-authoring conventions below apply to those editing commands too.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Content channels: prefer stdin / `--content`
|
|
6
6
|
|
|
7
7
|
`doc create` / `doc update` / `doc patch` / `doc append` all take the body from one of three mutually-exclusive channels. **For agent write-back, prefer stdin or `--content`** — pipe the markdown you already hold in memory, or pass it inline:
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ lumo doc append cmd_xxx --section "F 待办队列" --content "- [ ] 评估 XYZ
|
|
|
13
13
|
|
|
14
14
|
`--file` is the **fallback for content that already lives in an authoritative file on disk** (e.g. a repo-tracked `docs/live-docs/<file>.md` source you are editing). It is **not** the default channel: `--file` is sandboxed — the CLI rejects any path outside the current project directory or matching the sensitive-file denylist (`.env*`, keys, `credentials`, …), with no override. So feeding it generated content forces you to first write a scratch file _inside the repo_ just to pass it — an extra step that is the real-world friction that gets `--file` rejected. Pipe via stdin instead and skip the temp file; reach for `--file` only when the file _is_ the source of truth you're editing.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
## Markdown tables: keep them compact (no alignment padding)
|
|
17
17
|
|
|
18
18
|
When you author or edit a markdown doc that contains tables (live-docs, registers, reports), write the cells **compact — one space around each pipe, no column-alignment padding**:
|
|
19
19
|
|
|
@@ -30,7 +30,7 @@ not the aligned form (`| col | meaning |` padded so columns line up). Two reas
|
|
|
30
30
|
|
|
31
31
|
This is a pure authoring convention — the server stores your markdown **byte-for-byte** (`sourceMarkdown`), so `doc show --raw` and `doc diff` stay byte-exact (LUM-408); there is **no** server-side table normalization to lean on. The structure guard (LUM-410) compares _rendered_ structure and is whitespace-insensitive, so compactness buys local editability, not a verify pass.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## `lumo doc create [title] [flags]` — create a new document
|
|
34
34
|
|
|
35
35
|
Use this when the user wants to write a new document from the terminal. Title is positional and optional. When omitted, the doc title defaults to empty (server renders as "Untitled" via i18n).
|
|
36
36
|
|
|
@@ -64,44 +64,40 @@ Created cmd_xxx "RFC: doc CLI" https://www.uselumo.ai/workspace/lumo/documents/
|
|
|
64
64
|
Tags: rfc, draft
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
The cuid (`cmd_xxx`) is still printed as a stable identifier you can pass back into other `lumo doc ...` commands; the URL switched to the per-workspace slug shape (`slugify(title)-<number>`) and the cuid is no longer a valid web URL.
|
|
68
|
-
|
|
69
|
-
The `Tags:` line is omitted when no tags were attached.
|
|
67
|
+
The cuid (`cmd_xxx`) is still printed as a stable identifier you can pass back into other `lumo doc ...` commands; the URL switched to the per-workspace slug shape (`slugify(title)-<number>`) and the cuid is no longer a valid web URL. The `Tags:` line is omitted when no tags were attached.
|
|
70
68
|
|
|
71
69
|
### When to suggest `doc create`
|
|
72
70
|
|
|
73
71
|
- User says "write a doc", "draft an RFC", "new document" (in any language), or describes a deliverable that should live as a document.
|
|
74
72
|
- After a discussion that needs a write-up, offer to create the doc with a title and the right scope.
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
## `lumo doc update <doc> [flags]` — update a document
|
|
77
75
|
|
|
78
76
|
`<doc>` accepts a cuid or a case-insensitive title. Ambiguous titles fail with a candidate list — re-run with the cuid.
|
|
79
77
|
|
|
80
|
-
| Flag | Type | Notes
|
|
81
|
-
| ------------------------ | ------------------- |
|
|
82
|
-
| `--title <text>` | string | New title (cannot be empty).
|
|
83
|
-
| `--content <text>` | string | Replace content (inline).
|
|
84
|
-
| `--file <path>` | string | Replace content from file.
|
|
85
|
-
| (stdin) | — | Pipe to replace content. Empty / whitespace-only stdin (a non-TTY shell with nothing piped — the common agent case) is treated as **no content channel**, not a body clear (LUM-505).
|
|
86
|
-
| `--scope <scope>` | enum | `personal` / `workspace`.
|
|
87
|
-
| `--project <ref>` | string | Project name/slug. `--project ""` clears the filing.
|
|
88
|
-
| `--tag <name>` | string (repeatable) | **Bulk replace** the tag set by name. Creates tag if missing. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`.
|
|
89
|
-
| `--tag-id <cuid>` | string (repeatable) | **Bulk replace** the tag set by id. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`.
|
|
90
|
-
| `--add-tag <name>` | string (repeatable) | Attach tag by name (find-or-create). Max 20.
|
|
91
|
-
| `--add-tag-id <cuid>` | string (repeatable) | Attach tag by id. Max 20.
|
|
92
|
-
| `--remove-tag <name>` | string (repeatable) | Detach tag by name
|
|
93
|
-
| `--remove-tag-id <cuid>` | string (repeatable) | Detach tag by id. Unknown ids are a no-op
|
|
94
|
-
| `--allow-shrink` | boolean | Let a body update through even when it drops tables/rows/headings versus the stored body (see structure guard below).
|
|
78
|
+
| Flag | Type | Notes |
|
|
79
|
+
| ------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
80
|
+
| `--title <text>` | string | New title (cannot be empty). |
|
|
81
|
+
| `--content <text>` | string | Replace content (inline). |
|
|
82
|
+
| `--file <path>` | string | Replace content from file. |
|
|
83
|
+
| (stdin) | — | Pipe to replace content. Empty / whitespace-only stdin (a non-TTY shell with nothing piped — the common agent case) is treated as **no content channel**, not a body clear (LUM-505). |
|
|
84
|
+
| `--scope <scope>` | enum | `personal` / `workspace`. |
|
|
85
|
+
| `--project <ref>` | string | Project name/slug. `--project ""` clears the filing. |
|
|
86
|
+
| `--tag <name>` | string (repeatable) | **Bulk replace** the tag set by name. Creates tag if missing. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`. |
|
|
87
|
+
| `--tag-id <cuid>` | string (repeatable) | **Bulk replace** the tag set by id. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`. |
|
|
88
|
+
| `--add-tag <name>` | string (repeatable) | Attach tag by name (find-or-create). Max 20. |
|
|
89
|
+
| `--add-tag-id <cuid>` | string (repeatable) | Attach tag by id. Max 20. |
|
|
90
|
+
| `--remove-tag <name>` | string (repeatable) | Detach tag by name (find-or-create; an unknown name creates an orphan Tag row before the no-op detach — use `--remove-tag-id` to avoid orphans). Max 20. |
|
|
91
|
+
| `--remove-tag-id <cuid>` | string (repeatable) | Detach tag by id. Unknown ids are a no-op. Max 20. |
|
|
92
|
+
| `--allow-shrink` | boolean | Let a body update through even when it drops tables/rows/headings versus the stored body (see structure guard below). |
|
|
95
93
|
|
|
96
94
|
A **metadata-only update leaves the body untouched** (LUM-505): when no content channel is supplied (`--title`/`--scope`/`--project`/tag flags only), the body is omitted from the PATCH — it does not get blanked and the structure guard cannot fire. To deliberately clear or replace the body you must supply a content channel explicitly (`--content ""` to clear, which then hits the structure guard as a shrink → pair with `--allow-shrink`).
|
|
97
95
|
|
|
98
96
|
Optimistic concurrency (LUM-409): `--if-revision <n>` only applies the update if the doc body is still at revision `n` (from `doc show`). Mismatch → 409 conflict, nothing written — re-read, rebase, retry. `--if-revision` alone is not an update (still errors "no fields to update"); same for `--allow-shrink`.
|
|
99
97
|
|
|
100
|
-
**Structure guard (LUM-410), built into the server:** a body update whose new render has **fewer `table` / `tr` / heading elements than the stored body** is rejected with **422** before anything is written — the error names each shrunk category with old→new counts (e.g. `table 1→0, tr 4→0`). This is the `verify-live-doc.ts` reconciliation moved into the write path, so table flattening (LUM-349) and stale-base section loss (#460) fail loudly
|
|
101
|
-
|
|
102
|
-
`--tag` / `--tag-id` (bulk replace) are mutually exclusive with `--add-tag` / `--add-tag-id` / `--remove-tag` / `--remove-tag-id`. The CLI errors before any network call if both families are mixed.
|
|
98
|
+
**Structure guard (LUM-410), built into the server:** a body update whose new render has **fewer `table` / `tr` / heading elements than the stored body** is rejected with **422** before anything is written — the error names each shrunk category with old→new counts (e.g. `table 1→0, tr 4→0`). This is the `verify-live-doc.ts` reconciliation moved into the write path, so table flattening (LUM-349) and stale-base section loss (#460) fail loudly. When the deletion is intentional, re-run with `--allow-shrink`. On a 422: don't reach for `--allow-shrink` reflexively — first check whether your edit base is stale (`doc show <doc> --raw`) and rebase. Only markdown-path writes are guarded; web-editor edits and `doc sync` (Google authority) are not.
|
|
103
99
|
|
|
104
|
-
|
|
100
|
+
`--tag` / `--tag-id` (bulk replace) are mutually exclusive with `--add-tag` / `--add-tag-id` / `--remove-tag` / `--remove-tag-id`. The CLI errors before any network call if both families are mixed. Like `doc create`, `--file` is sandboxed.
|
|
105
101
|
|
|
106
102
|
Examples:
|
|
107
103
|
|
|
@@ -118,148 +114,11 @@ lumo doc update RFC --tag final --add-tag oops
|
|
|
118
114
|
### When to suggest `doc update`
|
|
119
115
|
|
|
120
116
|
- User wants to revise an existing doc **as a whole** (title, scope, or a full-body rewrite).
|
|
121
|
-
-
|
|
122
|
-
- For a small edit inside one section, prefer `doc patch` / `doc append` (below) — full `doc update` has the maximum clobber radius.
|
|
117
|
+
- For a small edit inside one section, prefer `doc patch` / `doc append` ([doc-editing.md](doc-editing.md)) — full `doc update` has the maximum clobber radius.
|
|
123
118
|
- When replacing the body from a previously fetched base, pass `--if-revision` so a concurrent edit fails loudly (409) instead of being overwritten.
|
|
124
119
|
- When the update intentionally deletes a section or table, pass `--allow-shrink` up front; otherwise expect (and want) the 422 structure guard on accidental structure loss.
|
|
125
120
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Default mode prints a key:value header (id, title, scope, project, created/updated timestamps, **revision**, mentioned tasks) and the content rendered back to markdown. `Revision:` is the body's optimistic-concurrency counter — feed it back as `--if-revision` on `doc update` / `doc patch` / `doc append`.
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
lumo doc show "RFC: doc CLI"
|
|
132
|
-
lumo doc show cmd_xxx --raw > base.md # byte-identical edit base (revision on stderr)
|
|
133
|
-
lumo doc show cmd_xxx --section "D 状态表" > sec.md # one section only (revision on stderr)
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**`--raw` (LUM-408)** prints the byte-identical markdown source of the last markdown upload — no header, no trailing newline added. The server stores the raw markdown (`sourceMarkdown`) alongside the rendered HTML on every markdown write (`doc create/update --content/--file/stdin`, gdoc import/sync), so `--raw` output IS a legal edit base: `doc show --raw > base.md`, edit, `doc update --file base.md` round-trips losslessly.
|
|
137
|
-
|
|
138
|
-
- A web-editor (HTML-direct) edit or revision restore **invalidates** the stored source — the doc's markdown source is gone until the next markdown upload.
|
|
139
|
-
- When no source is stored (legacy doc or after an HTML edit), `--raw` **fails with exit 1 and a rebuild hint** — it never silently falls back to the lossy HTML→markdown reverse render (that fallback flattened tables: LUM-349). Rebuild flow (LUM-446): run **`lumo doc rebuild-source <doc>`** — it regenerates the source from the stored HTML with a lossless serializer (tables round-trip) and a structure guard, so `--raw` works from then on. (The old manual flow — `doc show` rendered → hand-reconstruct → `doc update --file` — flattened tables and is superseded.)
|
|
140
|
-
- Raw output is verbatim (unsanitized) by design — redirect it to a file rather than reading it in a terminal when the doc's provenance is uncertain.
|
|
141
|
-
|
|
142
|
-
Note: the markdown rendered by **default-mode** `doc show` is still best-effort (tables flatten). Round-trip via `doc show > tmp.md && doc update --file tmp.md` is NOT a no-op — use `--raw` as the edit base instead.
|
|
143
|
-
|
|
144
|
-
Output budget (LUM-428): **default-mode** `doc show` caps the rendered body to the output-token budget (25,000 tokens) and, when truncated, ends in a pointer to `--section "<heading>"` (just-in-time slice) / `--raw` (full source). `--raw` and `--section` are **never** capped — they are byte-faithful edit bases.
|
|
145
|
-
|
|
146
|
-
**`--section <heading>` (LUM-409)** prints just one heading-addressed section of the markdown source — a byte-faithful slice from the heading line through (not including) the next same-or-higher-level heading, subsections included. No header on stdout (the slice is a legal `doc patch` base); the current revision is printed to **stderr** as `Revision: N`. Mutually exclusive with `--raw`.
|
|
147
|
-
|
|
148
|
-
- Section addressing: pass the heading text (`--section "D 状态表"`), matched in three tiers — exact, then case-insensitive, then (LUM-447) full-width↔half-width punctuation + whitespace normalization, so a half-width query (`问题(P4)`) lands on a full-width stored heading (`问题(P4)`) and vice-versa without copying the raw bytes. Prefix with `#…` to pin the level when the same text exists at several depths (`--section "## Status"`). Normalization never relaxes the ambiguity guard — if it matches more than one heading you still get the candidate list + exit 1.
|
|
149
|
-
- Missing heading → exit 1 listing the available headings; ambiguous heading → exit 1 with a depth-disambiguation hint.
|
|
150
|
-
- Requires a stored markdown source — same no-fallback rule and rebuild flow as `--raw`.
|
|
151
|
-
- Heading detection is markdown-aware: `#` lines inside fenced code blocks or blockquotes are never section boundaries.
|
|
152
|
-
|
|
153
|
-
Use default mode when the user wants to read a doc; use `--raw` whenever the full output will be edited and uploaded back; use `--section` when only one part matters (keeps the context window small and the patch radius smaller).
|
|
154
|
-
|
|
155
|
-
### When to suggest `doc show --raw` / `--section`
|
|
156
|
-
|
|
157
|
-
- Before any `doc update --file` that starts from existing remote content — fetch the base with `--raw`, never from rendered `doc show` output.
|
|
158
|
-
- Before a `doc patch` — read the section with `--section` first, note the `Revision:` line, edit, patch back with `--if-revision`.
|
|
159
|
-
- User asks "what's the exact source of this doc", or an agent needs a faithful local copy of a live doc.
|
|
160
|
-
- User asks "what's in section X" of a long doc — `--section` avoids pulling the whole body into context.
|
|
161
|
-
- If `--raw`/`--section` errors (no stored source), run `lumo doc rebuild-source <doc>` to regenerate it losslessly, then retry — don't edit the rendered output.
|
|
162
|
-
|
|
163
|
-
### `lumo doc patch <doc> --section <heading>` — replace one section
|
|
164
|
-
|
|
165
|
-
Replaces the **whole addressed section** (heading line included, subsections included) with the provided content, server-side, leaving every byte outside the section untouched. The new content comes from `--content`, `--file`, or piped stdin (one required; `--file` is sandboxed like `doc update`).
|
|
166
|
-
|
|
167
|
-
| Flag | Type | Notes |
|
|
168
|
-
| --------------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
|
|
169
|
-
| `--section <heading>` | string | Required. Heading text addressing the section; prefix `#…` pins the depth. |
|
|
170
|
-
| `--content <text>` | string | New section content (include the heading line — the whole section is replaced verbatim). |
|
|
171
|
-
| `--file <path>` | string | New section content from file (project-local sandbox). |
|
|
172
|
-
| `--if-revision <n>` | int | Only apply if the body is still at revision `n`. Recommended whenever the edit base was read earlier. |
|
|
173
|
-
| `--allow-shrink` | boolean | Let the patch through even when it drops tables/rows/headings within the addressed section (422 otherwise). |
|
|
174
|
-
|
|
175
|
-
Concurrency: the splice always commits **conditionally** on the revision the server read the source at — even without `--if-revision`, a concurrent body edit between read and write returns 409 instead of clobbering. On 409 the CLI prints the server reason plus a re-read-and-retry hint and exits 1.
|
|
176
|
-
|
|
177
|
-
Structure guard (LUM-410), **scoped to the addressed section**: a replacement whose render has fewer `table`/`tr`/heading elements than the old section's render is rejected with 422 naming each shrunk category (old→new counts); structure elsewhere in the document never factors in. Dropping the heading line itself trips the guard too (heading count shrinks). Pass `--allow-shrink` when the deletion is intentional. `doc append` is pure insertion and is never guarded.
|
|
178
|
-
|
|
179
|
-
Requires a stored markdown source (same rule as `--raw`); errors with the rebuild hint otherwise. Replacement is verbatim — if the new content omits the heading line, the heading is gone (an explicit choice the guard makes you confirm with `--allow-shrink`).
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
lumo doc show cmd_xxx --section "D 状态表" > sec.md # stderr: Revision: 6
|
|
183
|
-
# … edit sec.md …
|
|
184
|
-
lumo doc patch cmd_xxx --section "D 状态表" --file sec.md --if-revision 6
|
|
185
|
-
# → Patched cmd_xxx "登记册" § "D 状态表" revision 7
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### When to suggest `doc patch`
|
|
189
|
-
|
|
190
|
-
- User wants to change one section of a live doc ("update the status table", "rewrite section D") — patch beats full `doc update` on clobber radius and context size.
|
|
191
|
-
- Live-doc status updates that used to be read-whole/edit/upload-whole round-trips.
|
|
192
|
-
- If the patch 409s: re-read the section, rebase the edit, retry — never fall back to a full-body upload from the stale base.
|
|
193
|
-
- If the patch 422s (structure guard): the replacement drops tables/rows/headings the section has. Re-check the edit base first; add `--allow-shrink` only when the deletion is what the user wants.
|
|
194
|
-
|
|
195
|
-
### `lumo doc append <doc> [--section <heading>]` — append to a section (or the doc)
|
|
196
|
-
|
|
197
|
-
Inserts the new content at the **end of the addressed section** (just before the next same-or-higher-level heading), or at the end of the document when `--section` is omitted. Pure insertion: no pre-existing byte is modified, which makes it the natural write for running logs, ledgers and queues. Content channels and sandbox are the same as `doc patch`; separator blank lines are added automatically.
|
|
198
|
-
|
|
199
|
-
| Flag | Type | Notes |
|
|
200
|
-
| --------------------- | ------ | ---------------------------------------------------------------------------- |
|
|
201
|
-
| `--section <heading>` | string | Optional. Omit to append at the document end. |
|
|
202
|
-
| `--content <text>` | string | Content to append. |
|
|
203
|
-
| `--file <path>` | string | Content from file (project-local sandbox). |
|
|
204
|
-
| `--if-revision <n>` | int | Only apply if the body is still at revision `n`; 409 + retry hint otherwise. |
|
|
205
|
-
|
|
206
|
-
Same concurrency contract as `doc patch` (always a conditional commit; 409 on conflict). **End-of-document append (no `--section`) does NOT require a stored markdown source** (LUM-444): when the source is missing (web HTML edit / revision restore / legacy doc) it renders the new block to HTML and concatenates it onto the stored HTML body — so one web operation can no longer lock the whole doc against the agent write path. The source stays null (the doc is still HTML-only afterward; `--raw`/`--section`/`doc patch` keep erroring with the rebuild hint). **Section-addressed append (`--section`) still requires a stored source** — it needs the markdown to locate the heading boundary.
|
|
207
|
-
|
|
208
|
-
```bash
|
|
209
|
-
lumo doc append cmd_xxx --section "F 待办队列" --content "- [ ] 评估 XYZ 论文"
|
|
210
|
-
# → Appended to cmd_xxx "登记册" § "F 待办队列" revision 8
|
|
211
|
-
echo "## 2026-06-10\n吸收了 3 篇" | lumo doc append cmd_xxx # document end
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### When to suggest `doc append`
|
|
215
|
-
|
|
216
|
-
- User wants to add an entry/row/log line to a section ("把 X 加进待办队列", "append today's notes") — this is the killer op for ledger-style live docs: zero clobber risk by construction.
|
|
217
|
-
- Whenever the alternative would be re-uploading the whole doc just to add lines at the end of one section.
|
|
218
|
-
|
|
219
|
-
### `lumo doc diff <doc> --file <local.md>` — compare remote markdown source vs a local file
|
|
220
|
-
|
|
221
|
-
Compares the server-side stored markdown source (the byte-identical last markdown upload) against a local file, making remote/local split-brain visible on demand.
|
|
222
|
-
|
|
223
|
-
| Flag | Type | Notes |
|
|
224
|
-
| --------------- | ------ | --------------------------------------------------------------------------------- |
|
|
225
|
-
| `--file <path>` | string | Required. Local markdown file; same project-local sandbox as `doc update --file`. |
|
|
226
|
-
|
|
227
|
-
Exit codes: **0** = byte-identical (prints `Clean: …`), **1** = divergent (prints a unified diff, `--- remote/<id> (sourceMarkdown)` vs `+++ local/<file>`) or error. A doc without a stored markdown source errors explicitly (upload a markdown base once, then diff works).
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
lumo doc diff cmd_xxx --file docs/live-docs/research-intake-ledger.md
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### When to suggest `doc diff`
|
|
234
|
-
|
|
235
|
-
- Before uploading a locally edited file with `doc update --file` — check whether the remote source moved since the local copy was taken (prevents stale-upload clobbering, the #460 incident class).
|
|
236
|
-
- When a repo-tracked markdown source (e.g. `docs/live-docs/`) and the live doc may have drifted and the user asks which is current.
|
|
237
|
-
- After a suspected concurrent edit: a clean diff (exit 0) proves remote and local are in sync.
|
|
238
|
-
|
|
239
|
-
### `lumo doc rebuild-source <doc>` — regenerate the markdown source from the HTML body
|
|
240
|
-
|
|
241
|
-
The **recovery path for a source-less doc** (LUM-446). When a doc has no stored `sourceMarkdown` — a web HTML-direct edit or revision restore nulled it, or the doc predates source storage — every markdown write path (`--raw`, `--section`, `doc patch`, `doc append --section`, `doc diff`) is locked. This regenerates a valid source by serializing the **stored HTML structure model** back to markdown with a **lossless serializer that round-trips tables/rows/headings** (the default `doc show` render flattens tables — LUM-349 — so it was never a safe rebuild base). Only the `sourceMarkdown` column is backfilled; the rendered body is untouched, so the doc reads identically and you just regain the edit base.
|
|
242
|
-
|
|
243
|
-
| Flag | Type | Notes |
|
|
244
|
-
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
245
|
-
| `--allow-shrink` | boolean | Commit even if the rebuilt source re-renders with fewer tables/rows/headings than the stored body (default: 422 reject). |
|
|
246
|
-
| `--force` | boolean | Re-derive even when a source already exists (default: 409 — protects a byte-faithful human source from a downgrade). |
|
|
247
|
-
| `--if-revision <n>` | int | Only apply if the body is still at this revision (from `doc show`). |
|
|
248
|
-
|
|
249
|
-
The rebuild is **structure-guarded** (the same LUM-410 口径 as `doc update`/`doc patch`): if the serializer would drop any table/row/heading, it is rejected with **422** rather than silently committing a flattened source — `--allow-shrink` is the explicit escape hatch. A doc that already has a source is refused **409** unless `--force`.
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
lumo doc rebuild-source cmd_xxx # restore a source-less doc; --raw works after
|
|
253
|
-
lumo doc rebuild-source cmd_xxx --force # re-derive even if a source already exists
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### When to suggest `doc rebuild-source`
|
|
257
|
-
|
|
258
|
-
- A `--raw` / `--section` / `doc patch` / `doc diff` call errored with "no stored markdown source" — rebuild, then retry. This is the first thing to try, not a manual reconstruction.
|
|
259
|
-
- A table-heavy live doc (e.g. a `docs/live-docs/` registry) lost its source after a web operation and the markdown write path is locked.
|
|
260
|
-
- Do **not** run it on a doc that already has a good source unless the user explicitly wants to re-derive it (then pass `--force`) — it replaces the byte-faithful source with a serializer-derived one.
|
|
261
|
-
|
|
262
|
-
### `lumo doc list [flags]` — list documents
|
|
121
|
+
## `lumo doc list [flags]` — list documents
|
|
263
122
|
|
|
264
123
|
Default behavior: lists every document the current user can see, as fixed-width rows: `<cuid> <SCOPE> <project|-> <title>`.
|
|
265
124
|
|
|
@@ -271,11 +130,7 @@ Default behavior: lists every document the current user can see, as fixed-width
|
|
|
271
130
|
| `--limit <n>` | int | Cap output to the first N rows. |
|
|
272
131
|
| `--tree` | boolean | Render as an indented tree (two spaces per depth level) instead of the flat row table. |
|
|
273
132
|
|
|
274
|
-
`--tree` plus
|
|
275
|
-
|
|
276
|
-
When `--task` is combined with `--scope` / `--project`, those become client-side filters on the task-scoped result.
|
|
277
|
-
|
|
278
|
-
Examples:
|
|
133
|
+
`--tree` plus filters work together: the CLI fetches with filters applied, then builds the tree from whatever rows came back. Docs whose parent is **not** in the result render as top-level rows. `--limit N --tree` truncates the flat list to N rows _before_ the tree is built. When `--task` is combined with `--scope` / `--project`, those become client-side filters on the task-scoped result.
|
|
279
134
|
|
|
280
135
|
```bash
|
|
281
136
|
lumo doc list --scope workspace
|
|
@@ -288,40 +143,28 @@ lumo doc list --scope workspace --tree
|
|
|
288
143
|
- The user asks "what docs do I have", "show me workspace docs", "what's on LUM-42".
|
|
289
144
|
- Before suggesting `doc update` or `doc delete` when no doc ID is in context — run `doc list` first to surface candidates.
|
|
290
145
|
|
|
291
|
-
|
|
146
|
+
## `lumo doc move <doc> [flags]` — move a doc under a different parent or to root
|
|
292
147
|
|
|
293
|
-
Reparents a doc. `<doc>` accepts a cuid or a case-insensitive title; ambiguous titles fail with a candidate list
|
|
148
|
+
Reparents a doc. `<doc>` accepts a cuid or a case-insensitive title; ambiguous titles fail with a candidate list.
|
|
294
149
|
|
|
295
150
|
| Flag | Type | Notes |
|
|
296
151
|
| ---------------- | ------- | ------------------------------------------------ |
|
|
297
152
|
| `--parent <doc>` | string | New parent doc (cuid or case-insensitive title). |
|
|
298
153
|
| `--root` | boolean | Move to top level (`parentId = null`). |
|
|
299
154
|
|
|
300
|
-
`--parent` and `--root` are **mutually exclusive** and one
|
|
301
|
-
|
|
302
|
-
The new `sortOrder` is computed CLI-side as `max(sibling.sortOrder) + 1` — the moved doc lands at the end of its new sibling list. Reordering among siblings (`--before` / `--after`) is not yet supported; for now use the Web UI to reorder.
|
|
303
|
-
|
|
304
|
-
Examples:
|
|
155
|
+
`--parent` and `--root` are **mutually exclusive** and one is **required** (CLI errors before any network call otherwise). The new `sortOrder` is computed CLI-side as `max(sibling.sortOrder) + 1` — the moved doc lands at the end of its new sibling list. Reordering among siblings (`--before` / `--after`) is not yet supported; use the Web UI.
|
|
305
156
|
|
|
306
157
|
```bash
|
|
307
158
|
lumo doc move "Sub-doc" --parent "Roadmap"
|
|
308
159
|
lumo doc move "Sub-doc" --root
|
|
309
|
-
lumo doc move cmd_xxx --parent cmd_yyy
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Output:
|
|
313
|
-
|
|
314
|
-
```
|
|
315
|
-
Moved cmd_xxx "Sub-doc" → "Roadmap"
|
|
316
|
-
Moved cmd_xxx "Sub-doc" → root
|
|
317
160
|
```
|
|
318
161
|
|
|
319
162
|
### When to suggest `doc move`
|
|
320
163
|
|
|
321
164
|
- User says "move doc X under Y", "reparent X to root", "promote X to top level".
|
|
322
|
-
- After `doc create`, if the user realizes the new doc should live elsewhere
|
|
165
|
+
- After `doc create`, if the user realizes the new doc should live elsewhere — suggest `doc move` rather than recreating.
|
|
323
166
|
|
|
324
|
-
|
|
167
|
+
## `lumo doc delete <doc> --yes` — delete a document
|
|
325
168
|
|
|
326
169
|
Requires `--yes`; no interactive prompt (agent-friendly).
|
|
327
170
|
|
|
@@ -329,80 +172,40 @@ Requires `--yes`; no interactive prompt (agent-friendly).
|
|
|
329
172
|
lumo doc delete cmd_xxx --yes
|
|
330
173
|
```
|
|
331
174
|
|
|
332
|
-
|
|
175
|
+
## `lumo doc bind <doc> <task>` / `lumo doc unbind <doc> <task>` — task linkage
|
|
333
176
|
|
|
334
|
-
|
|
177
|
+
`bind` adds an explicit DocumentMention row (survives Web UI content edits). If a CONTENT-derived mention already exists for the same pair, it's upgraded to EXPLICIT so a later `unbind` can remove it. `unbind` removes EXPLICIT mentions only — a purely CONTENT-derived mention can't be unbound from CLI (fails 409, instructing the user to remove the `@LUM-N` from the doc body in the Web UI).
|
|
335
178
|
|
|
336
179
|
```bash
|
|
337
180
|
lumo doc bind cmd_xxx LUM-42
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
Bound cmd_xxx ↔ LUM-42
|
|
344
|
-
Bound cmd_xxx ↔ LUM-42 (upgraded from content) # when upgrading a CONTENT row
|
|
345
|
-
Already bound cmd_xxx ↔ LUM-42 # idempotent
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### `lumo doc unbind <doc> <task>` — unbind a doc from a task
|
|
349
|
-
|
|
350
|
-
Removes EXPLICIT mentions only. A purely CONTENT-derived mention can't be unbound from CLI — fails with 409 and a message instructing the user to remove the @LUM-N from the doc body in the Web UI.
|
|
351
|
-
|
|
352
|
-
```bash
|
|
181
|
+
# Bound cmd_xxx ↔ LUM-42
|
|
182
|
+
# Bound cmd_xxx ↔ LUM-42 (upgraded from content) # when upgrading a CONTENT row
|
|
183
|
+
# Already bound cmd_xxx ↔ LUM-42 # idempotent
|
|
353
184
|
lumo doc unbind cmd_xxx LUM-42
|
|
354
185
|
```
|
|
355
186
|
|
|
356
|
-
|
|
187
|
+
## `lumo doc share / unshare / share-list` — member sharing
|
|
357
188
|
|
|
358
|
-
|
|
189
|
+
`share <doc> <member> [--role viewer|editor|manager]` adds (or updates) a DocumentShare row. `<member>` resolves the same way as `task --assignee`: `me`, an email, or a display name (case-insensitive). Role defaults to `viewer`. **Auto-promotion**: invoked on a PRIVATE doc, the server transactionally flips visibility to SHARED before upserting the share row (sharing = intent that it should be SHARED). A second invocation with a different role updates the row in place.
|
|
359
190
|
|
|
360
|
-
|
|
191
|
+
`unshare <doc> <member>` is idempotent (prints `Not shared with <name>`, exit 0 if no row). It does **not** flip visibility back to PRIVATE — do that explicitly via `doc update --scope personal`.
|
|
361
192
|
|
|
362
|
-
|
|
193
|
+
`share-list <doc>` prints fixed-width rows `<displayName> <ROLE>`; empty output = no shares (may still be WORKSPACE-visible).
|
|
363
194
|
|
|
364
195
|
```bash
|
|
365
196
|
lumo doc share "RFC" alice@example.com --role editor
|
|
366
197
|
lumo doc share cmd_xxx "Alice Wong" --role manager
|
|
367
|
-
lumo doc share cmd_xxx me # share with yourself (rare; mostly for testing)
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
Output:
|
|
371
|
-
|
|
372
|
-
```
|
|
373
|
-
Shared cmd_xxx ↔ Alice Wong (EDITOR)
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
A second invocation with a different role updates the existing row in place (upsert semantics).
|
|
377
|
-
|
|
378
|
-
### `lumo doc unshare <doc> <member>` — remove a member's share
|
|
379
|
-
|
|
380
|
-
Idempotent. If the member has no share row, prints `Not shared with <name>` and exits 0.
|
|
381
|
-
|
|
382
|
-
```bash
|
|
383
198
|
lumo doc unshare "RFC" alice@example.com
|
|
384
|
-
# → Unshared cmd_xxx ↔ Alice Wong
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
Unshare does **not** flip visibility back to PRIVATE — that has to be done explicitly via `lumo doc update --scope personal`.
|
|
388
|
-
|
|
389
|
-
### `lumo doc share-list <doc>` — list current shares
|
|
390
|
-
|
|
391
|
-
Prints fixed-width rows: `<displayName> <ROLE>`. Empty output means the doc has no shares (it may still be WORKSPACE-visible).
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
199
|
lumo doc share-list "RFC"
|
|
395
|
-
# Alice Wong EDITOR
|
|
396
|
-
# Bob Chan VIEWER
|
|
397
200
|
```
|
|
398
201
|
|
|
399
202
|
### When to suggest the doc share commands
|
|
400
203
|
|
|
401
|
-
- User says "share doc X with Y", "give Y access to X"
|
|
402
|
-
- After `doc create --scope personal`, if
|
|
403
|
-
- Before `doc unshare`, run `doc share-list` if the user hasn't named a specific member
|
|
204
|
+
- User says "share doc X with Y", "give Y access to X".
|
|
205
|
+
- After `doc create --scope personal`, if teammates need access, suggest `doc share` rather than `doc update --scope workspace` when only specific members should see it.
|
|
206
|
+
- Before `doc unshare`, run `doc share-list` if the user hasn't named a specific member.
|
|
404
207
|
|
|
405
|
-
|
|
208
|
+
## `lumo doc import-gdoc <url> [--scope …] [--task LUM-N]` — import a Google Doc
|
|
406
209
|
|
|
407
210
|
One-way import of a Google Doc into Lumo. The doc is exported from Google as markdown and turned into a native Lumo document (markdown → HTML), storing the source `googleDocId` and importer so it can be re-synced later (`lumo doc sync`). `<url>` accepts a Google Doc URL or a bare doc id.
|
|
408
211
|
|
|
@@ -411,62 +214,30 @@ One-way import of a Google Doc into Lumo. The doc is exported from Google as mar
|
|
|
411
214
|
| `--scope <scope>` | enum | `personal` (→ PRIVATE) or `workspace` (→ WORKSPACE). Omit to use the server default scope. |
|
|
412
215
|
| `--task <LUM-N>` | string | Bind the imported doc to this task immediately after import. |
|
|
413
216
|
|
|
414
|
-
**Over-share note:** once imported, the content follows **Lumo's** sharing model (PRIVATE / SHARED / WORKSPACE) and is **no longer gated by Google permissions**. Importing a `workspace`-scoped doc can
|
|
415
|
-
|
|
416
|
-
Requires a connected Google Drive integration; connect it in the Web UI at `/settings/integrations`. There is no CLI `google auth` command.
|
|
217
|
+
**Over-share note:** once imported, the content follows **Lumo's** sharing model (PRIVATE / SHARED / WORKSPACE) and is **no longer gated by Google permissions**. Importing a `workspace`-scoped doc can expose it to everyone in the workspace even if the Google Doc was restricted — the command prints this reminder on success. Requires a connected Google Drive integration (connect in the Web UI at `/settings/integrations`; there is no CLI `google auth` command).
|
|
417
218
|
|
|
418
219
|
```bash
|
|
419
|
-
lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit"
|
|
420
220
|
lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit" --scope workspace --task LUM-127
|
|
421
221
|
```
|
|
422
222
|
|
|
423
|
-
Output:
|
|
424
|
-
|
|
425
|
-
```
|
|
426
|
-
Imported cmd_xxx "Quarterly Plan" https://www.uselumo.ai/workspace/lumo/documents/quarterly-plan-42
|
|
427
|
-
Note: imported content follows Lumo sharing and is no longer gated by Google permissions.
|
|
428
|
-
Bound cmd_xxx ↔ LUM-127
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
The `Bound ... ↔ LUM-N` line appears only when `--task` is supplied.
|
|
432
|
-
|
|
433
223
|
### When to suggest `doc import-gdoc`
|
|
434
224
|
|
|
435
225
|
- User pastes a Google Doc URL or says "import this Google Doc", "pull this gdoc into Lumo".
|
|
436
226
|
- User wants a Google Doc tracked alongside Lumo tasks/docs — suggest import (with `--task LUM-N` if a task is in context) and remind them about the over-share semantics for `--scope workspace`.
|
|
437
227
|
|
|
438
|
-
|
|
228
|
+
## `lumo doc sync <doc>` — re-sync an imported doc from Google
|
|
439
229
|
|
|
440
|
-
Re-imports a previously imported Google Doc and **overwrites the Lumo body** with the current Google content. **One-way and destructive** — any edits made
|
|
441
|
-
|
|
442
|
-
Sync always runs as the **importer** (owner model): it re-exports using the importer's stored Google token, not the token of whoever runs the command. If the importer has lost access to the Google Doc, sync fails with a clear error.
|
|
443
|
-
|
|
444
|
-
`<doc>` accepts a doc cuid or a case-insensitive title; ambiguous titles fail with a candidate list — re-run with the cuid.
|
|
230
|
+
Re-imports a previously imported Google Doc and **overwrites the Lumo body** with the current Google content. **One-way and destructive** — any edits made inside Lumo are discarded; Google is the source of truth for synced docs. Sync always runs as the **importer** (owner model): it re-exports using the importer's stored Google token, not the caller's. If the importer has lost access, sync fails with a clear error. `<doc>` accepts a cuid or case-insensitive title.
|
|
445
231
|
|
|
446
232
|
```bash
|
|
447
|
-
lumo doc sync cmd_xxx
|
|
448
233
|
lumo doc sync "Quarterly Plan"
|
|
449
234
|
```
|
|
450
235
|
|
|
451
|
-
Output:
|
|
452
|
-
|
|
453
|
-
```
|
|
454
|
-
Synced cmd_xxx "Quarterly Plan" from Google
|
|
455
|
-
```
|
|
456
|
-
|
|
457
236
|
### When to suggest `doc sync`
|
|
458
237
|
|
|
459
238
|
- User says "re-sync the Google Doc", "pull the latest from Google", "refresh the imported doc".
|
|
460
239
|
- After the user mentions the Google Doc changed upstream. Warn first that local Lumo edits to that doc will be overwritten (one-way, destructive).
|
|
461
240
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
The CLI does **not** currently support:
|
|
465
|
-
|
|
466
|
-
- `--from-editor` (interactive $EDITOR).
|
|
467
|
-
- Lossless markdown round-trip from **rendered** `doc show` output (use `doc show --raw` — lossless whenever a markdown source is stored).
|
|
468
|
-
- Reordering siblings within the same parent (`--before` / `--after`); use the Web UI for that.
|
|
469
|
-
|
|
470
|
-
### When to suggest session binding for docs
|
|
241
|
+
## When to suggest session binding for docs
|
|
471
242
|
|
|
472
|
-
If user creates a doc with `--task LUM-N` and the current Claude Code session is not bound, suggest `lumo session attach LUM-N` so subsequent hook events also tag that task.
|
|
243
|
+
If the user creates a doc with `--task LUM-N` and the current Claude Code session is not bound, suggest `lumo session attach LUM-N` so subsequent hook events also tag that task.
|
|
@@ -40,6 +40,9 @@ lumo memory sync --no-anchor-check # skip the post-sync code-anchor staleness ch
|
|
|
40
40
|
# Upsync locally-authored memories to the team (reverse direction)
|
|
41
41
|
lumo memory push # promote <memory-dir>/outbox/*.json to the team
|
|
42
42
|
lumo memory push --dry-run # list what would be pushed without sending
|
|
43
|
+
|
|
44
|
+
# Topic folding — autonomous (daily cron); the CLI is the read-only preview only
|
|
45
|
+
lumo memory fold --dry-run # preview the next auto-fold pass; writes nothing
|
|
43
46
|
```
|
|
44
47
|
|
|
45
48
|
When the session is bound (`lumo session attach <LUM-N>`), omit the identifier:
|
|
@@ -170,6 +173,32 @@ session or command:
|
|
|
170
173
|
`LUMO_DISABLE_MEMORY_AUTO=1` (disable **both** auto-paths). The `--no-anchor-check`
|
|
171
174
|
flag on `lumo memory sync` is unchanged.
|
|
172
175
|
|
|
176
|
+
### `lumo memory fold [project-ref] --dry-run` (topic folding — read-only preview)
|
|
177
|
+
|
|
178
|
+
Topic folding is the **third** memory-consolidation operation (orthogonal to P5
|
|
179
|
+
near-dup merge and P4 retire): it clusters a project's ACTIVE PROJECT memories by
|
|
180
|
+
**subsystem/theme** (related-but-not-duplicate fine cards — e.g. several Prisma
|
|
181
|
+
gotchas, several jest gotchas), synthesizes **one coarse "subsystem card"** per
|
|
182
|
+
cluster that preserves every source card's invariant/gotcha, and supersedes the
|
|
183
|
+
covered sources into it (失效非删, reversible). Folding **runs autonomously** on a
|
|
184
|
+
daily cron with **no human review** — a fail-closed machine coverage gate decides
|
|
185
|
+
which sources are safe to fold (a source is only superseded if a judge confirms,
|
|
186
|
+
with a verbatim quote, that its actionable detail is present in the coarse card).
|
|
187
|
+
There is **no manual apply and no `unfold`** — to correct a coarse card, edit it in
|
|
188
|
+
the web Memory tab; superseded sources are preserved.
|
|
189
|
+
|
|
190
|
+
The CLI exposes only a **read-only `--dry-run` preview** (bare `lumo memory fold`
|
|
191
|
+
without `--dry-run` just prints that folding is automatic). It prints each proposed
|
|
192
|
+
coarse card + how many fine cards it would fold / leave ACTIVE. Defaults to the
|
|
193
|
+
session-bound project; pass `[project-ref]` for another.
|
|
194
|
+
|
|
195
|
+
- `--dry-run` is required for the preview (writes nothing; the actual folding only
|
|
196
|
+
ever runs server-side in the daily cron).
|
|
197
|
+
|
|
198
|
+
**When to suggest**: when a project's PROJECT memory is "又多又细" (many fine-grained
|
|
199
|
+
but non-duplicate cards bloating the resident MEMORY.md index) and you want to see
|
|
200
|
+
how the next auto-fold pass would collapse it.
|
|
201
|
+
|
|
173
202
|
### Reconcile-on-write & deduplication
|
|
174
203
|
|
|
175
204
|
`memory add` does **not** unconditionally insert a new row. Before writing it:
|
|
@@ -26,7 +26,7 @@ lumo setup --project --force # same via global install: overwrite sk
|
|
|
26
26
|
lumo setup --project --agent codex # bake agent=codex into the hook commands
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
#### When to suggest
|
|
30
30
|
|
|
31
31
|
- The user asks "how do I set up lumo", "install the lumo skill", "wire up lumo hooks"
|
|
32
32
|
- A fresh checkout of a project that uses Lumo doesn't have `.claude/skills/lumo/SKILL.md` yet
|
|
@@ -45,7 +45,7 @@ Walks the user through creating an API key in the web app and pasting it back. O
|
|
|
45
45
|
lumo auth login
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
#### When to suggest
|
|
49
49
|
|
|
50
50
|
- `lumo whoami` reports "Not logged in"
|
|
51
51
|
- A bearer call returns 401 (`API key invalid or revoked`) and the user wants to re-auth
|
|
@@ -75,7 +75,7 @@ lumo whoami
|
|
|
75
75
|
# API: https://www.uselumo.ai
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
#### When to suggest
|
|
79
79
|
|
|
80
80
|
- The user asks "who am I", "which workspace am I in", "what account is this"
|
|
81
81
|
- You need to disambiguate whether `lumo task list` would hit the user's expected workspace
|
|
@@ -93,7 +93,7 @@ lumo update
|
|
|
93
93
|
|
|
94
94
|
The CLI also performs a passive check at most once every 24 hours (a detached child process refreshes a cache at `~/.lumo/update-check.json`), and prints a one-line "Update available" notice on subsequent invocations when a newer version is in the cache.
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
#### When to suggest
|
|
97
97
|
|
|
98
98
|
- The user asks "how do I update", "upgrade lumo", "is there a new version"
|
|
99
99
|
- A bug-fix or feature mentioned in conversation requires a newer CLI than the user is running
|