@lumoai/cli 1.25.0 → 1.25.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 +16 -2
- package/assets/skill/references/tasks.md +22 -0
- package/dist/cli/src/commands/doc-list.js +4 -0
- package/dist/cli/src/commands/memory-promote.js +11 -4
- package/dist/cli/src/commands/memory-rm.js +1 -1
- package/dist/cli/src/commands/task-artifact-add.js +2 -2
- package/dist/cli/src/commands/task-criteria-set.js +2 -2
- package/dist/cli/src/index.js +6 -1
- package/dist/cli/src/lib/doc-input.js +10 -1
- package/package.json +1 -1
package/assets/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lumo
|
|
3
|
-
description: 'Use the Lumo CLI to
|
|
3
|
+
description: 'Use the Lumo CLI to work with Lumo (project management for dev teams) from the terminal: load task context, bind/wrap Claude Code sessions, and create/update/list/show/comment on tasks, projects, milestones, sprints, documents, artifacts, Figma links, dependencies, and team memory — plus acceptance criteria, machine verification (verify / task status), lineage audit, worktree scaffolding, and CLI setup/auth/update. Activate when the user mentions a Lumo task identifier (LUM-N) or the lumo CLI; asks to load task background or bind/check/wrap a session; manages any of the resources above in Lumo; is starting, resuming, or about to claim completion of a task; or asks what to work on next. Key triggers: "LUM-", "lumo", "task context", "session attach", "session wrap", "验收", "verify", "task status", "milestone", "里程碑", "sprint", "冲刺", "文档", "记忆", "memory", "依赖", "deps", "lineage", "worktree", "下一个任务", "what should I work on", "设计稿", "验收标准", "机器验收", "恢复任务".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Prerequisites
|
|
@@ -26,7 +26,7 @@ The command catalog below is a **map**: it lists every command grouped by domain
|
|
|
26
26
|
| `task create/update/list/show/comment`, `next` | [references/tasks.md](references/tasks.md) |
|
|
27
27
|
| `task artifact*`, `task figma*` | [references/artifacts-figma.md](references/artifacts-figma.md) |
|
|
28
28
|
| `task criteria set/list`, drafting the acceptance contract | [references/criteria.md](references/criteria.md) |
|
|
29
|
-
| `verify`, `task status` — machine verification loop, claim-done flow, 自查/恢复
|
|
29
|
+
| `verify`, `task status` — machine verification loop, claim-done flow, 自查/恢复 | [references/verify.md](references/verify.md) |
|
|
30
30
|
| `project list`, `milestone*` | [references/milestones.md](references/milestones.md) |
|
|
31
31
|
| `doc*` | [references/docs.md](references/docs.md) |
|
|
32
32
|
| `sprint*` | [references/sprints.md](references/sprints.md) |
|
|
@@ -128,6 +128,20 @@ The command catalog below is a **map**: it lists every command grouped by domain
|
|
|
128
128
|
- `lumo worktree rm <LUM-N> --yes` — remove a worktree (keeps the branch unless `--delete-branch`)
|
|
129
129
|
- `lumo worktree list` — list `.worktrees/` worktrees (task id, branch, dirty, node_modules link)
|
|
130
130
|
|
|
131
|
+
## Commands & flags that do NOT exist (common mistakes)
|
|
132
|
+
|
|
133
|
+
Measured from real agent sessions (LUM-392) — don't guess these:
|
|
134
|
+
|
|
135
|
+
- No `lumo session start` — binding is `lumo session attach <LUM-N>`
|
|
136
|
+
- No `lumo task delete` — tasks can't be deleted from the CLI (web UI only)
|
|
137
|
+
- No `lumo task artifact edit` — it's `lumo task artifact update`
|
|
138
|
+
- No `lumo auth status` — identity check is `lumo whoami`
|
|
139
|
+
- No `--body` on `lumo task comment` — the body is a positional arg: `lumo task comment LUM-N "text"`
|
|
140
|
+
- No `--content` on `task/project memory add` — memory is structured fields (`--category` + per-category flags), see [memory.md](references/memory.md)
|
|
141
|
+
- No global `--verbose` flag on any command
|
|
142
|
+
- `lumo task comments list` (plural) **reads** the thread; `lumo task comment` (singular) **writes** one
|
|
143
|
+
- Status updates: `lumo task update LUM-N --status done` is one direct call — do **not** walk `in_progress → in_review → done` step by step (see [tasks.md](references/tasks.md) for the transition matrix; under the verify flow you shouldn't be setting `in_review`/`done` yourself at all)
|
|
144
|
+
|
|
131
145
|
## Core workflow
|
|
132
146
|
|
|
133
147
|
Typical flow when a user says "help me with LUM-42":
|
|
@@ -95,6 +95,23 @@ Tags: urgent
|
|
|
95
95
|
|
|
96
96
|
The `Tags:` line is omitted when the resulting tag set is empty.
|
|
97
97
|
|
|
98
|
+
### Status transitions — direct moves are legal
|
|
99
|
+
|
|
100
|
+
The server's transition matrix (`lib/task/state-machine.ts`):
|
|
101
|
+
|
|
102
|
+
| From | Allowed targets |
|
|
103
|
+
| ----------- | --------------------------------------------------- |
|
|
104
|
+
| TODO | IN_PROGRESS, IN_REVIEW, DONE |
|
|
105
|
+
| IN_PROGRESS | TODO, IN_REVIEW, DONE |
|
|
106
|
+
| IN_REVIEW | TODO, IN_PROGRESS, DONE |
|
|
107
|
+
| DONE | TODO, IN_PROGRESS (reopen only — **not** IN_REVIEW) |
|
|
108
|
+
|
|
109
|
+
Practical rules:
|
|
110
|
+
|
|
111
|
+
- **One call suffices.** `--status done` straight from TODO or IN_PROGRESS is legal — never walk `in_progress → in_review → done` as a ritual (measured in LUM-392: 70 such chains wasted ~75 calls).
|
|
112
|
+
- **Under the verify flow you don't set `in_review`/`done` at all** — `lumo verify` moves the task to IN_REVIEW on all-pass and the DONE adjudication is human-only.
|
|
113
|
+
- **DONE → IN_REVIEW is rejected (409).** To attach follow-up context to a DONE task, use `lumo task comment` instead of reopening.
|
|
114
|
+
|
|
98
115
|
### When to suggest `task update`
|
|
99
116
|
|
|
100
117
|
- The user describes a state change in natural language (e.g. "mark LUM-48 as in progress", "rename LUM-12 to ...", "assign LUM-30 to me", "bump the priority on LUM-7").
|
|
@@ -237,11 +254,13 @@ DISMISSED
|
|
|
237
254
|
CONFIRMED and SUGGESTED rows show the other task's identifier, title, current status, and source/evidence. DISMISSED rows render as `[shortId] <direction> <identifier> · 已忽略` only — no title, status, or source.
|
|
238
255
|
|
|
239
256
|
When there are no edges at all the output is:
|
|
257
|
+
|
|
240
258
|
```
|
|
241
259
|
Dependencies for LUM-42: 无依赖边。
|
|
242
260
|
```
|
|
243
261
|
|
|
244
262
|
**Evidence fields by detection signal:**
|
|
263
|
+
|
|
245
264
|
- `shared_files` — `shared_files(N 个共享文件: path1, path2, …)` — number of shared write-touched files in the 14-day window, plus up to 5 sample paths.
|
|
246
265
|
- `task_mention` — `task_mention(description)` or `task_mention(comment)` — the surface where the mention appeared.
|
|
247
266
|
|
|
@@ -258,6 +277,7 @@ lumo task deps add LUM-42 --blocked-by LUM-9
|
|
|
258
277
|
Both `<LUM-N>` and `--blocked-by` are required. The command errors on usage if either is missing.
|
|
259
278
|
|
|
260
279
|
**Service semantics (read before using):**
|
|
280
|
+
|
|
261
281
|
- **Self-edge** → 400 ("A task cannot depend on itself").
|
|
262
282
|
- **CONFIRMED edge in the same direction already exists** → 409 ("Dependency already exists").
|
|
263
283
|
- **CONFIRMED edge in the reverse direction already exists** → the cycle guard fires and returns 409 ("Dependency would create a cycle").
|
|
@@ -275,11 +295,13 @@ lumo task deps confirm LUM-42 e5f6a7b8 --reverse # flip direction before confir
|
|
|
275
295
|
```
|
|
276
296
|
|
|
277
297
|
**Edge selector semantics** (shared by `confirm`, `dismiss`, `rm`):
|
|
298
|
+
|
|
278
299
|
- **Other task's identifier** (e.g., `LUM-55`) — case-insensitive exact match against the edge's other-task identifier. Resolves unambiguously when there is exactly one edge to that task.
|
|
279
300
|
- **Edge-id prefix** — at least 6 characters of the short id (e.g., `e5f6a7`). Must match exactly one edge.
|
|
280
301
|
- If zero or more than one edge matches → prints all candidate edges with short ids and exits 1. Retry with a more specific selector.
|
|
281
302
|
|
|
282
303
|
**`--reverse` semantics:**
|
|
304
|
+
|
|
283
305
|
- The detector's direction heuristic is best-effort. If the suggested direction is backwards (e.g., the detector says "LUM-42 blocks LUM-55" but actually LUM-55 blocks LUM-42), confirm with `--reverse` to flip before writing.
|
|
284
306
|
- The service checks that the reversed pair does not already have an edge (→ 409), and re-runs the cycle guard with the flipped direction.
|
|
285
307
|
|
|
@@ -9,6 +9,10 @@ const doc_create_1 = require("./doc-create");
|
|
|
9
9
|
const doc_tree_1 = require("../lib/doc-tree");
|
|
10
10
|
const sanitize_1 = require("../lib/sanitize");
|
|
11
11
|
function visibilityLabel(v) {
|
|
12
|
+
// Some routes (e.g. /api/tasks/<id>/documents) have returned rows without a
|
|
13
|
+
// visibility field at runtime — fall back instead of crashing on .padEnd.
|
|
14
|
+
if (!v)
|
|
15
|
+
return '-';
|
|
12
16
|
if (v === 'PRIVATE')
|
|
13
17
|
return 'PERSONAL';
|
|
14
18
|
return v;
|
|
@@ -20,7 +20,10 @@ async function memoryPromote(memoryId) {
|
|
|
20
20
|
try {
|
|
21
21
|
res = await fetch(`${base}/api/memories/${encodeURIComponent(memoryId)}`, {
|
|
22
22
|
method: 'PATCH',
|
|
23
|
-
headers: {
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${creds.token}`,
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
},
|
|
24
27
|
body: JSON.stringify({ scope: 'PROJECT' }),
|
|
25
28
|
});
|
|
26
29
|
}
|
|
@@ -29,7 +32,7 @@ async function memoryPromote(memoryId) {
|
|
|
29
32
|
return 1;
|
|
30
33
|
}
|
|
31
34
|
if (res.status === 404) {
|
|
32
|
-
console.error(`Error: memory ${memoryId} not found`);
|
|
35
|
+
console.error(`Error: memory ${memoryId} not found — pass the full memory id (cuid) from \`lumo task memory list\` / \`lumo project memory list\`; truncated id prefixes are not resolved`);
|
|
33
36
|
return 1;
|
|
34
37
|
}
|
|
35
38
|
if (res.status === 409) {
|
|
@@ -43,8 +46,12 @@ async function memoryPromote(memoryId) {
|
|
|
43
46
|
if (typeof b.error === 'string')
|
|
44
47
|
m = b.error;
|
|
45
48
|
}
|
|
46
|
-
catch {
|
|
47
|
-
|
|
49
|
+
catch {
|
|
50
|
+
/* */
|
|
51
|
+
}
|
|
52
|
+
console.error(m
|
|
53
|
+
? `Error: ${(0, sanitize_1.sanitizeField)(m)}`
|
|
54
|
+
: `Error: promote failed (HTTP ${res.status})`);
|
|
48
55
|
return 1;
|
|
49
56
|
}
|
|
50
57
|
process.stdout.write(`Promoted ${memoryId} to PROJECT — every agent on this project now sees it.\n` +
|
|
@@ -31,7 +31,7 @@ async function memoryRm(memoryId, options) {
|
|
|
31
31
|
return 1;
|
|
32
32
|
}
|
|
33
33
|
if (res.status === 404) {
|
|
34
|
-
console.error(`Error: memory ${memoryId} not found`);
|
|
34
|
+
console.error(`Error: memory ${memoryId} not found — pass the full memory id (cuid) from \`lumo task memory list\` / \`lumo project memory list\`; truncated id prefixes are not resolved`);
|
|
35
35
|
return 1;
|
|
36
36
|
}
|
|
37
37
|
if (res.status !== 204) {
|
|
@@ -44,7 +44,7 @@ async function taskArtifactAdd(identifier, options) {
|
|
|
44
44
|
const verdict = (0, path_guard_1.checkArtifactFilePath)(options.file);
|
|
45
45
|
if (!verdict.ok) {
|
|
46
46
|
if (verdict.reason === 'unreadable') {
|
|
47
|
-
console.error(`Error:
|
|
47
|
+
console.error(`Error: ${(0, doc_input_1.unreadableFileMessage)(options.file)}`);
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
50
|
console.error(`Error: refusing to read ${options.file} — ${verdict.detail}. ` +
|
|
@@ -57,7 +57,7 @@ async function taskArtifactAdd(identifier, options) {
|
|
|
57
57
|
content = await (0, doc_input_1.readFileUtf8)(verdict.resolved);
|
|
58
58
|
}
|
|
59
59
|
catch {
|
|
60
|
-
console.error(`Error:
|
|
60
|
+
console.error(`Error: ${(0, doc_input_1.unreadableFileMessage)(options.file)}`);
|
|
61
61
|
return 1;
|
|
62
62
|
}
|
|
63
63
|
if (content.trim().length === 0) {
|
|
@@ -55,7 +55,7 @@ async function taskCriteriaSet(identifier, options) {
|
|
|
55
55
|
const verdict = (0, path_guard_1.checkArtifactFilePath)(options.file);
|
|
56
56
|
if (!verdict.ok) {
|
|
57
57
|
if (verdict.reason === 'unreadable') {
|
|
58
|
-
console.error(`Error:
|
|
58
|
+
console.error(`Error: ${(0, doc_input_1.unreadableFileMessage)(options.file)}`);
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
61
61
|
console.error(`Error: refusing to read ${options.file} — ${verdict.detail}. ` +
|
|
@@ -68,7 +68,7 @@ async function taskCriteriaSet(identifier, options) {
|
|
|
68
68
|
raw = await (0, doc_input_1.readFileUtf8)(verdict.resolved);
|
|
69
69
|
}
|
|
70
70
|
catch {
|
|
71
|
-
console.error(`Error:
|
|
71
|
+
console.error(`Error: ${(0, doc_input_1.unreadableFileMessage)(options.file)}`);
|
|
72
72
|
return 1;
|
|
73
73
|
}
|
|
74
74
|
const parsed = parseCriteriaJson(raw);
|
package/dist/cli/src/index.js
CHANGED
|
@@ -172,7 +172,12 @@ function wrap(fn) {
|
|
|
172
172
|
const program = new commander_1.Command()
|
|
173
173
|
.name('lumo')
|
|
174
174
|
.description('Lumo CLI — manage tasks and sessions from the terminal')
|
|
175
|
-
.version(pkg.version)
|
|
175
|
+
.version(pkg.version)
|
|
176
|
+
// Make usage errors actionable for agents: suggest near-miss spellings and
|
|
177
|
+
// point at --help instead of dead-ending on "unknown option". Subcommands
|
|
178
|
+
// created via .command() inherit these settings.
|
|
179
|
+
.showSuggestionAfterError(true)
|
|
180
|
+
.showHelpAfterError('(run the command with --help to list its valid flags and arguments)');
|
|
176
181
|
const auth = program.command('auth').description('Manage Lumo authentication');
|
|
177
182
|
auth
|
|
178
183
|
.command('login')
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.unreadableFileMessage = unreadableFileMessage;
|
|
36
37
|
exports.resolveDocContent = resolveDocContent;
|
|
37
38
|
exports.readStdinToString = readStdinToString;
|
|
38
39
|
exports.readFileUtf8 = readFileUtf8;
|
|
@@ -45,6 +46,14 @@ const path_guard_1 = require("./path-guard");
|
|
|
45
46
|
* mutually exclusive. stdin is only consulted when neither flag is set
|
|
46
47
|
* and the shell is non-TTY.
|
|
47
48
|
*/
|
|
49
|
+
/**
|
|
50
|
+
* Actionable message for an unreadable --file path: agents most often pass a
|
|
51
|
+
* path that doesn't exist relative to the current working directory.
|
|
52
|
+
*/
|
|
53
|
+
function unreadableFileMessage(file) {
|
|
54
|
+
return (`could not read file ${file} (cwd: ${process.cwd()}) — ` +
|
|
55
|
+
`check the path exists; pass a project-local relative path`);
|
|
56
|
+
}
|
|
48
57
|
async function resolveDocContent(args) {
|
|
49
58
|
const hasContent = args.content !== undefined;
|
|
50
59
|
const hasFile = args.file !== undefined && args.file.length > 0;
|
|
@@ -62,7 +71,7 @@ async function resolveDocContent(args) {
|
|
|
62
71
|
return {
|
|
63
72
|
kind: 'error',
|
|
64
73
|
message: check.reason === 'unreadable'
|
|
65
|
-
?
|
|
74
|
+
? unreadableFileMessage(args.file)
|
|
66
75
|
: `refusing to read ${args.file} — ${check.detail}. ` +
|
|
67
76
|
`--file must be a non-sensitive path inside the project directory.`,
|
|
68
77
|
};
|