@lumoai/cli 1.25.1 → 1.26.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 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", "设计稿", "验收标准", "机器验收", "恢复任务".'
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, in any language; 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", "acceptance criteria", "milestone", "sprint", "docs", "memory", "deps", "lineage", "worktree", "design link", "what should I work on", "resume task".'
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, 自查/恢复 | [references/verify.md](references/verify.md) |
29
+ | `verify`, `task status` — machine verification loop, claim-done flow, self-check/resume | [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) |
@@ -64,19 +64,19 @@ The command catalog below is a **map**: it lists every command grouped by domain
64
64
 
65
65
  **Task dependencies**
66
66
 
67
- - `lumo task deps list <id>` — list dependency edges in both directions, grouped CONFIRMED / SUGGESTED(待确认) / DISMISSED; each row shows a short edge id `[xxxxxxxx]`, the other task, and detected evidence (`shared_files(N 个共享文件: …)` / `task_mention(…)`); SUGGESTED rows include a copy-pasteable confirm hint
67
+ - `lumo task deps list <id>` — list dependency edges in both directions, grouped CONFIRMED / SUGGESTED (pending confirmation) / DISMISSED; each row shows a short edge id `[xxxxxxxx]`, the other task, and detected evidence (`shared_files(N shared files: …)` / `task_mention(…)`); SUGGESTED rows include a copy-pasteable confirm hint
68
68
  - `lumo task deps add <id> --blocked-by <LUM-N>` — declare a manual hard dependency (created CONFIRMED, source MANUAL; if a SUGGESTED/DISMISSED edge already exists in the same direction, it is confirmed in place rather than creating a new row)
69
69
  - `lumo task deps confirm <id> <edge> [--reverse]` — confirm a detected candidate; `<edge>` is a short edge-id prefix (≥6 chars) or the other task's identifier (case-insensitive); `--reverse` flips the direction when the detector guessed it backwards
70
70
  - `lumo task deps dismiss <id> <edge>` — dismiss a candidate (never re-suggested)
71
71
  - `lumo task deps rm <id> <edge> --yes` — delete an edge (refuses without `--yes`)
72
72
  - On an ambiguous/unknown `<edge>` selector the CLI prints all candidates with short ids and exits 1 — retry with one of them
73
73
 
74
- **Acceptance criteria(验收合约)** — see [criteria.md](references/criteria.md)
74
+ **Acceptance criteria (contract)** — see [criteria.md](references/criteria.md)
75
75
 
76
76
  - `lumo task criteria set <task> --file <criteria.json> [--human] [--cause <tag>]` — submit the whole contract: default = initial agent draft (AGENT_DRAFT, locked once submitted); `--human` = a HUMAN_EDIT revision transcribed from the conversation (desired final list; items with `id` keep/update, missing ones are deleted); `--cause` (with `--human`) annotates why the contract drifted: `NEW_INFO | SCOPE_CHANGE | DRAFT_BLIND_SPOT | GRANULARITY | OTHER`
77
77
  - `lumo task criteria list <task>` — print the contract (id, MACHINE/HUMAN, provenance source@round, checkpointer)
78
78
 
79
- **Verification(机器验收循环)** — see [verify.md](references/verify.md)
79
+ **Verification (machine acceptance loop)** — see [verify.md](references/verify.md)
80
80
 
81
81
  - `lumo verify [task] [--timeout <seconds>]` — run every MACHINE criterion's checkpointer locally, report one structured PASS/FAIL verdict per criterion to the server, print next actions. Defaults to the session-bound task. Round cap 3: an all-pass round moves the task to IN_REVIEW (agent stops there); a round-3 fail escalates to a human (stop retrying). **Run this before claiming a task is done.**
82
82
  - `lumo task status [task] [--json]` — read-only acceptance self-check (no LLM, milliseconds): the contract with each criterion's latest verdict (REVIEW_ADDED provenance visible), verification history, current round, last round's failure reasons, and `nextActions` = the unmet criteria (the declarative "what's next" — no separate plan). Defaults to the session-bound task; `--json` emits a versioned payload (`version` field). **Run it first when resuming a task in a new session or after a verification round was rejected.**
@@ -149,10 +149,10 @@ Typical flow when a user says "help me with LUM-42":
149
149
  1. `lumo session attach LUM-42` — bind this session
150
150
  2. `lumo task context LUM-42` — load background
151
151
  3. Review unresolved items, PR-review todos, and the task description
152
- 4. **If the task has no acceptance criteria** (context shows the 草拟提醒 instead of a contract): draft 3–7 outcome-level criteria and submit them with `lumo task criteria set` **before writing the first line of code** — see [criteria.md](references/criteria.md) for the drafting guide
152
+ 4. **If the task has no acceptance criteria** (context shows the draft reminder instead of a contract): draft 3–7 outcome-level criteria and submit them with `lumo task criteria set` **before writing the first line of code** — see [criteria.md](references/criteria.md) for the drafting guide
153
153
  5. Begin working on the task
154
154
  6. **Before claiming the work is done: run `lumo verify`** — the machine half of the acceptance loop. Fix failures and re-run (round cap 3). On all-pass the task moves to IN_REVIEW and you stop; never set DONE yourself after a verify loop — that adjudication is human-only. See [verify.md](references/verify.md)
155
155
 
156
156
  **Status-first recovery:** when you pick a task back up — a new session resuming earlier work, or a task that came back after a rejected verification round / review findings — run `lumo task status` **before** re-reading code or planning. It tells you where the loop stands (current round, what passed, what's unmet and why, any REVIEW_ADDED criteria appended during review) so you don't redo finished work or miss the reason it bounced. See [verify.md](references/verify.md)
157
157
 
158
- **Git-suggest at start:** when the session is unbound, session-start may infer the task from the git branch / recent commits and print a suggestion — `检测到 LUM-N 运行 lumo session attach LUM-N 绑定。` — **without** binding. Confirm it's the right task, then run `lumo session attach <LUM-N>` yourself (binding only happens on an explicit attach). See [sessions.md](references/sessions.md) for the full session-start behavior.
158
+ **Git-suggest at start:** when the session is unbound, session-start may infer the task from the git branch / recent commits and print a suggestion — `Detected LUM-N (from branch name). Run lumo session attach LUM-N to bind.` (or `from recent commits`) — **without** binding. Confirm it's the right task, then run `lumo session attach <LUM-N>` yourself (binding only happens on an explicit attach). See [sessions.md](references/sessions.md) for the full session-start behavior.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### Task ↔ Spec Artifacts
4
4
 
5
- Record Claude Code spec-engineering products (spec / plan / design …) on a task. The artifacts show up in the task detail "规格" (definition) layer and are injected into `lumo task context`.
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
6
 
7
7
  #### `lumo task artifact add <task> --kind <kind> --title <title> --file <path> --source <source> --agent <agent>`
8
8
 
@@ -1,16 +1,17 @@
1
- # Acceptance criteria(验收合约)
1
+ # Acceptance criteria (contract)
2
2
 
3
3
  The acceptance contract is a small set of structured criteria the task's work
4
4
  will be verified against (Acceptance v1, LUM-341/342). The agent drafts it;
5
5
  the server validates and stores it; verification rounds (`lumo verify`,
6
- Slice 1 任务 ) judge against it. Criteria are injected at session start and
7
- in `lumo task context` as the `## 验收标准(合约)` section.
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
8
 
9
9
  ## When to draft — the golden rule
10
10
 
11
11
  **Attach → read context → draft criteria → THEN write the first line of code.**
12
12
 
13
- If `lumo task context` / session-start shows 该任务尚无验收标准 instead of a
13
+ If `lumo task context` / session-start shows the draft reminder ("This task
14
+ has no acceptance criteria yet. …") instead of a
14
15
  contract, you MUST draft and submit criteria before starting implementation:
15
16
 
16
17
  1. Read the task description, comments, linked resources, and memory first —
@@ -38,7 +39,7 @@ to-do list you trim to fit what you built.
38
39
  - `MACHINE` = an executable check exists. It **must** carry a `checkpointer`
39
40
  (the runnable check: a test command, script, probe…). The checkpointer
40
41
  runs on the agent's machine; the structured verdict is reported back to
41
- the server (执行在客户端,裁判在服务端).
42
+ the server (execution on the client, adjudication on the server).
42
43
  - `HUMAN` = needs human judgment (UX feel, copy tone, design fidelity).
43
44
  - **If you can't attach a checkpointer to a MACHINE criterion, demote it to
44
45
  HUMAN** — the server rejects checkpointer-less MACHINE criteria rather
@@ -97,7 +98,7 @@ time, even after the agent lock. The file is the **desired final list**:
97
98
  - existing criteria missing from the file are **deleted** — refused with 409
98
99
  if the criterion already has verification runs (reword it instead)
99
100
 
100
- The revision is auto-recorded as a task comment with the session 出处
101
+ The revision is auto-recorded as a task comment with the session origin
101
102
  (`X-Lumo-Session-Id`). **Transcribing the contract is allowed; transcribing a
102
103
  verdict is never** — human verdicts only enter through human-initiated paths
103
104
  (web UI / Slack action), and the agent has no write path to them.
@@ -123,10 +124,10 @@ before a `--human` revision. Empty contract prints a drafting pointer.
123
124
 
124
125
  - **Session start** (bound task): the contract is the highest-priority
125
126
  section in the injection budget — ahead of memory/PR/Slack/Figma/web. If a
126
- still-open task has no criteria, a 草拟提醒 is injected instead.
127
- - **`lumo session attach`**: prints the contract (or the 草拟提醒) right after
127
+ still-open task has no criteria, the draft reminder is injected instead.
128
+ - **`lumo session attach`**: prints the contract (or the draft reminder) right after
128
129
  binding.
129
- - **`lumo task context`**: the `## 验收标准(合约)` section appears after the
130
+ - **`lumo task context`**: the `## Acceptance criteria (contract)` section appears after the
130
131
  task description, before memory.
131
132
  - Review-time gap findings (`REVIEW_ADDED`, appended at the round they
132
133
  surface) arrive via the verification loop, not via `criteria set`.
@@ -42,7 +42,7 @@ The `Tags:` line is omitted when no tags were attached.
42
42
 
43
43
  ### When to suggest `doc create`
44
44
 
45
- - User says "write a doc", "起一篇 RFC", "新建文档", or describes a deliverable that should live as a document.
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
46
  - After a discussion that needs a write-up, offer to create the doc with a title and the right scope.
47
47
 
48
48
  ### `lumo doc update <doc> [flags]` — update a document
@@ -156,7 +156,7 @@ Moved cmd_xxx "Sub-doc" → root
156
156
 
157
157
  ### When to suggest `doc move`
158
158
 
159
- - User says "move doc X under Y", "把文档 X 挂到 Y 下", "reparent X to root", "promote X to top level".
159
+ - User says "move doc X under Y", "reparent X to root", "promote X to top level".
160
160
  - After `doc create`, if the user realizes the new doc should live elsewhere in the tree — suggest `doc move` rather than recreating.
161
161
 
162
162
  ### `lumo doc delete <doc> --yes` — delete a document
@@ -232,7 +232,7 @@ lumo doc share-list "RFC"
232
232
 
233
233
  ### When to suggest the doc share commands
234
234
 
235
- - User says "share doc X with Y", "give Y access to X", "把文档分享给 Y"
235
+ - User says "share doc X with Y", "give Y access to X"
236
236
  - 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
237
237
  - Before `doc unshare`, run `doc share-list` if the user hasn't named a specific member
238
238
 
@@ -266,7 +266,7 @@ The `Bound ... ↔ LUM-N` line appears only when `--task` is supplied.
266
266
 
267
267
  ### When to suggest `doc import-gdoc`
268
268
 
269
- - User pastes a Google Doc URL or says "import this Google Doc", "pull this gdoc into Lumo", "把这篇 Google 文档导入".
269
+ - User pastes a Google Doc URL or says "import this Google Doc", "pull this gdoc into Lumo".
270
270
  - 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`.
271
271
 
272
272
  ### `lumo doc sync <doc>` — re-sync an imported doc from Google
@@ -290,7 +290,7 @@ Synced cmd_xxx "Quarterly Plan" from Google
290
290
 
291
291
  ### When to suggest `doc sync`
292
292
 
293
- - User says "re-sync the Google Doc", "pull the latest from Google", "refresh the imported doc", "更新一下从 Google 导入的文档".
293
+ - User says "re-sync the Google Doc", "pull the latest from Google", "refresh the imported doc".
294
294
  - After the user mentions the Google Doc changed upstream. Warn first that local Lumo edits to that doc will be overwritten (one-way, destructive).
295
295
 
296
296
  ### Out of scope (CLI v1)
@@ -45,7 +45,7 @@ lumo milestone list --all --search launch # search across archived + non-archi
45
45
 
46
46
  - Before suggesting `task create --milestone <ref>` or `task update --milestone <ref>` to confirm the milestone exists under the expected name.
47
47
  - When the user asks "what milestones do we have", "what's on v1.0", or similar.
48
- - When the user wants to **find/search milestones by keyword** ("find the launch milestone", "搜索里程碑", "which milestones mention X") — use `--search <text>` rather than listing all and eyeballing.
48
+ - When the user wants to **find/search milestones by keyword** ("find the launch milestone", "which milestones mention X") — use `--search <text>` rather than listing all and eyeballing.
49
49
 
50
50
  When to suggest: before `task create --project <ref>` when the workspace has more than one project and the user hasn't specified which one.
51
51
 
@@ -75,7 +75,7 @@ Prints a key:value header (name, status, **health**, dates, project, description
75
75
 
76
76
  It also prints a **Sprint coverage** section (above the task table) listing which
77
77
  sprints the milestone's tasks span — each row shows the sprint number, status, name,
78
- and `done/total` progress — plus an `未排期` line counting milestone tasks not in any
78
+ and `done/total` progress — plus an `Unscheduled` line counting milestone tasks not in any
79
79
  sprint (shown only when there are any). The section is derived from the milestone's
80
80
  tasks (no schema relation), so it needs no extra flags. When no task is in any sprint
81
81
  it prints `Sprint coverage: (no sprint coverage)`.
@@ -86,7 +86,7 @@ Example coverage section:
86
86
  Sprint coverage:
87
87
  #3 ACTIVE Sprint 3 4/6 done
88
88
  #4 DRAFT Sprint 4 0/2 done
89
- 未排期 1 task
89
+ Unscheduled 1 task
90
90
  ```
91
91
 
92
92
  ```bash
@@ -176,7 +176,7 @@ Q3 Launch: 1 removed, 1 skipped
176
176
 
177
177
  ### When to suggest `milestone add` / `milestone remove`
178
178
 
179
- - The user wants to attach/detach **several** tasks to a milestone at once ("挂这几个任务到 Q3", " LUM-1 LUM-2 LUM-3 都放进里程碑", "remove these from the milestone"). For a single task, `task update --milestone <ref>` (or `--milestone ""` to clear) is equally fine.
179
+ - The user wants to attach/detach **several** tasks to a milestone at once ("attach these tasks to Q3", "put LUM-1 LUM-2 LUM-3 into the milestone", "remove these from the milestone"). For a single task, `task update --milestone <ref>` (or `--milestone ""` to clear) is equally fine.
180
180
  - `remove` only clears the binding for tasks actually in the named milestone, so it's safe to pass a broad list — anything not in it is reported as skipped, not clobbered.
181
181
  - For one-at-a-time sprint binding see `lumo sprint add / remove` (sprint batch is not yet supported).
182
182
 
@@ -197,7 +197,7 @@ lumo milestone summary "Q3 Launch" --retry
197
197
  lumo milestone summary 11111111-2222-3333-4444-555555555555
198
198
  ```
199
199
 
200
- When to suggest: user asks "summarize the milestone", "milestone retro", "give me a summary of the Q3 milestone", "里程碑总结", "里程碑复盘".
200
+ When to suggest: user asks "summarize the milestone", "milestone retro", "give me a summary of the Q3 milestone".
201
201
 
202
202
  ### `lumo milestone reorder <ref...> [--project <ref>]` — set the full milestone order
203
203
 
@@ -241,4 +241,4 @@ Both commands resolve to a full ordered list client-side and call the same `PATC
241
241
 
242
242
  **Ambiguous names:** milestone names are not unique within a project (only the slug is). A ref whose name matches more than one milestone is **rejected** with an `ambiguous milestone name … re-run with the id` error listing the candidate cuids — pass the cuid instead. This applies to every name-based milestone ref (`reorder`, `move`, and also `show` / `update` / `delete` / `summary` / `add` / `remove`).
243
243
 
244
- When to suggest: user asks to "reorder milestones", "排序里程碑", "调整里程碑顺序", "move milestone X before/after Y", "把里程碑 X 移到 Y 前面/后面", "put this milestone first".
244
+ When to suggest: user asks to "reorder milestones", "move milestone X before/after Y", "put this milestone first".
@@ -13,7 +13,8 @@ infer the task from local git so it can **suggest** one — it never binds for y
13
13
  `LUM-<n>`.
14
14
  - On a hit it prints a single suggestion line and stops — the session stays
15
15
  **unbound** and no context is injected yet:
16
- `检测到 LUM-145(依据分支名/最近 commit)。运行 lumo session attach LUM-145 绑定。`
16
+ `Detected LUM-145 (from branch name). Run lumo session attach LUM-145 to bind.`
17
+ (the basis reads `from recent commits` when the hit came from commit subjects instead of the branch name)
17
18
  No task title is shown here because nothing was fetched; the title, memory,
18
19
  and PR-review todos appear only once you actually attach.
19
20
  - No match (detached HEAD, a non-lumo branch with no tagged commits, not a git
@@ -27,7 +28,7 @@ just attach the correct task instead.
27
28
 
28
29
  ### Layer 2 project-memory review at session start
29
30
 
30
- When the session is bound, session-start may inject a **"🆕 待核对:上次会话自动合并的项目级记忆"** section alongside the memory / PR-review blocks (LUM-165). It lists the **PROJECT-scope** memories that the member's **immediately-preceding session** auto-consolidated (Layer 2 runs asynchronously when a task is marked `done`). Each item shows its `id`.
31
+ When the session is bound, session-start may inject a **"🆕 Review needed: project memories auto-consolidated by the previous session"** section alongside the memory / PR-review blocks (LUM-165). It lists the **PROJECT-scope** memories that the member's **immediately-preceding session** auto-consolidated (Layer 2 runs asynchronously when a task is marked `done`). Each item shows its `id`.
31
32
 
32
33
  - **Why it's here:** Layer 2 promotions land async, so they can't be reviewed in the synchronous `session wrap` panel — they're surfaced at the _next_ session-start instead, when they've definitely landed.
33
34
  - **Show-once:** the section appears only at the session that immediately follows the one that produced the memories. It does **not** re-nag on later sessions, so act on it now or it scrolls off.
@@ -45,31 +46,31 @@ When the bound task has live blockers or SUGGESTED dependency candidates, the at
45
46
 
46
47
  **Output form A — live blockers exist (with or without SUGGESTED candidates):**
47
48
 
48
- Starts with the `## ⚠ 依赖告警` header, followed by one line per live blocker, then the advice line. If there are also SUGGESTED candidates the candidate-hint line is appended after the advice line.
49
+ Starts with the `## ⚠ Dependency alerts` header, followed by one line per live blocker, then the advice line. If there are also SUGGESTED candidates the candidate-hint line is appended after the advice line.
49
50
 
50
51
  ```
51
- ## ⚠ 依赖告警
52
+ ## ⚠ Dependency alerts
52
53
 
53
- - 本任务被 LUM-9Fix auth token expiry」阻塞(状态 IN_PROGRESS)
54
- - 本任务被 LUM-15Upgrade Postgres driver」阻塞(状态 IN_REVIEW,PR #42 merge)
54
+ - This task is blocked by LUM-9 "Fix auth token expiry" (status IN_PROGRESS).
55
+ - This task is blocked by LUM-15 "Upgrade Postgres driver" (status IN_REVIEW, PR #42 not merged).
55
56
 
56
- 建议先等 blocker 合并再开工,避免白跑 run;如边已过时,用 `lumo task deps rm/dismiss` 清理。
57
- 检测到 3 条候选依赖待确认:运行 `lumo task deps list LUM-42`。
57
+ Consider waiting for the blocker(s) to merge before starting work to avoid a wasted run; if an edge is stale, clean it up with `lumo task deps rm/dismiss`.
58
+ Detected 3 candidate dependencies awaiting confirmation: run `lumo task deps list LUM-42`.
58
59
  ```
59
60
 
60
61
  **Output form B — NO live blockers, but SUGGESTED candidates exist:**
61
62
 
62
- Output is **only** the hint line — no `## ⚠ 依赖告警` header, no advice line. Design rationale: 纯候选是提示不是告警,不戴头,避免稀释真告警。
63
+ Output is **only** the hint line — no `## ⚠ Dependency alerts` header, no advice line. Design rationale: pure candidates are a hint, not an alert — they don't get the header, so real alerts aren't diluted.
63
64
 
64
65
  ```
65
- 检测到 3 条候选依赖待确认:运行 `lumo task deps list LUM-42`。
66
+ Detected 3 candidate dependencies awaiting confirmation: run `lumo task deps list LUM-42`.
66
67
  ```
67
68
 
68
- - Each blocker line shows: identifier, title, current status. If the blocking task has an open pull request, a `,PR #N` note is appended (no space after the comma) — `,PR #N (draft) merge` for draft PRs.
69
- - The guidance line ("建议先等 blocker 合并…") appears only when there is at least one live blocker (form A).
69
+ - Each blocker line shows: identifier, title, current status. If the blocking task has an open pull request, a `, PR #N not merged` note is appended — `, PR #N (draft) not merged` for draft PRs.
70
+ - The guidance line ("Consider waiting for the blocker(s) to merge…") appears only when there is at least one live blocker (form A).
70
71
  - The function never throws — if it fails it silently returns an empty string, leaving the session start unaffected.
71
72
 
72
- **Agent guidance — watch for EITHER the `## ⚠ 依赖告警` header (form A) OR the standalone hint line (form B):**
73
+ **Agent guidance — watch for EITHER the `## ⚠ Dependency alerts` header (form A) OR the standalone hint line (form B):**
73
74
  - **Form A — live blockers:** Evaluate whether to wait. If the blocking task's work overlaps with yours (same files, same API surface), starting immediately risks rework. Read the blocker's status and open PR note before deciding.
74
75
  - **Stale or wrong edge?** Run `lumo task deps list <LUM-N>` to inspect the full edge list, then `lumo task deps rm <LUM-N> <edge> --yes` (if manually added and now obsolete) or `lumo task deps dismiss <LUM-N> <edge>` (if a false positive from detection).
75
76
  - **Candidate hint present (form A or B)?** Run `lumo task deps list <LUM-N>` and review each SUGGESTED edge — confirm real ones, dismiss false positives. Leaving SUGGESTED edges unreviewed means repeated hints every session.
@@ -89,7 +90,7 @@ What it does:
89
90
  - Calls `POST /api/sessions/<session_id>/bind-task` on the Lumo server, which sets the Session row's `taskId` and re-tags previously-untagged HookEvent rows in this session.
90
91
  - The binding lives entirely on the server (`Session.taskId`); subsequent hooks read it back via the session row. The CLI keeps no local sentinel.
91
92
 
92
- - Prints the task's **验收标准(合约)** (acceptance contract, LUM-342) right after the bind confirmation — or, when a still-open task has none, the 草拟提醒 telling you to draft 3–7 criteria before the first line of code (see [criteria.md](criteria.md)). The same section is auto-injected at session start when the session is already bound (highest priority in the injection budget, ahead of memory).
93
+ - Prints the task's **acceptance contract** (`## Acceptance criteria (contract)`, LUM-342) right after the bind confirmation — or, when a still-open task has none, the draft reminder telling you to draft 3–7 criteria before the first line of code (see [criteria.md](criteria.md)). The same section is auto-injected at session start when the session is already bound (highest priority in the injection budget, ahead of memory).
93
94
 
94
95
  **After attaching, always run `lumo task context <identifier>` to load the task background.**
95
96
 
@@ -97,7 +98,7 @@ What it does:
97
98
 
98
99
  Re-attaching a session that's already bound to a **different** task no longer silently clobbers the binding. The server returns the current binding instead of overwriting, and the CLI decides what to do:
99
100
 
100
- - **On a TTY** (a human running it directly): prompts `已绑定 LUM-X,覆盖为 LUM-Y? [y/N]`. Answering `y`/`yes` re-binds to `LUM-Y`; anything else (including bare Enter) cancels and leaves `LUM-X` bound.
101
+ - **On a TTY** (a human running it directly): prompts `Already bound to LUM-X. Rebind to LUM-Y? [y/N]`. Answering `y`/`yes` re-binds to `LUM-Y`; anything else (including bare Enter) cancels and leaves `LUM-X` bound.
101
102
  - **Off a TTY** (the usual agent case — `stdin` is not interactive): the command **refuses** and prints `Session already bound to LUM-X … Re-run with --force …` without overwriting. Exit code is `0` (a safe refusal, not an error).
102
103
  - **`--force`**: skips the prompt/refusal and overwrites unconditionally. Re-attaching to the **same** task is always a no-op re-bind (never prompts).
103
104
 
@@ -136,25 +137,25 @@ When to suggest: the user wants to stop tagging the current session with the act
136
137
 
137
138
  Session-end wrap-up panel with **four sections, run in order**:
138
139
 
139
- **1. 进度评论** — reads back the current Claude Code session's per-turn
140
- `turnSummary` rows (the one-line Chinese summaries written each STOP), aggregates
140
+ **1. Progress comment** — reads back the current Claude Code session's per-turn
141
+ `turnSummary` rows (the one-line summaries written each STOP), aggregates
141
142
  every turn **since the last progress comment** into one bulleted body, and — after
142
- a `[y] 发送 / [e] 编辑 / [s] 跳过` confirmation — posts it as a comment on the
143
+ a `[y] post / [e] edit / [s] skip` confirmation — posts it as a comment on the
143
144
  session's bound task. A server-side watermark (`Session.lastProgressCommentAt`)
144
145
  means re-running never re-posts the same turns.
145
146
 
146
- **2. 记忆审阅** — lists the Layer1 memories this session sedimented since the
147
+ **2. Memory review** — lists the Layer1 memories this session sedimented since the
147
148
  last review (deduped by a per-session watermark `Session.lastMemoryReviewAt`).
148
149
  Each new memory is shown as `[SCOPE] CATEGORY headline`, numbered from 1. You
149
150
  curate with a single line: `d 1,3` deletes rows 1 and 3, `p 2` promotes row 2 to
150
- project scope, and they combine (`d 1,3 p 2`). **回车 (empty) keeps all**; `s`
151
- skips the section. Keeping all (回车 or `--yes`) still **advances the watermark**
151
+ project scope, and they combine (`d 1,3 p 2`). **Enter (empty) keeps all**; `s`
152
+ skips the section. Keeping all (Enter or `--yes`) still **advances the watermark**
152
153
  so the next wrap won't re-list reviewed memories; `s` leaves them for next time.
153
154
  Out-of-range indices are ignored. Deletes/promotes run server-side, scoped to
154
155
  memories this session created (you can't touch other sessions' memories through
155
- this panel). With no new memories the section prints "(无内容)" and does nothing.
156
+ this panel). With no new memories the section prints "(no content)" and does nothing.
156
157
 
157
- **3. 上下文使用投票 (fragment-usage vote, LUM-300)** — lists the context
158
+ **3. Fragment-usage vote (LUM-300)** — lists the context
158
159
  fragments this session **consumed** (its lineage edges: memory / slack / web /
159
160
  figma / PR / review-todo / session), numbered from 1 with a content snippet
160
161
  label. The agent records which it **actually used** via
@@ -166,13 +167,12 @@ candidates and writes nothing** (edges stay `null` = not voted — honest, not
166
167
  upgrades the flywheel signal from "co-loaded" (constant, no information) to
167
168
  "actually used" (varies → discriminative); `task context` then prefers each
168
169
  fragment's usage-based merge rate, falling back to the weaker presence rate when
169
- usage samples are thin. With no consumed fragments the section prints "(无内容)".
170
+ usage samples are thin. With no consumed fragments the section prints "(no content)".
170
171
 
171
- **4. 卡住检测 (blocked-tag prompt, LUM-153)** — if the **same kind of failure
172
+ **4. Blocked check (blocked-tag prompt, LUM-153)** — if the **same kind of failure
172
173
  recurred ≥ 3 times** in this session (server-aggregated from
173
174
  `POST_TOOL_USE_FAILURE` events grouped by tool name, plus `STOP_FAILURE`
174
- turn-level failures), the section surfaces the dominant failure (`卡在 <tool>
175
- (N 次失败)` + last error summary) and prompts `[y] 标记 / [s] 跳过` whether to
175
+ turn-level failures), the section surfaces the dominant failure (`This session looks repeatedly stuck on <tool> (N failures).` + last error summary) and prompts `[y] tag / [s] skip` whether to
176
176
  flag the bound task with a **`blocked` tag**. **Prompt-only — never auto-flips
177
177
  status.** It uses a plain tag (no `TaskStatus` enum, no board column, **no
178
178
  schema migration**). The prompt is **suppressed** when: there's no bound task,
@@ -183,7 +183,7 @@ opt-in), so a stray Enter never tags the task. Confirming with an explicit `y`
183
183
  attaches the tag idempotently. **`--yes` does NOT auto-tag** — tagging the
184
184
  shared board requires an interactive `y`, so `--yes` (and non-TTY) prints the
185
185
  suggestion and moves on rather than silently flipping board state. When there's
186
- nothing to prompt, the section prints "(无内容)".
186
+ nothing to prompt, the section prints "(no content)".
187
187
 
188
188
  ```bash
189
189
  lumo session wrap # interactive: preview each section, choose per-section
@@ -200,8 +200,8 @@ see the numbered fragment list, decide which you actually used, then re-run with
200
200
 
201
201
  - Requires `$CLAUDE_CODE_SESSION_ID` (must run inside Claude Code) and a bound
202
202
  task (`lumo session attach <LUM-N>` first). With no bound task or no new turn
203
- summaries, the 进度评论 section prints "(无内容)" and posts nothing.
204
- - `[e] 编辑` (进度评论) opens `$EDITOR` (fallback vi/nano) on the drafted body;
203
+ summaries, the Progress comment section prints "(no content)" and posts nothing.
204
+ - `[e] edit` (Progress comment) opens `$EDITOR` (fallback vi/nano) on the drafted body;
205
205
  the edited text is posted and the watermark still advances to the turns the
206
206
  draft covered.
207
207
  - `--yes` posts the progress comment AND keeps all memories (no
@@ -36,7 +36,7 @@ lumo sprint create --name "Sprint 4" --start 2026-06-01 --end 2026-06-14 --team
36
36
 
37
37
  On success: `Created sprint #<number> "<name>" <id>`.
38
38
 
39
- When to suggest: user says "create a sprint", "new sprint", "新建冲刺", "start a new iteration".
39
+ When to suggest: user says "create a sprint", "new sprint", "start a new iteration".
40
40
 
41
41
  ### `lumo sprint show <identifier> [--team <ref>]` — show sprint detail + task table
42
42
 
@@ -63,7 +63,7 @@ lumo sprint show 3 --team backend
63
63
  lumo sprint show 11111111-2222-3333-4444-555555555555
64
64
  ```
65
65
 
66
- When to suggest: user asks "what's in sprint 3", "show me the current sprint", "冲刺里有什么", "what tasks are in this sprint", "is this sprint at risk", "冲刺风险", "sprint health".
66
+ When to suggest: user asks "what's in sprint 3", "show me the current sprint", "what tasks are in this sprint", "is this sprint at risk", "sprint health".
67
67
 
68
68
  ### `lumo sprint update <identifier> [flags]` — patch a sprint
69
69
 
@@ -101,7 +101,7 @@ No additional flags. Fails if the sprint is already ACTIVE or CLOSED.
101
101
  lumo sprint start 3
102
102
  ```
103
103
 
104
- When to suggest: user says "start the sprint", "开始冲刺", "kick off sprint 3", "activate sprint".
104
+ When to suggest: user says "start the sprint", "kick off sprint 3", "activate sprint".
105
105
 
106
106
  ### `lumo sprint close <identifier> [flags]` — transition ACTIVE → CLOSED
107
107
 
@@ -119,7 +119,7 @@ lumo sprint close 3 --move-all --yes # move unfinished to next sprint
119
119
  lumo sprint close 3 --backlog-all --yes # send unfinished to backlog
120
120
  ```
121
121
 
122
- When to suggest: user says "close the sprint", "关闭冲刺", "end sprint 3", "wrap up the sprint". If they haven't decided what to do with unfinished tasks, ask before adding `--move-all` or `--backlog-all`.
122
+ When to suggest: user says "close the sprint", "end sprint 3", "wrap up the sprint". If they haven't decided what to do with unfinished tasks, ask before adding `--move-all` or `--backlog-all`.
123
123
 
124
124
  ### `lumo sprint summary <identifier> [--retry]` — fetch AI-generated sprint retro
125
125
 
@@ -134,7 +134,7 @@ lumo sprint summary 3
134
134
  lumo sprint summary 3 --retry
135
135
  ```
136
136
 
137
- When to suggest: user asks "summarize the sprint", "sprint retro", "give me a summary of sprint 3", "冲刺总结".
137
+ When to suggest: user asks "summarize the sprint", "sprint retro", "give me a summary of sprint 3".
138
138
 
139
139
  ### `lumo sprint add <identifier> <task>` — bind a task to a sprint
140
140
 
@@ -144,7 +144,7 @@ Adds `<task>` (e.g. `LUM-48`) to the sprint. Same-team check applies — the tas
144
144
  lumo sprint add 3 LUM-48
145
145
  ```
146
146
 
147
- When to suggest: user says "add LUM-48 to sprint 3", "把任务挂到冲刺", "put this task in the sprint".
147
+ When to suggest: user says "add LUM-48 to sprint 3", "put this task in the sprint".
148
148
 
149
149
  ### `lumo sprint remove <identifier> <task>` — unbind a task from a sprint
150
150
 
@@ -15,25 +15,25 @@ Example: `lumo task context LUM-42`
15
15
  The command prints a markdown document to stdout containing:
16
16
 
17
17
  1. **Task header** — identifier, title, status, description
18
- 2. **验收标准(合约)** — the task's acceptance criteria (LUM-342), shown right after the header. Each line: `[MACHINE|HUMAN] statement` with a `↳ check:` line for MACHINE checkpointers; HUMAN_EDIT / REVIEW_ADDED provenance is tagged inline. If a still-open task has none, a 草拟提醒 appears instead — draft 3–7 criteria **before writing code** (see [criteria.md](criteria.md))
18
+ 2. **Acceptance criteria (contract)** — the task's acceptance criteria (LUM-342), shown right after the header as the `## Acceptance criteria (contract)` section. Each line: `[MACHINE|HUMAN] statement` with a `↳ check:` line for MACHINE checkpointers; HUMAN_EDIT / REVIEW_ADDED provenance is tagged inline. If a still-open task has none, a draft reminder appears instead — draft 3–7 criteria **before writing code** (see [criteria.md](criteria.md))
19
19
  3. **Memory section** — cross-session learnings accumulated over prior sessions; treat as trusted background context
20
20
  4. **Inline source cards** — Slack / web / Figma / artifacts / documents / comments / Pull Requests (see "Context Retrieval" below)
21
- 5. **PR Review 待办** — mirrored PR review comments as a checkbox todo list: each line-level reviewer comment (shown as `` `file:line` `` + the reviewer's ask + a link to the GitHub comment) and each `changes_requested` review summary (shown as "🛑 整体要求改动"). Present only when the task's PR(s) have review comments. This same block is **auto-injected at session start** (alongside the memory section) when the session is bound to a task — so reviewer asks surface without re-running `task context`.
21
+ 5. **PR review todos** — mirrored PR review comments as a checkbox todo list under the `## PR review todos` header: each line-level reviewer comment (shown as `` `file:line` `` + the reviewer's ask + a link to the GitHub comment) and each `changes_requested` review summary (shown as "🛑 Changes requested (whole PR)"). Present only when the task's PR(s) have review comments. This same block is **auto-injected at session start** (alongside the memory section) when the session is bound to a task — so reviewer asks surface without re-running `task context`.
22
22
  - The **inline source cards** (Slack / web / Figma / PR) are likewise **auto-injected at session start** when the session is bound, under a single global token budget shared with the memory section (priority: criteria > memory > PR > Slack > Figma > web). Cards that don't fit the budget are degraded to a one-line manifest carrying just the title and its `lumo task … show` retrieval command — so you still know they exist and can pull the full content on demand.
23
23
  6. **Previous sessions** — ordered newest-first, each with:
24
24
  - A headline summary of what was done
25
25
  - Unresolved items (carry-over TODOs from that session)
26
- 7. **飞轮信号 · 历史 merge 贡献** — appended at the very end. For each context fragment with enough history, a one-line historical merge-contribution signal (e.g. `出现在 5 个已闭合任务·4 merged (80%)`), computed from accumulated lineage edges. Denominator = distinct tasks where the fragment appeared with a resolved (non-UNKNOWN) outcome; only fragments with ≥3 such tasks are shown (cold-start gate), so the block is often absent early on. This is **historical correlation, not causation** — don't read it as a prediction.
26
+ 7. **Flywheel signal · historical merge contribution** — appended at the very end as the `## Flywheel signal · historical merge contribution` section. For each context fragment with enough history, a one-line historical merge-contribution signal (e.g. `appeared in 5 resolved tasks · 4 merged (80%)`), computed from accumulated lineage edges. Denominator = distinct tasks where the fragment appeared with a resolved (non-UNKNOWN) outcome; only fragments with ≥3 such tasks are shown (cold-start gate), so the block is often absent early on. This is **historical correlation, not causation** — don't read it as a prediction.
27
27
 
28
28
  ### How to use the context
29
29
 
30
30
  - **Unresolved items** from the most recent session are the highest-priority carry-overs — address them before starting new work unless the user says otherwise
31
- - **PR Review 待办** items are reviewer-requested changes — treat each unchecked box as a TODO to resolve, then reply on the PR (a Lumo comment mirrors back to GitHub)
31
+ - **PR review todos** items are reviewer-requested changes — treat each unchecked box as a TODO to resolve, then reply on the PR (a Lumo comment mirrors back to GitHub)
32
32
  - **Memory section** provides validated context that persists across sessions — use it to avoid re-learning decisions or constraints
33
33
  - Focus on the **most recent 1–2 sessions** for relevant state; older sessions are for historical reference only
34
34
  - If there are **no prior sessions**, this is a fresh start — read the task description carefully and ask clarifying questions if needed
35
35
 
36
- ## Context Retrieval (按需取全文)
36
+ ## Context Retrieval (full text on demand)
37
37
 
38
38
  LUM-122 split task context injection into tiers. `lumo task context <LUM-N>`
39
39
  now emits a **cheap inline card** for each source — a short summary or just
@@ -199,7 +199,7 @@ Top 3 recommended tasks (of 12 open):
199
199
  Next: lumo session attach LUM-42 && lumo task context LUM-42
200
200
  ```
201
201
 
202
- When to suggest: the user asks "what should I work on", "what's next", "推荐下一个任务", "pick my next task", or starts a session without a task in mind. After they choose, run `session attach` + `task context` for the picked task.
202
+ When to suggest: the user asks "what should I work on", "what's next", "recommend my next task" (in any language), "pick my next task", or starts a session without a task in mind. After they choose, run `session attach` + `task context` for the picked task.
203
203
 
204
204
  ### `lumo task show <identifier>` — print one task's detail
205
205
 
@@ -227,7 +227,7 @@ The CLI does not support @-mention chip syntax. If the user wants to ping someon
227
227
 
228
228
  ### `lumo task deps list <LUM-N>` — show all dependency edges
229
229
 
230
- Prints the task's dependency edges grouped into three sections: **CONFIRMED**, **SUGGESTED(待确认)**, and **DISMISSED**. Each row includes a short 8-character edge id in square brackets, the direction (`blocked by` / `blocks`), the other task's identifier and title, the other task's current status, the source (`MANUAL` or `DETECTED`), and inline evidence for detected edges.
230
+ Prints the task's dependency edges grouped into three sections: **CONFIRMED**, **SUGGESTED (pending confirmation)**, and **DISMISSED**. Each row includes a short 8-character edge id in square brackets, the direction (`blocked by` / `blocks`), the other task's identifier and title, the other task's current status, the source (`MANUAL` or `DETECTED`), and inline evidence for detected edges.
231
231
 
232
232
  ```bash
233
233
  lumo task deps list LUM-42
@@ -239,29 +239,29 @@ Example output:
239
239
  Dependencies for LUM-42 (3)
240
240
 
241
241
  CONFIRMED
242
- [a1b2c3d4] blocked by LUM-9Fix auth token expiry IN_PROGRESS · MANUAL
242
+ [a1b2c3d4] blocked by LUM-9 "Fix auth token expiry" IN_PROGRESS · MANUAL
243
243
 
244
- SUGGESTED(待确认)
245
- [e5f6a7b8] blocks LUM-55Migrate DB schema TODO · shared_files(4 个共享文件: src/db/schema.ts, src/db/migrate.ts, ...)
246
- 确认: lumo task deps confirm LUM-42 e5f6a7b8(方向反了加 --reverse;误报: dismiss)
247
- [c9d0e1f2] blocked by LUM-38Add OAuth scopes IN_REVIEW · task_mention(description)
248
- 确认: lumo task deps confirm LUM-42 c9d0e1f2(方向反了加 --reverse;误报: dismiss)
244
+ SUGGESTED (pending confirmation)
245
+ [e5f6a7b8] blocks LUM-55 "Migrate DB schema" TODO · shared_files(4 shared files: src/db/schema.ts, src/db/migrate.ts, ...)
246
+ confirm: lumo task deps confirm LUM-42 e5f6a7b8 (add --reverse if direction is flipped; false positive: dismiss)
247
+ [c9d0e1f2] blocked by LUM-38 "Add OAuth scopes" IN_REVIEW · task_mention(description)
248
+ confirm: lumo task deps confirm LUM-42 c9d0e1f2 (add --reverse if direction is flipped; false positive: dismiss)
249
249
 
250
250
  DISMISSED
251
- [b3c4d5e6] blocks LUM-12 · 已忽略
251
+ [b3c4d5e6] blocks LUM-12 · dismissed
252
252
  ```
253
253
 
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.
254
+ CONFIRMED and SUGGESTED rows show the other task's identifier, title, current status, and source/evidence. DISMISSED rows render as `[shortId] <direction> <identifier> · dismissed` only — no title, status, or source.
255
255
 
256
256
  When there are no edges at all the output is:
257
257
 
258
258
  ```
259
- Dependencies for LUM-42: 无依赖边。
259
+ Dependencies for LUM-42: no dependency edges.
260
260
  ```
261
261
 
262
262
  **Evidence fields by detection signal:**
263
263
 
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.
264
+ - `shared_files` — `shared_files(N shared files: path1, path2, …)` — number of shared write-touched files in the 14-day window, plus up to 5 sample paths.
265
265
  - `task_mention` — `task_mention(description)` or `task_mention(comment)` — the surface where the mention appeared.
266
266
 
267
267
  CONFIRMED rows also show `source`: `MANUAL` (user-declared via `deps add`) or `DETECTED` (auto-found then confirmed via `deps confirm`).
@@ -314,7 +314,7 @@ lumo task deps dismiss LUM-42 e5f6a7b8
314
314
  lumo task deps dismiss LUM-42 LUM-38
315
315
  ```
316
316
 
317
- Output: `Dismissed: [e5f6a7b8] LUM-38Add OAuth scopes(不再建议)`
317
+ Output: `Dismissed: [e5f6a7b8] LUM-38 "Add OAuth scopes" (won't be suggested again)`
318
318
 
319
319
  Use `dismiss` for false positives. Use `rm` only when you want the pair to be eligible for re-detection in the future (the detection service can re-suggest pairs with no existing row).
320
320
 
@@ -352,6 +352,6 @@ Output: `Removed [a1b2c3d4] from LUM-42`
352
352
 
353
353
  - **After `session attach` output shows a blocker warning or candidate-count hint** → run `lumo task deps list <LUM-N>` to review the full edge list, then `confirm` or `dismiss` each SUGGESTED candidate.
354
354
  - **User says "X needs to wait for Y" or "LUM-42 is blocked by LUM-9"** → run `lumo task deps add LUM-42 --blocked-by LUM-9`.
355
- - **Agent sees a `## ⚠ 依赖告警` block (form A — live blockers) at session-start** → evaluate whether to wait for the blocker to merge before starting work; if the edge is stale or wrong, clean it with `deps rm` or `deps dismiss`.
356
- - **Agent sees only a standalone hint line `检测到 N 条候选依赖待确认…` (form B — no live blockers)** → no immediate blocker, but run `lumo task deps list <LUM-N>` to review and confirm/dismiss SUGGESTED candidates. See [sessions.md](sessions.md) for the full alert format.
355
+ - **Agent sees a `## ⚠ Dependency alerts` block (form A — live blockers) at session-start** → evaluate whether to wait for the blocker to merge before starting work; if the edge is stale or wrong, clean it with `deps rm` or `deps dismiss`.
356
+ - **Agent sees only a standalone hint line `Detected N candidate dependencies awaiting confirmation…` (form B — no live blockers)** → no immediate blocker, but run `lumo task deps list <LUM-N>` to review and confirm/dismiss SUGGESTED candidates. See [sessions.md](sessions.md) for the full alert format.
357
357
  - **User reports a false positive dependency suggestion** → `lumo task deps dismiss <LUM-N> <edge>` to permanently suppress it for this pair.
@@ -1,11 +1,11 @@
1
- # lumo verify — machine verification loop(机器验收循环)
1
+ # lumo verify — machine verification loop
2
2
 
3
3
  `lumo verify` is the machine half of the acceptance system (Acceptance v1,
4
4
  LUM-343). It executes every **MACHINE** criterion's checkpointer in the local
5
5
  repo, reports one structured PASS/FAIL verdict per criterion to the server,
6
6
  and prints what to do next. The judge lives server-side: round numbering, the
7
7
  3-round cap, escalation, and the IN_REVIEW transition all happen there
8
- (执行在客户端,裁判在服务端).
8
+ (execution on the client, adjudication on the server).
9
9
 
10
10
  ## The claim-done rule
11
11
 
@@ -71,7 +71,7 @@ the failures — re-running without changes burns a round and (at round 3)
71
71
  pages a human. A FAIL round never changes task status; only an all-pass round
72
72
  moves it (to IN_REVIEW, never further).
73
73
 
74
- ## lumo task status — the read half(自查入口)
74
+ ## lumo task status — the read half (self-check entry point)
75
75
 
76
76
  `lumo task status [task] [--json]` is the read-only counterpart of the loop
77
77
  (LUM-344): pure read, milliseconds, no LLM, never writes — running it costs
@@ -34,7 +34,7 @@ function sprintCoverageLines(coverage) {
34
34
  return ` ${num} ${status} ${name} ${s.doneCount}/${s.taskCount} done`;
35
35
  });
36
36
  if (coverage.unsprinted > 0) {
37
- const label = '未排期'.padEnd(numW + 2 + statusW + 2 + nameW);
37
+ const label = 'Unscheduled'.padEnd(numW + 2 + statusW + 2 + nameW);
38
38
  rows.push(` ${label} ${coverage.unsprinted} task${coverage.unsprinted === 1 ? '' : 's'}`);
39
39
  }
40
40
  return ['', 'Sprint coverage:', ...rows];
@@ -97,7 +97,7 @@ function formatNextOutput(ranked, totalOpen) {
97
97
  lines.push('');
98
98
  lines.push(`Next: lumo session attach ${first.identifier} && lumo task context ${first.identifier}`);
99
99
  if (ranked.length > 1) {
100
- lines.push('(也可换成列表里任意一个 LUM-N');
100
+ lines.push('(or pick any other LUM-N from the list)');
101
101
  }
102
102
  return lines.join('\n');
103
103
  }
@@ -20,7 +20,7 @@ const line_prompt_1 = require("../lib/line-prompt");
20
20
  * longer silently clobbers `Session.taskId` (LUM-266): the server returns
21
21
  * the current binding and we confirm before overwriting —
22
22
  * - `--force` skips the prompt and overwrites directly;
23
- * - on a TTY we ask `已绑定 LUM-X,覆盖为 LUM-Y? [y/N]`;
23
+ * - on a TTY we ask `Already bound to LUM-X. Rebind to LUM-Y? [y/N]`;
24
24
  * - off a TTY (the usual agent case) we refuse and point at `--force`.
25
25
  */
26
26
  async function sessionAttach(identifier, options = {}) {
@@ -95,9 +95,9 @@ async function sessionAttach(identifier, options = {}) {
95
95
  '(or run `lumo session detach` first).');
96
96
  return 0;
97
97
  }
98
- const answer = await (0, line_prompt_1.promptLine)(`已绑定 ${body.currentTaskIdentifier},覆盖为 ${identifier}? [y/N] `);
98
+ const answer = await (0, line_prompt_1.promptLine)(`Already bound to ${body.currentTaskIdentifier}. Rebind to ${identifier}? [y/N] `);
99
99
  if (!/^y(es)?$/i.test(answer)) {
100
- console.log(`已取消,仍绑定 ${body.currentTaskIdentifier}。`);
100
+ console.log(`Cancelled — still bound to ${body.currentTaskIdentifier}.`);
101
101
  return 0;
102
102
  }
103
103
  const second = await bind(true);
@@ -112,7 +112,8 @@ async function sessionAttach(identifier, options = {}) {
112
112
  }
113
113
  console.log(`Attached session ${sessionId} to ${body.taskIdentifier} "${(0, sanitize_1.sanitizeField)(body.taskTitle)}"`);
114
114
  console.log(`Re-tagged ${body.retaggedEventCount} previously-untagged event${body.retaggedEventCount === 1 ? '' : 's'} in this session.`);
115
- // 告警先于 contract/memory:与 hook 注入顺序一致,短且可操作的信息优先。
115
+ // Warnings come before contract/memory: matches the hook injection order —
116
+ // short, actionable information first.
116
117
  if (body.blockerWarningSection) {
117
118
  console.log('');
118
119
  console.log((0, sanitize_1.sanitizeField)(body.blockerWarningSection));
@@ -99,7 +99,7 @@ async function taskCriteriaSet(identifier, options) {
99
99
  Authorization: `Bearer ${creds.token}`,
100
100
  'Content-Type': 'application/json',
101
101
  };
102
- // Session 出处 for HUMAN_EDIT transcriptions — the server records the
102
+ // Session provenance for HUMAN_EDIT transcriptions — the server records the
103
103
  // revision (with this session id) as a task comment.
104
104
  const sessionId = process.env.CLAUDE_CODE_SESSION_ID;
105
105
  if (sessionId)
@@ -10,7 +10,7 @@ exports.taskDepsRm = taskDepsRm;
10
10
  const config_1 = require("../lib/config");
11
11
  const api_1 = require("../lib/api");
12
12
  const sanitize_1 = require("../lib/sanitize");
13
- /** id = 8 位,展示与 selector 都用它。 */
13
+ /** Short id = first 8 chars; used for both display and selectors. */
14
14
  const shortId = (id) => id.slice(0, 8);
15
15
  /**
16
16
  * Resolve a user-supplied edge selector against the task's edge list. Accepts
@@ -35,7 +35,7 @@ function resolveEdgeSelector(edges, selector) {
35
35
  */
36
36
  function formatDepsList(identifier, edges) {
37
37
  if (edges.length === 0)
38
- return `Dependencies for ${identifier}: 无依赖边。`;
38
+ return `Dependencies for ${identifier}: no dependency edges.`;
39
39
  const lines = [
40
40
  `Dependencies for ${identifier} (${edges.length})`,
41
41
  '',
@@ -43,7 +43,7 @@ function formatDepsList(identifier, edges) {
43
43
  const dirLabel = (e) => e.direction === 'BLOCKED_BY' ? 'blocked by' : 'blocks';
44
44
  const evidence = (e) => {
45
45
  if (e.reason === 'shared_files')
46
- return ` · shared_files(${e.detail?.count ?? '?'} 个共享文件${e.detail?.sample?.length ? ': ' + e.detail.sample.join(', ') : ''})`;
46
+ return ` · shared_files(${e.detail?.count ?? '?'} shared files${e.detail?.sample?.length ? ': ' + e.detail.sample.join(', ') : ''})`;
47
47
  if (e.reason === 'task_mention')
48
48
  return ` · task_mention(${e.detail?.surface ?? ''})`;
49
49
  return '';
@@ -60,14 +60,14 @@ function formatDepsList(identifier, edges) {
60
60
  const suggested = edges.filter(e => e.status === 'SUGGESTED');
61
61
  const dismissed = edges.filter(e => e.status === 'DISMISSED');
62
62
  section('CONFIRMED', confirmed, e => [
63
- ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier}「${e.other.title} ${e.other.status} · ${e.source}${evidence(e)}`,
63
+ ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier} "${e.other.title}" ${e.other.status} · ${e.source}${evidence(e)}`,
64
64
  ]);
65
- section('SUGGESTED(待确认)', suggested, e => [
66
- ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier}「${e.other.title} ${e.other.status}${evidence(e)}`,
67
- ` 确认: lumo task deps confirm ${identifier} ${shortId(e.id)}(方向反了加 --reverse;误报: dismiss)`,
65
+ section('SUGGESTED (pending confirmation)', suggested, e => [
66
+ ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier} "${e.other.title}" ${e.other.status}${evidence(e)}`,
67
+ ` confirm: lumo task deps confirm ${identifier} ${shortId(e.id)} (add --reverse if direction is flipped; false positive: dismiss)`,
68
68
  ]);
69
69
  section('DISMISSED', dismissed, e => [
70
- ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier} · 已忽略`,
70
+ ` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier} · dismissed`,
71
71
  ]);
72
72
  return lines.join('\n').trimEnd();
73
73
  }
@@ -160,11 +160,11 @@ async function fetchDeps(ctx, taskDbId) {
160
160
  function printSelectorCandidates(edges, selector) {
161
161
  console.error(`Error: no unique dependency edge matches "${selector}". Candidates:`);
162
162
  if (edges.length === 0) {
163
- console.error(' (无依赖边)');
163
+ console.error(' (no dependency edges)');
164
164
  return;
165
165
  }
166
166
  for (const e of edges) {
167
- console.error((0, sanitize_1.sanitizeField)(` [${shortId(e.id)}] ${e.status} ${e.other.identifier}「${e.other.title}」`));
167
+ console.error((0, sanitize_1.sanitizeField)(` [${shortId(e.id)}] ${e.status} ${e.other.identifier} "${e.other.title}"`));
168
168
  }
169
169
  }
170
170
  /**
@@ -247,7 +247,7 @@ async function taskDepsConfirm(identifier, selector, opts) {
247
247
  }, 'dependency confirm');
248
248
  if (typeof res === 'number')
249
249
  return res;
250
- console.log((0, sanitize_1.sanitizeField)(`Confirmed: [${shortId(edge.id)}] ${task.identifier} ${edge.direction === 'BLOCKED_BY' ? 'blocked by' : 'blocks'} ${edge.other.identifier}「${edge.other.title}」${opts.reverse ? ' (reversed)' : ''}`));
250
+ console.log((0, sanitize_1.sanitizeField)(`Confirmed: [${shortId(edge.id)}] ${task.identifier} ${edge.direction === 'BLOCKED_BY' ? 'blocked by' : 'blocks'} ${edge.other.identifier} "${edge.other.title}"${opts.reverse ? ' (reversed)' : ''}`));
251
251
  }
252
252
  /** `lumo task deps dismiss <LUM-N> <edge>` */
253
253
  async function taskDepsDismiss(identifier, selector) {
@@ -262,7 +262,7 @@ async function taskDepsDismiss(identifier, selector) {
262
262
  const res = await depsFetch(ctx, `/api/tasks/${encodeURIComponent(task.id)}/dependencies/${encodeURIComponent(edge.id)}`, { method: 'PATCH', body: JSON.stringify({ action: 'dismiss' }) }, 'dependency dismiss');
263
263
  if (typeof res === 'number')
264
264
  return res;
265
- console.log((0, sanitize_1.sanitizeField)(`Dismissed: [${shortId(edge.id)}] ${edge.other.identifier}「${edge.other.title}(不再建议)`));
265
+ console.log((0, sanitize_1.sanitizeField)(`Dismissed: [${shortId(edge.id)}] ${edge.other.identifier} "${edge.other.title}" (won't be suggested again)`));
266
266
  }
267
267
  /** `lumo task deps rm <LUM-N> <edge> --yes` */
268
268
  async function taskDepsRm(identifier, selector, opts) {
@@ -14,7 +14,7 @@ function tail(s, max) {
14
14
  return s.length > max ? `…${s.slice(-max)}` : s;
15
15
  }
16
16
  /**
17
- * Execute one MACHINE checkpointer in the local repo (执行在客户端 — the
17
+ * Execute one MACHINE checkpointer in the local repo (runs client-side — the
18
18
  * server can't run repo tests) and fold the result into a structured verdict.
19
19
  * Exit 0 = PASS with a `cmd:` evidence pointer; non-zero = CRITERION_UNMET;
20
20
  * spawn failure or timeout = CHECK_EXECUTION_ERROR.
@@ -14,7 +14,7 @@ const failure_summary_api_1 = require("../../lib/failure-summary-api");
14
14
  */
15
15
  class BlockedPromptSection {
16
16
  deps;
17
- title = '卡住检测';
17
+ title = 'Blocked check';
18
18
  draft = null;
19
19
  constructor(deps) {
20
20
  this.deps = deps;
@@ -28,14 +28,14 @@ class BlockedPromptSection {
28
28
  if (!draft || !draft.shouldPrompt || !draft.taskIdentifier)
29
29
  return;
30
30
  const top = draft.topFailure;
31
- const where = top ? (0, sanitize_1.sanitizeField)(top.label) : '某个操作';
31
+ const where = top ? (0, sanitize_1.sanitizeField)(top.label) : 'an operation';
32
32
  const count = top ? top.count : 0;
33
- process.stdout.write(`看起来本次会话反复卡在 ${where}(${count} 次失败)。\n`);
33
+ process.stdout.write(`This session looks repeatedly stuck on ${where} (${count} failures).\n`);
34
34
  if (top?.lastErrorSummary) {
35
- process.stdout.write(`最后错误:${(0, sanitize_1.sanitizeField)(top.lastErrorSummary)}\n`);
35
+ process.stdout.write(`Last error: ${(0, sanitize_1.sanitizeField)(top.lastErrorSummary)}\n`);
36
36
  }
37
37
  if (opts.dryRun) {
38
- process.stdout.write(`(dry-run,未改动;确认后会给 ${draft.taskIdentifier} blocked)\n`);
38
+ process.stdout.write(`(dry-run, no changes; confirming would tag ${draft.taskIdentifier} blocked)\n`);
39
39
  return;
40
40
  }
41
41
  // Tagging the shared board is opt-in: it requires an explicit interactive
@@ -43,10 +43,10 @@ class BlockedPromptSection {
43
43
  // does NOT auto-tag — silently flipping shared board state is exactly what
44
44
  // LUM-153 set out to avoid. We surface the suggestion and move on.
45
45
  if (opts.yes) {
46
- process.stdout.write(`(--yes 不自动标记;如确认请交互式回答 y,或手动 \`lumo task update ${draft.taskIdentifier} --add-tag blocked\`)\n`);
46
+ process.stdout.write(`(--yes does not auto-tag; answer y interactively, or run \`lumo task update ${draft.taskIdentifier} --add-tag blocked\` manually)\n`);
47
47
  return;
48
48
  }
49
- const choice = (await (0, line_prompt_1.promptLine)(`要在 ${draft.taskIdentifier} blocked 吗?[y] 标记 [s] 跳过 > `))
49
+ const choice = (await (0, line_prompt_1.promptLine)(`Tag ${draft.taskIdentifier} as blocked? [y] tag [s] skip > `))
50
50
  .trim()
51
51
  .toLowerCase();
52
52
  if (choice === 'y') {
@@ -54,11 +54,11 @@ class BlockedPromptSection {
54
54
  return;
55
55
  }
56
56
  // Empty / 's' / anything else → do nothing. Tagging is opt-in.
57
- process.stdout.write('已跳过,未标记。\n');
57
+ process.stdout.write('Skipped — not tagged.\n');
58
58
  }
59
59
  async mark() {
60
60
  const { taskIdentifier, tag } = await (0, failure_summary_api_1.markTaskBlocked)(this.deps.creds, this.deps.sessionId);
61
- process.stdout.write(`已给 ${taskIdentifier} ${tag}。\n`);
61
+ process.stdout.write(`Tagged ${taskIdentifier} with ${tag}.\n`);
62
62
  }
63
63
  }
64
64
  exports.BlockedPromptSection = BlockedPromptSection;
@@ -24,7 +24,7 @@ function parseUsedHandles(spec) {
24
24
  */
25
25
  class FragmentUsageSection {
26
26
  deps;
27
- title = '上下文使用投票';
27
+ title = 'Fragment-usage vote';
28
28
  draft = null;
29
29
  constructor(deps) {
30
30
  this.deps = deps;
@@ -38,19 +38,19 @@ class FragmentUsageSection {
38
38
  if (!draft || draft.candidates.length === 0)
39
39
  return;
40
40
  if (draft.alreadyVoted) {
41
- process.stdout.write(' session 已投票,跳过。\n');
41
+ process.stdout.write('This session already voted — skipping.\n');
42
42
  return;
43
43
  }
44
- process.stdout.write('本次会话注入的 context fragment:\n');
44
+ process.stdout.write('Context fragments injected in this session:\n');
45
45
  draft.candidates.forEach((c, i) => {
46
46
  process.stdout.write(` [${i + 1}] ${(0, sanitize_1.sanitizeField)(c.label)}\n`);
47
47
  });
48
48
  if (opts.dryRun) {
49
- process.stdout.write('(dry-run,未改动)\n');
49
+ process.stdout.write('(dry-run, no changes)\n');
50
50
  return;
51
51
  }
52
52
  if (this.deps.used === undefined) {
53
- process.stdout.write(' `lumo session wrap --used <序号>`( `--used none`)记录你实际用到的 fragment。\n');
53
+ process.stdout.write('Use `lumo session wrap --used <indices>` (or `--used none`) to record which fragments you actually used.\n');
54
54
  return;
55
55
  }
56
56
  const idx = parseUsedHandles(this.deps.used);
@@ -60,7 +60,7 @@ class FragmentUsageSection {
60
60
  fragmentId: draft.candidates[i].fragmentId,
61
61
  }));
62
62
  const { used, unused } = await (0, fragment_usage_api_1.applyFragmentUsage)(this.deps.creds, this.deps.sessionId, { usedRefs });
63
- process.stdout.write(`已记录:用过 ${used} 个,未用 ${unused} 个。\n`);
63
+ process.stdout.write(`Recorded: ${used} used, ${unused} unused.\n`);
64
64
  }
65
65
  }
66
66
  exports.FragmentUsageSection = FragmentUsageSection;
@@ -32,7 +32,7 @@ function parseReviewInstruction(line) {
32
32
  */
33
33
  class MemoryReviewSection {
34
34
  deps;
35
- title = '记忆审阅';
35
+ title = 'Memory review';
36
36
  draft = null;
37
37
  constructor(deps) {
38
38
  this.deps = deps;
@@ -45,19 +45,19 @@ class MemoryReviewSection {
45
45
  const draft = this.draft;
46
46
  if (!draft || !draft.watermark || draft.memories.length === 0)
47
47
  return;
48
- process.stdout.write(`本次会话新增了这 ${draft.memories.length} memory:\n`);
48
+ process.stdout.write(`This session recorded ${draft.memories.length} new memories:\n`);
49
49
  process.stdout.write(`${(0, sanitize_1.sanitizeField)((0, memory_content_1.formatMemoryReviewList)(draft.memories))}\n`);
50
50
  if (opts.dryRun) {
51
- process.stdout.write('(dry-run,未改动)\n');
51
+ process.stdout.write('(dry-run, no changes)\n');
52
52
  return;
53
53
  }
54
54
  if (opts.yes) {
55
55
  await this.apply(draft.watermark, [], []);
56
56
  return;
57
57
  }
58
- const line = (await (0, line_prompt_1.promptLine)('[回车] 全部保留 [d 1,3] 删除 [p 2] 提升到项目级 [s] 跳过 > ')).trim();
58
+ const line = (await (0, line_prompt_1.promptLine)('[Enter] keep all [d 1,3] delete [p 2] promote to project [s] skip > ')).trim();
59
59
  if (line.toLowerCase() === 's') {
60
- process.stdout.write('已跳过本节。\n');
60
+ process.stdout.write('Section skipped.\n');
61
61
  return;
62
62
  }
63
63
  if (line === '') {
@@ -75,7 +75,7 @@ class MemoryReviewSection {
75
75
  }
76
76
  async apply(watermark, deleteIds, promoteIds) {
77
77
  const { deleted, promoted } = await (0, session_memory_api_1.applyMemoryReview)(this.deps.creds, this.deps.sessionId, { watermark, deleteIds, promoteIds });
78
- process.stdout.write(`已删除 ${deleted} 条,提升 ${promoted} 条到项目级。\n`);
78
+ process.stdout.write(`Deleted ${deleted}, promoted ${promoted} to project scope.\n`);
79
79
  }
80
80
  }
81
81
  exports.MemoryReviewSection = MemoryReviewSection;
@@ -6,7 +6,7 @@ const sanitize_1 = require("../../lib/sanitize");
6
6
  const line_prompt_1 = require("../../lib/line-prompt");
7
7
  const editor_1 = require("../../lib/editor");
8
8
  const progress_comment_api_1 = require("../../lib/progress-comment-api");
9
- const HEADER = '本次会话进度';
9
+ const HEADER = 'Session progress';
10
10
  /** Join turn summaries into a bulleted progress comment body under a header. */
11
11
  function formatProgressBody(summaries) {
12
12
  return [HEADER, ...summaries.map(s => `- ${s}`)].join('\n');
@@ -18,7 +18,7 @@ function formatProgressBody(summaries) {
18
18
  */
19
19
  class ProgressCommentSection {
20
20
  deps;
21
- title = '进度评论';
21
+ title = 'Progress comment';
22
22
  draft = null;
23
23
  body = '';
24
24
  constructor(deps) {
@@ -37,31 +37,31 @@ class ProgressCommentSection {
37
37
  if (!draft || !draft.watermark)
38
38
  return;
39
39
  // Preview: sanitize the server free-text before it hits the terminal.
40
- process.stdout.write(`将发到 ${draft.taskIdentifier} "${(0, sanitize_1.sanitizeField)(draft.taskTitle ?? '')}":\n`);
40
+ process.stdout.write(`Will post to ${draft.taskIdentifier} "${(0, sanitize_1.sanitizeField)(draft.taskTitle ?? '')}":\n`);
41
41
  process.stdout.write(`${(0, sanitize_1.sanitizeField)(this.body)}\n`);
42
42
  if (opts.dryRun) {
43
- process.stdout.write('(dry-run,未发送)\n');
43
+ process.stdout.write('(dry-run, not posted)\n');
44
44
  return;
45
45
  }
46
46
  if (opts.yes) {
47
47
  await this.post(draft.watermark, this.body);
48
48
  return;
49
49
  }
50
- const choice = (await (0, line_prompt_1.promptLine)('[y] 发送 [e] 编辑 [s] 跳过 > ')).toLowerCase();
50
+ const choice = (await (0, line_prompt_1.promptLine)('[y] post [e] edit [s] skip > ')).toLowerCase();
51
51
  if (choice === 's' || choice === '') {
52
- process.stdout.write('已跳过。\n');
52
+ process.stdout.write('Skipped.\n');
53
53
  return;
54
54
  }
55
55
  if (choice === 'e') {
56
56
  const edited = (await (0, editor_1.editInEditor)(this.body)).trim();
57
57
  if (edited.length === 0) {
58
- process.stdout.write('正文为空,已跳过。\n');
58
+ process.stdout.write('Empty body — skipped.\n');
59
59
  return;
60
60
  }
61
61
  process.stdout.write(`${(0, sanitize_1.sanitizeField)(edited)}\n`);
62
- const confirm = (await (0, line_prompt_1.promptLine)('[y] 发送 [s] 跳过 > ')).toLowerCase();
62
+ const confirm = (await (0, line_prompt_1.promptLine)('[y] post [s] skip > ')).toLowerCase();
63
63
  if (confirm !== 'y') {
64
- process.stdout.write('已跳过。\n');
64
+ process.stdout.write('Skipped.\n');
65
65
  return;
66
66
  }
67
67
  await this.post(draft.watermark, edited);
@@ -71,11 +71,11 @@ class ProgressCommentSection {
71
71
  await this.post(draft.watermark, this.body);
72
72
  return;
73
73
  }
74
- process.stdout.write('无法识别的选择,已跳过。\n');
74
+ process.stdout.write('Unrecognized choice — skipped.\n');
75
75
  }
76
76
  async post(watermark, body) {
77
77
  const { commentId } = await (0, progress_comment_api_1.postProgressComment)(this.deps.creds, this.deps.sessionId, { body, watermark });
78
- process.stdout.write(`已发送进度评论 (comment ${commentId})\n`);
78
+ process.stdout.write(`Posted progress comment (comment ${commentId})\n`);
79
79
  }
80
80
  }
81
81
  exports.ProgressCommentSection = ProgressCommentSection;
@@ -396,7 +396,7 @@ taskCriteria
396
396
  .command('set <task>')
397
397
  .description('Submit the whole acceptance contract from a JSON file. Default = initial agent draft (locked once submitted); --human records a HUMAN_EDIT revision (desired final list; items with "id" keep/update, missing ones are deleted).')
398
398
  .requiredOption('--file <path>', 'JSON array of criteria: [{"statement","verifierType":"MACHINE"|"HUMAN","checkpointer?","evidenceRequired?","id?"}]')
399
- .option('--human', 'Record a human contract revision (HUMAN_EDIT) transcribed from the conversation, with session 出处')
399
+ .option('--human', 'Record a human contract revision (HUMAN_EDIT) transcribed from the conversation, with session provenance')
400
400
  .option('--cause <tag>', 'Why the contract drifted (with --human): NEW_INFO | SCOPE_CHANGE | DRAFT_BLIND_SPOT | GRANULARITY | OTHER')
401
401
  .action(wrap((taskId, options) => (0, task_criteria_set_1.taskCriteriaSet)(taskId, options)));
402
402
  taskCriteria
@@ -102,7 +102,7 @@ function formatHookStdoutLines(path, responseBody, now = new Date()) {
102
102
  const sessionId = body.sessionId;
103
103
  const tb = body.taskBinding;
104
104
  if (tb && tb.bound === true) {
105
- lines.push(`[Lumo] session_id=${sessionId} | 当前任务: ${tb.taskIdentifier} - ${(0, sanitize_1.sanitizeField)(tb.taskTitle ?? '')}`);
105
+ lines.push(`[Lumo] session_id=${sessionId} | Current task: ${tb.taskIdentifier} - ${(0, sanitize_1.sanitizeField)(tb.taskTitle ?? '')}`);
106
106
  }
107
107
  else if (tb && tb.bound === false) {
108
108
  lines.push(unboundPromptLine(sessionId));
@@ -135,11 +135,11 @@ function formatHookStdoutLines(path, responseBody, now = new Date()) {
135
135
  * no task could be inferred from local git — the user is asked to name one.
136
136
  */
137
137
  function unboundPromptLine(sessionId) {
138
- return `[Lumo] session_id=${sessionId} | 当前未绑定任务。请告诉我你要处理的任务编号(如 LUM-42),或说"跳过"。`;
138
+ return `[Lumo] session_id=${sessionId} | No task bound. Tell me the task you want to work on (e.g. LUM-42), or say "skip".`;
139
139
  }
140
140
  const MAX_UNRESOLVED = 5;
141
141
  /**
142
- * Render the "续上次进度" recovery card from the structured previousSession
142
+ * Render the "resuming previous session" recovery card from the structured previousSession
143
143
  * payload. Returns undefined when there's nothing to show (null payload or an
144
144
  * empty headline). Free text (headline / unresolved) is sanitized here — it's
145
145
  * LLM-generated and routed to Claude Code stdout. unresolved is capped at
@@ -152,18 +152,18 @@ function renderRecoveryCard(prev, taskIdentifier, now) {
152
152
  const ago = (0, format_1.relativeTime)(new Date(prev.lastActivityAt), now);
153
153
  const dur = (0, format_1.formatDuration)(prev.durationMs);
154
154
  const lines = [
155
- `## 续上次进度 (上一段 session · ${ago} · ${dur})`,
156
- `上次干到:${(0, sanitize_1.sanitizeField)(prev.headline)}`,
155
+ `## Resuming previous session (${ago} · ${dur})`,
156
+ `Last stopped at: ${(0, sanitize_1.sanitizeField)(prev.headline)}`,
157
157
  ];
158
158
  const unresolved = Array.isArray(prev.unresolved) ? prev.unresolved : [];
159
159
  if (unresolved.length > 0) {
160
- lines.push('未完成:');
160
+ lines.push('Unfinished:');
161
161
  const shown = unresolved.slice(0, MAX_UNRESOLVED);
162
162
  for (const u of shown)
163
163
  lines.push(`- ${(0, sanitize_1.sanitizeField)(u)}`);
164
164
  const extra = unresolved.length - shown.length;
165
165
  if (extra > 0) {
166
- lines.push(`- …(+${extra} more, lumo task context ${taskIdentifier} 查看全部)`);
166
+ lines.push(`- … (+${extra} more run \`lumo task context ${taskIdentifier}\` for the full list)`);
167
167
  }
168
168
  }
169
169
  return lines.join('\n');
@@ -193,8 +193,8 @@ function sessionContextEnvelope(parts) {
193
193
  * context surface only once the user actually attaches.
194
194
  */
195
195
  function formatSuggestLine(sessionId, match) {
196
- const basis = match.source === 'branch' ? '分支名' : '最近 commit';
197
- return `[Lumo] session_id=${sessionId} | 检测到 ${match.identifier}(依据${basis})。运行 lumo session attach ${match.identifier} 绑定。`;
196
+ const basis = match.source === 'branch' ? 'branch name' : 'recent commits';
197
+ return `[Lumo] session_id=${sessionId} | Detected ${match.identifier} (from ${basis}). Run \`lumo session attach ${match.identifier}\` to bind.`;
198
198
  }
199
199
  /**
200
200
  * Build the stdout lines for a session-start response. When the server reports
@@ -7,7 +7,7 @@ async function runWrapPanel(sections, opts) {
7
7
  process.stdout.write(`\n━━ ${section.title} ━━\n`);
8
8
  const hasContent = await section.prepare();
9
9
  if (!hasContent) {
10
- process.stdout.write('(无内容)\n');
10
+ process.stdout.write('(no content)\n');
11
11
  continue;
12
12
  }
13
13
  await section.run(opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumoai/cli",
3
- "version": "1.25.1",
3
+ "version": "1.26.0",
4
4
  "description": "Lumo CLI — manage tasks and sessions from the terminal",
5
5
  "license": "MIT",
6
6
  "author": "cli@uselumo.ai",