@lumoai/cli 1.53.0 → 1.54.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,6 +1,6 @@
1
1
  ---
2
2
  name: lumo
3
- description: 'Use when the user mentions a Lumo task id (LUM-N, or any team prefix like SPEC-12) or the `lumo` CLI, in any language; is starting, resuming, or about to claim completion of a task; asks what to work on next; or works with any Lumo resource — task context, sessions, acceptance criteria, machine verification, tasks, projects, milestones, sprints, docs, artifacts, Figma links, dependencies, team memory, or worktrees. Key triggers: "LUM-", "lumo", "task context", "session attach", "verify", "task status", "acceptance criteria", "what should I work on", "resume task".'
3
+ description: 'Use when the user mentions a Lumo task id (LUM-N, or any team prefix like SPEC-12) or the `lumo` CLI, in any language; is starting, resuming, or about to claim completion of a task; asks what to work on next; or works with any Lumo resource — task context, sessions, acceptance criteria, machine verification, tasks, ideas, projects, milestones, sprints, docs, artifacts, Figma links, dependencies, team memory, priorities, or worktrees. Key triggers: "LUM-", "lumo", "task context", "session attach", "verify", "task status", "acceptance criteria", "what should I work on", "resume task".'
4
4
  ---
5
5
 
6
6
  ## Prerequisites
@@ -33,6 +33,7 @@ The command catalog below is a **map**: it lists every command grouped by domain
33
33
  | `cost` — per-operation (per-tool) token cost read-out; `task lineage` Top-5 | [references/task-context.md](references/task-context.md) |
34
34
  | `outcome show/record/rate` — post-hoc outcome well (falsifier oracle, rejection-only) | [references/outcome.md](references/outcome.md) |
35
35
  | `fidelity show` — per-delivery grounding + independence change-pattern read-out | [references/fidelity.md](references/fidelity.md) |
36
+ | `priority [text]`, `priority list` — team priority declaration (append-only) | [references/priority.md](references/priority.md) |
36
37
  | `project list`, `milestone*` | [references/milestones.md](references/milestones.md) |
37
38
  | `doc create/update/list/move/bind/share/import` (CRUD) | [references/docs.md](references/docs.md) |
38
39
  | `doc show --raw/--section`, `doc patch/append/diff/rebuild-source` (editing live docs) | [references/doc-editing.md](references/doc-editing.md) |
@@ -69,6 +70,11 @@ The command catalog below is a **map**: it lists every command grouped by domain
69
70
  - `lumo task show <id>` — print one task's detail
70
71
  - `lumo task comment <id> <body>` — leave a comment
71
72
 
73
+ **Ideas (team-level capture pool)**
74
+
75
+ - `lumo idea "<statement>" [--context <text>]` — capture a team-level idea into the pool in <10s. Provenance is grabbed opportunistically and never blocks the capture: the current `CLAUDE_CODE_SESSION_ID` (→ sourceSessionId) and the session's bound task (→ sourceTaskId, null when nothing is bound). Prints an **I-prefixed** id (`✓ 想法 LUM-I42 已入池`) — the `I` keeps ideas from colliding with task ids (`LUM-42`) in the same team namespace. Ideas are team-scoped (no project); the workspace's default team owns them. The unprocessed (CAPTURED) pool is what the Phase-2 transformer consumes.
76
+ - **When to suggest**: the user has a stray idea/thought/improvement they want to park without derailing the current task ("记一下这个想法", "capture this idea", "add to the backlog of ideas"). Prefer this over `task create` for un-triaged sparks — an idea is above projects and isn't yet actionable work.
77
+
72
78
  **Task dependencies** — see [task-deps.md](references/task-deps.md)
73
79
 
74
80
  - `lumo task deps list <id>` — list dependency edges both directions, grouped CONFIRMED / SUGGESTED / DISMISSED (each row: short edge id + other task + detected evidence)
@@ -94,6 +100,11 @@ The command catalog below is a **map**: it lists every command grouped by domain
94
100
 
95
101
  - `lumo cost [--task <id>|--session <id>|--since <date>] [--by tool|model|member|session] [--json]` — per-operation token cost read-out, attributing each model step's token delta to the tool(s) it ran. Scope is mutually exclusive (default = workspace last-30-days). For the per-task Top-5 inline, see `lumo task lineage`.
96
102
 
103
+ **Priority (team-level declaration)** — see [priority.md](references/priority.md)
104
+
105
+ - `lumo priority [text]` — declare the team's current priority (a 1-3 sentence plain-text statement). With `text`, records it directly; with no argument, prompts interactively. **Append-only**: a new declaration supersedes the old by a later `effectiveFrom` — nothing is ever overwritten or deleted.
106
+ - `lumo priority list` — print the append history newest-first (the current declaration is marked `*`).
107
+
97
108
  **Criteria audit** — workspace-level referent-kind analytics
98
109
 
99
110
  - `lumo criteria audit` — workspace-level read-out: count of acceptance criteria by effective referent kind (EXTERNAL_FACT / AGENT_CONSTRUCTED_STATE / PENDING_OUTCOME / UNVERIFIED_ASSERTION / UNCLASSIFIED) + the self-confirming-green ratio
@@ -0,0 +1,55 @@
1
+ # Priority (team-level declaration)
2
+
3
+ The team **priority** is a short (1-3 sentence) plain-text statement of what the
4
+ team is currently focused on. It is **team-level** — scoped to the caller's
5
+ workspace default team, with no project or milestone binding.
6
+
7
+ Priority is **append-only by design**: every declaration inserts a new row.
8
+ A newer statement supersedes an older one by a later `effectiveFrom`; the
9
+ history is **never mutated or deleted**. There is deliberately no edit/remove
10
+ command — to change the priority, declare a new one.
11
+
12
+ ## `lumo priority [text]`
13
+
14
+ Declare the current team priority.
15
+
16
+ - **With `text`** — records it directly:
17
+ ```bash
18
+ lumo priority "Ship the idea-capture MVP; defer the converter to Spec 2."
19
+ ```
20
+ - **With no argument** — prompts interactively for a 1-3 sentence statement:
21
+ ```bash
22
+ lumo priority
23
+ # Priority (1-3 sentences): _
24
+ ```
25
+ An empty prompt records nothing and exits non-zero.
26
+
27
+ On success it prints `Recorded priority: <statement>`. A blank statement is
28
+ rejected (the API's Zod layer returns 400).
29
+
30
+ ## `lumo priority list`
31
+
32
+ Print the append history, newest-first. The current declaration (the row with
33
+ the latest `effectiveFrom`) is marked with a leading `*`:
34
+
35
+ ```
36
+ * 2026-02-01 Now focus on the converter state machine
37
+ 2026-01-01 Ship the idea-capture MVP
38
+ ```
39
+
40
+ Prints `No priorities declared yet.` when the team has none.
41
+
42
+ ## When to suggest
43
+
44
+ - The user wants to **set or change the team's focus/priority** in plain words
45
+ ("我们现在的重点是…", "set our team priority to…", "记录一下当前优先级").
46
+ - The user asks **what the team is currently prioritizing** → `lumo priority list`.
47
+ - Do **not** confuse this with task priority (`lumo task update --priority
48
+ low|medium|high|urgent`), which is a per-task enum, not a team declaration.
49
+
50
+ ## Under the hood
51
+
52
+ - `POST /api/priorities` `{ statement }` → 201 `{ priority }` (append).
53
+ - `GET /api/priorities` → `{ current, history }` (history newest-first).
54
+ - Dual-auth (bearer or Clerk); the team is resolved as the caller's workspace
55
+ default team. Layers: `priority.repository` → `priority.service` → route → CLI.
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatCapturedIdeaLine = formatCapturedIdeaLine;
4
+ exports.ideaCapture = ideaCapture;
5
+ const config_1 = require("../lib/config");
6
+ const api_1 = require("../lib/api");
7
+ const resolve_bound_task_1 = require("../lib/resolve-bound-task");
8
+ const sanitize_1 = require("../lib/sanitize");
9
+ /**
10
+ * Success line for a captured idea. Echoes the human-referenceable id
11
+ * (`<TEAM>-I<n>`, e.g. `LUM-I42`) — the `I` prefix keeps it visibly distinct
12
+ * from task ids (`LUM-42`).
13
+ */
14
+ function formatCapturedIdeaLine(idea) {
15
+ return `✓ 想法 ${idea.identifier} 已入池`;
16
+ }
17
+ /**
18
+ * `lumo idea "<statement>"` — a <10s frictionless capture of a team-level idea.
19
+ *
20
+ * Provenance is gathered opportunistically and never blocks the capture:
21
+ * the current `CLAUDE_CODE_SESSION_ID` (→ sourceSessionId) and the task the
22
+ * session is bound to (→ sourceTaskId, null when nothing is bound). An
23
+ * optional `--context` note becomes sourceContext.
24
+ */
25
+ async function ideaCapture(statement, opts) {
26
+ if (!statement || statement.trim().length === 0) {
27
+ console.error('Error: missing <statement>. Usage: lumo idea "<your idea>" [--context <text>]');
28
+ return 1;
29
+ }
30
+ const creds = (0, config_1.readCredentials)();
31
+ if (!creds) {
32
+ console.error('Error: not logged in. Run `lumo auth login` first.');
33
+ return 1;
34
+ }
35
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
36
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
37
+ const url = `${base}/api/ideas`;
38
+ const sourceSessionId = process.env.CLAUDE_CODE_SESSION_ID ?? null;
39
+ const sourceTaskId = await (0, resolve_bound_task_1.resolveBoundTaskIdentifier)(apiUrl, creds.token);
40
+ const body = { statement: statement.trim() };
41
+ if (opts.context !== undefined)
42
+ body.sourceContext = opts.context;
43
+ if (sourceSessionId)
44
+ body.sourceSessionId = sourceSessionId;
45
+ if (sourceTaskId)
46
+ body.sourceTaskId = sourceTaskId;
47
+ const headers = {
48
+ Authorization: `Bearer ${creds.token}`,
49
+ 'Content-Type': 'application/json',
50
+ };
51
+ let res;
52
+ try {
53
+ res = await fetch(url, {
54
+ method: 'POST',
55
+ headers,
56
+ body: JSON.stringify(body),
57
+ });
58
+ }
59
+ catch (err) {
60
+ const msg = err instanceof Error ? err.message : String(err);
61
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
62
+ return 1;
63
+ }
64
+ if (res.status === 401) {
65
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
66
+ return 1;
67
+ }
68
+ if (res.status === 201) {
69
+ const data = (await res.json());
70
+ process.stdout.write(formatCapturedIdeaLine(data.idea) + '\n');
71
+ return;
72
+ }
73
+ let serverMsg = null;
74
+ try {
75
+ const errBody = (await res.json());
76
+ if (typeof errBody.error === 'string')
77
+ serverMsg = errBody.error;
78
+ }
79
+ catch {
80
+ // Body wasn't JSON; fall through to status-only message
81
+ }
82
+ if (serverMsg) {
83
+ console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
84
+ }
85
+ else {
86
+ console.error(`Error: idea capture failed (HTTP ${res.status})`);
87
+ }
88
+ return 1;
89
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.priority = priority;
4
+ exports.priorityList = priorityList;
5
+ exports.formatPriorityHistory = formatPriorityHistory;
6
+ const config_1 = require("../lib/config");
7
+ const api_1 = require("../lib/api");
8
+ const sanitize_1 = require("../lib/sanitize");
9
+ const line_prompt_1 = require("../lib/line-prompt");
10
+ /**
11
+ * `lumo priority [text]`
12
+ *
13
+ * With `text`: records it directly as the team's current priority.
14
+ * Without `text`: interactively prompts for a 1-3 sentence statement.
15
+ *
16
+ * Priority is append-only — recording a new one supersedes the old without
17
+ * deleting it. Read the append history with `lumo priority list`.
18
+ */
19
+ async function priority(text) {
20
+ const creds = (0, config_1.readCredentials)();
21
+ if (!creds) {
22
+ console.error('Error: not logged in. Run `lumo auth login` first.');
23
+ return 1;
24
+ }
25
+ let statement = (text ?? '').trim();
26
+ if (!statement) {
27
+ statement = (await (0, line_prompt_1.promptLine)('Priority (1-3 sentences): ')).trim();
28
+ }
29
+ if (!statement) {
30
+ console.error('Error: no priority provided. Nothing recorded.');
31
+ return 1;
32
+ }
33
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
34
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
35
+ let res;
36
+ try {
37
+ res = await fetch(`${base}/api/priorities`, {
38
+ method: 'POST',
39
+ headers: {
40
+ Authorization: `Bearer ${creds.token}`,
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: JSON.stringify({ statement }),
44
+ });
45
+ }
46
+ catch (err) {
47
+ const msg = err instanceof Error ? err.message : String(err);
48
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
49
+ return 1;
50
+ }
51
+ if (res.status === 401) {
52
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
53
+ return 1;
54
+ }
55
+ if (res.status !== 201) {
56
+ console.error(await errorMessage(res, 'record priority'));
57
+ return 1;
58
+ }
59
+ const data = (await res.json());
60
+ process.stdout.write(`Recorded priority: ${(0, sanitize_1.sanitizeField)(data.priority.statement)}\n`);
61
+ }
62
+ /**
63
+ * `lumo priority list`
64
+ *
65
+ * Prints the append history newest-first, marking the current declaration
66
+ * (the row with the latest effectiveFrom).
67
+ */
68
+ async function priorityList() {
69
+ const creds = (0, config_1.readCredentials)();
70
+ if (!creds) {
71
+ console.error('Error: not logged in. Run `lumo auth login` first.');
72
+ return 1;
73
+ }
74
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
75
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
76
+ let res;
77
+ try {
78
+ res = await fetch(`${base}/api/priorities`, {
79
+ headers: { Authorization: `Bearer ${creds.token}` },
80
+ });
81
+ }
82
+ catch (err) {
83
+ const msg = err instanceof Error ? err.message : String(err);
84
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
85
+ return 1;
86
+ }
87
+ if (res.status === 401) {
88
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
89
+ return 1;
90
+ }
91
+ if (!res.ok) {
92
+ console.error(await errorMessage(res, 'list priorities'));
93
+ return 1;
94
+ }
95
+ const data = (await res.json());
96
+ if (data.history.length === 0) {
97
+ process.stdout.write('No priorities declared yet.\n');
98
+ return;
99
+ }
100
+ const currentId = data.current?.id;
101
+ process.stdout.write(formatPriorityHistory(data.history, currentId));
102
+ }
103
+ /** Render the append history newest-first; the current row is marked `*`. */
104
+ function formatPriorityHistory(history, currentId) {
105
+ return (history
106
+ .map(p => {
107
+ const marker = p.id === currentId ? '*' : ' ';
108
+ const when = p.effectiveFrom.slice(0, 10);
109
+ return `${marker} ${when} ${(0, sanitize_1.sanitizeField)(p.statement)}`;
110
+ })
111
+ .join('\n') + '\n');
112
+ }
113
+ /** Best-effort extraction of a server `{ error }` message for a failed fetch. */
114
+ async function errorMessage(res, action) {
115
+ let serverMsg = null;
116
+ try {
117
+ const errBody = (await res.json());
118
+ if (typeof errBody.error === 'string')
119
+ serverMsg = errBody.error;
120
+ }
121
+ catch {
122
+ // Body wasn't JSON
123
+ }
124
+ return serverMsg
125
+ ? `Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`
126
+ : `Error: could not ${action} (HTTP ${res.status})`;
127
+ }
@@ -46,7 +46,9 @@ const hook_1 = require("./commands/hook");
46
46
  const session_attach_1 = require("./commands/session-attach");
47
47
  const session_status_1 = require("./commands/session-status");
48
48
  const next_1 = require("./commands/next");
49
+ const idea_1 = require("./commands/idea");
49
50
  const cost_1 = require("./commands/cost");
51
+ const priority_1 = require("./commands/priority");
50
52
  const criteria_audit_1 = require("./commands/criteria-audit");
51
53
  const fidelity_1 = require("./commands/fidelity");
52
54
  const verify_1 = require("./commands/verify");
@@ -276,6 +278,11 @@ program
276
278
  .option('-n, --count <N>', 'Number of tasks to recommend (default 3)')
277
279
  .option('--claimable', 'Only recommend agent-claimable tasks that are unblocked (F3) and within milestone budget (F2)')
278
280
  .action(wrap(options => (0, next_1.nextCommand)(options)));
281
+ program
282
+ .command('idea <statement>')
283
+ .description('Capture a team-level idea into the pool (<10s, no friction). Grabs the current Claude Code session id and its bound task as provenance; prints an I-prefixed id (e.g. LUM-I42). The transformer (Spec 2) consumes the pool.')
284
+ .option('-c, --context <text>', 'Free-text origin context for the idea')
285
+ .action(wrap((statement, options) => (0, idea_1.ideaCapture)(statement, options)));
279
286
  program
280
287
  .command('cost')
281
288
  .description('Show per-operation (per-tool) token cost. Defaults to a workspace 30-day window; scope with --task / --session')
@@ -285,6 +292,14 @@ program
285
292
  .option('--by <dim>', 'Headline grouping: tool | model | member | session (case-insensitive; default tool)')
286
293
  .option('--json', 'Emit the versioned payload as JSON')
287
294
  .action(wrap(options => (0, cost_1.cost)(options)));
295
+ const priorityCmd = program
296
+ .command('priority [text]')
297
+ .description('Declare the team priority (append-only). With [text] records it directly; with no argument prompts interactively for 1-3 sentences. A newer declaration supersedes the old without deleting it.')
298
+ .action(wrap((text) => (0, priority_1.priority)(text)));
299
+ priorityCmd
300
+ .command('list')
301
+ .description('Print the team priority append history, newest-first (the current declaration is marked with *).')
302
+ .action(wrap(() => (0, priority_1.priorityList)()));
288
303
  const criteria = program
289
304
  .command('criteria')
290
305
  .description('Workspace-level acceptance-criteria analytics');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumoai/cli",
3
- "version": "1.53.0",
3
+ "version": "1.54.0",
4
4
  "description": "Lumo CLI — manage tasks and sessions from the terminal",
5
5
  "license": "MIT",
6
6
  "author": "cli@uselumo.ai",