@lumoai/cli 1.29.0 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skill/SKILL.md +163 -0
- package/assets/skill/references/artifacts-figma.md +124 -0
- package/assets/skill/references/criteria.md +160 -0
- package/assets/skill/references/docs.md +405 -0
- package/assets/skill/references/memory.md +103 -0
- package/assets/skill/references/milestones.md +244 -0
- package/assets/skill/references/onboarding.md +102 -0
- package/assets/skill/references/sessions.md +225 -0
- package/assets/skill/references/sprints.md +157 -0
- package/assets/skill/references/task-context.md +136 -0
- package/assets/skill/references/tasks.md +357 -0
- package/assets/skill/references/verify.md +148 -0
- package/dist/cli/src/commands/doc-list.js +2 -2
- package/dist/cli/src/index.js +19 -19
- package/package.json +1 -1
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# Document Management
|
|
2
|
+
|
|
3
|
+
## Document Management
|
|
4
|
+
|
|
5
|
+
### `lumo doc create [title] [flags]` — create a new document
|
|
6
|
+
|
|
7
|
+
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).
|
|
8
|
+
|
|
9
|
+
| Flag | Type | Notes |
|
|
10
|
+
| ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `--content <text>` | string | Inline markdown body. |
|
|
12
|
+
| `--file <path>` | string | Read markdown body from file. |
|
|
13
|
+
| (stdin) | — | Pipe markdown; treated as content when stdin is not a TTY. |
|
|
14
|
+
| `--scope <scope>` | enum | `personal` (default) or `workspace`. Maps to PRIVATE / WORKSPACE. |
|
|
15
|
+
| `--project <ref>` | string | Project name or slug to file under. Default null. (Note: v1 does not server-resolve names; pass a cuid or omit.) |
|
|
16
|
+
| `--task <LUM-N>` | string | Bind to this task immediately after create. |
|
|
17
|
+
| `--tag <name>` | string (repeatable) | Attach tag by name. Creates tag if missing. Max 20 per call. |
|
|
18
|
+
| `--tag-id <cuid>` | string (repeatable) | Attach tag by id. Combines with `--tag`. Max 20 per call. |
|
|
19
|
+
| `--parent <doc>` | string | cuid or case-insensitive title. Files the new doc under this parent. Omit for root. |
|
|
20
|
+
|
|
21
|
+
The three content channels (`--content`, `--file`, stdin) are mutually exclusive — specify at most one. **`--file` is sandboxed:** the CLI rejects a path that resolves outside the current project directory (parent-traversal, absolute, escaping symlinks) or matches a sensitive-file denylist (`.env*`, private keys, `*.pem`/`*.key`, `credentials`, `.ssh`/`.aws` contents, …). Pass only project-local, non-secret files; there is no override flag.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
lumo doc create "RFC: doc CLI" --scope workspace --file scratch/rfc.md --task LUM-66
|
|
27
|
+
lumo doc create "RFC: tags" --tag rfc --tag draft
|
|
28
|
+
lumo doc create "Design spec" --tag-id clm_abc123 --tag draft
|
|
29
|
+
lumo doc create "Sub-doc" --parent "RFC"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Success output:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Created cmd_xxx "RFC: doc CLI" https://www.uselumo.ai/workspace/lumo/documents/rfc-doc-cli-42
|
|
36
|
+
Tags: rfc, draft
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
The `Tags:` line is omitted when no tags were attached.
|
|
42
|
+
|
|
43
|
+
### When to suggest `doc create`
|
|
44
|
+
|
|
45
|
+
- User says "write a doc", "draft an RFC", "new document" (in any language), or describes a deliverable that should live as a document.
|
|
46
|
+
- After a discussion that needs a write-up, offer to create the doc with a title and the right scope.
|
|
47
|
+
|
|
48
|
+
### `lumo doc update <doc> [flags]` — update a document
|
|
49
|
+
|
|
50
|
+
`<doc>` accepts a cuid or a case-insensitive title. Ambiguous titles fail with a candidate list — re-run with the cuid.
|
|
51
|
+
|
|
52
|
+
| Flag | Type | Notes |
|
|
53
|
+
| ------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| `--title <text>` | string | New title (cannot be empty). |
|
|
55
|
+
| `--content <text>` | string | Replace content (inline). |
|
|
56
|
+
| `--file <path>` | string | Replace content from file. |
|
|
57
|
+
| (stdin) | — | Pipe to replace content. |
|
|
58
|
+
| `--scope <scope>` | enum | `personal` / `workspace`. |
|
|
59
|
+
| `--project <ref>` | string | Project name/slug. `--project ""` clears the filing. |
|
|
60
|
+
| `--tag <name>` | string (repeatable) | **Bulk replace** the tag set by name. Creates tag if missing. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`. |
|
|
61
|
+
| `--tag-id <cuid>` | string (repeatable) | **Bulk replace** the tag set by id. Max 20. Mutually exclusive with `--add-tag*` / `--remove-tag*`. |
|
|
62
|
+
| `--add-tag <name>` | string (repeatable) | Attach tag by name (find-or-create). Max 20. |
|
|
63
|
+
| `--add-tag-id <cuid>` | string (repeatable) | Attach tag by id. Max 20. |
|
|
64
|
+
| `--remove-tag <name>` | string (repeatable) | Detach tag by name. `--remove-tag <name>` resolves the name via find-or-create on the workspace. If the name was unknown, a new Tag row is created (orphan, no attachments) before the detach runs as a no-op. Use `--remove-tag-id <cuid>` to avoid orphans. Max 20. |
|
|
65
|
+
| `--remove-tag-id <cuid>` | string (repeatable) | Detach tag by id. Unknown ids are a no-op (no side effects). Max 20. |
|
|
66
|
+
|
|
67
|
+
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").
|
|
68
|
+
|
|
69
|
+
`--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.
|
|
70
|
+
|
|
71
|
+
Like `doc create`, `--file` is sandboxed: the CLI rejects paths that resolve outside the project directory or match the sensitive-file denylist (`.env*`, private keys, `credentials`, …). No override flag.
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
lumo doc update "RFC: doc CLI" --scope personal
|
|
77
|
+
lumo doc update cmd_xxx --title "RFC v2"
|
|
78
|
+
lumo doc update cmd_xxx --file rfc-v2.md
|
|
79
|
+
lumo doc update RFC --add-tag urgent --remove-tag draft
|
|
80
|
+
lumo doc update cmd_xxx --tag final --tag approved # replace entire tag set
|
|
81
|
+
lumo doc update RFC --tag final --add-tag oops
|
|
82
|
+
# Error: --tag/--tag-id are mutually exclusive with --add-tag/--add-tag-id/--remove-tag/--remove-tag-id
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### When to suggest `doc update`
|
|
86
|
+
|
|
87
|
+
- User wants to revise an existing doc **as a whole** (title, scope, or a full-body rewrite).
|
|
88
|
+
- After running `lumo doc list` or `doc show`, if the user wants to change a doc's scope, title, or content.
|
|
89
|
+
- For a small edit inside one section, prefer `doc patch` / `doc append` (below) — full `doc update` has the maximum clobber radius.
|
|
90
|
+
- When replacing the body from a previously fetched base, pass `--if-revision` so a concurrent edit fails loudly (409) instead of being overwritten.
|
|
91
|
+
|
|
92
|
+
### `lumo doc show <doc> [--raw | --section <heading>]` — print one document's detail
|
|
93
|
+
|
|
94
|
+
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`.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
lumo doc show "RFC: doc CLI"
|
|
98
|
+
lumo doc show cmd_xxx --raw > base.md # byte-identical edit base (revision on stderr)
|
|
99
|
+
lumo doc show cmd_xxx --section "D 状态表" > sec.md # one section only (revision on stderr)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**`--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.
|
|
103
|
+
|
|
104
|
+
- 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.
|
|
105
|
+
- 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: `doc show` (rendered) → reconstruct the markdown faithfully → `doc update --file rebuilt.md` → `--raw` works from then on.
|
|
106
|
+
- 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.
|
|
107
|
+
|
|
108
|
+
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.
|
|
109
|
+
|
|
110
|
+
**`--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`.
|
|
111
|
+
|
|
112
|
+
- Section addressing: pass the heading text (`--section "D 状态表"`), case-insensitive fallback after an exact pass. Prefix with `#…` to pin the level when the same text exists at several depths (`--section "## Status"`).
|
|
113
|
+
- Missing heading → exit 1 listing the available headings; ambiguous heading → exit 1 with a depth-disambiguation hint.
|
|
114
|
+
- Requires a stored markdown source — same no-fallback rule and rebuild flow as `--raw`.
|
|
115
|
+
- Heading detection is markdown-aware: `#` lines inside fenced code blocks or blockquotes are never section boundaries.
|
|
116
|
+
|
|
117
|
+
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).
|
|
118
|
+
|
|
119
|
+
### When to suggest `doc show --raw` / `--section`
|
|
120
|
+
|
|
121
|
+
- Before any `doc update --file` that starts from existing remote content — fetch the base with `--raw`, never from rendered `doc show` output.
|
|
122
|
+
- Before a `doc patch` — read the section with `--section` first, note the `Revision:` line, edit, patch back with `--if-revision`.
|
|
123
|
+
- User asks "what's the exact source of this doc", or an agent needs a faithful local copy of a live doc.
|
|
124
|
+
- User asks "what's in section X" of a long doc — `--section` avoids pulling the whole body into context.
|
|
125
|
+
- If `--raw`/`--section` errors (no stored source), walk the rebuild flow above instead of editing the rendered output.
|
|
126
|
+
|
|
127
|
+
### `lumo doc patch <doc> --section <heading>` — replace one section
|
|
128
|
+
|
|
129
|
+
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`).
|
|
130
|
+
|
|
131
|
+
| Flag | Type | Notes |
|
|
132
|
+
| --------------------- | ------ | --------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `--section <heading>` | string | Required. Heading text addressing the section; prefix `#…` pins the depth. |
|
|
134
|
+
| `--content <text>` | string | New section content (include the heading line — the whole section is replaced verbatim). |
|
|
135
|
+
| `--file <path>` | string | New section content from file (project-local sandbox). |
|
|
136
|
+
| `--if-revision <n>` | int | Only apply if the body is still at revision `n`. Recommended whenever the edit base was read earlier. |
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
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 (that is an explicit choice, not a merge).
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
lumo doc show cmd_xxx --section "D 状态表" > sec.md # stderr: Revision: 6
|
|
144
|
+
# … edit sec.md …
|
|
145
|
+
lumo doc patch cmd_xxx --section "D 状态表" --file sec.md --if-revision 6
|
|
146
|
+
# → Patched cmd_xxx "登记册" § "D 状态表" revision 7
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### When to suggest `doc patch`
|
|
150
|
+
|
|
151
|
+
- 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.
|
|
152
|
+
- Live-doc status updates that used to be read-whole/edit/upload-whole round-trips.
|
|
153
|
+
- If the patch 409s: re-read the section, rebase the edit, retry — never fall back to a full-body upload from the stale base.
|
|
154
|
+
|
|
155
|
+
### `lumo doc append <doc> [--section <heading>]` — append to a section (or the doc)
|
|
156
|
+
|
|
157
|
+
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.
|
|
158
|
+
|
|
159
|
+
| Flag | Type | Notes |
|
|
160
|
+
| --------------------- | ------ | --------------------------------------------------------------------------- |
|
|
161
|
+
| `--section <heading>` | string | Optional. Omit to append at the document end. |
|
|
162
|
+
| `--content <text>` | string | Content to append. |
|
|
163
|
+
| `--file <path>` | string | Content from file (project-local sandbox). |
|
|
164
|
+
| `--if-revision <n>` | int | Only apply if the body is still at revision `n`; 409 + retry hint otherwise. |
|
|
165
|
+
|
|
166
|
+
Same concurrency contract as `doc patch` (always a conditional commit; 409 on conflict). Requires a stored markdown source.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
lumo doc append cmd_xxx --section "F 待办队列" --content "- [ ] 评估 XYZ 论文"
|
|
170
|
+
# → Appended to cmd_xxx "登记册" § "F 待办队列" revision 8
|
|
171
|
+
echo "## 2026-06-10\n吸收了 3 篇" | lumo doc append cmd_xxx # document end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### When to suggest `doc append`
|
|
175
|
+
|
|
176
|
+
- 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.
|
|
177
|
+
- Whenever the alternative would be re-uploading the whole doc just to add lines at the end of one section.
|
|
178
|
+
|
|
179
|
+
### `lumo doc diff <doc> --file <local.md>` — compare remote markdown source vs a local file
|
|
180
|
+
|
|
181
|
+
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.
|
|
182
|
+
|
|
183
|
+
| Flag | Type | Notes |
|
|
184
|
+
| --------------- | ------ | ---------------------------------------------------------------------------------- |
|
|
185
|
+
| `--file <path>` | string | Required. Local markdown file; same project-local sandbox as `doc update --file`. |
|
|
186
|
+
|
|
187
|
+
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).
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
lumo doc diff cmd_xxx --file docs/live-docs/research-intake-ledger.md
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### When to suggest `doc diff`
|
|
194
|
+
|
|
195
|
+
- 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).
|
|
196
|
+
- 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.
|
|
197
|
+
- After a suspected concurrent edit: a clean diff (exit 0) proves remote and local are in sync.
|
|
198
|
+
|
|
199
|
+
### `lumo doc list [flags]` — list documents
|
|
200
|
+
|
|
201
|
+
Default behavior: lists every document the current user can see, as fixed-width rows: `<cuid> <SCOPE> <project|-> <title>`.
|
|
202
|
+
|
|
203
|
+
| Flag | Type | Notes |
|
|
204
|
+
| ----------------- | ------- | -------------------------------------------------------------------------------------- |
|
|
205
|
+
| `--scope <scope>` | enum | `personal`, `workspace`, or `all`. Default `all`. |
|
|
206
|
+
| `--project <ref>` | string | Filter by project. |
|
|
207
|
+
| `--task <LUM-N>` | string | Only docs bound to this task. Routes through `GET /api/tasks/<id>/documents`. |
|
|
208
|
+
| `--limit <n>` | int | Cap output to the first N rows. |
|
|
209
|
+
| `--tree` | boolean | Render as an indented tree (two spaces per depth level) instead of the flat row table. |
|
|
210
|
+
|
|
211
|
+
`--tree` plus the existing filters work together: the CLI fetches with the filters applied, then builds the tree from whatever rows came back. Docs whose parent is **not** in the result (e.g. filtered out by `--task LUM-N`) render as top-level rows. `--limit N --tree` truncates the flat list to N rows _before_ the tree is built, so a deeply nested subtree may not appear contiguously.
|
|
212
|
+
|
|
213
|
+
When `--task` is combined with `--scope` / `--project`, those become client-side filters on the task-scoped result.
|
|
214
|
+
|
|
215
|
+
Examples:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
lumo doc list --scope workspace
|
|
219
|
+
lumo doc list --task LUM-42
|
|
220
|
+
lumo doc list --scope workspace --tree
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### When to suggest `doc list`
|
|
224
|
+
|
|
225
|
+
- The user asks "what docs do I have", "show me workspace docs", "what's on LUM-42".
|
|
226
|
+
- Before suggesting `doc update` or `doc delete` when no doc ID is in context — run `doc list` first to surface candidates.
|
|
227
|
+
|
|
228
|
+
### `lumo doc move <doc> [flags]` — move a doc under a different parent or to root
|
|
229
|
+
|
|
230
|
+
Reparents a doc. `<doc>` accepts a cuid or a case-insensitive title; ambiguous titles fail with a candidate list — re-run with the cuid.
|
|
231
|
+
|
|
232
|
+
| Flag | Type | Notes |
|
|
233
|
+
| ---------------- | ------- | ------------------------------------------------ |
|
|
234
|
+
| `--parent <doc>` | string | New parent doc (cuid or case-insensitive title). |
|
|
235
|
+
| `--root` | boolean | Move to top level (`parentId = null`). |
|
|
236
|
+
|
|
237
|
+
`--parent` and `--root` are **mutually exclusive** and one of them is **required**. CLI errors before any network call if both or neither is supplied.
|
|
238
|
+
|
|
239
|
+
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.
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
lumo doc move "Sub-doc" --parent "Roadmap"
|
|
245
|
+
lumo doc move "Sub-doc" --root
|
|
246
|
+
lumo doc move cmd_xxx --parent cmd_yyy
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Output:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
Moved cmd_xxx "Sub-doc" → "Roadmap"
|
|
253
|
+
Moved cmd_xxx "Sub-doc" → root
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### When to suggest `doc move`
|
|
257
|
+
|
|
258
|
+
- User says "move doc X under Y", "reparent X to root", "promote X to top level".
|
|
259
|
+
- After `doc create`, if the user realizes the new doc should live elsewhere in the tree — suggest `doc move` rather than recreating.
|
|
260
|
+
|
|
261
|
+
### `lumo doc delete <doc> --yes` — delete a document
|
|
262
|
+
|
|
263
|
+
Requires `--yes`; no interactive prompt (agent-friendly).
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
lumo doc delete cmd_xxx --yes
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### `lumo doc bind <doc> <task>` — bind a doc to a task
|
|
270
|
+
|
|
271
|
+
Adds an explicit DocumentMention row. Survives content edits in the Web UI. If a CONTENT-derived mention already exists for the same pair, it's upgraded to EXPLICIT so a later `unbind` can remove it.
|
|
272
|
+
|
|
273
|
+
Output:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
Bound cmd_xxx ↔ LUM-42
|
|
277
|
+
Bound cmd_xxx ↔ LUM-42 (upgraded from content) # when upgrading a CONTENT row
|
|
278
|
+
Already bound cmd_xxx ↔ LUM-42 # idempotent
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `lumo doc unbind <doc> <task>` — unbind a doc from a task
|
|
282
|
+
|
|
283
|
+
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.
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
lumo doc unbind cmd_xxx LUM-42
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### `lumo doc share <doc> <member> [--role viewer|editor|manager]` — share with a workspace member
|
|
290
|
+
|
|
291
|
+
Adds (or updates) a DocumentShare row for the given member. `<member>` resolves the same way as `task --assignee`: `me`, an email, or a display name (case-insensitive). Role defaults to `viewer`.
|
|
292
|
+
|
|
293
|
+
**Auto-promotion**: when invoked on a PRIVATE doc, server-side transactionally flips the doc's visibility to SHARED before upserting the share row. No flag needed — sharing a doc is treated as expressing the intent that it should be SHARED.
|
|
294
|
+
|
|
295
|
+
Examples:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
lumo doc share "RFC" alice@example.com --role editor
|
|
299
|
+
lumo doc share cmd_xxx "Alice Wong" --role manager
|
|
300
|
+
lumo doc share cmd_xxx me # share with yourself (rare; mostly for testing)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Output:
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
Shared cmd_xxx ↔ Alice Wong (EDITOR)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
A second invocation with a different role updates the existing row in place (upsert semantics).
|
|
310
|
+
|
|
311
|
+
### `lumo doc unshare <doc> <member>` — remove a member's share
|
|
312
|
+
|
|
313
|
+
Idempotent. If the member has no share row, prints `Not shared with <name>` and exits 0.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
lumo doc unshare "RFC" alice@example.com
|
|
317
|
+
# → Unshared cmd_xxx ↔ Alice Wong
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Unshare does **not** flip visibility back to PRIVATE — that has to be done explicitly via `lumo doc update --scope personal`.
|
|
321
|
+
|
|
322
|
+
### `lumo doc share-list <doc>` — list current shares
|
|
323
|
+
|
|
324
|
+
Prints fixed-width rows: `<displayName> <ROLE>`. Empty output means the doc has no shares (it may still be WORKSPACE-visible).
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
lumo doc share-list "RFC"
|
|
328
|
+
# Alice Wong EDITOR
|
|
329
|
+
# Bob Chan VIEWER
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### When to suggest the doc share commands
|
|
333
|
+
|
|
334
|
+
- User says "share doc X with Y", "give Y access to X"
|
|
335
|
+
- After `doc create --scope personal`, if the user mentions teammates needing access, suggest `doc share` rather than `doc update --scope workspace` when only specific members should see it
|
|
336
|
+
- Before `doc unshare`, run `doc share-list` if the user hasn't named a specific member
|
|
337
|
+
|
|
338
|
+
### `lumo doc import-gdoc <url> [--scope personal|workspace] [--task LUM-N]` — import a Google Doc
|
|
339
|
+
|
|
340
|
+
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.
|
|
341
|
+
|
|
342
|
+
| Flag | Type | Notes |
|
|
343
|
+
| ----------------- | ------ | ------------------------------------------------------------------------------------------ |
|
|
344
|
+
| `--scope <scope>` | enum | `personal` (→ PRIVATE) or `workspace` (→ WORKSPACE). Omit to use the server default scope. |
|
|
345
|
+
| `--task <LUM-N>` | string | Bind the imported doc to this task immediately after import. |
|
|
346
|
+
|
|
347
|
+
**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 therefore expose it to everyone in the workspace even if the Google Doc was restricted — the command prints this reminder on success.
|
|
348
|
+
|
|
349
|
+
Requires a connected Google Drive integration; connect it in the Web UI at `/settings/integrations`. There is no CLI `google auth` command.
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit"
|
|
353
|
+
lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit" --scope workspace --task LUM-127
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Output:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
Imported cmd_xxx "Quarterly Plan" https://www.uselumo.ai/workspace/lumo/documents/quarterly-plan-42
|
|
360
|
+
Note: imported content follows Lumo sharing and is no longer gated by Google permissions.
|
|
361
|
+
Bound cmd_xxx ↔ LUM-127
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The `Bound ... ↔ LUM-N` line appears only when `--task` is supplied.
|
|
365
|
+
|
|
366
|
+
### When to suggest `doc import-gdoc`
|
|
367
|
+
|
|
368
|
+
- User pastes a Google Doc URL or says "import this Google Doc", "pull this gdoc into Lumo".
|
|
369
|
+
- 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`.
|
|
370
|
+
|
|
371
|
+
### `lumo doc sync <doc>` — re-sync an imported doc from Google
|
|
372
|
+
|
|
373
|
+
Re-imports a previously imported Google Doc and **overwrites the Lumo body** with the current Google content. **One-way and destructive** — any edits made to the doc inside Lumo are discarded; Google is the source of truth for synced docs.
|
|
374
|
+
|
|
375
|
+
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.
|
|
376
|
+
|
|
377
|
+
`<doc>` accepts a doc cuid or a case-insensitive title; ambiguous titles fail with a candidate list — re-run with the cuid.
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
lumo doc sync cmd_xxx
|
|
381
|
+
lumo doc sync "Quarterly Plan"
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Output:
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
Synced cmd_xxx "Quarterly Plan" from Google
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### When to suggest `doc sync`
|
|
391
|
+
|
|
392
|
+
- User says "re-sync the Google Doc", "pull the latest from Google", "refresh the imported doc".
|
|
393
|
+
- After the user mentions the Google Doc changed upstream. Warn first that local Lumo edits to that doc will be overwritten (one-way, destructive).
|
|
394
|
+
|
|
395
|
+
### Out of scope (CLI v1)
|
|
396
|
+
|
|
397
|
+
The CLI does **not** currently support:
|
|
398
|
+
|
|
399
|
+
- `--from-editor` (interactive $EDITOR).
|
|
400
|
+
- Lossless markdown round-trip from **rendered** `doc show` output (use `doc show --raw` — lossless whenever a markdown source is stored).
|
|
401
|
+
- Reordering siblings within the same parent (`--before` / `--after`); use the Web UI for that.
|
|
402
|
+
|
|
403
|
+
### When to suggest session binding for docs
|
|
404
|
+
|
|
405
|
+
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.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Memory Management
|
|
2
|
+
|
|
3
|
+
## Memory management
|
|
4
|
+
|
|
5
|
+
Record and curate the long-term Memory that Claude reads on future sessions.
|
|
6
|
+
Memories are scoped **TASK** (useful only for one task) or **PROJECT** (useful
|
|
7
|
+
across the whole project). Automated extraction (layer1) and promotion (layer2,
|
|
8
|
+
on task→done) already run; these commands are the manual override.
|
|
9
|
+
|
|
10
|
+
### Commands
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# List
|
|
14
|
+
lumo task memory list [LUM-N] [--category trap|decision|convention|procedural] [-n N]
|
|
15
|
+
lumo project memory list [<project>] [--category ...] [-n N]
|
|
16
|
+
|
|
17
|
+
# Add (per-category fields; <task>/<project> default to the session-bound task)
|
|
18
|
+
lumo task memory add [LUM-N] --category trap --trigger "..." --outcome "..." [--workaround "..."] [--agent <agent>]
|
|
19
|
+
lumo task memory add [LUM-N] --category decision --what "..." --why "..." [--alternatives "..."] [--implications "..."] [--agent <agent>]
|
|
20
|
+
lumo task memory add [LUM-N] --category convention --rule "..." --applies "..." [--agent <agent>]
|
|
21
|
+
lumo task memory add [LUM-N] --category procedural --workflow "..." --trigger "..." [--step "..." --step "..."] [--agent <agent>]
|
|
22
|
+
lumo project memory add [<project>] --category ... [--agent <agent>] # same flags; records at PROJECT scope
|
|
23
|
+
|
|
24
|
+
# --agent values: claude-code | codex | cursor | gemini-cli | github-copilot | windsurf (default claude-code)
|
|
25
|
+
# Aliases: gemini → gemini-cli, copilot → github-copilot (case-insensitive)
|
|
26
|
+
# Omitting --agent records the memory as produced by Claude Code.
|
|
27
|
+
|
|
28
|
+
# Single-memory ops (memoryId from `... memory list` column 1)
|
|
29
|
+
lumo memory promote <memoryId> # TASK → PROJECT
|
|
30
|
+
lumo memory rm <memoryId> --yes # hard delete
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
When the session is bound (`lumo session attach <LUM-N>`), omit the identifier:
|
|
34
|
+
`lumo task memory add --category trap --trigger ... --outcome ...` records onto
|
|
35
|
+
the bound task; `lumo project memory add ...` records onto its project.
|
|
36
|
+
|
|
37
|
+
### Reconcile-on-write & deduplication
|
|
38
|
+
|
|
39
|
+
`memory add` does **not** unconditionally insert a new row. Before writing it:
|
|
40
|
+
|
|
41
|
+
1. Retrieves the nearest existing memories in the store (embedding similarity).
|
|
42
|
+
2. If a near-duplicate is found — including a **cross-language match** (e.g. you
|
|
43
|
+
supply content in Chinese but an equivalent English memory already exists) —
|
|
44
|
+
an LLM decides the outcome; otherwise the new memory is inserted directly.
|
|
45
|
+
|
|
46
|
+
The command prints **one** of these outcome lines:
|
|
47
|
+
|
|
48
|
+
| Output line | Meaning |
|
|
49
|
+
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
50
|
+
| `Added <CATEGORY> <SCOPE> memory …` | No near-duplicate found; stored as a new row. |
|
|
51
|
+
| `Merged into existing memory <id> (near-duplicate) …` | Near-duplicate found; the existing row was refined/updated in-place (UPDATE). |
|
|
52
|
+
| `Superseded an existing memory; new version <id> …` | New content contradicts an old memory; old row invalidated, new row created (SUPERSEDE). |
|
|
53
|
+
| `Skipped — duplicate of existing memory <id>, nothing written …` | Exact or near-exact duplicate; no write performed (NOOP). |
|
|
54
|
+
|
|
55
|
+
Content is **always normalized to English** before storing — the memory store has
|
|
56
|
+
a single canonical language. If you supply text in another language the CLI
|
|
57
|
+
translates it automatically; the stored memory will be in English.
|
|
58
|
+
|
|
59
|
+
### Lumo memory vs the harness memory tool
|
|
60
|
+
|
|
61
|
+
Claude Code / the Claude API may expose a file-based **memory tool** (a
|
|
62
|
+
`/memories` directory the model writes autonomously). That store is the agent's
|
|
63
|
+
private scratchpad — un-grounded, free-form, invisible to the team, and outside
|
|
64
|
+
Lumo's flywheel. **Project engineering lessons always go through `lumo task/project
|
|
65
|
+
memory add`** — never record them only in the harness memory tool, and never bulk-copy
|
|
66
|
+
harness memory files into Lumo memory (they are ungrounded drafts; Lumo's
|
|
67
|
+
extract→ground→reconcile write path is the only vetted entry). The two stores are
|
|
68
|
+
layered, not mirrored: transient working notes may live in the harness tool,
|
|
69
|
+
durable team knowledge lives in Lumo memory.
|
|
70
|
+
|
|
71
|
+
### When to record a memory (worthiness)
|
|
72
|
+
|
|
73
|
+
Record only knowledge that is **invisible in the codebase** — the _why_ behind a
|
|
74
|
+
choice, a gotcha that only surfaces at runtime, a rule that lives in people's
|
|
75
|
+
heads, a non-obvious failure cause, a non-trivial workflow. **Skip** routine work
|
|
76
|
+
(reading files, plain edits, normal git, successful builds) and anything a
|
|
77
|
+
developer could learn from the source, git log, or docs. When unsure, don't.
|
|
78
|
+
|
|
79
|
+
### Which category
|
|
80
|
+
|
|
81
|
+
- `trap` — a pitfall. Describe the problem (`--trigger`, `--outcome`); put a **one-line fix** inline via `--workaround`. Only when the fix is a genuine multi-step workflow, omit `--workaround` and add a separate `procedural` instead — never both (a `procedural` that just restates a trap's `--workaround` is a double-write).
|
|
82
|
+
- `decision` — an engineering decision (`--what` + `--why`, optional `--alternatives`/`--implications`).
|
|
83
|
+
- `convention` — a team rule (`--rule` + `--applies` = where it applies).
|
|
84
|
+
- `procedural` — a reusable workflow (`--workflow` + `--trigger` + `--step`…).
|
|
85
|
+
|
|
86
|
+
### TASK vs PROJECT (at add time)
|
|
87
|
+
|
|
88
|
+
Default to **TASK** (`lumo task memory add`). Record directly to **PROJECT**
|
|
89
|
+
(`lumo project memory add`) only when it helps _any_ task in the project: a
|
|
90
|
+
toolchain/environment trap, a team-wide convention, a cross-task decision. When
|
|
91
|
+
unsure → TASK.
|
|
92
|
+
|
|
93
|
+
### When to promote (TASK → PROJECT)
|
|
94
|
+
|
|
95
|
+
`lumo memory promote <id>` only when the lesson **recurs across 2+ different
|
|
96
|
+
tasks**, would help a _different_ task, and no equivalent PROJECT memory exists.
|
|
97
|
+
A wrong promotion is costly (every agent sees it forever) — prefer leaving it at TASK.
|
|
98
|
+
|
|
99
|
+
### When to reach for this
|
|
100
|
+
|
|
101
|
+
After a non-trivial debugging session, a pitfall you hit, or establishing a
|
|
102
|
+
convention → consider `lumo task memory add`. When you notice a lesson recurring
|
|
103
|
+
across multiple tasks → consider `lumo memory promote`.
|