@plune-ai/cli 0.24.0 → 0.25.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/README.md CHANGED
@@ -120,7 +120,7 @@ the last resort, used only when `PLUNE_TOKEN` is set nowhere else. Nothing write
120
120
  | `plune login` | Save a [Plune platform](https://plune.ai/platform) API token so `sync` and `ingest` can reach it. The token is **checked against the API before it is saved**, so a wrong one fails here rather than two commands later. Get one at `https://beta.plune.ai` → Account → API tokens. Flags: `--token <token>` (omit to paste it or pipe it via stdin), `--skip-verify` (save without checking, for offline setup). In CI, skip this step: every platform command reads `PLUNE_TOKEN` from the environment first, and the saved login only when it is not set. |
121
121
  | `plune logout` | Remove the saved token. |
122
122
  | `plune sync` | Upload the latest local run to the platform. Flags: `--file <path>` to send a specific run JSON, `--fail-on-failures` to read the run's summary back once after the upload and exit with [the gate](#a-release-gate---fail-on-failures)'s code. Each upload carries an `Idempotency-Key` (SHA-256 of the body), so sending the same run again returns the same run rather than creating a second one. |
123
- | `plune run import <file>` | Turn a **JUnit XML** or **Playwright JSON** report, or an **Allure `allure-results` folder**, into a run in Plune. Needs no provider key — nothing is generated. Flags: `--format junit\|playwright-json\|allure` (detected from the file when omitted; a folder is Allure), `--key <externalKey>` to land several reports in one run (with `PLUNE_SHARED_RUN=1` — see below), `--create` to offer unmatched tests to the review queue, `--fail-on-missing-attachments` to exit 1 when a file a JUnit report names with `[[ATTACHMENT\|path]]`, or an Allure result names as a `source`, is not there (the results are sent either way), `--upload-concurrency <n>` for files uploaded at once ([1 to 32, default 4](#many-screenshots-upload-concurrency-and-retries)), `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code on the summary the platform froze when this import finished the run. |
123
+ | `plune run import <file>` | Turn a **JUnit XML** or **Playwright JSON** report, or an **Allure `allure-results` folder**, into a run in Plune. Needs no provider key — nothing is generated. Flags: `--format junit\|playwright-json\|allure` (detected from the file when omitted; a folder is Allure), `--key <externalKey>` to land several reports in one run (with `PLUNE_SHARED_RUN=1` — see below), `--create` to offer unmatched tests to the review queue, `--fail-on-missing-attachments` to exit 1 when a file a JUnit report names with `[[ATTACHMENT\|path]]`, or an Allure result names as a `source`, is not there (the results are sent either way), `--upload-concurrency <n>` for files uploaded at once ([1 to 32, default 16](#many-screenshots-upload-concurrency-and-retries)), `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code on the summary the platform froze when this import finished the run. |
124
124
  | `plune run start` | Open a platform run — or join the one already carrying `--key` — and print its id. Flags: `--key <externalKey>` (generated when absent), `--json` for one machine-readable line. |
125
125
  | `plune run finish <id>` | Close a platform run. This is what the reporter tells you to do for a run it had to leave open. Flags: `--terminate` to record it as cut short, `--reason <text>`, `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code on the summary the platform froze. |
126
126
  | `plune run exec -- <command>` | Open a run, run the command inside it, close the run — and exit with whatever the command returned. Sets `PLUNE_SHARED_RUN` for you, so anything reporting inside joins that run. Flags: `--key <externalKey>`, `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code instead — or with the command's own when no result reached the run. |
@@ -129,6 +129,7 @@ the last resort, used only when `PLUNE_TOKEN` is set nowhere else. Nothing write
129
129
  | `plune ingest [dir]` | Record a [Cairn](https://github.com/plune-ai/cairn) run in Plune. Omit `[dir]` for the newest run under `./runs`, or name the directory holding `report.json`. Generated cases arrive as **review proposals** — nothing is created until a person approves it. |
130
130
  | `plune pull [file]` | Write the project's test cases as **one Markdown document** — Testomat's classical format, plus Plune's own columns — to `plune/cases.md` (or `[file]`). Refuses to overwrite a file git sees as modified; `--force` overrides, `--suite <id>` takes one suite or folder and what is under it. |
131
131
  | `plune push [file] [--dry-run]` | Send the document back. Cases are matched by `id`; a block without one becomes a draft; an unknown id is refused by line; **nothing is deleted**. Prints the report — created, updated, unchanged, refused, warnings — and with `--dry-run` writes nothing. Exit `4` when anything was refused. |
132
+ | `plune requirements import <file.md>` | Import requirements from a **Markdown file** (an SRS, a PRD, a page exported from a wiki). With no mapping flags it lists the tables and sections the file has and exits `0`; with `--table` or `--sections` it is a **dry run** that writes nothing until you add `--yes`. See [Requirements from a Markdown file](#requirements-from-a-markdown-file). Needs a Plune platform that serves `POST /v1/projects/:id/requirements/import` (plune-ai/plune#1621). |
132
133
  | `plune plan grep <id>` | Print a [test plan](https://docs.plune.ai/platform/plans/) as **one `--grep` pattern** — `npx playwright test --grep "$(plune plan grep <id>)"` or `npx vitest run -t "$(plune plan grep <id>)"` runs exactly what the plan collects. Each case contributes its title path from the `path-title` key the reporter wrote (the file left out, `[ >#]+` between the segments — a space for Playwright, jest, mocha and vitest ≤ 4, ` > ` for vitest 5), escaped, joined with `\|`; a case without one goes in by title. The pattern is all that reaches stdout; the summary — how many cases, how many keyed — goes to stderr. An empty plan prints `(?!)`, which matches nothing, because an empty `--grep` would run everything. |
133
134
  | `plune run grep <run> [--failed]` | Print a run's **automated** cases as one `--grep` pattern, built the way `plan grep` builds a plan's — so the commands Plune shows for a run work as they are: `npx playwright test --grep "$(plune run grep <run-id> --failed)"` re-runs an automated run's failures where your tests run (Plune starts no CI), and `PLUNE_RUN=<key> npx playwright test --grep "$(plune run grep <key>)"` runs the automated half of a mixed run launched without a plan. `<run>` is the run's id (a UUID) or its key (`rk_` and ten hex); anything else is refused with exit `2` before anything is sent, and a key no run carries exits `1`. The cases are the ones the run expected — cases added on its page among them, run or not — or holds a result of; manual cases are left out. `--failed` keeps the cases whose latest result the platform counts as failed or broken, so a muted case's failure stays out, as from **Re-run N failed**. Only the pattern reaches stdout; how many cases were taken and how many left out goes to stderr; nothing taken prints `(?!)`. Needs a Plune that serves `GET /v1/runs/:id/cases` (plune-ai/plune#1333). |
134
135
 
@@ -136,6 +137,38 @@ Global flags: `-c, --config <path>` · `-v, --verbose` · `--no-color`.
136
137
 
137
138
  **Exit codes:** `0` everything passed · `1` an assertion failed · `2` configuration or execution error · `3` `pull` refused to overwrite uncommitted changes · `4` `push` had refusals.
138
139
 
140
+ ### Requirements from a Markdown file
141
+
142
+ `plune requirements import <file.md>` reads a requirements document (UTF-8, at most 1 MiB, at most 2000 requirements) and brings it into the project in two steps. It sends the file as it is and changes nothing by itself: you say which table or section holds the requirements and which column is which, and the platform tells you what it would do before it does it.
143
+
144
+ **1. See what the file holds.** With no mapping flags:
145
+
146
+ ```bash
147
+ plune requirements import srs.md
148
+ ```
149
+
150
+ It lists every table (number, nearest heading, header cells, row count, and `<- looks like requirements` where the first column holds ids and a header names a requirement) and every section (number, heading, bullets and sub-headings under it), then the commands to try next. Exit `0`.
151
+
152
+ **2a. A table.** Name the table and its columns by their header text (any case):
153
+
154
+ ```bash
155
+ plune requirements import srs.md --table 0 --key-column ID --text-column Requirement --priority-column Priority --map-priority "Critical=Must-have,High=Should-have"
156
+ ```
157
+
158
+ `--key-column`, `--title-column` or `--text-column` (one of the two: the title is the first line of the text and the whole text is kept as the description), `--description-column`, `--priority-column`, `--category-column`, `--type-column`, `--status-column` (`draft`, `active` or `done`), `--criteria-column`; `--map-priority` and `--map-category` (`"Quality=non-functional"`) rename values; `--type user_story|feature|epic` is the type of a row that has none.
159
+
160
+ **2b. A section.** One requirement per bullet or per sub-heading:
161
+
162
+ ```bash
163
+ plune requirements import prd.md --sections Requirements --unit bullets --key-prefix REQ-
164
+ ```
165
+
166
+ `--sections` takes the heading or its number; `--unit bullets|subheadings` (default: sub-headings when the section has any); `--key-prefix` is text a bullet starts with that carries its key (`REQ-12: The system shall ...`; no pattern is accepted); `--priority-from subheading` makes the bullets under a sub-heading take its text as their priority.
167
+
168
+ **3. Read the plan, then write it.** Without `--yes` nothing is written. The report is the summary line (`create 3 · update 0 · unchanged 12 · skip 1`), the rows grouped by action with their line in the file, key, title and reason (a skipped row says why), the priority and category values seen and what they map to, and what was **not imported** (other tables, other sections, frontmatter keys). It ends with `dry run — nothing was written; add --yes to apply`. Add `--yes` to apply the same plan in one transaction. A row with a key matches the requirement of that key; one without matches by exact title; running the same file again changes nothing (every row is `unchanged`). A skipped row does not fail the command.
169
+
170
+ `--json` prints the platform's answer instead. The project is the only one of the account, or `--project <id>` / `PLUNE_PROJECT` (a token only works in the project it was minted in). Exit `0` on success, `2` for a flag combination that cannot work (`--table` with `--sections`, `--title-column` with `--text-column`), an unreadable or over-limit file, or a rejected token, `1` when the platform refuses (400, 403, 409 a key taken meanwhile, 413) or does not have the routes yet.
171
+
139
172
  ### A release gate: `--fail-on-failures`
140
173
 
141
174
  `plune run finish`, `plune run exec`, `plune run import` and `plune sync` take `--fail-on-failures`. The exit
@@ -193,7 +226,7 @@ bytes, up to 512 KiB each and 10 per result. The summary counts them apart from
193
226
  A project that has connected its own storage bucket in Plune takes more. Every file an attempt kept
194
227
  — screenshots, text, a Playwright trace, a video — goes straight from this machine into that bucket
195
228
  over short-lived links Plune hands out per file, so a trace of up to 100 MB is streamed from disk,
196
- never read into memory. Up to four files go at once. The limits there are larger: 10 MiB for an
229
+ never read into memory. Up to 16 files go at once (`PLUNE_UPLOAD_CONCURRENCY`). The limits there are larger: 10 MiB for an
197
230
  image, 5 MiB for a text, 50 MiB for any other file and 100 MiB for a video or a trace, 30 files a
198
231
  result. The links are write keys to your bucket, so they are never printed — not in the log, not with
199
232
  `--verbose`, not in an error; a line names the file and a code. The first time a run finds the
@@ -322,6 +355,32 @@ job closes the run instead of the explicit `finish`.
322
355
  `plune run exec` sets it for you — anything reporting inside it, this command included, joins
323
356
  without closing.
324
357
 
358
+ #### Closing a sharded run by itself: `PLUNE_SHARD`
359
+
360
+ When you know how many shards there are, let them close the run instead of a final `finish` step:
361
+
362
+ ```bash
363
+ PLUNE_SHARD=2/4 plune run import ./junit.xml --key "$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
364
+ ```
365
+
366
+ `PLUNE_SHARD=k/N` says "this job is shard k of N" (k counts from 1, `1 <= k <= N <= 1000`; a Playwright
367
+ `--shard=2/4` user passes the same string). It implies `PLUNE_SHARED_RUN`: the job uploads its results, then tells
368
+ Plune that shard k is done. The run closes once, when N different shards have said so, in whatever order and however
369
+ often a shard is retried (a repeat counts once; a red shard counts too). The job's exit code does not depend on the counts.
370
+
371
+ ```
372
+ plune: shard 2 of 4 done — 2 of 4 shards reported
373
+ ```
374
+
375
+ The shard that is the Nth also prints `plune: run closed` just above that line, so the `shard ... done` line is always the last one.
376
+
377
+ - A value that is not `k/N` (`0/4`, `5/4`, `2`, `a/b`) is a configuration error: nothing is sent and the command fails.
378
+ Every shard of a run must use the same N; a different one fails with a message naming both numbers, and the run is left as it was.
379
+ - The index is not guessed from CI variables (matrix indexes are 0-based on some runners and 1-based on others); set it.
380
+ - On a platform that does not support it yet, the job warns once (`the platform does not support shard auto-close yet`)
381
+ and leaves the run open, as `PLUNE_SHARED_RUN` does: close it with `plune run finish`.
382
+ - With `--fail-on-failures`, the shard that closes the run gates on its summary; the others have no verdict yet.
383
+
325
384
  ### Many screenshots: upload concurrency and retries
326
385
 
327
386
  Files go to Plune up to 100 to a request: `uploads-batch` announces them (with the SHA-256 of each, read as a stream,
@@ -329,18 +388,19 @@ so a file the platform already has is answered `exists` and is not sent again),
329
388
  (or, for a project without one, 100 files or 6 MiB at a time through `direct-batch`), and `complete-batch` confirms
330
389
  them. Every file has its own verdict: one that is refused is reported and the rest of its request goes through. A
331
390
  report of 2760 screenshots is about 56 requests to Plune instead of 5500. The bytes themselves still go one request each,
332
- so the PUTs to a bucket are bound by the round trip, not by bandwidth: four at a time by default, raise it on a runner
333
- with a slow link:
391
+ so the PUTs to a bucket are bound by the round trip, not by bandwidth: 16 at a time by default, lower it on a runner
392
+ with a fragile link or a bucket that throttles:
334
393
 
335
394
  ```bash
336
- PLUNE_UPLOAD_CONCURRENCY=16 plune run import ./playwright.json # 1 to 32, default 4
337
- plune run import ./playwright.json --upload-concurrency 16 # the same, as a flag (it wins)
395
+ PLUNE_UPLOAD_CONCURRENCY=4 plune run import ./playwright.json # 1 to 32, default 16
396
+ plune run import ./playwright.json --upload-concurrency 4 # the same, as a flag (it wins)
338
397
  ```
339
398
 
340
399
  When the platform answers 429 with a `Retry-After` (seconds or an HTTP date, never waited longer than 60 s),
341
400
  the request is tried again after that time plus up to half as much, and every other request of the import
342
401
  waits through the same pause. `PLUNE_UPLOAD_RETRIES` (0 to 20, default 6) says how many times; a 429 that
343
- names no time is the quota and is not retried. A platform that is down (a 5xx or no answer) is still tried
402
+ carries a `quota` object in its body is the quota and is not retried, with or without `Retry-After` (the message names the
403
+ limit and when it resets); one with neither the object nor a `Retry-After` is read the same way. A platform that is down (a 5xx or no answer) is still tried
344
404
  twice (three times for a bucket). A 429 on a whole request waits the same way; a file that the bucket did not
345
405
  answer for at `complete` is confirmed again, on its own. A file that stays refused is reported without it, never fails
346
406
  the import, and is counted in the line printed before the summary: