@plune-ai/cli 0.34.4 → 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/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}` };
@@ -467,7 +467,15 @@ var PLUNE_TOOLS = [
467
467
  }),
468
468
  output: import_zod2.z.object({
469
469
  revealed: import_zod2.z.array(import_zod2.z.string()),
470
- available: import_zod2.z.array(import_zod2.z.object({ name: import_zod2.z.string(), title: import_zod2.z.string(), description: import_zod2.z.string(), write: import_zod2.z.boolean() })),
470
+ available: import_zod2.z.array(
471
+ import_zod2.z.object({
472
+ name: import_zod2.z.string(),
473
+ title: import_zod2.z.string(),
474
+ description: import_zod2.z.string(),
475
+ write: import_zod2.z.boolean(),
476
+ inputSchema: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional().describe("JSON Schema of the arguments, where the tool list is fixed and a tool is called by its name")
477
+ })
478
+ ),
471
479
  visible: import_zod2.z.array(import_zod2.z.string())
472
480
  }),
473
481
  write: false,
@@ -563,6 +571,7 @@ function defaultVisibleTools(scope) {
563
571
 
564
572
  // src/mcp-tools/execute.ts
565
573
  var import_node_crypto = require("crypto");
574
+ var import_zod3 = require("zod");
566
575
 
567
576
  // src/mcp-tools/client.ts
568
577
  var PluneApiError = class extends Error {
@@ -871,11 +880,18 @@ function createPluneToolExecutor(opts) {
871
880
  if (unknown.length > 0) return fail("invalid_input", `No such tool: ${unknown.join(", ")}. Call this tool without names to see what there is.`);
872
881
  const refused = names.filter((n) => scope === "read" && pluneTool(n).write);
873
882
  if (refused.length > 0) return fail("read_only_token", readScopeMessage(`${refused.join(", ")} cannot be switched on`));
874
- for (const n of names) revealed.add(n);
883
+ const fixed = opts.fixedToolList === true;
884
+ if (!fixed) for (const n of names) revealed.add(n);
875
885
  const words = (i["query"] ?? "").toLowerCase().split(/\s+/).filter(Boolean);
876
886
  const visible = visibleTools();
877
- const available = PLUNE_TOOLS.filter((t) => !visible.includes(t.name) && !(scope === "read" && t.write)).filter((t) => words.every((w) => `${t.name} ${t.title} ${t.description}`.toLowerCase().includes(w))).map((t) => ({ name: t.name, title: t.title, description: t.description, write: t.write }));
878
- return { revealed: names, available, visible };
887
+ const available = PLUNE_TOOLS.filter((t) => !visible.includes(t.name) && !(scope === "read" && t.write)).filter((t) => words.every((w) => `${t.name} ${t.title} ${t.description}`.toLowerCase().includes(w))).map((t) => ({
888
+ name: t.name,
889
+ title: t.title,
890
+ description: t.description,
891
+ write: t.write,
892
+ ...fixed ? { inputSchema: import_zod3.z.toJSONSchema(t.input, { target: "draft-2020-12", io: "input" }) } : {}
893
+ }));
894
+ return { revealed: fixed ? [] : names, available, visible };
879
895
  },
880
896
  async plune_cases_update(i) {
881
897
  const cases = i["cases"];
@@ -676,6 +676,7 @@ declare const PLUNE_TOOLS: readonly [{
676
676
  title: z.ZodString;
677
677
  description: z.ZodString;
678
678
  write: z.ZodBoolean;
679
+ inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
679
680
  }, z.core.$strip>>;
680
681
  visible: z.ZodArray<z.ZodString>;
681
682
  }, z.core.$strip>;
@@ -903,6 +904,11 @@ interface PluneToolExecutorOptions {
903
904
  fetchImpl?: typeof fetch | undefined;
904
905
  userAgent?: string | undefined;
905
906
  now?: (() => number) | undefined;
907
+ /**
908
+ * The client's tool list never changes: discover_tools switches nothing on and describes the hidden tools with their
909
+ * input schemas, and a hidden tool is called by its name. MCP 2026-07-28 asks this of tools/list.
910
+ */
911
+ fixedToolList?: boolean | undefined;
906
912
  }
907
913
  interface PluneToolExecutor {
908
914
  readonly sessionId: string;
@@ -676,6 +676,7 @@ declare const PLUNE_TOOLS: readonly [{
676
676
  title: z.ZodString;
677
677
  description: z.ZodString;
678
678
  write: z.ZodBoolean;
679
+ inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
679
680
  }, z.core.$strip>>;
680
681
  visible: z.ZodArray<z.ZodString>;
681
682
  }, z.core.$strip>;
@@ -903,6 +904,11 @@ interface PluneToolExecutorOptions {
903
904
  fetchImpl?: typeof fetch | undefined;
904
905
  userAgent?: string | undefined;
905
906
  now?: (() => number) | undefined;
907
+ /**
908
+ * The client's tool list never changes: discover_tools switches nothing on and describes the hidden tools with their
909
+ * input schemas, and a hidden tool is called by its name. MCP 2026-07-28 asks this of tools/list.
910
+ */
911
+ fixedToolList?: boolean | undefined;
906
912
  }
907
913
  interface PluneToolExecutor {
908
914
  readonly sessionId: string;
package/dist/mcp-tools.js CHANGED
@@ -431,7 +431,15 @@ var PLUNE_TOOLS = [
431
431
  }),
432
432
  output: z2.object({
433
433
  revealed: z2.array(z2.string()),
434
- available: z2.array(z2.object({ name: z2.string(), title: z2.string(), description: z2.string(), write: z2.boolean() })),
434
+ available: z2.array(
435
+ z2.object({
436
+ name: z2.string(),
437
+ title: z2.string(),
438
+ description: z2.string(),
439
+ write: z2.boolean(),
440
+ inputSchema: z2.record(z2.string(), z2.unknown()).optional().describe("JSON Schema of the arguments, where the tool list is fixed and a tool is called by its name")
441
+ })
442
+ ),
435
443
  visible: z2.array(z2.string())
436
444
  }),
437
445
  write: false,
@@ -527,6 +535,7 @@ function defaultVisibleTools(scope) {
527
535
 
528
536
  // src/mcp-tools/execute.ts
529
537
  import { createHash, randomBytes, randomUUID } from "crypto";
538
+ import { z as z3 } from "zod";
530
539
 
531
540
  // src/mcp-tools/client.ts
532
541
  var PluneApiError = class extends Error {
@@ -835,11 +844,18 @@ function createPluneToolExecutor(opts) {
835
844
  if (unknown.length > 0) return fail("invalid_input", `No such tool: ${unknown.join(", ")}. Call this tool without names to see what there is.`);
836
845
  const refused = names.filter((n) => scope === "read" && pluneTool(n).write);
837
846
  if (refused.length > 0) return fail("read_only_token", readScopeMessage(`${refused.join(", ")} cannot be switched on`));
838
- for (const n of names) revealed.add(n);
847
+ const fixed = opts.fixedToolList === true;
848
+ if (!fixed) for (const n of names) revealed.add(n);
839
849
  const words = (i["query"] ?? "").toLowerCase().split(/\s+/).filter(Boolean);
840
850
  const visible = visibleTools();
841
- const available = PLUNE_TOOLS.filter((t) => !visible.includes(t.name) && !(scope === "read" && t.write)).filter((t) => words.every((w) => `${t.name} ${t.title} ${t.description}`.toLowerCase().includes(w))).map((t) => ({ name: t.name, title: t.title, description: t.description, write: t.write }));
842
- return { revealed: names, available, visible };
851
+ const available = PLUNE_TOOLS.filter((t) => !visible.includes(t.name) && !(scope === "read" && t.write)).filter((t) => words.every((w) => `${t.name} ${t.title} ${t.description}`.toLowerCase().includes(w))).map((t) => ({
852
+ name: t.name,
853
+ title: t.title,
854
+ description: t.description,
855
+ write: t.write,
856
+ ...fixed ? { inputSchema: z3.toJSONSchema(t.input, { target: "draft-2020-12", io: "input" }) } : {}
857
+ }));
858
+ return { revealed: fixed ? [] : names, available, visible };
843
859
  },
844
860
  async plune_cases_update(i) {
845
861
  const cases = i["cases"];
@@ -485,7 +485,7 @@ function cutErrorContext(attempt, home) {
485
485
  const text = attempt.errors.map((e) => e.text).filter((t) => t !== "").join("\n\n");
486
486
  return cut(machineless(text, attempt.repoRoot, home));
487
487
  }
488
- var printable = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f]/g, "?");
488
+ var printable = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g, "?");
489
489
  var mediaTypeOf = (contentType) => contentType?.split(";")[0]?.trim().toLowerCase() || void 0;
490
490
  var TEXT_TYPES = /* @__PURE__ */ new Set(["application/json", "text/plain"]);
491
491
  var isTextType = (contentType) => TEXT_TYPES.has(mediaTypeOf(contentType) ?? "");
@@ -435,7 +435,7 @@ function cutErrorContext(attempt, home) {
435
435
  const text = attempt.errors.map((e) => e.text).filter((t) => t !== "").join("\n\n");
436
436
  return cut(machineless(text, attempt.repoRoot, home));
437
437
  }
438
- var printable = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f]/g, "?");
438
+ var printable = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g, "?");
439
439
  var mediaTypeOf = (contentType) => contentType?.split(";")[0]?.trim().toLowerCase() || void 0;
440
440
  var TEXT_TYPES = /* @__PURE__ */ new Set(["application/json", "text/plain"]);
441
441
  var isTextType = (contentType) => TEXT_TYPES.has(mediaTypeOf(contentType) ?? "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plune-ai/cli",
3
- "version": "0.34.4",
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",
@@ -43,7 +43,8 @@
43
43
  "files": [
44
44
  "dist",
45
45
  "README.md",
46
- "LICENSE"
46
+ "LICENSE",
47
+ "THIRD_PARTY_NOTICES"
47
48
  ],
48
49
  "engines": {
49
50
  "node": ">=20.0.0"
@@ -68,7 +69,8 @@
68
69
  "dependencies": {
69
70
  "@anthropic-ai/sdk": "^0.102.0",
70
71
  "@clack/prompts": "^1.5.1",
71
- "@modelcontextprotocol/sdk": "^1.32.2",
72
+ "@modelcontextprotocol/client": "2.4.0",
73
+ "@modelcontextprotocol/server": "2.4.0",
72
74
  "ajv": "^8.17.1",
73
75
  "commander": "^12.0.0",
74
76
  "dotenv": "^16.4.0",
@@ -81,6 +83,7 @@
81
83
  },
82
84
  "devDependencies": {
83
85
  "@huggingface/transformers": "4.2.0",
86
+ "@modelcontextprotocol/sdk": "1.32.2",
84
87
  "@types/better-sqlite3": "^7.6.13",
85
88
  "@types/node": "^25.9.1",
86
89
  "@typescript-eslint/eslint-plugin": "^8.0.0",