@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.
- package/dist/cli/src/commands/doc-section-edit.js +113 -0
- package/dist/cli/src/commands/doc-show.js +48 -1
- package/dist/cli/src/commands/doc-update.js +22 -1
- package/dist/cli/src/index.js +20 -1
- package/dist/cli/src/lib/markdown-sections.js +12 -0
- package/dist/shared/src/markdown-sections.js +162 -0
- package/package.json +1 -1
- package/assets/skill/SKILL.md +0 -160
- package/assets/skill/references/artifacts-figma.md +0 -124
- package/assets/skill/references/criteria.md +0 -160
- package/assets/skill/references/docs.md +0 -339
- package/assets/skill/references/memory.md +0 -103
- package/assets/skill/references/milestones.md +0 -244
- package/assets/skill/references/onboarding.md +0 -102
- package/assets/skill/references/sessions.md +0 -225
- package/assets/skill/references/sprints.md +0 -157
- package/assets/skill/references/task-context.md +0 -136
- package/assets/skill/references/tasks.md +0 -357
- package/assets/skill/references/verify.md +0 -148
|
@@ -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.
|