@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.
- package/assets/skill/SKILL.md +7 -7
- package/assets/skill/references/artifacts-figma.md +1 -1
- package/assets/skill/references/criteria.md +10 -9
- package/assets/skill/references/docs.md +5 -5
- package/assets/skill/references/milestones.md +6 -6
- package/assets/skill/references/sessions.md +30 -30
- package/assets/skill/references/sprints.md +6 -6
- package/assets/skill/references/task-context.md +5 -5
- package/assets/skill/references/tasks.md +15 -15
- package/assets/skill/references/verify.md +3 -3
- package/dist/cli/src/commands/milestone-show.js +1 -1
- package/dist/cli/src/commands/next.js +1 -1
- package/dist/cli/src/commands/session-attach.js +5 -4
- package/dist/cli/src/commands/task-criteria-set.js +1 -1
- package/dist/cli/src/commands/task-deps.js +12 -12
- package/dist/cli/src/commands/verify.js +1 -1
- package/dist/cli/src/commands/wrap/blocked-prompt-section.js +9 -9
- package/dist/cli/src/commands/wrap/fragment-usage-section.js +6 -6
- package/dist/cli/src/commands/wrap/memory-review-section.js +6 -6
- package/dist/cli/src/commands/wrap/progress-comment-section.js +11 -11
- package/dist/cli/src/index.js +1 -1
- package/dist/cli/src/lib/hook-runner.js +9 -9
- package/dist/cli/src/lib/wrap-panel.js +1 -1
- package/package.json +1 -1
package/assets/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lumo
|
|
3
|
-
description: 'Use the Lumo CLI to 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", "
|
|
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,
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 —
|
|
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 "
|
|
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
|
|
7
|
-
in `lumo task context` as the `##
|
|
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
|
|
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,
|
|
127
|
-
- **`lumo session attach`**: prints the contract (or the
|
|
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 `##
|
|
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", "
|
|
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", "
|
|
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"
|
|
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"
|
|
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"
|
|
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", "
|
|
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
|
|
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
|
-
|
|
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 ("
|
|
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", "
|
|
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
|
-
|
|
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 **"🆕
|
|
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 `## ⚠
|
|
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
|
-
-
|
|
54
|
-
-
|
|
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
|
-
|
|
57
|
-
|
|
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 `## ⚠
|
|
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
|
-
|
|
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
|
|
69
|
-
- The guidance line ("
|
|
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 `## ⚠
|
|
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
|
|
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
|
|
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.
|
|
140
|
-
`turnSummary` rows (the one-line
|
|
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]
|
|
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.
|
|
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`).
|
|
151
|
-
skips the section. Keeping all (
|
|
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 "(
|
|
156
|
+
this panel). With no new memories the section prints "(no content)" and does nothing.
|
|
156
157
|
|
|
157
|
-
**3.
|
|
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.
|
|
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 (
|
|
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
|
|
204
|
-
- `[e]
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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", "
|
|
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(
|
|
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-9
|
|
242
|
+
[a1b2c3d4] blocked by LUM-9 "Fix auth token expiry" IN_PROGRESS · MANUAL
|
|
243
243
|
|
|
244
|
-
SUGGESTED(
|
|
245
|
-
[e5f6a7b8] blocks LUM-55
|
|
246
|
-
|
|
247
|
-
[c9d0e1f2] blocked by LUM-38
|
|
248
|
-
|
|
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> ·
|
|
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
|
|
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-38
|
|
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 `## ⚠
|
|
356
|
-
- **Agent sees only a standalone hint line
|
|
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 = '
|
|
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('
|
|
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
|
|
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)(
|
|
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(
|
|
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
|
-
//
|
|
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
|
|
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
|
-
/**
|
|
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 ?? '?'}
|
|
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}
|
|
63
|
+
` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier} "${e.other.title}" ${e.other.status} · ${e.source}${evidence(e)}`,
|
|
64
64
|
]);
|
|
65
|
-
section('SUGGESTED(
|
|
66
|
-
` [${shortId(e.id)}] ${dirLabel(e)} ${e.other.identifier}
|
|
67
|
-
`
|
|
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}
|
|
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}
|
|
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}
|
|
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 (
|
|
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(
|
|
33
|
+
process.stdout.write(`This session looks repeatedly stuck on ${where} (${count} failures).\n`);
|
|
34
34
|
if (top?.lastErrorSummary) {
|
|
35
|
-
process.stdout.write(
|
|
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
|
|
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
|
|
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)(
|
|
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('
|
|
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(
|
|
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('
|
|
41
|
+
process.stdout.write('This session already voted — skipping.\n');
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
process.stdout.write('
|
|
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
|
|
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('
|
|
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(
|
|
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(
|
|
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
|
|
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)('[
|
|
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('
|
|
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(
|
|
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(
|
|
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
|
|
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]
|
|
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('
|
|
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('
|
|
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]
|
|
62
|
+
const confirm = (await (0, line_prompt_1.promptLine)('[y] post [s] skip > ')).toLowerCase();
|
|
63
63
|
if (confirm !== 'y') {
|
|
64
|
-
process.stdout.write('
|
|
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('
|
|
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(
|
|
78
|
+
process.stdout.write(`Posted progress comment (comment ${commentId})\n`);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
exports.ProgressCommentSection = ProgressCommentSection;
|
package/dist/cli/src/index.js
CHANGED
|
@@ -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} |
|
|
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} |
|
|
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 "
|
|
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
|
-
`##
|
|
156
|
-
|
|
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
|
|
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' ? '
|
|
197
|
-
return `[Lumo] session_id=${sessionId} |
|
|
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('(
|
|
10
|
+
process.stdout.write('(no content)\n');
|
|
11
11
|
continue;
|
|
12
12
|
}
|
|
13
13
|
await section.run(opts);
|