plures 1.13.0 → 1.15.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.
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var VERSION = "1.13.0";
2
+ var VERSION = "1.15.0";
3
3
  var PluresError = class extends Error {
4
4
  constructor(message, status, type) {
5
5
  super(message);
package/dist/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var import_node_path = require("path");
30
30
  var import_node_fs = require("fs");
31
31
 
32
32
  // src/index.ts
33
- var VERSION = "1.13.0";
33
+ var VERSION = "1.15.0";
34
34
  var PluresError = class extends Error {
35
35
  constructor(message, status, type) {
36
36
  super(message);
@@ -327,7 +327,9 @@ function drawMap(m, url) {
327
327
  const chip = (id) => {
328
328
  const n = nodes.get(id), c2 = cps.get(id);
329
329
  const label = cut(n.label, 34);
330
- const tag = !c2 ? "" : c2.status === "added" ? ` ${C.cyan("\u2713 checked")}` : ` ${C.violet("\u25CC check suggested")}`;
330
+ const rate = c2?.draft?.sample;
331
+ const often = rate ? `, 1 in ${Math.round(1 / rate)}` : "";
332
+ const tag = !c2 ? "" : c2.status === "added" ? ` ${C.cyan(`\u2713 checked${often}`)}` : ` ${C.violet(`\u25CC check suggested${often}`)}`;
331
333
  if (n.kind === "action") {
332
334
  if (c2?.status === "added") return `${C.cyan(ICON.action)} ${C.text(label)}${tag}`;
333
335
  const risky = n.risk === "high" || n.risk === "medium";
@@ -379,6 +381,9 @@ function drawMap(m, url) {
379
381
  const on = actions.filter((a) => cps.get(a.id)?.status === "added").length;
380
382
  const sugg = m.checkpoints.filter((c2) => c2.status === "suggested").length;
381
383
  out.write(` ${LANES.map((c2) => c2("\u2501")).join("")} ${C.bold(C.text(sugg ? `Plures found ${sugg} ${sugg === 1 ? "place" : "places"} a person should check.` : "Your map is ready."))}
384
+ `);
385
+ const scored = m.engine?.version;
386
+ if (scored) out.write(` ${C.dim(`Scored by the Plures oversight engine ${scored}: how risky each action is, and how often people check it.`)}
382
387
  `);
383
388
  out.write(` ${C.mute(`${on} of ${actions.length} ${actions.length === 1 ? "action" : "actions"} with consequences ${on === 1 ? "has" : "have"} a human check.`)}
384
389
 
@@ -499,14 +504,15 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
499
504
  }
500
505
  const pageUrl = `${base}/dashboard/apps/${encodeURIComponent(app)}/map`;
501
506
  if (fancy) {
502
- banner(app, "Reading this repo on your machine with Claude Code. Only file names and the map go to Plures.");
507
+ banner(app, "Claude Code reads this repo on your machine for the Plures oversight engine. Only file names and the map go to Plures.");
503
508
  if (key && !noOpen && await openBrowser(pageUrl)) console.log(` ${dim("Watch it live in your browser:")} ${cyan(pageUrl)}
504
509
  `);
505
510
  } else console.log(`Reading this repo ${process.env.CI ? "on this runner" : "on your machine"} with Claude Code. ${dim("Only the map is sent to Plures.")}
506
511
  `);
507
512
  const prompt = `${guide}
508
513
 
509
- Write the map to ${FILE} in the current directory now. Read the code; don't run it or change anything else.`;
514
+ Write the map to ${FILE} in the current directory now, as JSON, with the Write tool. Read the code; don't run it or change anything else. Don't write a script to generate the map: you can't run scripts or other commands here, so write the JSON itself.`;
515
+ const nudge = `${FILE} doesn't exist yet. Write it now with the Write tool: the complete map as JSON, directly. You can't run scripts or commands here, so don't write one. Use what you've already read.`;
510
516
  const live = key ? client() : null;
511
517
  const session = (0, import_node_crypto.randomBytes)(12).toString("base64url");
512
518
  const source = process.env.GITHUB_ACTIONS ? "github" : "cli";
@@ -532,8 +538,8 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
532
538
  let result = "";
533
539
  const ticker = fancy ? liveLine() : null;
534
540
  ticker?.start();
535
- const code = await new Promise((resolve) => {
536
- const p = (0, import_node_child_process.spawn)("claude", ["-p", prompt, "--output-format", "stream-json", "--verbose", "--allowedTools", "Read,Grep,Glob,Write,Bash(git rev-parse:*),Bash(git remote:*)"], { stdio: ["ignore", "pipe", "inherit"], env: childEnv });
541
+ const runClaude = (args) => new Promise((resolve) => {
542
+ const p = (0, import_node_child_process.spawn)("claude", [...args, "--output-format", "stream-json", "--verbose", "--allowedTools", "Read,Grep,Glob,Write,Bash(git rev-parse:*),Bash(git remote:*)"], { stdio: ["ignore", "pipe", "inherit"], env: childEnv });
537
543
  let buf = "";
538
544
  p.stdout.on("data", (chunk) => {
539
545
  buf += chunk.toString();
@@ -569,18 +575,21 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
569
575
  p.on("close", (n) => resolve(n ?? 1));
570
576
  p.on("error", () => resolve(1));
571
577
  });
578
+ const wrote = () => (0, import_node_fs.existsSync)(FILE) && (0, import_node_fs.statSync)(FILE).mtimeMs !== before;
579
+ let code = await runClaude(["-p", prompt]);
580
+ if (!wrote()) code = await runClaude(["-p", nudge, "--continue"]);
572
581
  clearInterval(tick);
573
582
  ticker?.stop();
574
583
  if (result && !fancy) console.log(`
575
584
  ${result.trim()}
576
585
  `);
577
586
  if (fancy) console.log(` ${cyan("\u2713")} ${reads} ${reads === 1 ? "file" : "files"} read. ${dim("Sending the map to Plures. No source code is sent.")}`);
578
- const ok = code === 0 && (0, import_node_fs.existsSync)(FILE) && (0, import_node_fs.statSync)(FILE).mtimeMs !== before;
587
+ const ok = code === 0 && wrote();
579
588
  if (!ok) {
580
589
  phase = "failed";
581
590
  await flush();
582
591
  } else await flush();
583
- if (code !== 0 || !(0, import_node_fs.existsSync)(FILE) || (0, import_node_fs.statSync)(FILE).mtimeMs === before) die(`Claude Code didn't write ${FILE}. Ask it directly, then run npx plures map upload.`);
592
+ if (!ok) die(`Claude Code didn't write ${FILE}${process.env.CI ? ". This run costs nothing." : `. Ask it directly, then run npx plures map upload.`}`);
584
593
  await upload(FILE);
585
594
  }
586
595
  async function workflow() {
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Plures,
4
4
  PluresError,
5
5
  VERSION
6
- } from "./chunk-GIOZT6FB.js";
6
+ } from "./chunk-JSR4MDEK.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { spawnSync, spawn } from "child_process";
@@ -109,7 +109,9 @@ function drawMap(m, url) {
109
109
  const chip = (id) => {
110
110
  const n = nodes.get(id), c2 = cps.get(id);
111
111
  const label = cut(n.label, 34);
112
- const tag = !c2 ? "" : c2.status === "added" ? ` ${C.cyan("\u2713 checked")}` : ` ${C.violet("\u25CC check suggested")}`;
112
+ const rate = c2?.draft?.sample;
113
+ const often = rate ? `, 1 in ${Math.round(1 / rate)}` : "";
114
+ const tag = !c2 ? "" : c2.status === "added" ? ` ${C.cyan(`\u2713 checked${often}`)}` : ` ${C.violet(`\u25CC check suggested${often}`)}`;
113
115
  if (n.kind === "action") {
114
116
  if (c2?.status === "added") return `${C.cyan(ICON.action)} ${C.text(label)}${tag}`;
115
117
  const risky = n.risk === "high" || n.risk === "medium";
@@ -161,6 +163,9 @@ function drawMap(m, url) {
161
163
  const on = actions.filter((a) => cps.get(a.id)?.status === "added").length;
162
164
  const sugg = m.checkpoints.filter((c2) => c2.status === "suggested").length;
163
165
  out.write(` ${LANES.map((c2) => c2("\u2501")).join("")} ${C.bold(C.text(sugg ? `Plures found ${sugg} ${sugg === 1 ? "place" : "places"} a person should check.` : "Your map is ready."))}
166
+ `);
167
+ const scored = m.engine?.version;
168
+ if (scored) out.write(` ${C.dim(`Scored by the Plures oversight engine ${scored}: how risky each action is, and how often people check it.`)}
164
169
  `);
165
170
  out.write(` ${C.mute(`${on} of ${actions.length} ${actions.length === 1 ? "action" : "actions"} with consequences ${on === 1 ? "has" : "have"} a human check.`)}
166
171
 
@@ -281,14 +286,15 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
281
286
  }
282
287
  const pageUrl = `${base}/dashboard/apps/${encodeURIComponent(app)}/map`;
283
288
  if (fancy) {
284
- banner(app, "Reading this repo on your machine with Claude Code. Only file names and the map go to Plures.");
289
+ banner(app, "Claude Code reads this repo on your machine for the Plures oversight engine. Only file names and the map go to Plures.");
285
290
  if (key && !noOpen && await openBrowser(pageUrl)) console.log(` ${dim("Watch it live in your browser:")} ${cyan(pageUrl)}
286
291
  `);
287
292
  } else console.log(`Reading this repo ${process.env.CI ? "on this runner" : "on your machine"} with Claude Code. ${dim("Only the map is sent to Plures.")}
288
293
  `);
289
294
  const prompt = `${guide}
290
295
 
291
- Write the map to ${FILE} in the current directory now. Read the code; don't run it or change anything else.`;
296
+ Write the map to ${FILE} in the current directory now, as JSON, with the Write tool. Read the code; don't run it or change anything else. Don't write a script to generate the map: you can't run scripts or other commands here, so write the JSON itself.`;
297
+ const nudge = `${FILE} doesn't exist yet. Write it now with the Write tool: the complete map as JSON, directly. You can't run scripts or commands here, so don't write one. Use what you've already read.`;
292
298
  const live = key ? client() : null;
293
299
  const session = randomBytes(12).toString("base64url");
294
300
  const source = process.env.GITHUB_ACTIONS ? "github" : "cli";
@@ -314,8 +320,8 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
314
320
  let result = "";
315
321
  const ticker = fancy ? liveLine() : null;
316
322
  ticker?.start();
317
- const code = await new Promise((resolve) => {
318
- const p = spawn("claude", ["-p", prompt, "--output-format", "stream-json", "--verbose", "--allowedTools", "Read,Grep,Glob,Write,Bash(git rev-parse:*),Bash(git remote:*)"], { stdio: ["ignore", "pipe", "inherit"], env: childEnv });
323
+ const runClaude = (args) => new Promise((resolve) => {
324
+ const p = spawn("claude", [...args, "--output-format", "stream-json", "--verbose", "--allowedTools", "Read,Grep,Glob,Write,Bash(git rev-parse:*),Bash(git remote:*)"], { stdio: ["ignore", "pipe", "inherit"], env: childEnv });
319
325
  let buf = "";
320
326
  p.stdout.on("data", (chunk) => {
321
327
  buf += chunk.toString();
@@ -351,18 +357,21 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
351
357
  p.on("close", (n) => resolve(n ?? 1));
352
358
  p.on("error", () => resolve(1));
353
359
  });
360
+ const wrote = () => existsSync(FILE) && statSync(FILE).mtimeMs !== before;
361
+ let code = await runClaude(["-p", prompt]);
362
+ if (!wrote()) code = await runClaude(["-p", nudge, "--continue"]);
354
363
  clearInterval(tick);
355
364
  ticker?.stop();
356
365
  if (result && !fancy) console.log(`
357
366
  ${result.trim()}
358
367
  `);
359
368
  if (fancy) console.log(` ${cyan("\u2713")} ${reads} ${reads === 1 ? "file" : "files"} read. ${dim("Sending the map to Plures. No source code is sent.")}`);
360
- const ok = code === 0 && existsSync(FILE) && statSync(FILE).mtimeMs !== before;
369
+ const ok = code === 0 && wrote();
361
370
  if (!ok) {
362
371
  phase = "failed";
363
372
  await flush();
364
373
  } else await flush();
365
- if (code !== 0 || !existsSync(FILE) || statSync(FILE).mtimeMs === before) die(`Claude Code didn't write ${FILE}. Ask it directly, then run npx plures map upload.`);
374
+ if (!ok) die(`Claude Code didn't write ${FILE}${process.env.CI ? ". This run costs nothing." : `. Ask it directly, then run npx plures map upload.`}`);
366
375
  await upload(FILE);
367
376
  }
368
377
  async function workflow() {
package/dist/index.cjs CHANGED
@@ -26,7 +26,7 @@ __export(index_exports, {
26
26
  default: () => index_default
27
27
  });
28
28
  module.exports = __toCommonJS(index_exports);
29
- var VERSION = "1.13.0";
29
+ var VERSION = "1.15.0";
30
30
  var PluresError = class extends Error {
31
31
  constructor(message, status, type) {
32
32
  super(message);
package/dist/index.d.cts CHANGED
@@ -4,7 +4,7 @@ type Option = {
4
4
  };
5
5
  /** "verifiers" asks ID verified people first; "team" goes straight to your own people ($0.05 per internal vote live). */
6
6
  type Route = "verifiers" | "team";
7
- type TaskStatus = "pending" | "completed" | "expired" | "canceled";
7
+ type TaskStatus = "pending" | "completed" | "expired" | "canceled" | "skipped";
8
8
  type Result = {
9
9
  answer: string | null;
10
10
  label: string | null;
@@ -57,6 +57,8 @@ type Task = {
57
57
  app?: string | null;
58
58
  mode: "live" | "test";
59
59
  status: TaskStatus;
60
+ /** false when a sampled check didn't pick this task: it wasn't sent to people, and result.answer is the first option. */
61
+ sampled?: boolean;
60
62
  question: string;
61
63
  context: string | null;
62
64
  image_url: string | null;
@@ -350,7 +352,7 @@ type WebhookEvent = {
350
352
  created_at: string;
351
353
  data: Task;
352
354
  };
353
- declare const VERSION = "1.13.0";
355
+ declare const VERSION = "1.15.0";
354
356
  type GitHubLink = {
355
357
  object: "github_link";
356
358
  app: string;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ type Option = {
4
4
  };
5
5
  /** "verifiers" asks ID verified people first; "team" goes straight to your own people ($0.05 per internal vote live). */
6
6
  type Route = "verifiers" | "team";
7
- type TaskStatus = "pending" | "completed" | "expired" | "canceled";
7
+ type TaskStatus = "pending" | "completed" | "expired" | "canceled" | "skipped";
8
8
  type Result = {
9
9
  answer: string | null;
10
10
  label: string | null;
@@ -57,6 +57,8 @@ type Task = {
57
57
  app?: string | null;
58
58
  mode: "live" | "test";
59
59
  status: TaskStatus;
60
+ /** false when a sampled check didn't pick this task: it wasn't sent to people, and result.answer is the first option. */
61
+ sampled?: boolean;
60
62
  question: string;
61
63
  context: string | null;
62
64
  image_url: string | null;
@@ -350,7 +352,7 @@ type WebhookEvent = {
350
352
  created_at: string;
351
353
  data: Task;
352
354
  };
353
- declare const VERSION = "1.13.0";
355
+ declare const VERSION = "1.15.0";
354
356
  type GitHubLink = {
355
357
  object: "github_link";
356
358
  app: string;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PluresError,
4
4
  VERSION,
5
5
  index_default
6
- } from "./chunk-GIOZT6FB.js";
6
+ } from "./chunk-JSR4MDEK.js";
7
7
  export {
8
8
  Plures,
9
9
  PluresError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plures",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "description": "Human judgment as an API for AI agents. Send a decision to ID verified people and get their consensus back.",
5
5
  "keywords": [
6
6
  "human-in-the-loop",