@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 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 sees at most 20 tools at
636
- once; the rest it finds with `plune_discover_tools`. Every write tool takes `dry_run`, batches stop at
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
- #### Is the server alive: `plune mcp check`
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
- #### Checking any MCP server: `plune mcp lint`
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
- #### Contract snapshots: `plune mcp snapshot` and `plune mcp diff`
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 (runnerCode !== void 0 && notRun === 0 && Object.values(counts3).every((k) => !k)) {
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 (runnerCode !== void 0 && notRun === 0 && Object.values(counts).every((k) => !k)) {
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 (runnerCode !== void 0 && notRun === 0 && Object.values(counts).every((k) => !k)) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plune-ai/cli",
3
- "version": "0.34.5",
3
+ "version": "0.34.6",
4
4
  "description": "AI-powered assertion testing for LLM apps — write assertions, run them against any provider, get a pass/fail report (local, CI, or diff).",
5
5
  "keywords": [
6
6
  "ai",