@lumoai/cli 1.56.0 → 1.57.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 +2 -2
- package/assets/skill/references/criteria.md +1 -1
- package/assets/skill/references/verify.md +18 -13
- package/assets/skill/references/worktree.md +19 -5
- package/dist/cli/src/commands/worktree-add.js +10 -2
- package/dist/cli/src/commands/worktree-list.js +1 -1
- package/dist/cli/src/commands/worktree-rm.js +10 -2
- package/dist/cli/src/lib/worktree-ref.js +53 -0
- package/dist/cli/src/lib/worktree.js +13 -11
- package/package.json +1 -1
package/assets/skill/SKILL.md
CHANGED
|
@@ -158,7 +158,7 @@ The command catalog below is a **map**: it lists every command grouped by domain
|
|
|
158
158
|
|
|
159
159
|
**Verification (machine acceptance loop)** — see [verify.md](references/verify.md)
|
|
160
160
|
|
|
161
|
-
- `lumo verify [task] --note "<self-report>" [--timeout <seconds>]` — run every MACHINE criterion's checkpointer locally and report a structured PASS/FAIL verdict per criterion. **`--note` is required** when a round will be posted: a one-line self-report ("我改了 X,因为 Y 可验收") frozen as your task's claim (source AGENT) the moment the round passes into IN_REVIEW, then checked against the diff for faithfulness. No note → no round posted (none burned).
|
|
161
|
+
- `lumo verify [task] --note "<self-report>" [--timeout <seconds>]` — run every MACHINE criterion's checkpointer locally and report a structured PASS/FAIL verdict per criterion. **`--note` is required** when a round will be posted: a one-line self-report ("我改了 X,因为 Y 可验收") frozen as your task's claim (source AGENT) the moment the round passes into IN_REVIEW, then checked against the diff for faithfulness. No note → no round posted (none burned). The round cap is the workspace's `verificationMaxRounds` (default 3) — read the actual budget off the `Round N/M` line the command prints, never assume a number. All-pass → task to IN_REVIEW (agent stops); a fail on the last round → escalate to human. **Run this before claiming a task is done.**
|
|
162
162
|
- `lumo task status [task] [--json]` — read-only acceptance self-check (no LLM): the contract with each criterion's latest verdict, verification history/round, last failure reasons, `nextActions` (the unmet criteria), and any OPEN boundary crossings (fails closed — `null`/`⚠` means "could not confirm", not safe). **Run it first when resuming a task or after a round was rejected.**
|
|
163
163
|
- `lumo verdict [task] --pass | --fail` — acceptance verdicts. `--pass` deep-links to the human verdict bar (records nothing). `--fail --reason <enum> …` records an AGENT send-back → IN_PROGRESS. **An unresolved send-back blocks the DONE transition with 409.**
|
|
164
164
|
- `lumo crossing explain <id> --note "<text>"` — append an agent self-explanation ("申辩") to a boundary crossing; append-only, **never clears the crossing or unblocks Done** (disposition stays web + human-only). A review aid, not a self-clear.
|
|
@@ -302,7 +302,7 @@ Don't guess these:
|
|
|
302
302
|
2. `lumo task context LUM-42` — load background; review unresolved items, PR-review todos, the description
|
|
303
303
|
3. **If the task has no acceptance criteria** (context shows the draft reminder, not a contract): draft outcome-level criteria sized to the task (3–7 for a typical multi-file task; 1–2 for a micro task) and submit them with `lumo task criteria set` **before writing the first line of code** — see [criteria.md](references/criteria.md)
|
|
304
304
|
4. Do the work
|
|
305
|
-
5. **Before claiming done: `lumo verify`** — the machine half of the acceptance loop. Fix failures and re-run (
|
|
305
|
+
5. **Before claiming done: `lumo verify`** — the machine half of the acceptance loop. Fix failures and re-run (the cap is workspace-configurable — see the `Round N/M` line in the output). All-pass → task moves to IN_REVIEW and you stop. See [verify.md](references/verify.md)
|
|
306
306
|
|
|
307
307
|
**Resuming / after a send-back** — `lumo task status` **before** re-reading code or planning. It tells you where the loop stands (round, what passed, what's unmet and why, any REVIEW_ADDED criteria from review) so you don't redo finished work or miss why it bounced. A send-back means **fix in place / amend the contract — do not spin off a new task.** See [verify.md](references/verify.md)
|
|
308
308
|
|
|
@@ -219,4 +219,4 @@ lumo task criteria list LUM-42
|
|
|
219
219
|
|
|
220
220
|
## After the contract: the verification loop
|
|
221
221
|
|
|
222
|
-
The contract is judged by `lumo verify` — run it before claiming the task is done. See [verify.md](verify.md) for the loop (round cap 3, IN_REVIEW on all-pass, escalation on a round
|
|
222
|
+
The contract is judged by `lumo verify` — run it before claiming the task is done. See [verify.md](verify.md) for the loop (a workspace-configurable round cap — `verificationMaxRounds`, default 3, shown as `Round N/M` in the output — IN_REVIEW on all-pass, escalation on a last-round fail).
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
`lumo verify` is the machine half of the acceptance system: it executes every **MACHINE** criterion's checkpointer in the local
|
|
4
4
|
repo, POSTs one structured PASS/FAIL verdict per criterion, and prints what to
|
|
5
5
|
do next. Execution is on the client; adjudication is server-side — round
|
|
6
|
-
numbering, the **
|
|
7
|
-
happen on the server.
|
|
6
|
+
numbering, the **round cap**, escalation, and the **IN_REVIEW** transition all
|
|
7
|
+
happen on the server. The cap is **not a universal constant** — it is the
|
|
8
|
+
workspace-level `verificationMaxRounds` (default 3); the effective value for
|
|
9
|
+
_this_ workspace is the `M` in the `Round N/M` line that `lumo verify` and
|
|
10
|
+
`lumo task status` print.
|
|
8
11
|
|
|
9
12
|
## The claim-done rule
|
|
10
13
|
|
|
@@ -39,11 +42,11 @@ still verifies — the claim degrades to the synthesized run-summary fallback.)
|
|
|
39
42
|
TaskActivity event.
|
|
40
43
|
4. Prints the round outcome:
|
|
41
44
|
|
|
42
|
-
| Round outcome
|
|
43
|
-
|
|
|
44
|
-
| **All PASS**
|
|
45
|
-
| **Any FAIL**
|
|
46
|
-
| **
|
|
45
|
+
| Round outcome | Effect | What to do |
|
|
46
|
+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
47
|
+
| **All PASS** | Task transitions to **IN_REVIEW** (existing state machine + TASK_IN_REVIEW notification); the `--note` self-report is frozen as the task's claim (source `AGENT`) | **Stop here.** Human adjudication + any HUMAN criteria take over; **never set DONE yourself** |
|
|
48
|
+
| **Any FAIL** | Task status untouched; unmet criteria printed as next actions (statement, checkpointer, failure tail) | Fix and re-run |
|
|
49
|
+
| **Last round still failing** (round = the cap `M`) | Loop escalates: a human is notified (AGENT_VERIFY, requires action); further `lumo verify` rounds are rejected with **409** | **Stop retrying**; fix only what the human directs |
|
|
47
50
|
|
|
48
51
|
Exit code 0 = all passed (or nothing to run); 1 = failures, escalation, or
|
|
49
52
|
errors.
|
|
@@ -74,9 +77,10 @@ errors.
|
|
|
74
77
|
|
|
75
78
|
## Round discipline
|
|
76
79
|
|
|
77
|
-
Rounds are a hard budget
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
Rounds are a hard budget, not a retry loop — and the size of that budget is a
|
|
81
|
+
workspace setting (`verificationMaxRounds`, default 3), so check `Round N/M`
|
|
82
|
+
instead of assuming three. Between rounds, actually fix the failures —
|
|
83
|
+
re-running without changes burns a round and (on the last one) pages a human. **A FAIL round never changes task status; only an all-pass round moves it
|
|
80
84
|
(to IN_REVIEW, never further).**
|
|
81
85
|
|
|
82
86
|
## Review-time drift habits (gap findings)
|
|
@@ -120,7 +124,7 @@ what's unmet and why (the exact failure tails), and how many rounds are left.
|
|
|
120
124
|
|
|
121
125
|
### What it prints
|
|
122
126
|
|
|
123
|
-
- **Header** — task identifier/title/status + `verification round N/
|
|
127
|
+
- **Header** — task identifier/title/status + `verification round N/M` (`M` = the workspace's `verificationMaxRounds`; round 0 = never verified) + an escalation warning when the machine loop is exhausted.
|
|
124
128
|
- **Claim vs verification** — 规律 2 声称vs核验: the headline contrast, printed right after the header (whenever the contract exists), so the report shows **both** columns instead of only the verification one. Two sides:
|
|
125
129
|
- **Claim** — what the agent _says_ it did: an **unverified self-report** (`agent self-report · estimated, not verification`), estimate-tier provenance (估, not 测). Sourced by layering (LUM-597, preference `AGENT > RUN_SUMMARY > DIGEST > null`): if you supplied a `lumo verify --note`, that **own self-report** is the claim (`↳ source: agent self-report (verify --note)`) — the汇报者's voice, what faithfulness judges. Absent a self-report (old CLI / non-verify path) it degrades to the summarizer paraphrase, labelled honestly (`↳ source: synthesized run summary (no self-report)`). **Fail-closed**: when only a raw STOP turn digest exists it prints `generating — the formal run summary is still being synthesized` and **withholds the raw digest**; with no material at all it prints `not generated yet — …`, never a fabricated claim.
|
|
126
130
|
- **Verification** — what was actually _confirmed_ (measured): the machine-verification rollup `N machine-verified / M human override (of T MACHINE criteria)` over the active MACHINE criteria (relocated here from its old standalone line under `Criteria`), plus `X of Y criteria met by their latest verdict`. **Fail-closed**: before any round runs it prints `no verification has run yet — the claim is unconfirmed` rather than implying a pass.
|
|
@@ -179,8 +183,9 @@ fields don't. Pin on `version` when scripting against it.
|
|
|
179
183
|
- **`openCrossings` is `null` when the crossings check failed** — distinct from `[]` (a successful read with zero open crossings). Script consumers must treat `null` as "unknown / could not confirm", **not** "safe".
|
|
180
184
|
|
|
181
185
|
`status` reads; `verify` judges. Running status never starts a round, never
|
|
182
|
-
escalates, and never changes task state — loop rules (
|
|
183
|
-
all-pass, human-only DONE) live entirely in `lumo verify` and the
|
|
186
|
+
escalates, and never changes task state — loop rules (the workspace round cap,
|
|
187
|
+
IN_REVIEW on all-pass, human-only DONE) live entirely in `lumo verify` and the
|
|
188
|
+
server.
|
|
184
189
|
|
|
185
190
|
## lumo verdict — the three verdict channels
|
|
186
191
|
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
# Worktrees
|
|
2
2
|
|
|
3
|
-
Local dev tooling that scaffolds parallel git worktrees.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Local dev tooling that scaffolds parallel git worktrees. These are almost
|
|
4
|
+
entirely local — they shell out to git and the local filesystem; the **only**
|
|
5
|
+
server call is a single `GET /api/teams` to expand a **bare number** to your
|
|
6
|
+
default team prefix (see "Accepted identifier forms" below), and even that is
|
|
7
|
+
skipped when you pass an explicit `<TEAM>-N`. **Run `add` and `rm` from the main
|
|
8
|
+
checkout**, not from inside a worktree (the node_modules symlink source and
|
|
9
|
+
`.worktrees/` both live there); `list` is read-only and runnable from anywhere.
|
|
10
|
+
|
|
11
|
+
### Accepted identifier forms
|
|
12
|
+
|
|
13
|
+
The `<id>` argument to `add`/`rm` is a task id with **any team prefix**, not
|
|
14
|
+
just `LUM-` (the `<LUM-N>` in the examples below is just a placeholder):
|
|
15
|
+
|
|
16
|
+
- `LUM-267`, `SPEC-12`, `spec-12` — an explicit `<TEAM>-<n>` (1–10 letters,
|
|
17
|
+
case-insensitive; the prefix is upper-cased). Resolved locally, no network.
|
|
18
|
+
- `267` — a **bare number**: expanded to the workspace's **default team**
|
|
19
|
+
prefix via `GET /api/teams` (or the sole team in a single-team workspace).
|
|
20
|
+
In a multi-team workspace with no default, this errors and asks for an
|
|
21
|
+
explicit prefix; the same happens when you're offline / not logged in.
|
|
8
22
|
|
|
9
23
|
## `lumo worktree add <LUM-N> [slug]`
|
|
10
24
|
|
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const child_process_1 = require("child_process");
|
|
40
40
|
const next_steps_1 = require("../lib/next-steps");
|
|
41
41
|
const worktree_1 = require("../lib/worktree");
|
|
42
|
+
const worktree_ref_1 = require("../lib/worktree-ref");
|
|
42
43
|
function printGuidance(dir, branch) {
|
|
43
44
|
console.log('');
|
|
44
45
|
console.log(`✓ Worktree ready: ${dir}`);
|
|
@@ -59,9 +60,16 @@ function printGuidance(dir, branch) {
|
|
|
59
60
|
console.log(' cli/package.json haste collision silently runs the wrong tests.');
|
|
60
61
|
}
|
|
61
62
|
async function worktreeAdd(rawId, slug, opts) {
|
|
62
|
-
|
|
63
|
+
let taskId;
|
|
64
|
+
try {
|
|
65
|
+
taskId = await (0, worktree_ref_1.resolveWorktreeTaskId)(rawId);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
63
71
|
if (!taskId) {
|
|
64
|
-
console.error(`Error: "${rawId}" is not a task id (expected LUM-
|
|
72
|
+
console.error(`Error: "${rawId}" is not a task id (expected <TEAM>-N like LUM-42 or SPEC-12, or a bare number)`);
|
|
65
73
|
return 1;
|
|
66
74
|
}
|
|
67
75
|
if (!(0, worktree_1.isMainCheckout)()) {
|
|
@@ -46,7 +46,7 @@ function nodeModulesLinked(dir) {
|
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
/** Pull a
|
|
49
|
+
/** Pull a `<TEAM>-N` id from a worktree dir basename or its branch, else '—'. */
|
|
50
50
|
function taskIdOf(dirBase, branch) {
|
|
51
51
|
return ((0, git_task_1.matchTaskIdentifier)(dirBase) ??
|
|
52
52
|
(branch ? (0, git_task_1.matchTaskIdentifier)(branch) : null) ??
|
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.worktreeRm = worktreeRm;
|
|
4
4
|
const worktree_1 = require("../lib/worktree");
|
|
5
|
+
const worktree_ref_1 = require("../lib/worktree-ref");
|
|
5
6
|
async function worktreeRm(rawId, opts) {
|
|
6
|
-
|
|
7
|
+
let taskId;
|
|
8
|
+
try {
|
|
9
|
+
taskId = await (0, worktree_ref_1.resolveWorktreeTaskId)(rawId);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
7
15
|
if (!taskId) {
|
|
8
|
-
console.error(`Error: "${rawId}" is not a task id (expected LUM-
|
|
16
|
+
console.error(`Error: "${rawId}" is not a task id (expected <TEAM>-N like LUM-42 or SPEC-12, or a bare number)`);
|
|
9
17
|
return 1;
|
|
10
18
|
}
|
|
11
19
|
if (!(0, worktree_1.isMainCheckout)()) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveWorktreeTaskId = resolveWorktreeTaskId;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const api_1 = require("./api");
|
|
6
|
+
const worktree_1 = require("./worktree");
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a user-supplied worktree ref to a canonical `<TEAM>-<n>` id.
|
|
9
|
+
*
|
|
10
|
+
* - Explicit prefix (`SPEC-12`, `lum-267`) → normalized locally, NO network.
|
|
11
|
+
* - Bare number (`267`) → completed with the workspace's DEFAULT team prefix
|
|
12
|
+
* (GET /api/teams: the `isDefault` team, or the sole team in a single-team
|
|
13
|
+
* workspace). Worktree tooling is otherwise purely local; the API is touched
|
|
14
|
+
* only to disambiguate a prefix-less number.
|
|
15
|
+
*
|
|
16
|
+
* Returns null when `raw` is not a task-id shape at all (caller prints the
|
|
17
|
+
* "expected <TEAM>-N" usage error). Throws Error with a user-facing message
|
|
18
|
+
* when a bare number needs the default team but it can't be determined
|
|
19
|
+
* (not logged in / offline / ambiguous multi-team without a default) — every
|
|
20
|
+
* message points the user at passing an explicit prefix instead.
|
|
21
|
+
*/
|
|
22
|
+
async function resolveWorktreeTaskId(raw) {
|
|
23
|
+
const explicit = (0, worktree_1.normalizeTaskId)(raw);
|
|
24
|
+
if (explicit)
|
|
25
|
+
return explicit;
|
|
26
|
+
const bare = raw.trim().match(/^(\d+)$/);
|
|
27
|
+
if (!bare)
|
|
28
|
+
return null;
|
|
29
|
+
const n = bare[1];
|
|
30
|
+
const creds = (0, config_1.readCredentials)();
|
|
31
|
+
if (!creds) {
|
|
32
|
+
throw new Error(`a bare number needs your default team prefix — run \`lumo auth login\`, or pass an explicit id like LUM-${n}`);
|
|
33
|
+
}
|
|
34
|
+
const base = (0, api_1.trimTrailingSlash)((0, api_1.resolveAuthedApiUrl)(creds.apiUrl));
|
|
35
|
+
let teams;
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(`${base}/api/teams`, {
|
|
38
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok)
|
|
41
|
+
throw new Error(`HTTP ${res.status}`);
|
|
42
|
+
({ teams } = (await res.json()));
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
throw new Error(`could not resolve the default team for bare id "${n}" (${msg}) — pass an explicit id like LUM-${n}`);
|
|
47
|
+
}
|
|
48
|
+
const def = teams.find(t => t.isDefault) ?? (teams.length === 1 ? teams[0] : undefined);
|
|
49
|
+
if (!def) {
|
|
50
|
+
throw new Error(`workspace has multiple teams and no default — pass an explicit id like SPEC-${n} instead of a bare number`);
|
|
51
|
+
}
|
|
52
|
+
return `${def.identifier.toUpperCase()}-${n}`;
|
|
53
|
+
}
|
|
@@ -46,19 +46,21 @@ exports.getGitCommonDir = getGitCommonDir;
|
|
|
46
46
|
exports.isMainCheckout = isMainCheckout;
|
|
47
47
|
exports.getRepoRoot = getRepoRoot;
|
|
48
48
|
/**
|
|
49
|
-
* Normalize a user-supplied task reference to canonical
|
|
50
|
-
* Accepts `LUM-267`, `
|
|
51
|
-
*
|
|
49
|
+
* Normalize a user-supplied task reference to canonical `<TEAM>-<n>` form.
|
|
50
|
+
* Accepts any team prefix (`LUM-267`, `SPEC-12`, `spec-12`), not just LUM —
|
|
51
|
+
* the prefix is a team's configured identifier (1–10 letters), matching the
|
|
52
|
+
* single-sourced shape in shared/src/task-identifier.ts (LUM-419). The prefix
|
|
53
|
+
* is upper-cased; the number is preserved.
|
|
54
|
+
*
|
|
55
|
+
* A bare number (`267`) returns null here: without team context the prefix
|
|
56
|
+
* cannot be inferred locally. Team-aware bare-number resolution (via the
|
|
57
|
+
* workspace's default team) lives in `resolveWorktreeTaskId` (worktree-ref.ts).
|
|
52
58
|
*/
|
|
53
59
|
function normalizeTaskId(input) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const bare = trimmed.match(/^(\d+)$/);
|
|
59
|
-
if (bare)
|
|
60
|
-
return `LUM-${bare[1]}`;
|
|
61
|
-
return null;
|
|
60
|
+
const m = input.trim().match(/^([A-Za-z]{1,10})-(\d+)$/);
|
|
61
|
+
if (!m)
|
|
62
|
+
return null;
|
|
63
|
+
return `${m[1].toUpperCase()}-${m[2]}`;
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
66
|
* Turn an arbitrary human slug into a branch/dir-safe segment: lowercase,
|