plures 1.12.0 → 1.14.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
@@ -51,7 +51,7 @@ if (event.type === "task.completed" && event.data.result?.agreement === 1) {
51
51
  - `plures.maps.createKey(app, { label? })`, `.keys(app)`, `.revokeKey(app, id)`: map keys for CI, which can only read and upload that app's map. `.workflow(app, branch?)` returns the GitHub Action that maps on every push; `npx plures map workflow` writes it to `.github/workflows/plures-map.yml`. Or use **Map with GitHub** on the app's map page and Plures opens the pull request for you, then **Turn on mapping** merges it. `plures.maps.startRun(app)` and `.runs(app)`: in CI with no Claude key, `npx plures map` starts a run where Plures runs Claude (first map free, then Claude's cost plus 30%, at most $5).
52
52
  - In your terminal, `npx plures map` opens your map page so you can watch it build (`--no-open` to skip), keeps one live line of what it's reading, and ends by drawing the map's flows with the checks Plures suggests. On GitHub and other CI it prints a plain log.
53
53
  - `npx plures map` shows each file as Claude Code reads it and sends the file names (never their contents) to Plures, so the map page shows the reading live and the map builds itself when it lands. `plures.maps.progress(app, { session, phase, events })` is the call behind it.
54
- - `plures.github.repos()`, `.connect(app, "owner/repo")`, `.turnOn(app)`, `.status(app)`, `.disconnect(app)`: Map with GitHub from code. Plures sets the repo's map key secret and opens a pull request adding the workflow; `turnOn` merges it. Installing the Plures GitHub App needs a browser once (`install_url`). From a terminal: `npx plures map github connect`, then `npx plures map github on`.
54
+ - `plures.github.repos()`, `.connect(app, "owner/repo")`, `.turnOn(app)`, `.run(app)` (map now), `.status(app)`, `.disconnect(app)`: Map with GitHub from code. Plures sets the repo's map key secret and opens a pull request adding the workflow; `turnOn` merges it. Installing the Plures GitHub App needs a browser once (`install_url`). From a terminal: `npx plures map github connect`, then `npx plures map github on`; `npx plures map github run` maps again now.
55
55
  - `plures.quality.sample({ rubric, output, input? })`: send every output; a share is graded by people against the rubric, with personal details taken out first. `plures.quality.rubrics.create({ name, question, options, sample_rate })`, `.retrieve(name)` (score by day, this week against the four before, drift), `.list()`, `.update()`, `.del()`. `plures.quality.compare({ release, input, a, b })` and `plures.quality.release(name)` for blind release checks. `plures.quality.export(rubric)` returns graded samples for evals or fine tuning.
56
56
  - `plures.testMix.set({ unanimous: 70, split: 20, tie: 5, expired: 5 })`, `.get()`, `.clear()`: with a test key, makes a share of test tasks split, tie or expire at random so you can test routing to your team without changing your agent. A task can override it with `metadata.test_mix`, and `metadata.test_outcome` beats both.
57
57
  - `plures.routing.get()`, `plures.routing.set({ groups, rules, team })`: named teams of your own people and rules by category, like `{ groups: [{ name: "Finance", emails: ["ap@acme.com"] }], rules: [{ category: "refunds", route: "team", group: "Finance" }] }`. What you send replaces what's there; the rest stays.
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var VERSION = "1.12.0";
2
+ var VERSION = "1.14.0";
3
3
  var PluresError = class extends Error {
4
4
  constructor(message, status, type) {
5
5
  super(message);
@@ -130,6 +130,8 @@ var Plures = class {
130
130
  /** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
131
131
  connect: (app, repo) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github`, { repo }),
132
132
  /** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
133
+ /** Map now: runs the app's map workflow on GitHub. { update: true } first updates an older workflow. Watch it on the map page. */
134
+ run: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/run`, opts),
133
135
  turnOn: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/merge`, {}),
134
136
  /** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
135
137
  disconnect: (app) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/github`)
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.12.0";
33
+ var VERSION = "1.14.0";
34
34
  var PluresError = class extends Error {
35
35
  constructor(message, status, type) {
36
36
  super(message);
@@ -161,6 +161,8 @@ var Plures = class {
161
161
  /** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
162
162
  connect: (app2, repo) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/github`, { repo }),
163
163
  /** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
164
+ /** Map now: runs the app's map workflow on GitHub. { update: true } first updates an older workflow. Watch it on the map page. */
165
+ run: (app2, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/github/run`, opts),
164
166
  turnOn: (app2) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/github/merge`, {}),
165
167
  /** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
166
168
  disconnect: (app2) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app2)}/github`)
@@ -504,7 +506,8 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
504
506
  `);
505
507
  const prompt = `${guide}
506
508
 
507
- Write the map to ${FILE} in the current directory now. Read the code; don't run it or change anything else.`;
509
+ 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.`;
510
+ 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.`;
508
511
  const live = key ? client() : null;
509
512
  const session = (0, import_node_crypto.randomBytes)(12).toString("base64url");
510
513
  const source = process.env.GITHUB_ACTIONS ? "github" : "cli";
@@ -530,8 +533,8 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
530
533
  let result = "";
531
534
  const ticker = fancy ? liveLine() : null;
532
535
  ticker?.start();
533
- const code = await new Promise((resolve) => {
534
- 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 });
536
+ const runClaude = (args) => new Promise((resolve) => {
537
+ 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 });
535
538
  let buf = "";
536
539
  p.stdout.on("data", (chunk) => {
537
540
  buf += chunk.toString();
@@ -567,18 +570,21 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
567
570
  p.on("close", (n) => resolve(n ?? 1));
568
571
  p.on("error", () => resolve(1));
569
572
  });
573
+ const wrote = () => (0, import_node_fs.existsSync)(FILE) && (0, import_node_fs.statSync)(FILE).mtimeMs !== before;
574
+ let code = await runClaude(["-p", prompt]);
575
+ if (!wrote()) code = await runClaude(["-p", nudge, "--continue"]);
570
576
  clearInterval(tick);
571
577
  ticker?.stop();
572
578
  if (result && !fancy) console.log(`
573
579
  ${result.trim()}
574
580
  `);
575
581
  if (fancy) console.log(` ${cyan("\u2713")} ${reads} ${reads === 1 ? "file" : "files"} read. ${dim("Sending the map to Plures. No source code is sent.")}`);
576
- const ok = code === 0 && (0, import_node_fs.existsSync)(FILE) && (0, import_node_fs.statSync)(FILE).mtimeMs !== before;
582
+ const ok = code === 0 && wrote();
577
583
  if (!ok) {
578
584
  phase = "failed";
579
585
  await flush();
580
586
  } else await flush();
581
- 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.`);
587
+ 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.`}`);
582
588
  await upload(FILE);
583
589
  }
584
590
  async function workflow() {
@@ -617,6 +623,20 @@ Turn it on: ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${a
617
623
  };
618
624
  try {
619
625
  if (!action || action === "status") return show(await p.github.status(app));
626
+ if (action === "run") {
627
+ let r;
628
+ try {
629
+ r = await p.github.run(app);
630
+ } catch (e) {
631
+ if (!(e instanceof PluresError && e.type === "workflow_outdated")) throw e;
632
+ console.log(dim("This repo's map workflow is from before Map now. Updating it, which merges a small change to the workflow file."));
633
+ r = await p.github.run(app, { update: true });
634
+ }
635
+ const url = `${base}/dashboard/apps/${encodeURIComponent(app)}/map`;
636
+ console.log(`${bold("Mapping now on GitHub.")} Watch it build: ${cyan(url)}`);
637
+ if (fancy && !noOpen) await openBrowser(url);
638
+ return void r;
639
+ }
620
640
  if (action === "on") {
621
641
  const r = await p.github.turnOn(app);
622
642
  console.log(r.already_merged ? "Mapping was already on." : `${bold("Mapping is on.")} The first run is starting on GitHub.`);
@@ -638,7 +658,7 @@ Turn it on: ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${a
638
658
  It adds one workflow file. Review it, then turn mapping on with ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${app}` : ""}, or merge it on GitHub.`);
639
659
  return;
640
660
  }
641
- die("npx plures map github [connect [owner/repo] | on | off]");
661
+ die("npx plures map github [connect [owner/repo] | on | run | off]");
642
662
  } catch (e) {
643
663
  if (e instanceof PluresError) die(e.message);
644
664
  throw e;
@@ -655,6 +675,7 @@ async function main() {
655
675
  npx plures map workflow add a GitHub Action that maps on every push
656
676
  npx plures map github connect let Plures add that Action by pull request
657
677
  npx plures map github on merge it, which turns mapping on
678
+ npx plures map github run map now on GitHub, and watch it build
658
679
 
659
680
  Docs: https://plures.si/docs`);
660
681
  return;
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Plures,
4
4
  PluresError,
5
5
  VERSION
6
- } from "./chunk-GDI4KDAH.js";
6
+ } from "./chunk-7OMKME5Z.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { spawnSync, spawn } from "child_process";
@@ -288,7 +288,8 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
288
288
  `);
289
289
  const prompt = `${guide}
290
290
 
291
- Write the map to ${FILE} in the current directory now. Read the code; don't run it or change anything else.`;
291
+ 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.`;
292
+ 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
293
  const live = key ? client() : null;
293
294
  const session = randomBytes(12).toString("base64url");
294
295
  const source = process.env.GITHUB_ACTIONS ? "github" : "cli";
@@ -314,8 +315,8 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
314
315
  let result = "";
315
316
  const ticker = fancy ? liveLine() : null;
316
317
  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 });
318
+ const runClaude = (args) => new Promise((resolve) => {
319
+ 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
320
  let buf = "";
320
321
  p.stdout.on("data", (chunk) => {
321
322
  buf += chunk.toString();
@@ -351,18 +352,21 @@ Write the map to ${FILE} in the current directory now. Read the code; don't run
351
352
  p.on("close", (n) => resolve(n ?? 1));
352
353
  p.on("error", () => resolve(1));
353
354
  });
355
+ const wrote = () => existsSync(FILE) && statSync(FILE).mtimeMs !== before;
356
+ let code = await runClaude(["-p", prompt]);
357
+ if (!wrote()) code = await runClaude(["-p", nudge, "--continue"]);
354
358
  clearInterval(tick);
355
359
  ticker?.stop();
356
360
  if (result && !fancy) console.log(`
357
361
  ${result.trim()}
358
362
  `);
359
363
  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;
364
+ const ok = code === 0 && wrote();
361
365
  if (!ok) {
362
366
  phase = "failed";
363
367
  await flush();
364
368
  } 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.`);
369
+ 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
370
  await upload(FILE);
367
371
  }
368
372
  async function workflow() {
@@ -401,6 +405,20 @@ Turn it on: ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${a
401
405
  };
402
406
  try {
403
407
  if (!action || action === "status") return show(await p.github.status(app));
408
+ if (action === "run") {
409
+ let r;
410
+ try {
411
+ r = await p.github.run(app);
412
+ } catch (e) {
413
+ if (!(e instanceof PluresError && e.type === "workflow_outdated")) throw e;
414
+ console.log(dim("This repo's map workflow is from before Map now. Updating it, which merges a small change to the workflow file."));
415
+ r = await p.github.run(app, { update: true });
416
+ }
417
+ const url = `${base}/dashboard/apps/${encodeURIComponent(app)}/map`;
418
+ console.log(`${bold("Mapping now on GitHub.")} Watch it build: ${cyan(url)}`);
419
+ if (fancy && !noOpen) await openBrowser(url);
420
+ return void r;
421
+ }
404
422
  if (action === "on") {
405
423
  const r = await p.github.turnOn(app);
406
424
  console.log(r.already_merged ? "Mapping was already on." : `${bold("Mapping is on.")} The first run is starting on GitHub.`);
@@ -422,7 +440,7 @@ Turn it on: ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${a
422
440
  It adds one workflow file. Review it, then turn mapping on with ${cyan("npx plures map github on")}${app !== "default" ? ` --app ${app}` : ""}, or merge it on GitHub.`);
423
441
  return;
424
442
  }
425
- die("npx plures map github [connect [owner/repo] | on | off]");
443
+ die("npx plures map github [connect [owner/repo] | on | run | off]");
426
444
  } catch (e) {
427
445
  if (e instanceof PluresError) die(e.message);
428
446
  throw e;
@@ -439,6 +457,7 @@ async function main() {
439
457
  npx plures map workflow add a GitHub Action that maps on every push
440
458
  npx plures map github connect let Plures add that Action by pull request
441
459
  npx plures map github on merge it, which turns mapping on
460
+ npx plures map github run map now on GitHub, and watch it build
442
461
 
443
462
  Docs: https://plures.si/docs`);
444
463
  return;
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.12.0";
29
+ var VERSION = "1.14.0";
30
30
  var PluresError = class extends Error {
31
31
  constructor(message, status, type) {
32
32
  super(message);
@@ -157,6 +157,8 @@ var Plures = class {
157
157
  /** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
158
158
  connect: (app, repo) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github`, { repo }),
159
159
  /** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
160
+ /** Map now: runs the app's map workflow on GitHub. { update: true } first updates an older workflow. Watch it on the map page. */
161
+ run: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/run`, opts),
160
162
  turnOn: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/merge`, {}),
161
163
  /** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
162
164
  disconnect: (app) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/github`)
package/dist/index.d.cts CHANGED
@@ -350,7 +350,7 @@ type WebhookEvent = {
350
350
  created_at: string;
351
351
  data: Task;
352
352
  };
353
- declare const VERSION = "1.12.0";
353
+ declare const VERSION = "1.14.0";
354
354
  type GitHubLink = {
355
355
  object: "github_link";
356
356
  app: string;
@@ -550,6 +550,15 @@ declare class Plures {
550
550
  /** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
551
551
  connect: (app: string, repo: string) => Promise<GitHubLink>;
552
552
  /** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
553
+ /** Map now: runs the app's map workflow on GitHub. { update: true } first updates an older workflow. Watch it on the map page. */
554
+ run: (app: string, opts?: {
555
+ update?: boolean;
556
+ }) => Promise<{
557
+ object: "map_run_request";
558
+ app: string;
559
+ started: true;
560
+ updated_workflow: boolean;
561
+ }>;
553
562
  turnOn: (app: string) => Promise<{
554
563
  object: "github_merge";
555
564
  app: string;
package/dist/index.d.ts CHANGED
@@ -350,7 +350,7 @@ type WebhookEvent = {
350
350
  created_at: string;
351
351
  data: Task;
352
352
  };
353
- declare const VERSION = "1.12.0";
353
+ declare const VERSION = "1.14.0";
354
354
  type GitHubLink = {
355
355
  object: "github_link";
356
356
  app: string;
@@ -550,6 +550,15 @@ declare class Plures {
550
550
  /** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
551
551
  connect: (app: string, repo: string) => Promise<GitHubLink>;
552
552
  /** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
553
+ /** Map now: runs the app's map workflow on GitHub. { update: true } first updates an older workflow. Watch it on the map page. */
554
+ run: (app: string, opts?: {
555
+ update?: boolean;
556
+ }) => Promise<{
557
+ object: "map_run_request";
558
+ app: string;
559
+ started: true;
560
+ updated_workflow: boolean;
561
+ }>;
553
562
  turnOn: (app: string) => Promise<{
554
563
  object: "github_merge";
555
564
  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-GDI4KDAH.js";
6
+ } from "./chunk-7OMKME5Z.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.12.0",
3
+ "version": "1.14.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",