@plune-ai/cli 0.34.5 → 0.34.6
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 +50 -15
- package/dist/cli.cjs +2 -2
- package/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -232,6 +232,7 @@ code is then the platform's verdict on the run, not the runner's: the summary th
|
|
|
232
232
|
| an expected case that never reported (`notRun`): a dead shard is not green | `1` |
|
|
233
233
|
| only `passed`, `blocked`, `skipped` and muted failures | `0` |
|
|
234
234
|
| no verdict: the platform could not be reached, the finish was refused, the run was left open or terminated | `1` |
|
|
235
|
+
| no result reached the run and nothing was expected of it: `run import` of a report whose cases the project does not have yet, `run finish` or `sync` of an empty run | `1` |
|
|
235
236
|
| `run exec` where no result reached the run and nothing was expected of it | the command's own code |
|
|
236
237
|
|
|
237
238
|
Without the flag every command exits as it always did. `plune run` (local evals) is not affected: it stays the
|
|
@@ -632,11 +633,15 @@ useful for a self-hosted backend. The Markdown format `pull` writes and `push` r
|
|
|
632
633
|
### Plune tools for AI agents: `plune mcp`
|
|
633
634
|
|
|
634
635
|
`plune mcp serve` gives an agent the same Plune your token reaches — the review queue, cases,
|
|
635
|
-
missions, questions, strategy, reports — as MCP tools over stdio. The agent
|
|
636
|
-
|
|
637
|
-
100 items, and the agent is held to the platform's agent rules: a proposed case lands in the review
|
|
636
|
+
missions, questions, strategy, reports — as MCP tools over stdio. The agent is shown 17 tools, 8 with a
|
|
637
|
+
read token or `--read-only`; five more write tools it finds with `plune_discover_tools`. Every write
|
|
638
|
+
tool takes `dry_run`, batches stop at 100 items, and the agent is held to the platform's agent rules: a proposed case lands in the review
|
|
638
639
|
queue, a bulk decision needs a confirm, and nothing is deleted through MCP.
|
|
639
640
|
|
|
641
|
+
The full guide is [Plune tools for AI agents](https://docs.plune.ai/cli/guides/mcp/): a project
|
|
642
|
+
`.mcp.json` and the Cursor file, the two protocol revisions the server speaks, how to keep the tool list
|
|
643
|
+
small, and what the contract tests check (snapshot, lint, token budget) and what they do not.
|
|
644
|
+
|
|
640
645
|
`plune_cases_generate` asks the project's AI for case proposals from pasted text (a requirement, a
|
|
641
646
|
story, a ticket's text; 20 words to 12 000 characters, up to 12 proposals, 8 by default). It spends the
|
|
642
647
|
project's AI budget, and its proposals wait in the review queue like any other until a person approves
|
|
@@ -671,7 +676,46 @@ per-request `_meta`. In the newer revision a client learns that `plune_discover_
|
|
|
671
676
|
list through `subscriptions/listen`; `tools/list` is sent with `ttlMs: 0`, so a client that does not
|
|
672
677
|
listen reads the current list every time.
|
|
673
678
|
|
|
674
|
-
####
|
|
679
|
+
#### Dependencies
|
|
680
|
+
|
|
681
|
+
| Package | Version | License | Why |
|
|
682
|
+
|---|---|---|---|
|
|
683
|
+
| `@modelcontextprotocol/server` | 2.4.0 | Apache-2.0 | The MCP server and its stdio transport for `plune mcp serve`; it serves revision `2026-07-28` and the `initialize`-based revisions from one process. |
|
|
684
|
+
|
|
685
|
+
The version is pinned exactly, not with a range, on purpose: the 2.x line moves fast, and each update is taken
|
|
686
|
+
deliberately with the protocol tests run against it.
|
|
687
|
+
|
|
688
|
+
## Test an MCP server
|
|
689
|
+
|
|
690
|
+
Four commands test **any** MCP server, yours or someone else's, started over stdio or reached over
|
|
691
|
+
HTTP. They need no Plune account and no model, and they do not talk to the Plune platform: each one
|
|
692
|
+
starts the server you name after `--`, or connects to the address you give with `--url`, and nothing
|
|
693
|
+
else. Everything after `--` runs as your user, so point them only at a server you would run yourself.
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
npx -y @plune-ai/cli mcp check -- node dist/server.js # does it start and answer
|
|
697
|
+
npx -y @plune-ai/cli mcp lint -- node dist/server.js # what in its tools confuses a model
|
|
698
|
+
npx -y @plune-ai/cli mcp snapshot --out mcp-snapshot.json -- node dist/server.js # its contract, to commit
|
|
699
|
+
npx -y @plune-ai/cli mcp diff mcp-snapshot.json -- node dist/server.js # what changed, and what breaks
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
| Exit | Meaning |
|
|
703
|
+
|------|---------|
|
|
704
|
+
| `0` | Nothing at the `--fail-on` level, and everything was measured. For `snapshot`: the file was written. |
|
|
705
|
+
| `1` | A finding (`lint`), a failed check or smoke call (`check`), or a change (`diff`) at the `--fail-on` level. `snapshot` has no `1`. |
|
|
706
|
+
| `2` | Nothing about the server is proven, and you can fix why: the command was not found or did not start, the server could not be reached or answered `401` or `403`, a file cannot be read, or a flag is wrong. |
|
|
707
|
+
| `3` | Nothing was measured: the server lists no tools, there is nothing to compare, or a check could not run. Not measured is not a pass. |
|
|
708
|
+
|
|
709
|
+
Each command's own table is in [the CLI reference](docs/guide/cli.md#plune-mcp-check). The guides on
|
|
710
|
+
docs.plune.ai walk through a sample server with three defects, every lint rule with an example, the
|
|
711
|
+
exceptions file, snapshots in a pull request, and the run in Plune through JUnit:
|
|
712
|
+
|
|
713
|
+
- [Test an MCP server](https://docs.plune.ai/cli/guides/mcp-test-a-server/): the four commands, the exit codes and states, stdio and HTTP, `plune run import`;
|
|
714
|
+
- [Lint an MCP server](https://docs.plune.ai/cli/guides/mcp-lint/): the rules, `--fail-on`, `plune-mcp-ignore.yaml`, `--call`;
|
|
715
|
+
- [Snapshot and diff an MCP server](https://docs.plune.ai/cli/guides/mcp-snapshot-diff/): what a change costs a client, and the security signal;
|
|
716
|
+
- [MCP server checks in CI](https://docs.plune.ai/eval-action/recipes/mcp-in-ci/): `plune-ai/eval-action/mcp@v1` for a stdio or an HTTP server.
|
|
717
|
+
|
|
718
|
+
### Is the server alive: `plune mcp check`
|
|
675
719
|
|
|
676
720
|
`plune mcp check` answers the question before any other: does the server start, finish the
|
|
677
721
|
handshake and answer the basic requests? It runs the checks in order (`start`, `initialize`,
|
|
@@ -693,7 +737,7 @@ be measured. `--out` writes a report you can attach to a ticket: no URL, command
|
|
|
693
737
|
header in it, the server's stderr masked. The states, the options and the exit codes are in
|
|
694
738
|
[the CLI reference](docs/guide/cli.md#plune-mcp-check).
|
|
695
739
|
|
|
696
|
-
|
|
740
|
+
### Checking any MCP server: `plune mcp lint`
|
|
697
741
|
|
|
698
742
|
`plune mcp lint` reads the whole tool list of a server you name, stdio or HTTP, and reports what
|
|
699
743
|
makes a model choose the wrong tool: missing descriptions, look-alike tools, a string where an `enum`
|
|
@@ -711,7 +755,7 @@ It exits `1` on a finding at `--fail-on` (`error` by default; heuristic advice i
|
|
|
711
755
|
a clean one. Accepted findings go in `plune-mcp-ignore.yaml`, each with a reason. The options, the
|
|
712
756
|
file's format and the exit codes are in [the CLI reference](docs/guide/cli.md#plune-mcp-lint).
|
|
713
757
|
|
|
714
|
-
|
|
758
|
+
### Contract snapshots: `plune mcp snapshot` and `plune mcp diff`
|
|
715
759
|
|
|
716
760
|
A server's author rarely notices a contract change — a removed tool, a parameter that became
|
|
717
761
|
required, a rewritten description — and clients break later, quietly. `plune mcp snapshot` writes
|
|
@@ -730,15 +774,6 @@ destructive, and a new tool that does not say it is read-only carry a `security`
|
|
|
730
774
|
for a reviewer to look, not a failure by itself. The format, the rules and the exit codes are in
|
|
731
775
|
[the CLI reference](docs/guide/cli.md#plune-mcp-snapshot).
|
|
732
776
|
|
|
733
|
-
#### Dependencies
|
|
734
|
-
|
|
735
|
-
| Package | Version | License | Why |
|
|
736
|
-
|---|---|---|---|
|
|
737
|
-
| `@modelcontextprotocol/server` | 2.4.0 | Apache-2.0 | The MCP server and its stdio transport for `plune mcp serve`; it serves revision `2026-07-28` and the `initialize`-based revisions from one process. |
|
|
738
|
-
|
|
739
|
-
The version is pinned exactly, not with a range, on purpose: the 2.x line moves fast, and each update is taken
|
|
740
|
-
deliberately with the protocol tests run against it.
|
|
741
|
-
|
|
742
777
|
## Programmatic API
|
|
743
778
|
|
|
744
779
|
The same engine that powers `plune run` is exported for use from your own code. Unlike the
|
package/dist/cli.cjs
CHANGED
|
@@ -4335,8 +4335,8 @@ function gateExit(verdict, runnerCode) {
|
|
|
4335
4335
|
if (counts3 == null) return { code: 1, line: "plune: gate failed \u2014 no verdict from the platform, and no verdict is no pass." };
|
|
4336
4336
|
const n = (status) => counts3[status] ?? 0;
|
|
4337
4337
|
const notRun = verdict?.notRun?.length ?? 0;
|
|
4338
|
-
if (
|
|
4339
|
-
return { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` };
|
|
4338
|
+
if (notRun === 0 && Object.values(counts3).every((k) => !k)) {
|
|
4339
|
+
return runnerCode !== void 0 ? { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` } : { code: 1, line: "plune: gate failed \u2014 no result reached the run, so there is nothing to pass." };
|
|
4340
4340
|
}
|
|
4341
4341
|
const said = `${n("failed")} failed \xB7 ${n("broken")} broken \xB7 ${notRun} not run \xB7 ${n("muted")} muted`;
|
|
4342
4342
|
return n("failed") + n("broken") > 0 || notRun > 0 ? { code: 1, line: `plune: gate failed \u2014 ${said}` } : { code: 0, line: `plune: gate passed \u2014 ${said}` };
|
package/dist/index.cjs
CHANGED
|
@@ -1891,8 +1891,8 @@ function gateExit(verdict, runnerCode) {
|
|
|
1891
1891
|
if (counts == null) return { code: 1, line: "plune: gate failed \u2014 no verdict from the platform, and no verdict is no pass." };
|
|
1892
1892
|
const n = (status) => counts[status] ?? 0;
|
|
1893
1893
|
const notRun = verdict?.notRun?.length ?? 0;
|
|
1894
|
-
if (
|
|
1895
|
-
return { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` };
|
|
1894
|
+
if (notRun === 0 && Object.values(counts).every((k) => !k)) {
|
|
1895
|
+
return runnerCode !== void 0 ? { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` } : { code: 1, line: "plune: gate failed \u2014 no result reached the run, so there is nothing to pass." };
|
|
1896
1896
|
}
|
|
1897
1897
|
const said = `${n("failed")} failed \xB7 ${n("broken")} broken \xB7 ${notRun} not run \xB7 ${n("muted")} muted`;
|
|
1898
1898
|
return n("failed") + n("broken") > 0 || notRun > 0 ? { code: 1, line: `plune: gate failed \u2014 ${said}` } : { code: 0, line: `plune: gate passed \u2014 ${said}` };
|
package/dist/index.js
CHANGED
|
@@ -1831,8 +1831,8 @@ function gateExit(verdict, runnerCode) {
|
|
|
1831
1831
|
if (counts == null) return { code: 1, line: "plune: gate failed \u2014 no verdict from the platform, and no verdict is no pass." };
|
|
1832
1832
|
const n = (status) => counts[status] ?? 0;
|
|
1833
1833
|
const notRun = verdict?.notRun?.length ?? 0;
|
|
1834
|
-
if (
|
|
1835
|
-
return { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` };
|
|
1834
|
+
if (notRun === 0 && Object.values(counts).every((k) => !k)) {
|
|
1835
|
+
return runnerCode !== void 0 ? { code: runnerCode, line: `plune: gate \u2014 no result reached the run, so the exit code is the command's own (${runnerCode}).` } : { code: 1, line: "plune: gate failed \u2014 no result reached the run, so there is nothing to pass." };
|
|
1836
1836
|
}
|
|
1837
1837
|
const said = `${n("failed")} failed \xB7 ${n("broken")} broken \xB7 ${notRun} not run \xB7 ${n("muted")} muted`;
|
|
1838
1838
|
return n("failed") + n("broken") > 0 || notRun > 0 ? { code: 1, line: `plune: gate failed \u2014 ${said}` } : { code: 0, line: `plune: gate passed \u2014 ${said}` };
|
package/package.json
CHANGED