@lumoai/cli 1.28.0 → 1.29.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.
@@ -1,124 +0,0 @@
1
- # Task Artifacts & Figma Designs
2
-
3
- ### Task ↔ Spec Artifacts
4
-
5
- Record Claude Code spec-engineering products (spec / plan / design …) on a task. The artifacts show up in the task detail definition ("spec") layer and are injected into `lumo task context`.
6
-
7
- #### `lumo task artifact add <task> --kind <kind> --title <title> --file <path> --source <source> --agent <agent>`
8
-
9
- Attaches an artifact to a task. `--kind`, `--title`, `--source` are stored verbatim — **`kind` is opaque** (no enumeration; `spec` / `plan` / `requirements` / anything is accepted). `--source` is **required** and names the spec-generation framework that produced the artifact, written as its **formal name** (`Superpowers` / `Spec Kit` / `BMad` / `OpenSpec` / `GSD` / …) — it is also opaque (no enumeration), but there is **no default**, so you must pass it. Quote names that contain spaces (`--source "Spec Kit"`). `--file` supplies the body (file contents). Each call appends to the end of the task's artifact list — call once per artifact (e.g. Superpowers: one `spec`, one `plan`). The `<task>` (e.g. `LUM-42`) is resolved server-side. **`--file` is sandboxed:** the CLI rejects any path that resolves **outside the current project directory** (parent-traversal, absolute paths, escaping symlinks) or that matches a **sensitive-file denylist** (`.env*`, `id_rsa`/`id_ed25519`, `*.pem`/`*.key`, `.aws`/`.ssh` contents, `credentials`, `.npmrc`, …). There is no override flag — pass only project-local, non-secret files (e.g. `docs/spec.md`). To record an out-of-tree file, copy it into the project first.
10
-
11
- `--agent` is **required** and names the coding tool that produced the artifact (enum). Valid values: `claude-code | codex | cursor | gemini-cli | github-copilot | windsurf` (case-insensitive; `gemini` and `copilot` are accepted aliases).
12
-
13
- ```bash
14
- lumo task artifact add LUM-42 --kind spec --title "Spec" --file docs/spec.md --source Superpowers --agent claude-code
15
- lumo task artifact add LUM-42 --kind plan --title "Implementation plan" --file docs/plan.md --source "Spec Kit" --agent claude-code
16
- ```
17
-
18
- Output: `Added [spec] "Spec" to LUM-42`
19
-
20
- #### `lumo task artifact update <task> <artifact-id> [--kind <kind>] [--title <title>] [--source <source>] [--agent <agent>]`
21
-
22
- Patches an existing artifact's metadata in place. Editable fields are **`kind`, `title`, `source`, and `agent`** — **content is immutable** (to change the body, `rm` the artifact and `add` it again). At least one flag is required; passing none errors before any network call. Empty values (`--kind ""`) are rejected. The `<artifact-id>` is the cuid in column 1 of `artifact list`. `--agent` accepts the same valid values and aliases as `artifact add`: `claude-code | codex | cursor | gemini-cli | github-copilot | windsurf` (case-insensitive; `gemini` and `copilot` are accepted aliases).
23
-
24
- ```bash
25
- lumo task artifact update LUM-42 cma_xxx --kind plan # re-classify spec → plan
26
- lumo task artifact update LUM-42 cma_xxx --source "Spec Kit" # fix the framework label
27
- lumo task artifact update LUM-42 cma_xxx --title "Final spec" --source OpenSpec
28
- lumo task artifact update LUM-42 cma_xxx --agent cursor # fix the agent label
29
- ```
30
-
31
- Output: `Updated [plan] "Final spec" (OpenSpec) on LUM-42`. A 404 (task or artifact missing in this workspace) prints the server message and exits 1.
32
-
33
- #### `lumo task artifact list <task>`
34
-
35
- Lists artifacts on the task in order: `<id> <kind> <agent> <source> <title>`. Prints `No artifacts on <task>` when there are none.
36
-
37
- ```bash
38
- lumo task artifact list LUM-42
39
- ```
40
-
41
- #### `lumo task artifact show <task> <artifact-id>`
42
-
43
- Prints one artifact's key:value header (id, kind, title, agent, source, order, task) followed by the full content body. The `<artifact-id>` is the cuid in column 1 of `artifact list`.
44
-
45
- ```bash
46
- lumo task artifact show LUM-42 cma_xxx
47
- ```
48
-
49
- #### `lumo task artifact rm <task> <artifact-id> --yes`
50
-
51
- Deletes an artifact from a task. Irreversible — `--yes` is required and there is no interactive prompt (agent-friendly). On success prints `Removed <artifact-id> from <task>`. A 404 (task or artifact missing in this workspace) prints the server message and exits 1.
52
-
53
- ```bash
54
- lumo task artifact rm LUM-42 cma_xxx --yes
55
- ```
56
-
57
- When to suggest: after running a spec/plan workflow in Claude Code, offer to record the product(s) with `task artifact add` (one call per artifact) — always pass `--source` with the framework you used. Use `task artifact list` to see what's already recorded, `task artifact show` to inspect a single artifact's content, `task artifact update` to fix a wrong kind/title/source without re-uploading, and `task artifact rm` to drop one that's wrong or stale.
58
-
59
- ### Task ↔ Figma Designs
60
-
61
- #### `lumo task figma add <task> <url>` — attach a Figma file/frame
62
-
63
- Fetches file name, frame name, and thumbnail via Figma OAuth and stores them
64
- on the task.
65
-
66
- ```bash
67
- lumo task figma add LUM-42 "https://www.figma.com/design/abc123/Onboarding?node-id=1-234"
68
- ```
69
-
70
- If the URL omits `node-id`, the link is stored as file-level; the CLI prints
71
- a `(file-level, thumbnail from "...")` note showing the auto-picked
72
- representative frame.
73
-
74
- Idempotent — re-adding the same URL within 7 days returns the existing row
75
- without re-calling Figma.
76
-
77
- **Not connected?** Errors with:
78
-
79
- ```
80
- ✗ You haven't connected Figma yet.
81
- Run: open https://www.uselumo.ai/settings/integrations
82
- ```
83
-
84
- #### `lumo task figma list <task>` — list attachments
85
-
86
- ```
87
- $ lumo task figma list LUM-42
88
- cfl_xxx1 Onboarding Welcome screen 2026-05-28
89
- cfl_xxx2 Design System Button / Primary 2026-05-27
90
- cfl_xxx3 Onboarding (file-level) 2026-05-20 ⚠ thumbnail stale
91
- ```
92
-
93
- `⚠ thumbnail stale` appears when `thumbnailFetchedAt > 25 days`.
94
-
95
- #### `lumo task figma rm <task> <link-id-or-url>` — remove an attachment
96
-
97
- Accepts a `cfl_*` cuid or the original URL. Idempotent (`Not linked: ...` + exit 0 when no match).
98
-
99
- #### `lumo task figma refresh <task>` — manual refresh
100
-
101
- Re-fetches metadata + thumbnail for every Figma link on the task. Per-link
102
- failures are isolated.
103
-
104
- ```
105
- $ lumo task figma refresh LUM-42
106
- Refreshed 3 Figma links on LUM-42
107
- ✓ Onboarding · Welcome screen
108
- ✓ Onboarding · Sign-up form
109
- ✗ Design System · Button (file not accessible)
110
- ```
111
-
112
- ### When to suggest the `task figma` commands
113
-
114
- - User pastes a Figma URL or mentions a design ("here's the mock", "the
115
- Figma is at...").
116
- - User asks "what designs are linked to this task" or "show me the Figma
117
- for LUM-42".
118
- - After implementing a UI change, suggest `lumo task figma refresh <task>`
119
- if the user mentioned updating the Figma source.
120
-
121
- OAuth connection lives in the Web UI at
122
- `/settings/integrations`. The CLI does **not** have a `figma auth`
123
- command; if the user tries `task figma add` without connecting, the error
124
- message directs them to the Web UI.
@@ -1,160 +0,0 @@
1
- # Acceptance criteria (contract)
2
-
3
- The acceptance contract is a small set of structured criteria the task's work
4
- will be verified against (Acceptance v1, LUM-341/342). The agent drafts it;
5
- the server validates and stores it; verification rounds (`lumo verify`,
6
- Slice 1 task #3) judge against it. Criteria are injected at session start and
7
- in `lumo task context` as the `## Acceptance criteria (contract)` section.
8
-
9
- ## When to draft — the golden rule
10
-
11
- **Attach → read context → draft criteria → THEN write the first line of code.**
12
-
13
- If `lumo task context` / session-start shows the draft reminder ("This task
14
- has no acceptance criteria yet. …") instead of a
15
- contract, you MUST draft and submit criteria before starting implementation:
16
-
17
- 1. Read the task description, comments, linked resources, and memory first —
18
- the contract distills what "done" means, so understand the task before
19
- writing it.
20
- 2. Draft **3–7 criteria** for a typical multi-file task (soft range — the
21
- server warns outside it but never rejects; if you genuinely need more,
22
- merge related checks instead). **Scale the count down for small tasks** —
23
- see "Scale the contract to the task size" below.
24
- 3. Submit with `lumo task criteria set <task> --file <criteria.json>`.
25
- Submission **locks the contract for agent edits** — get it right before
26
- submitting, because afterwards only human revisions (`--human`) can change it.
27
-
28
- Once you (the agent) have started work, you can NOT change your own contract.
29
- That's by design: the contract is what your work gets judged against, not a
30
- to-do list you trim to fit what you built.
31
-
32
- ## Scale the contract to the task size
33
-
34
- The 3–7 range is calibrated for typical multi-file tasks. The criterion count
35
- must track the size of the work — it is not a fixed ritual:
36
-
37
- - **Micro task** (expected to fit a single session, blast radius of one or
38
- two files — a docs tweak, a copy change, a small targeted fix): **1–2
39
- criteria IS a complete contract** — one targeted MACHINE criterion plus at
40
- most one HUMAN criterion.
41
- - **Never pad toward the soft floor.** A filler criterion (a restated
42
- baseline, a third angle on the same check) dilutes the contract and taxes
43
- every verification round. For a micro task the below-minimum warning is
44
- expected — ignore it.
45
- - **Shrink the contract, never skip it.** Every task still drafts and locks a
46
- contract before the first line of code; task size only changes how many
47
- criteria that takes.
48
-
49
- ## How to write good criteria
50
-
51
- - **Outcome-level definition of done, not micro-steps.** A criterion states a
52
- verifiable result ("`lumo task criteria set` rejects a second agent draft
53
- with 409"), not a task step ("add a check in the service").
54
- - **Repo-wide baselines don't take slots.** Tests pass / `tsc --noEmit` clean /
55
- i18n locale parity / lint are already required by the repo's PR checklist —
56
- never spend one of your 3–7 criteria on them.
57
- - **MACHINE vs HUMAN:**
58
- - `MACHINE` = an executable check exists. It **must** carry a `checkpointer`
59
- (the runnable check: a test command, script, probe…). The checkpointer
60
- runs on the agent's machine; the structured verdict is reported back to
61
- the server (execution on the client, adjudication on the server).
62
- - `HUMAN` = needs human judgment (UX feel, copy tone, design fidelity).
63
- - **If you can't attach a checkpointer to a MACHINE criterion, demote it to
64
- HUMAN** — the server rejects checkpointer-less MACHINE criteria rather
65
- than silently rewriting them. Don't invent a fake checkpointer to keep it
66
- MACHINE.
67
- - `evidenceRequired: true` marks criteria whose verdict must point at proof
68
- (a MACHINE PASS always requires evidence regardless of this flag).
69
-
70
- ## JSON file format
71
-
72
- `criteria.json` is a JSON array; one object per criterion:
73
-
74
- ```json
75
- [
76
- {
77
- "statement": "PUT /api/tasks/[id]/criteria rejects a second AGENT_DRAFT submission with 409",
78
- "verifierType": "MACHINE",
79
- "checkpointer": "npx jest __tests__/task-criteria.service.test.ts -t 'agent lock'"
80
- },
81
- {
82
- "statement": "The criteria section reads naturally as part of the task statement",
83
- "verifierType": "HUMAN"
84
- },
85
- {
86
- "statement": "Session-start injection shows the contract ahead of memory",
87
- "verifierType": "MACHINE",
88
- "checkpointer": "npx jest __tests__/cli/hook-runner-session-start-stdout.test.ts",
89
- "evidenceRequired": true
90
- }
91
- ]
92
- ```
93
-
94
- Fields: `statement` (required, ≤2000 chars), `verifierType` (`"MACHINE"` |
95
- `"HUMAN"`), `checkpointer` (required for MACHINE), `evidenceRequired`
96
- (optional, default false), `id` (only in `--human` revisions — see below).
97
-
98
- ## Commands
99
-
100
- ### `lumo task criteria set <task> --file <criteria.json>`
101
-
102
- Initial agent draft. Creates the whole contract as `AGENT_DRAFT` at round 0.
103
- Rejected with 409 once **any** criteria exist on the task — the agent lock.
104
- Echoes the stored criteria (with ids) plus the 3–7 soft-cap warning if
105
- outside range.
106
-
107
- ### `lumo task criteria set <task> --file <criteria.json> --human`
108
-
109
- Record a **human contract revision** (HUMAN_EDIT), e.g. when the user changes
110
- the contract in conversation — you transcribe their decision. Allowed at any
111
- time, even after the agent lock. The file is the **desired final list**:
112
-
113
- - items carrying an `id` (from `criteria list`) keep that criterion — changed
114
- fields update it and stamp it `HUMAN_EDIT`; identical fields leave it (and
115
- its provenance) untouched
116
- - items without `id` are created as `HUMAN_EDIT`
117
- - existing criteria missing from the file are **deleted** — refused with 409
118
- if the criterion already has verification runs (reword it instead)
119
-
120
- The revision is auto-recorded as a task comment with the session origin
121
- (`X-Lumo-Session-Id`). **Transcribing the contract is allowed; transcribing a
122
- verdict is never** — human verdicts only enter through human-initiated paths
123
- (web UI / Slack action), and the agent has no write path to them.
124
-
125
- Only use `--human` for decisions a human actually made (in conversation, in a
126
- comment, in review). Never use it to work around your own lock.
127
-
128
- Optionally annotate **why** the contract drifted with
129
- `--cause <NEW_INFO|SCOPE_CHANGE|DRAFT_BLIND_SPOT|GRANULARITY|OTHER>` — pick
130
- the tag from the human's stated reason (new information, scope moved, the
131
- draft missed it, wrong granularity). The tag lands in the drift record
132
- (TaskActivity payload), feeding the Slice-3 drift-cause distribution. Every
133
- criteria add/update/delete is mirrored as a structured `CRITERION_CHANGED`
134
- activity automatically; `--cause` just enriches it.
135
-
136
- ### `lumo task criteria list <task>`
137
-
138
- Print the contract: `<id> [MACHINE|HUMAN] SOURCE@rN [evidence] statement`
139
- plus an indented `↳ check:` line for checkpointers. Use it to fetch ids
140
- before a `--human` revision. Empty contract prints a drafting pointer.
141
-
142
- ## Injection behavior
143
-
144
- - **Session start** (bound task): the contract is the highest-priority
145
- section in the injection budget — ahead of memory/PR/Slack/Figma/web. If a
146
- still-open task has no criteria, the draft reminder is injected instead.
147
- - **`lumo session attach`**: prints the contract (or the draft reminder) right after
148
- binding.
149
- - **`lumo task context`**: the `## Acceptance criteria (contract)` section appears after the
150
- task description, before memory.
151
- - Review-time gap findings are appended at the round they surface and show up
152
- in the contract automatically — `REVIEW_ADDED` provenance via human review
153
- paths; findings a human raises in conversation are transcribed with
154
- `--human` + `--cause` (see verify.md "Review-time drift habits").
155
-
156
- ## After the contract: the verification loop
157
-
158
- The contract is judged by `lumo verify` — run it before claiming the task is
159
- done. See [verify.md](verify.md) for the loop (round cap 3, IN_REVIEW on
160
- all-pass, escalation on a round-3 fail).
@@ -1,339 +0,0 @@
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
- `--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.
68
-
69
- 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.
70
-
71
- Examples:
72
-
73
- ```bash
74
- lumo doc update "RFC: doc CLI" --scope personal
75
- lumo doc update cmd_xxx --title "RFC v2"
76
- lumo doc update cmd_xxx --file rfc-v2.md
77
- lumo doc update RFC --add-tag urgent --remove-tag draft
78
- lumo doc update cmd_xxx --tag final --tag approved # replace entire tag set
79
- lumo doc update RFC --tag final --add-tag oops
80
- # Error: --tag/--tag-id are mutually exclusive with --add-tag/--add-tag-id/--remove-tag/--remove-tag-id
81
- ```
82
-
83
- ### When to suggest `doc update`
84
-
85
- - User wants to revise an existing doc.
86
- - After running `lumo doc list` or `doc show`, if the user wants to change a doc's scope, title, or content.
87
-
88
- ### `lumo doc show <doc> [--raw]` — print one document's detail
89
-
90
- Default mode prints a key:value header (id, title, scope, project, created/updated timestamps, mentioned tasks) and the content rendered back to markdown.
91
-
92
- ```bash
93
- lumo doc show "RFC: doc CLI"
94
- lumo doc show cmd_xxx --raw > base.md # byte-identical edit base
95
- ```
96
-
97
- **`--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.
98
-
99
- - 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.
100
- - 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.
101
- - 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.
102
-
103
- 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.
104
-
105
- Use default mode when the user wants to read a doc; use `--raw` whenever the output will be edited and uploaded back.
106
-
107
- ### When to suggest `doc show --raw`
108
-
109
- - Before any `doc update --file` that starts from existing remote content — fetch the base with `--raw`, never from rendered `doc show` output.
110
- - User asks "what's the exact source of this doc", or an agent needs a faithful local copy of a live doc.
111
- - If `--raw` errors (no stored source), walk the rebuild flow above instead of editing the rendered output.
112
-
113
- ### `lumo doc diff <doc> --file <local.md>` — compare remote markdown source vs a local file
114
-
115
- 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.
116
-
117
- | Flag | Type | Notes |
118
- | --------------- | ------ | ---------------------------------------------------------------------------------- |
119
- | `--file <path>` | string | Required. Local markdown file; same project-local sandbox as `doc update --file`. |
120
-
121
- 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).
122
-
123
- ```bash
124
- lumo doc diff cmd_xxx --file docs/live-docs/research-intake-ledger.md
125
- ```
126
-
127
- ### When to suggest `doc diff`
128
-
129
- - 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).
130
- - 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.
131
- - After a suspected concurrent edit: a clean diff (exit 0) proves remote and local are in sync.
132
-
133
- ### `lumo doc list [flags]` — list documents
134
-
135
- Default behavior: lists every document the current user can see, as fixed-width rows: `<cuid> <SCOPE> <project|-> <title>`.
136
-
137
- | Flag | Type | Notes |
138
- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
139
- | `--scope <scope>` | enum | `personal`, `workspace`, or `all`. Default `all`. |
140
- | `--project <ref>` | string | Filter by project. |
141
- | `--task <LUM-N>` | string | Only docs bound to this task. Routes through `GET /api/tasks/<id>/documents`. |
142
- | `--limit <n>` | int | Cap output to the first N rows. |
143
- | `--tree` | boolean | Render as an indented tree (two spaces per depth level) instead of the flat row table. |
144
-
145
- `--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.
146
-
147
- When `--task` is combined with `--scope` / `--project`, those become client-side filters on the task-scoped result.
148
-
149
- Examples:
150
-
151
- ```bash
152
- lumo doc list --scope workspace
153
- lumo doc list --task LUM-42
154
- lumo doc list --scope workspace --tree
155
- ```
156
-
157
- ### When to suggest `doc list`
158
-
159
- - The user asks "what docs do I have", "show me workspace docs", "what's on LUM-42".
160
- - Before suggesting `doc update` or `doc delete` when no doc ID is in context — run `doc list` first to surface candidates.
161
-
162
- ### `lumo doc move <doc> [flags]` — move a doc under a different parent or to root
163
-
164
- Reparents a doc. `<doc>` accepts a cuid or a case-insensitive title; ambiguous titles fail with a candidate list — re-run with the cuid.
165
-
166
- | Flag | Type | Notes |
167
- | ---------------- | ------- | ------------------------------------------------ |
168
- | `--parent <doc>` | string | New parent doc (cuid or case-insensitive title). |
169
- | `--root` | boolean | Move to top level (`parentId = null`). |
170
-
171
- `--parent` and `--root` are **mutually exclusive** and one of them is **required**. CLI errors before any network call if both or neither is supplied.
172
-
173
- 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.
174
-
175
- Examples:
176
-
177
- ```bash
178
- lumo doc move "Sub-doc" --parent "Roadmap"
179
- lumo doc move "Sub-doc" --root
180
- lumo doc move cmd_xxx --parent cmd_yyy
181
- ```
182
-
183
- Output:
184
-
185
- ```
186
- Moved cmd_xxx "Sub-doc" → "Roadmap"
187
- Moved cmd_xxx "Sub-doc" → root
188
- ```
189
-
190
- ### When to suggest `doc move`
191
-
192
- - User says "move doc X under Y", "reparent X to root", "promote X to top level".
193
- - After `doc create`, if the user realizes the new doc should live elsewhere in the tree — suggest `doc move` rather than recreating.
194
-
195
- ### `lumo doc delete <doc> --yes` — delete a document
196
-
197
- Requires `--yes`; no interactive prompt (agent-friendly).
198
-
199
- ```bash
200
- lumo doc delete cmd_xxx --yes
201
- ```
202
-
203
- ### `lumo doc bind <doc> <task>` — bind a doc to a task
204
-
205
- 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.
206
-
207
- Output:
208
-
209
- ```
210
- Bound cmd_xxx ↔ LUM-42
211
- Bound cmd_xxx ↔ LUM-42 (upgraded from content) # when upgrading a CONTENT row
212
- Already bound cmd_xxx ↔ LUM-42 # idempotent
213
- ```
214
-
215
- ### `lumo doc unbind <doc> <task>` — unbind a doc from a task
216
-
217
- 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.
218
-
219
- ```bash
220
- lumo doc unbind cmd_xxx LUM-42
221
- ```
222
-
223
- ### `lumo doc share <doc> <member> [--role viewer|editor|manager]` — share with a workspace member
224
-
225
- 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`.
226
-
227
- **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.
228
-
229
- Examples:
230
-
231
- ```bash
232
- lumo doc share "RFC" alice@example.com --role editor
233
- lumo doc share cmd_xxx "Alice Wong" --role manager
234
- lumo doc share cmd_xxx me # share with yourself (rare; mostly for testing)
235
- ```
236
-
237
- Output:
238
-
239
- ```
240
- Shared cmd_xxx ↔ Alice Wong (EDITOR)
241
- ```
242
-
243
- A second invocation with a different role updates the existing row in place (upsert semantics).
244
-
245
- ### `lumo doc unshare <doc> <member>` — remove a member's share
246
-
247
- Idempotent. If the member has no share row, prints `Not shared with <name>` and exits 0.
248
-
249
- ```bash
250
- lumo doc unshare "RFC" alice@example.com
251
- # → Unshared cmd_xxx ↔ Alice Wong
252
- ```
253
-
254
- Unshare does **not** flip visibility back to PRIVATE — that has to be done explicitly via `lumo doc update --scope personal`.
255
-
256
- ### `lumo doc share-list <doc>` — list current shares
257
-
258
- Prints fixed-width rows: `<displayName> <ROLE>`. Empty output means the doc has no shares (it may still be WORKSPACE-visible).
259
-
260
- ```bash
261
- lumo doc share-list "RFC"
262
- # Alice Wong EDITOR
263
- # Bob Chan VIEWER
264
- ```
265
-
266
- ### When to suggest the doc share commands
267
-
268
- - User says "share doc X with Y", "give Y access to X"
269
- - 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
270
- - Before `doc unshare`, run `doc share-list` if the user hasn't named a specific member
271
-
272
- ### `lumo doc import-gdoc <url> [--scope personal|workspace] [--task LUM-N]` — import a Google Doc
273
-
274
- 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.
275
-
276
- | Flag | Type | Notes |
277
- | ----------------- | ------ | ------------------------------------------------------------------------------------------ |
278
- | `--scope <scope>` | enum | `personal` (→ PRIVATE) or `workspace` (→ WORKSPACE). Omit to use the server default scope. |
279
- | `--task <LUM-N>` | string | Bind the imported doc to this task immediately after import. |
280
-
281
- **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.
282
-
283
- Requires a connected Google Drive integration; connect it in the Web UI at `/settings/integrations`. There is no CLI `google auth` command.
284
-
285
- ```bash
286
- lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit"
287
- lumo doc import-gdoc "https://docs.google.com/document/d/<id>/edit" --scope workspace --task LUM-127
288
- ```
289
-
290
- Output:
291
-
292
- ```
293
- Imported cmd_xxx "Quarterly Plan" https://www.uselumo.ai/workspace/lumo/documents/quarterly-plan-42
294
- Note: imported content follows Lumo sharing and is no longer gated by Google permissions.
295
- Bound cmd_xxx ↔ LUM-127
296
- ```
297
-
298
- The `Bound ... ↔ LUM-N` line appears only when `--task` is supplied.
299
-
300
- ### When to suggest `doc import-gdoc`
301
-
302
- - User pastes a Google Doc URL or says "import this Google Doc", "pull this gdoc into Lumo".
303
- - 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`.
304
-
305
- ### `lumo doc sync <doc>` — re-sync an imported doc from Google
306
-
307
- 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.
308
-
309
- 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.
310
-
311
- `<doc>` accepts a doc cuid or a case-insensitive title; ambiguous titles fail with a candidate list — re-run with the cuid.
312
-
313
- ```bash
314
- lumo doc sync cmd_xxx
315
- lumo doc sync "Quarterly Plan"
316
- ```
317
-
318
- Output:
319
-
320
- ```
321
- Synced cmd_xxx "Quarterly Plan" from Google
322
- ```
323
-
324
- ### When to suggest `doc sync`
325
-
326
- - User says "re-sync the Google Doc", "pull the latest from Google", "refresh the imported doc".
327
- - After the user mentions the Google Doc changed upstream. Warn first that local Lumo edits to that doc will be overwritten (one-way, destructive).
328
-
329
- ### Out of scope (CLI v1)
330
-
331
- The CLI does **not** currently support:
332
-
333
- - `--from-editor` (interactive $EDITOR).
334
- - Lossless markdown round-trip from **rendered** `doc show` output (use `doc show --raw` — lossless whenever a markdown source is stored).
335
- - Reordering siblings within the same parent (`--before` / `--after`); use the Web UI for that.
336
-
337
- ### When to suggest session binding for docs
338
-
339
- 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.