@lumoai/cli 1.57.0 → 1.59.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 +52 -130
- package/assets/skill/references/artifacts-figma.md +4 -3
- package/assets/skill/references/confirmation.md +131 -0
- package/assets/skill/references/criteria.md +60 -20
- package/assets/skill/references/doc-editing.md +11 -9
- package/assets/skill/references/docs.md +4 -3
- package/assets/skill/references/ideas.md +82 -0
- package/assets/skill/references/initiatives.md +28 -0
- package/assets/skill/references/memory.md +4 -2
- package/assets/skill/references/milestones.md +3 -2
- package/assets/skill/references/outcome.md +1 -14
- package/assets/skill/references/plan-runs.md +32 -0
- package/assets/skill/references/sessions.md +7 -5
- package/assets/skill/references/sprints.md +18 -17
- package/assets/skill/references/task-context.md +1 -1
- package/assets/skill/references/task-deps.md +4 -3
- package/assets/skill/references/tasks.md +34 -2
- package/assets/skill/references/verify.md +46 -68
- package/assets/skill/references/worktree.md +13 -7
- package/dist/cli/src/commands/doc-delete.js +35 -23
- package/dist/cli/src/commands/doc-rebuild-source.js +16 -4
- package/dist/cli/src/commands/memory-rm.js +68 -10
- package/dist/cli/src/commands/milestone-delete.js +13 -10
- package/dist/cli/src/commands/outcome.js +0 -77
- package/dist/cli/src/commands/session-attach.js +8 -2
- package/dist/cli/src/commands/sprint-close.js +29 -9
- package/dist/cli/src/commands/sprint-delete.js +13 -10
- package/dist/cli/src/commands/sprint-show.js +3 -9
- package/dist/cli/src/commands/task-artifact-rm.js +58 -28
- package/dist/cli/src/commands/task-criteria-list.js +1 -4
- package/dist/cli/src/commands/task-criteria-set.js +3 -12
- package/dist/cli/src/commands/task-deps.js +20 -6
- package/dist/cli/src/commands/task-status.js +165 -110
- package/dist/cli/src/commands/task-update.js +129 -0
- package/dist/cli/src/commands/verify.js +22 -13
- package/dist/cli/src/commands/worktree-rm.js +35 -7
- package/dist/cli/src/index.js +47 -47
- package/dist/cli/src/lib/blocked-error.js +178 -0
- package/dist/cli/src/lib/confirmation.js +89 -0
- package/dist/cli/src/lib/hook-runner.js +23 -11
- package/dist/shared/src/referent-kind.js +31 -1
- package/dist/shared/src/security-scan.js +29 -0
- package/package.json +1 -1
- package/assets/skill/references/fidelity.md +0 -32
- package/dist/cli/src/commands/fidelity.js +0 -108
- package/dist/cli/src/commands/verdict.js +0 -189
|
@@ -120,14 +120,46 @@ The server's transition matrix (`lib/task/state-machine.ts`):
|
|
|
120
120
|
|
|
121
121
|
Practical rules:
|
|
122
122
|
|
|
123
|
-
- **One call suffices.** `--status done` straight from TODO or IN_PROGRESS is legal — never walk `in_progress → in_review → done` as a ritual; it just wastes calls.
|
|
123
|
+
- **One call suffices.** `--status done` straight from TODO or IN_PROGRESS is legal — never walk `in_progress → in_review → done` as a ritual; it just wastes calls. That one call **walks the confirmation protocol** (see below): without `--confirm` it exits 4 with an envelope, never a prompt.
|
|
124
124
|
- **Under the verify flow you don't set `in_review`/`done` at all** — `lumo verify` moves the task to IN_REVIEW on all-pass and the DONE adjudication is human-only.
|
|
125
125
|
- **A DONE task can be reopened** — to IN_REVIEW, IN_PROGRESS, or TODO. Reopening is a plain status change and does not alter any recorded acceptance verdict; verdict adjudication is human-only and has no CLI path. To attach context without reopening, `lumo task comment` works.
|
|
126
126
|
|
|
127
|
+
### `--status done` requires `--confirm` — the confirmation envelope (LUM-755)
|
|
128
|
+
|
|
129
|
+
DONE is the one status move that needs a human's sign-off. It no longer prompts (LUM-731's interactive yes/no question is gone): run without `--confirm`, the CLI looks the task up, prints a **confirmation envelope** on stdout and exits **4** with nothing sent — no PATCH, and no tag resolution either. The envelope's `changes` carry everything the old prompt showed, including a trailing `⚠` line when a linked PR has not merged (that line replaces the old second prompt — the protocol is one step, and you relay the whole block). Full protocol: [confirmation.md](confirmation.md).
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
lumo task update LUM-48 --status done
|
|
133
|
+
# exit 4, stdout (JSON when piped; the same lines as text on a TTY):
|
|
134
|
+
# {
|
|
135
|
+
# "status": "confirmation_required",
|
|
136
|
+
# "command": "task update",
|
|
137
|
+
# "changes": [
|
|
138
|
+
# "Will move LUM-48 \"Title\" to DONE",
|
|
139
|
+
# "Status: IN_REVIEW → DONE",
|
|
140
|
+
# "#926 merged refactor: panel https://github.com/o/r/pull/926",
|
|
141
|
+
# "#930 open feat: wip https://github.com/o/r/pull/930",
|
|
142
|
+
# "⚠ 1 linked pull request not merged: #930 open"
|
|
143
|
+
# ],
|
|
144
|
+
# "confirmCommand": "lumo task update LUM-48 --status done --confirm"
|
|
145
|
+
# }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
With no linked PR the PR lines collapse to `Pull requests: none linked` and there is no `⚠` line. Other flags on the same call (`--title`, `--add-tag`…) are preserved in `confirmCommand`.
|
|
149
|
+
|
|
150
|
+
- **Show `changes` to the user and wait for an explicit yes**, then run `confirmCommand` unchanged. `--confirm` means the user confirmed — never add it on your own.
|
|
151
|
+
- With `--confirm` the PATCH goes out directly. The server's own DONE gates still apply on top — an unresolved send-back, an undispositioned boundary crossing or a blocking security finding refuses with **409**, which the CLI turns into a structured **`DONE_BLOCKED` error on stdout with exit 5**: the 409 text verbatim, a `blockers[]` with ids (built from `task status` + the crossings read model), and `remediation` lines that only name human-side paths. There is no CLI path to clear any of them — relay, don't retry. See [confirmation.md](confirmation.md) "When the gate is human-only". Any other 409 stays plain text, exit 1.
|
|
152
|
+
- `--confirm` is the only flag: there is no `--yes` or `--force` on `task update`. Every other status (`todo` / `in_progress` / `in_review`) PATCHes directly and ignores `--confirm`.
|
|
153
|
+
- A failed lookup (404, network) exits 1 before any envelope.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
lumo task update LUM-48 --status done --confirm # only after the user approved the envelope
|
|
157
|
+
```
|
|
158
|
+
|
|
127
159
|
### When to suggest `task update`
|
|
128
160
|
|
|
129
161
|
- The user describes a state change in natural language (e.g. "mark LUM-48 as in progress", "rename LUM-12 to ...", "assign LUM-30 to me", "bump the priority on LUM-7").
|
|
130
|
-
- After the agent finishes a task and the user confirms —
|
|
162
|
+
- After the agent finishes a task and the user confirms — tell the user to run `lumo task update LUM-N --status done` in their terminal (it prompts for confirmation; an agent shell is non-TTY and is refused).
|
|
131
163
|
- Multiple status changes in a row should each be a separate `update` invocation rather than batched.
|
|
132
164
|
|
|
133
165
|
### Sprint output format
|
|
@@ -21,22 +21,19 @@ lumo verify LUM-42 --note "…" # explicit task (overrides the sessio
|
|
|
21
21
|
lumo verify --note "…" --timeout 900 # per-checkpointer timeout in seconds (default 600)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
**`--note` is
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
让 post"); whether it is \_truthful* is the faithfulness audit's job — so don't
|
|
31
|
-
under- or over-state it. A missing/blank note is refused **before** the round is
|
|
32
|
-
posted, so no round is burned; just re-run with one. (An old CLI that omits it
|
|
33
|
-
still verifies — the claim degrades to the synthesized run-summary fallback.)
|
|
24
|
+
**`--note` is optional (LUM-733).** It is your one-line self-report — what
|
|
25
|
+
you did and why it is ready ("我改了 X,因为 Y 可验收"). When given and the round
|
|
26
|
+
all-passes into IN_REVIEW, it is frozen as the task's claim (source `AGENT`) and
|
|
27
|
+
judged for faithfulness in the background; when omitted the round still posts
|
|
28
|
+
and the claim degrades to the synthesized run summary. It is never a
|
|
29
|
+
precondition for verifying — don't hold a round back to word it.
|
|
34
30
|
|
|
35
31
|
## What one round does
|
|
36
32
|
|
|
37
33
|
1. Loads the task's acceptance contract and picks out MACHINE criteria.
|
|
38
|
-
2. Runs each checkpointer locally (shell, cwd = current directory
|
|
39
|
-
|
|
34
|
+
2. Runs each checkpointer locally (shell, cwd = current directory, env = the
|
|
35
|
+
CLI's own minus its private `LUMO_NO_HINTS` mute — `--no-hints` never
|
|
36
|
+
reaches a checkpointer), one at a time, echoing PASS/FAIL as it goes.
|
|
40
37
|
3. POSTs the structured verdicts; the server records one VerificationRun per
|
|
41
38
|
criterion at round = previous max + 1 and mirrors each verdict as a
|
|
42
39
|
TaskActivity event.
|
|
@@ -64,16 +61,15 @@ errors.
|
|
|
64
61
|
|
|
65
62
|
## Edge cases
|
|
66
63
|
|
|
67
|
-
| Case | Behavior
|
|
68
|
-
| ----------------------------------------------- |
|
|
69
|
-
| **No contract yet** | Error pointing at `lumo task criteria set`; draft the contract first (criteria.md golden rule).
|
|
70
|
-
| **HUMAN-only contract** (zero MACHINE criteria) | Nothing to run; CLI says so and suggests `lumo task update <id> --status in_review` for human review. No server write happens.
|
|
71
|
-
| **Partial round** | A round must cover every MACHINE criterion; the CLI always runs all of them and the server rejects partial rounds.
|
|
72
|
-
| **`REVIEW_ADDED` criteria** | Criteria added during review appear in the contract and are picked up automatically by the next round.
|
|
73
|
-
| **Session bound to a different task** | Server returns 409, surfaced as an error. No advisory printed; the verify round is rejected outright.
|
|
74
|
-
| **
|
|
75
|
-
| **
|
|
76
|
-
| **Unconfirmed session binding** | `bindingAdvisory: 'unconfirmed'` → softer advisory `⚠ Could not confirm this session is attached to the task.` Same remediation: `lumo session attach <LUM-N>`. |
|
|
64
|
+
| Case | Behavior |
|
|
65
|
+
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| **No contract yet** | Error pointing at `lumo task criteria set`; draft the contract first (criteria.md golden rule). |
|
|
67
|
+
| **HUMAN-only contract** (zero MACHINE criteria) | Nothing to run; CLI says so and suggests `lumo task update <id> --status in_review` for human review. No server write happens. |
|
|
68
|
+
| **Partial round** | A round must cover every MACHINE criterion; the CLI always runs all of them and the server rejects partial rounds. |
|
|
69
|
+
| **`REVIEW_ADDED` criteria** | Criteria added during review appear in the contract and are picked up automatically by the next round. |
|
|
70
|
+
| **Session bound to a different task** | Server returns 409, surfaced as an error. No advisory printed; the verify round is rejected outright. |
|
|
71
|
+
| **Provably-unbound session** | Response carries `bindingAdvisory: 'unbound'`; prints `⚠ Working unbound — this verify ran from a Claude Code session not attached to the task.` Recorded as a `SESSION_BINDING_MISSING` boundary crossing (visible in `lumo task status` open crossings). Run `lumo session attach <LUM-N>` before the next verify. |
|
|
72
|
+
| **Unconfirmed session binding** | `bindingAdvisory: 'unconfirmed'` → softer advisory `⚠ Could not confirm this session is attached to the task.` Same remediation: `lumo session attach <LUM-N>`. |
|
|
77
73
|
|
|
78
74
|
## Round discipline
|
|
79
75
|
|
|
@@ -105,8 +101,9 @@ nothing and burns no round. Defaults to the session-bound task; an explicit
|
|
|
105
101
|
identifier overrides.
|
|
106
102
|
|
|
107
103
|
```bash
|
|
108
|
-
lumo task status # session-bound task
|
|
104
|
+
lumo task status # session-bound task (core: criteria / next actions / open crossings)
|
|
109
105
|
lumo task status LUM-42 # explicit task
|
|
106
|
+
lumo task status --full # + verification rollup, history, cost, struggle trail, trend
|
|
110
107
|
lumo task status --json # versioned machine-readable payload
|
|
111
108
|
```
|
|
112
109
|
|
|
@@ -125,19 +122,16 @@ what's unmet and why (the exact failure tails), and how many rounds are left.
|
|
|
125
122
|
### What it prints
|
|
126
123
|
|
|
127
124
|
- **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.
|
|
128
|
-
- **
|
|
129
|
-
|
|
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.
|
|
131
|
-
- **Faithfulness** — the third state over the two columns: whether the CLAIM itself is _true_, read from the persisted faithfulness verdict (no LLM at read time), printed as `▸ Faithfulness — does the claim match the delivery`. One of `faithful` / `overstated` (claim says more than the diff/PR shows) / `under-reported` (claim says less) / `unjudgeable`, with an `· evidence: PR #N · <sha>` pointer when the judge cited one. **Fail-closed**: `not yet judged` (no verdict — distinct from a real `unjudgeable` verdict) and `… (stale — re-checked on the next batch)` (judged against an older claim snapshot, the verdict still shown) are surfaced explicitly. Omitted only against an older server that doesn't emit it.
|
|
132
|
-
- Carried in `--json` as `claim { text, source }` (`source: 'AGENT' | 'RUN_SUMMARY' | 'DIGEST' | null`; `AGENT` = the verify self-report, `RUN_SUMMARY`/`DIGEST` = the synthesized fallback; `text: null` = none generated) and `faithfulness { state, verdict, evidence, diffShas, prNumbers, judgedAt, judgedClaimAt }` (`state` adds `PENDING`/`STALE` over the four verdicts). Omitted only against an older server that doesn't emit the field. The machine-verification rollup is still carried top-level as `machineVerification`.
|
|
125
|
+
- **Default = the self-check core (LUM-733):** header, Criteria, Last round failures, Next actions, Open boundary crossings. The dashboard sections below (**Verification**, **History**, **Cost**, **Struggle**, **Trend**) print only with `--full` — they are human-dashboard material and cost context. The claim text and faithfulness verdict are no longer rendered in the terminal (`--json` still carries `claim` / `faithfulness` for scripts).
|
|
126
|
+
- **Verification** (`--full`) — the measured rollup `N machine-verified / M human override (of T MACHINE criteria)` plus `X of Y criteria met by their latest verdict`; before any round it prints `no verification has run yet`.
|
|
133
127
|
- **Criteria** — every criterion as `<glyph> <id> [TYPE] SOURCE@rN statement` (✓ latest verdict passed / ✗ failed / ○ no verdict yet) with its checkpointer and latest verdict line (failure tail on fail). `REVIEW_ADDED@rN` provenance is visible per row.
|
|
134
128
|
- A passing **MACHINE** criterion's verdict line carries a machine-state tag derived from the read model's `machinePassed` flag, NOT the latest verdict: `· machine-verified` when a checkpointer actually passed it (even after a human later signs the task off), or `· human override (no machine pass)` when it passes only on a human sign-off with no machine run underneath. This keeps the terminal honest with web — a machine-verified criterion that a human co-signed no longer reads as a plain human pass.
|
|
135
129
|
- A verdict's **evidence is drillable**, rendered as an indented `↳ evidence:` line under the verdict (PASS _and_ FAIL) instead of the inert raw pointer that used to ride the verdict line — so a conclusion points at real proof you can act on, not just the `check:` command: a `cmd:` pointer prints the actual command + exit code (`ran \`…\` → exit N · re-run to reproduce`), a `file:`pointer prints a terminal-clickable`path:line`, and a `commit:` pointer prints a navigable web URL (`<repo>/commit/<hash>`, resolved from the local git `origin`remote) or a`git show <hash>`fallback when no remote resolves. A criterion that **requires evidence but has none recorded yet** (e.g. a HUMAN evidence criterion before sign-off) renders an explicit`↳ evidence: pending — no reference recorded yet`(fail-closed) instead of a bare, dead`[evidence]` tag.
|
|
136
130
|
- A pass can carry a **`⚠ pre-edit version`** note: the criterion was changed after that verdict (reworded, or its checkpointer was swapped so the recorded evidence ran a different command). The pass still counts as met (a stale pass does not block DONE — render-only signal), but it vouches for an older version — **re-run `lumo verify` to re-confirm against the current criterion.** This is the habit whenever you edit a MACHINE criterion's checkpointer mid-task: change the check, then re-verify so the green is honest.
|
|
137
|
-
- **History** — one line per recorded round: `rN · timestamp · X PASS / Y FAIL`.
|
|
131
|
+
- **History** (`--full`) — one line per recorded round: `rN · timestamp · X PASS / Y FAIL`.
|
|
138
132
|
- **Last round failures** — the most recent round's FAIL verdicts with their rejection reasons (why the last round bounced).
|
|
139
|
-
- **Cost** — 规律 1: the costs a human should weigh, on the same report as the verdict instead of scattered across the web delivery card and `task lineage`. Three lines: **Tokens** (total input+output+cache across the task's sessions), **Active time** (non-idle agent seconds — Σ per-turn `STOP − prompt`), and **Rework rounds** (verify rounds that recorded a FAIL). Read from the **same** server-side source the web delivery card consumes (`retrospectiveRepository.loadActuals`), so the two reports cannot drift. Token cost is **fail-closed**: when no session usage was recorded it prints `Tokens: not recorded (no session usage captured)`, kept distinct from a measured `0` (没测到 vs 花了0). Carried in `--json` as `cost { tokenCost, activeTimeSec, reworkRounds }` (`tokenCost: null` = not measured). Omitted only against an older server that doesn't emit the field.
|
|
140
|
-
- **Struggle / rework / outstanding** — the anti-mum-and-deaf block: **always printed when the contract exists, even on a clean 0-unmet task** so a passing task still shows its scars instead of wiping them to a single PASS count. Lists, when present:
|
|
133
|
+
- **Cost** (`--full`) — 规律 1: the costs a human should weigh, on the same report as the verdict instead of scattered across the web delivery card and `task lineage`. Three lines: **Tokens** (total input+output+cache across the task's sessions), **Active time** (non-idle agent seconds — Σ per-turn `STOP − prompt`), and **Rework rounds** (verify rounds that recorded a FAIL). Read from the **same** server-side source the web delivery card consumes (`retrospectiveRepository.loadActuals`), so the two reports cannot drift. Token cost is **fail-closed**: when no session usage was recorded it prints `Tokens: not recorded (no session usage captured)`, kept distinct from a measured `0` (没测到 vs 花了0). Carried in `--json` as `cost { tokenCost, activeTimeSec, reworkRounds }` (`tokenCost: null` = not measured). Omitted only against an older server that doesn't emit the field.
|
|
134
|
+
- **Struggle / rework / outstanding** (`--full`) — the anti-mum-and-deaf block: **always printed when the contract exists, even on a clean 0-unmet task** so a passing task still shows its scars instead of wiping them to a single PASS count. Lists, when present:
|
|
141
135
|
- **rework rounds** — verify rounds that had a FAIL;
|
|
142
136
|
- **send-backs** — criteria sent back by a human/agent verdict (a MACHINE verify-loop FAIL is not a 打回), with their open/resolved lifecycle, preserved even for since-removed criteria;
|
|
143
137
|
- **leftover follow-ups** — criteria whose latest verdict is `PASS_WITH_FOLLOWUP`;
|
|
@@ -146,9 +140,12 @@ what's unmet and why (the exact failure tails), and how many rounds are left.
|
|
|
146
140
|
|
|
147
141
|
When the trail is genuinely empty it states the **basis** (`None recorded — N rounds run, 0 FAIL, no send-backs, no reopens, no leftover follow-ups`); when nothing has been verified yet it says so (`No verification has run yet — cannot confirm there were no difficulties`) rather than rendering an implicitly-clean slate. Carried in `--json` as `struggleTrail` (incl. `pullRequests` + `reopens`).
|
|
148
142
|
|
|
149
|
-
- **Trend** — 规律 7 趋势非快照: the _movement_ of the key quantities across the task's attempts, not a single snapshot. Where History/Cost/Struggle list current values, this shows direction: **Pass rate** across verification rounds (`r1 60% → r2 100% (↑ +40pts)`), **Cost/session** across the task's sessions (`4.2K → 1.1K tokens (↓), 5.3K total` — per-session spend from the same source as the **Cost** total, so the trajectory's points sum to it), and **Rework** accrual (`3 accrued — 1 FAIL round, 1 reopen, +1 PR cycle (↑ from 0)`). **Honest about a single point:** with only one round and one session every quantity is one data point, so it prints `Single attempt so far — no trajectory yet (a trend needs ≥2 rounds or sessions)` rather than drawing a fake arrow off one value. When nothing was verified and no cost was measured it says `No verification rounds or measured cost yet — nothing to trend`. Carried in `--json` as `trend { passRate[], cost[], rework{} }`. Omitted only against an older server.
|
|
143
|
+
- **Trend** (`--full`) — 规律 7 趋势非快照: the _movement_ of the key quantities across the task's attempts, not a single snapshot. Where History/Cost/Struggle list current values, this shows direction: **Pass rate** across verification rounds (`r1 60% → r2 100% (↑ +40pts)`), **Cost/session** across the task's sessions (`4.2K → 1.1K tokens (↓), 5.3K total` — per-session spend from the same source as the **Cost** total, so the trajectory's points sum to it), and **Rework** accrual (`3 accrued — 1 FAIL round, 1 reopen, +1 PR cycle (↑ from 0)`). **Honest about a single point:** with only one round and one session every quantity is one data point, so it prints `Single attempt so far — no trajectory yet (a trend needs ≥2 rounds or sessions)` rather than drawing a fake arrow off one value. When nothing was verified and no cost was measured it says `No verification rounds or measured cost yet — nothing to trend`. Carried in `--json` as `trend { passRate[], cost[], rework{} }`. Omitted only against an older server.
|
|
150
144
|
|
|
151
|
-
- **Next actions** — the unmet criteria (latest verdict is not a pass: failed or never verified, HUMAN ones included)
|
|
145
|
+
- **Next actions** — the unmet criteria (latest verdict is not a pass: failed or never verified, HUMAN ones included) followed by any **undispositioned PR security findings** (LUM-737) as `• [SECURITY] [SEVERITY] PROVENANCE ruleId — file:line — title (PR #n · blocks DONE | advisory)`. This list IS the plan — recomputed from the event log + the latest scan per linked PR on every read, never maintained separately. The header counts them separately: `Next actions (N unmet · M security findings)`. A finding is **not** a criterion: fix it and push (a fixed fingerprint disappears from the next scan) or a human dispositions it in the web delivery panel — there is no CLI path to clear one, and a `blocks DONE` finding refuses DONE with 409. Empty + rounds recorded = awaiting human adjudication.
|
|
146
|
+
- **Per-PR scan status** (LUM-735): before the unconfirmed-PR lines, one line per linked PR that has a latest scan (open or closed alike): `PR #945 · scan CLEAN · secrets RAN · external RAN (2 external findings) · supplyChain RAN (1 dependency finding, 1 already on main) · judge RAN · hunt PARTIAL` — stage segments only for keys present, in secrets/external/supplyChain/judge/hunt order (`hunt` = the L3 vulnerability hunt, LUM-739: absent when `LUMO_SECURITY_HUNT=off`, `SKIPPED` when no trigger fired, `PENDING` while the workflow runs, `RAN` / `PARTIAL` (budget or subgraph cap hit) / `FAILED`; its findings are `LLM_JUDGE` advisory, never `blocks DONE`); `(N external findings)` (singular at 1) decorates only the external segment, and only when N > 0; `(N dependency findings[, M already on main])` (LUM-738; singular at 1) decorates only the supplyChain segment, only when N > 0, and prints bare `supplyChain <state>` against an older server without the counts; ` · partial` appended when the scan is partial; and, when `stages.external === 'FAILED'` and a **scrubbed scanner reason** was recorded (the `error` column carries the `external: ` provenance prefix), an appended ` — <reason without the prefix, tail 200 chars>` — an unprefixed `error` (stage A's own raw crash text) is never printed here, same P8 rule as the web panel and the PR summary (LUM-756). Fed by the additive `securityFindings.scans` array; omitted entirely (no lines) against an older server that doesn't send it.
|
|
147
|
+
- **Persisting findings** (LUM-738): a finding whose fingerprint is already on the repository's default-branch baseline (`persisting`) is not this PR's — it never appears as a next action, is not counted in `openFindings`, and shows muted (_already on the default branch_) in the web panel. Dependency findings (`kind=DEPENDENCY`, from osv-scanner) are advisory: they never block DONE.
|
|
148
|
+
- **Fails closed:** `⚠ Security-scan check failed — could not confirm …` when the scan read errored, and one `⚠ PR #n: … — could not confirm it is clean.` line per open PR whose latest scan is missing / FAILED / still running. Silence means a successful read with nothing open, never a failed check.
|
|
152
149
|
- **Open boundary crossings** — a trailing safety block when the task has ≥1 OPEN (undispositioned) forbidden-action crossing: a count, then one line per crossing `• [SEVERITY] CATEGORY — <clipped detail>` (highest-severity first), each followed by a read-only **attribution** line `↳ by model=<m> · agent=<type>[/branch] · session=<8-char prefix>` (who/what crossed; any dimension that couldn't be resolved server-side prints `unknown`, never a fabricated value), then a pointer to the web acceptance panel. Silent when there are none, so it never overshadows the criteria.
|
|
153
150
|
- **Read-only awareness** — this surfaces crossings detected elsewhere; there is no CLI path to disposition or clear one. Disposition stays web + human-only: an agent/CLI bearer cannot clear its own crossing from the terminal.
|
|
154
151
|
- **The check fails closed:** if the crossings read itself errors (network / server / parse), the block prints `⚠ Boundary-crossing check failed (network/server error) — could not confirm whether any are undispositioned` instead of staying silent. Silence means a successful read with zero open crossings, never a failed check — a hiccup can no longer masquerade as "all clear".
|
|
@@ -181,50 +178,33 @@ fields don't. Pin on `version` when scripting against it.
|
|
|
181
178
|
- the payload carries a top-level `machineVerification` aggregate `{ total, machineVerified, humanOverridden }` over the active MACHINE criteria — read these, not `latestVerdict` alone, to tell a machine-verified criterion from a human override;
|
|
182
179
|
- open boundary crossings ride along as an additive top-level `openCrossings`, each entry `{ id, category, severity, detail, attribution }` where `attribution` is `{ workspaceMemberId, sessionId, agent, worktreeBranch, model }` with every field nullable — null = unknown, never fabricated; the array length is the count. Same read-only awareness, no write path;
|
|
183
180
|
- **`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".
|
|
181
|
+
- `nextActions` entries carry a `kind` discriminator (LUM-737): `CRITERION` (the pre-737 shape: `criterionId`, `statement`, `verifierType`, `checkpointer`, `judgeSteps`, `source`, `addedAtRound`, `rejectionReason`) or `SECURITY_FINDING` (`findingId`, `statement`, `severity`, `provenance`, `blocking`, `prNumber`, `ruleId`, `filePath`, `line`). Additive — a missing `kind` means `CRITERION`; do not index every entry by `criterionId`;
|
|
182
|
+
- a top-level `securityFindings` summary `{ open, blocking, unconfirmedPrs: [{ number, reason: NONE|FAILED|PENDING|RUNNING }], scans?: [{ prNumber, status, stages, partial, error, externalFindings, openFindings }] }`; **`null` means the scan read failed** — treat as "unknown / could not confirm", not "safe". `scans` is additive (LUM-735) — absent against an older server, one entry per linked PR with a latest scan otherwise.
|
|
184
183
|
|
|
185
184
|
`status` reads; `verify` judges. Running status never starts a round, never
|
|
186
185
|
escalates, and never changes task state — loop rules (the workspace round cap,
|
|
187
186
|
IN_REVIEW on all-pass, human-only DONE) live entirely in `lumo verify` and the
|
|
188
187
|
server.
|
|
189
188
|
|
|
190
|
-
##
|
|
189
|
+
## Verdict channels
|
|
191
190
|
|
|
192
|
-
`lumo verify` is the MACHINE channel.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
lumo verdict LUM-42 --pass
|
|
199
|
-
lumo verdict --fail --reason CRITERION_UNMET --note "the retry path is still missing"
|
|
200
|
-
lumo verdict LUM-42 --fail --reason scope_mismatch --criterion c-abc123
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### --pass — a deep link, never a write
|
|
204
|
-
|
|
205
|
-
This resolves the task, then opens the browser to its verdict bar focused on
|
|
206
|
-
Pass. **The CLI writes nothing** — PASS only ever lands from a human's own click
|
|
207
|
-
(Clerk session). Use this to hand a finished task to a human for the final pass;
|
|
208
|
-
it carries them one click from recording it.
|
|
209
|
-
|
|
210
|
-
### --fail — the AGENT send-back
|
|
211
|
-
|
|
212
|
-
`--fail --reason <enum>` records a real verdict row server-side with
|
|
213
|
-
verifierType=AGENT (a channel distinct from MACHINE and HUMAN, so "machine
|
|
214
|
-
all-pass but human FAIL" stays an uncontaminated signal). The verdict is
|
|
215
|
-
hard-coded FAIL — there is no agent path to a passing verdict. It:
|
|
216
|
-
|
|
217
|
-
- `--reason <enum>` required (case-insensitive): `CRITERION_UNMET | EVIDENCE_INSUFFICIENT | CHECK_EXECUTION_ERROR | SCOPE_MISMATCH | OTHER` — the agent pays the structured tax a human send-back is spared;
|
|
218
|
-
- `--note <text>` optional, posted as a task comment (@mentions and images for free) and summarized onto the verdict row;
|
|
219
|
-
- `--criterion <id>` repeatable, narrows the send-back; omitted, it fans out to the whole contract;
|
|
220
|
-
- `round` = the current max (not a new round); bounces the task back to IN_PROGRESS, with the unmet criteria surfacing through `lumo task status`.
|
|
191
|
+
`lumo verify` is the MACHINE channel. The only other channel is the **human**
|
|
192
|
+
one: a person records PASS or a send-back (FAIL) in the web verdict bar (Clerk
|
|
193
|
+
session). There is no agent-facing verdict command — the former
|
|
194
|
+
`lumo verdict --fail` AGENT send-back was removed in LUM-733 (17 rows all-time,
|
|
195
|
+
none in the last 30 days); when the machine loop runs out of rounds the task is
|
|
196
|
+
escalated to a human instead. **No passing data row is ever agent-produced.**
|
|
221
197
|
|
|
222
198
|
### The DONE gate
|
|
223
199
|
|
|
224
|
-
Once any criterion's latest verdict is FAIL — machine
|
|
200
|
+
Once any criterion's latest verdict is FAIL — machine or human — moving
|
|
225
201
|
the task to DONE on the agent/CLI path is refused with **409** and the unresolved
|
|
226
202
|
items listed. Clear the send-back (fix + re-verify, or a human PASS) before
|
|
227
|
-
`lumo task update <id> --status done
|
|
203
|
+
`lumo task update <id> --status done` — which itself walks the confirmation
|
|
204
|
+
protocol: without `--confirm` it exits 4 with an envelope for the user to
|
|
205
|
+
approve, and when the server gate still refuses it exits 5 with a structured
|
|
206
|
+
`DONE_BLOCKED` error listing every blocker (LUM-755, see [tasks.md](tasks.md)
|
|
207
|
+
and [confirmation.md](confirmation.md)).
|
|
228
208
|
|
|
229
209
|
- A task with no criteria, or whose criteria were never adjudicated, transitions freely — **the gate only blocks an actual send-back, never an un-adjudicated criterion.**
|
|
230
210
|
- When the machine loop has left a task IN_REVIEW with no send-back standing, the agent may move it to DONE directly; a human-PASS row is a provable manual override, not a required ticket.
|
|
@@ -259,9 +239,7 @@ When someone reports a defect in conversation, your action depends on whether th
|
|
|
259
239
|
task has **ever entered IN_REVIEW**:
|
|
260
240
|
|
|
261
241
|
- **Not yet** (still your first working pass) → just fix it and continue. No verdict needed — nothing was claimed complete, so there's nothing to contradict.
|
|
262
|
-
- **Already submitted** (entered IN_REVIEW / DONE / merged) → **do not silently fix and re-pass.**
|
|
263
|
-
- record your own send-back `lumo verdict --fail` (noting it was human-reported — this is _your_ honest concurrence, not a forged human verdict), or
|
|
264
|
-
- ask the reporter to record a human FAIL via the web UI / Slack (the only channel that can attribute it to a human).
|
|
242
|
+
- **Already submitted** (entered IN_REVIEW / DONE / merged) → **do not silently fix and re-pass.** Ask the reporter to record a human send-back (FAIL) via the web verdict bar / Slack — the only channel that can attribute it to a human — then fix in place and re-run `lumo verify` so the fix lands as a fresh machine round on the record.
|
|
265
243
|
|
|
266
244
|
If the defect is a **new requirement** not covered by any criterion, first
|
|
267
245
|
transcribe it with `lumo task criteria set --human`, then proceed. You can never
|
|
@@ -55,15 +55,21 @@ Errors if the target dir already exists; reuses the branch if it already exists
|
|
|
55
55
|
|
|
56
56
|
## `lumo worktree rm <LUM-N>`
|
|
57
57
|
|
|
58
|
-
Removes the worktree for a task.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
Removes the worktree for a task. Gated by the confirmation protocol
|
|
59
|
+
([confirmation.md](confirmation.md)): without `--confirm` — or with
|
|
60
|
+
uncommitted changes and no `--force` — it exits **4** with an envelope naming
|
|
61
|
+
the path and branch, a `⚠ … uncommitted changes … discarded` line when dirty,
|
|
62
|
+
and whether the branch is kept or deleted; nothing is removed. A dirty tree's
|
|
63
|
+
`confirmCommand` carries `--force --confirm`. `--yes` is a legacy alias of
|
|
64
|
+
`--confirm`. Keeps the branch by default (it may hold unpushed work / an open
|
|
65
|
+
PR); `--delete-branch` removes it with `git branch -d` (which itself refuses
|
|
66
|
+
an unmerged branch).
|
|
62
67
|
|
|
63
68
|
```bash
|
|
64
|
-
lumo worktree rm LUM-267
|
|
65
|
-
lumo worktree rm LUM-267 --
|
|
66
|
-
lumo worktree rm LUM-267 --
|
|
69
|
+
lumo worktree rm LUM-267 # exit 4 + envelope
|
|
70
|
+
lumo worktree rm LUM-267 --confirm # after the user approved (clean tree)
|
|
71
|
+
lumo worktree rm LUM-267 --force --confirm # dirty tree: discard uncommitted changes
|
|
72
|
+
lumo worktree rm LUM-267 --confirm --delete-branch # also delete lumo/LUM-267…
|
|
67
73
|
```
|
|
68
74
|
|
|
69
75
|
## `lumo worktree list`
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeDocDelete = describeDocDelete;
|
|
3
4
|
exports.docDelete = docDelete;
|
|
4
5
|
const config_1 = require("../lib/config");
|
|
5
6
|
const api_1 = require("../lib/api");
|
|
6
|
-
const resolve_doc_1 = require("../lib/resolve-doc");
|
|
7
7
|
const resolve_doc_id_1 = require("../lib/resolve-doc-id");
|
|
8
8
|
const sanitize_1 = require("../lib/sanitize");
|
|
9
|
+
const confirmation_1 = require("../lib/confirmation");
|
|
10
|
+
/** The changes[] block for the confirmation envelope. */
|
|
11
|
+
function describeDocDelete(id, title) {
|
|
12
|
+
const escaped = (0, sanitize_1.sanitizeField)(title).replace(/"/g, '\\"');
|
|
13
|
+
return [
|
|
14
|
+
`Will delete document ${id} "${escaped}"`,
|
|
15
|
+
'Every document nested under it is deleted with it; task bindings and shares are dropped',
|
|
16
|
+
];
|
|
17
|
+
}
|
|
9
18
|
async function docDelete(reference, opts) {
|
|
10
19
|
if (!reference) {
|
|
11
|
-
console.error('Error: missing <doc>. Usage: lumo doc delete <doc> --
|
|
12
|
-
return 1;
|
|
13
|
-
}
|
|
14
|
-
if (!opts.yes) {
|
|
15
|
-
console.error('Error: Refusing to delete without --yes');
|
|
20
|
+
console.error('Error: missing <doc>. Usage: lumo doc delete <doc> --confirm');
|
|
16
21
|
return 1;
|
|
17
22
|
}
|
|
18
23
|
const creds = (0, config_1.readCredentials)();
|
|
@@ -21,26 +26,33 @@ async function docDelete(reference, opts) {
|
|
|
21
26
|
return 1;
|
|
22
27
|
}
|
|
23
28
|
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
+
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
30
|
+
const headers = { Authorization: `Bearer ${creds.token}` };
|
|
31
|
+
const id = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, reference);
|
|
32
|
+
if (!id) {
|
|
33
|
+
console.error(`Error: Document not found: ${reference}`);
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
// Always fetch the doc first: the envelope needs its real title, and the
|
|
37
|
+
// same GET confirms it exists before anything destructive happens.
|
|
38
|
+
const showRes = await fetch(`${base}/api/documents/${id}`, { headers });
|
|
39
|
+
if (!showRes.ok) {
|
|
40
|
+
const text = await showRes.text();
|
|
41
|
+
console.error(`Error: ${showRes.status} ${showRes.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
|
|
42
|
+
return 1;
|
|
29
43
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// (Alternative: change lookupDocId to return the full DocLike. For now keep it simple.)
|
|
39
|
-
title = reference;
|
|
44
|
+
const { document } = (await showRes.json());
|
|
45
|
+
const title = document.title ?? '';
|
|
46
|
+
// LUM-755: no --confirm → envelope, no DELETE.
|
|
47
|
+
if (!(0, confirmation_1.isConfirmed)(opts)) {
|
|
48
|
+
return (0, confirmation_1.emitConfirmation)({
|
|
49
|
+
command: 'doc delete',
|
|
50
|
+
changes: describeDocDelete(id, title),
|
|
51
|
+
});
|
|
40
52
|
}
|
|
41
|
-
const res = await fetch(`${
|
|
53
|
+
const res = await fetch(`${base}/api/documents/${id}`, {
|
|
42
54
|
method: 'DELETE',
|
|
43
|
-
headers
|
|
55
|
+
headers,
|
|
44
56
|
});
|
|
45
57
|
if (!res.ok) {
|
|
46
58
|
const text = await res.text();
|
|
@@ -5,6 +5,7 @@ const config_1 = require("../lib/config");
|
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
6
|
const resolve_doc_id_1 = require("../lib/resolve-doc-id");
|
|
7
7
|
const sanitize_1 = require("../lib/sanitize");
|
|
8
|
+
const confirmation_1 = require("../lib/confirmation");
|
|
8
9
|
/**
|
|
9
10
|
* `lumo doc rebuild-source <doc>` (LUM-446).
|
|
10
11
|
*
|
|
@@ -13,7 +14,9 @@ const sanitize_1 = require("../lib/sanitize");
|
|
|
13
14
|
* re-enabling `doc show --raw` / `diff` / `patch` / `append`. The rebuilt source
|
|
14
15
|
* is structure-guarded server-side: any table/tr/heading shrink is rejected 422
|
|
15
16
|
* (zero silent flattening — LUM-410 口径) unless --allow-shrink is passed. A doc
|
|
16
|
-
* that already has a source is refused 409 unless --force re-derives it
|
|
17
|
+
* that already has a source is refused 409 unless --force re-derives it —
|
|
18
|
+
* that refusal is surfaced as a confirmation envelope (LUM-755): exit 4 with
|
|
19
|
+
* the server's reason and a confirmCommand carrying `--force --confirm`.
|
|
17
20
|
*/
|
|
18
21
|
async function docRebuildSource(reference, opts) {
|
|
19
22
|
if (!reference) {
|
|
@@ -57,11 +60,20 @@ async function docRebuildSource(reference, opts) {
|
|
|
57
60
|
if (!res.ok) {
|
|
58
61
|
const text = await res.text();
|
|
59
62
|
const message = (0, api_1.extractErrorMessage)(text);
|
|
63
|
+
if (res.status === 409 && !opts.force) {
|
|
64
|
+
// The --force gate: nothing was written. Hand back the protocol
|
|
65
|
+
// envelope so the agent shows the user what --force would replace.
|
|
66
|
+
return (0, confirmation_1.emitConfirmation)({
|
|
67
|
+
command: 'doc rebuild-source',
|
|
68
|
+
changes: [
|
|
69
|
+
(0, sanitize_1.sanitizeField)(message),
|
|
70
|
+
`Will replace the existing (byte-faithful) markdown source of ${id} with a freshly serialized one — inspect it first with \`lumo doc show ${reference} --raw\``,
|
|
71
|
+
],
|
|
72
|
+
extraFlags: ['--force'],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
60
75
|
if (res.status === 409) {
|
|
61
76
|
console.error(`Error: ${(0, sanitize_1.sanitizeField)(message)}`);
|
|
62
|
-
console.error('Hint: the doc already has a markdown source. Inspect it with ' +
|
|
63
|
-
`\`lumo doc show ${reference} --raw\`; pass --force only if you intend ` +
|
|
64
|
-
'to replace it with a freshly serialized one.');
|
|
65
77
|
return 1;
|
|
66
78
|
}
|
|
67
79
|
if (res.status === 422) {
|
|
@@ -1,15 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.summarizeMemoryContent = summarizeMemoryContent;
|
|
4
|
+
exports.describeMemoryRm = describeMemoryRm;
|
|
3
5
|
exports.memoryRm = memoryRm;
|
|
4
6
|
const config_1 = require("../lib/config");
|
|
5
7
|
const api_1 = require("../lib/api");
|
|
8
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
9
|
+
const confirmation_1 = require("../lib/confirmation");
|
|
10
|
+
const SUMMARY_MAX = 120;
|
|
11
|
+
/**
|
|
12
|
+
* One-line summary of a memory's structured content for the envelope: the
|
|
13
|
+
* first string field (memory cards lead with their headline field), trimmed.
|
|
14
|
+
*/
|
|
15
|
+
function summarizeMemoryContent(content) {
|
|
16
|
+
if (typeof content === 'string')
|
|
17
|
+
return truncate(content);
|
|
18
|
+
if (content && typeof content === 'object') {
|
|
19
|
+
for (const value of Object.values(content)) {
|
|
20
|
+
if (typeof value === 'string' && value.trim())
|
|
21
|
+
return truncate(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function truncate(s) {
|
|
27
|
+
const flat = s.replace(/\s+/g, ' ').trim();
|
|
28
|
+
return flat.length > SUMMARY_MAX ? `${flat.slice(0, SUMMARY_MAX - 1)}…` : flat;
|
|
29
|
+
}
|
|
30
|
+
/** The changes[] block for the confirmation envelope. */
|
|
31
|
+
function describeMemoryRm(memory) {
|
|
32
|
+
const lines = [`Will hard-delete memory ${memory.id} [${memory.category}]`];
|
|
33
|
+
const summary = summarizeMemoryContent(memory.content);
|
|
34
|
+
if (summary)
|
|
35
|
+
lines.push(`Content: ${summary}`);
|
|
36
|
+
lines.push('The memory is removed for the whole team; there is no undo');
|
|
37
|
+
return lines.map(l => (0, sanitize_1.sanitizeField)(l));
|
|
38
|
+
}
|
|
6
39
|
async function memoryRm(memoryId, options) {
|
|
7
40
|
if (!memoryId) {
|
|
8
|
-
console.error('Error: missing <memoryId>. Usage: lumo memory rm <memoryId> --
|
|
9
|
-
return 1;
|
|
10
|
-
}
|
|
11
|
-
if (!options.yes) {
|
|
12
|
-
console.error('Error: refusing to delete without --yes. Re-run with --yes to confirm.');
|
|
41
|
+
console.error('Error: missing <memoryId>. Usage: lumo memory rm <memoryId> --confirm');
|
|
13
42
|
return 1;
|
|
14
43
|
}
|
|
15
44
|
const creds = (0, config_1.readCredentials)();
|
|
@@ -19,19 +48,48 @@ async function memoryRm(memoryId, options) {
|
|
|
19
48
|
}
|
|
20
49
|
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
21
50
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
51
|
+
const url = `${base}/api/memories/${encodeURIComponent(memoryId)}`;
|
|
52
|
+
const headers = { Authorization: `Bearer ${creds.token}` };
|
|
53
|
+
const notFound = `Error: memory ${memoryId} not found — pass the full memory id (cuid) from \`lumo task memory list\` / \`lumo project memory list\`; truncated id prefixes are not resolved`;
|
|
54
|
+
// LUM-755: without --confirm, fetch the card so the envelope shows what
|
|
55
|
+
// would be deleted, then stop — no DELETE is sent.
|
|
56
|
+
if (!(0, confirmation_1.isConfirmed)(options)) {
|
|
57
|
+
let showRes;
|
|
58
|
+
try {
|
|
59
|
+
showRes = await fetch(url, { headers });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.error(`Error: could not reach Lumo API at ${apiUrl} (${err instanceof Error ? err.message : String(err)})`);
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
if (showRes.status === 401) {
|
|
66
|
+
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
if (showRes.status === 404) {
|
|
70
|
+
console.error(notFound);
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
if (!showRes.ok) {
|
|
74
|
+
console.error(`Error: memory lookup failed (HTTP ${showRes.status})`);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
const { memory } = (await showRes.json());
|
|
78
|
+
return (0, confirmation_1.emitConfirmation)({
|
|
79
|
+
command: 'memory rm',
|
|
80
|
+
changes: describeMemoryRm(memory),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
22
83
|
let res;
|
|
23
84
|
try {
|
|
24
|
-
res = await fetch(
|
|
25
|
-
method: 'DELETE',
|
|
26
|
-
headers: { Authorization: `Bearer ${creds.token}` },
|
|
27
|
-
});
|
|
85
|
+
res = await fetch(url, { method: 'DELETE', headers });
|
|
28
86
|
}
|
|
29
87
|
catch (err) {
|
|
30
88
|
console.error(`Error: could not reach Lumo API at ${apiUrl} (${err instanceof Error ? err.message : String(err)})`);
|
|
31
89
|
return 1;
|
|
32
90
|
}
|
|
33
91
|
if (res.status === 404) {
|
|
34
|
-
console.error(
|
|
92
|
+
console.error(notFound);
|
|
35
93
|
return 1;
|
|
36
94
|
}
|
|
37
95
|
if (res.status !== 204) {
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.describeMilestoneDelete = describeMilestoneDelete;
|
|
4
4
|
exports.milestoneDelete = milestoneDelete;
|
|
5
5
|
const config_1 = require("../lib/config");
|
|
6
6
|
const api_1 = require("../lib/api");
|
|
7
7
|
const resolve_1 = require("../lib/resolve");
|
|
8
8
|
const sanitize_1 = require("../lib/sanitize");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const confirmation_1 = require("../lib/confirmation");
|
|
10
|
+
/** The changes[] block for the confirmation envelope (LUM-755). */
|
|
11
|
+
function describeMilestoneDelete(name, taskCount) {
|
|
12
|
+
const lines = [`Will delete milestone "${(0, sanitize_1.sanitizeField)(name)}"`];
|
|
13
|
+
if (taskCount > 0)
|
|
14
|
+
lines.push(`${taskCount} tasks under it keep their data; only milestoneId is cleared`);
|
|
15
|
+
return lines;
|
|
15
16
|
}
|
|
16
17
|
function totalTasks(counts) {
|
|
17
18
|
return counts.TODO + counts.IN_PROGRESS + counts.IN_REVIEW + counts.DONE;
|
|
@@ -59,9 +60,11 @@ async function milestoneDelete(identifier, opts) {
|
|
|
59
60
|
const { milestone } = (await showRes.json());
|
|
60
61
|
const name = resolvedName || milestone.name;
|
|
61
62
|
const total = totalTasks(milestone.taskCounts);
|
|
62
|
-
if (!
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
if (!(0, confirmation_1.isConfirmed)(opts)) {
|
|
64
|
+
return (0, confirmation_1.emitConfirmation)({
|
|
65
|
+
command: 'milestone delete',
|
|
66
|
+
changes: describeMilestoneDelete(name, total),
|
|
67
|
+
});
|
|
65
68
|
}
|
|
66
69
|
let res;
|
|
67
70
|
try {
|