orchestra-agents 1.0.0 → 1.0.1

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
@@ -1,11 +1,24 @@
1
1
  # Orchestra
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/orchestra-agents)](https://www.npmjs.com/package/orchestra-agents)
4
+ [![license](https://img.shields.io/npm/l/orchestra-agents)](./LICENSE)
5
+ [![node](https://img.shields.io/node/v/orchestra-agents)](https://nodejs.org)
6
+
3
7
  A multi-agent system in one file. One conductor agent takes your goal, breaks it into independent tasks, fans out specialist sub-agents in parallel (scouts, analysts, writers), sends adversarial critics to attack their work, then synthesizes everything into one final deliverable.
4
8
 
9
+ **Runs on your Claude subscription. No API credits required.**
10
+
5
11
  Built on the Claude API (`@anthropic-ai/sdk`). No framework. This is the "main agent talks to a bunch of sub-agents" pattern, owned end to end in plain JavaScript you can read in one sitting.
6
12
 
7
13
  ## Install
8
14
 
15
+ ```
16
+ npm install -g orchestra-agents
17
+ orchestra "your goal here"
18
+ ```
19
+
20
+ Or clone it and read the source (it's three small files):
21
+
9
22
  ```
10
23
  git clone https://github.com/vyn-store/orchestra-agents.git
11
24
  cd orchestra-agents
@@ -31,6 +44,42 @@ node orchestra.mjs "quick test" --cheap # haiku everywhere, costs pennies
31
44
  | `--worker M` | sub-agent model (default: same as --model) |
32
45
  | `--no-critics` | skip the adversarial pass |
33
46
 
47
+ ## What a run looks like
48
+
49
+ A real run, on a Claude subscription, costing nothing in API credits:
50
+
51
+ ```
52
+ $ orchestra-max "name 3 colours that work well together" --agents 2 --cheap
53
+
54
+ ORCHESTRA MAX subscription-powered · conductor=haiku workers=haiku max-agents=2
55
+ [goal] name 3 colours that work well together
56
+ [conductor] planning...
57
+ [conductor] strategy: Decompose into research, evaluation, and presentation tasks to
58
+ identify three visually distinct, complementary colors.
59
+ [conductor] casting 2 sub-agents
60
+ [agent 1] scout: Research popular, distinct colors
61
+ [agent 2] analyst: Evaluate color harmony and contrast
62
+ [agent 1] Research popular, distinct colors done
63
+ [agent 2] Evaluate color harmony and contrast done
64
+ [critic 1] score 8/10
65
+ [critic 2] score 7/10
66
+ [conductor] synthesizing final deliverable...
67
+
68
+ ------------------------------------------------------------
69
+ ### 1. Navy Blue + Warm Gold + Cream White
70
+ Why it works: complementary pair (navy and gold sit opposite on the colour wheel).
71
+ High contrast but sophisticated, the cream softens the intensity.
72
+ ...
73
+ ------------------------------------------------------------
74
+
75
+ DONE 6 agent calls · 283,593 in / 7,582 out tokens
76
+ $0.00 API credits (nominal value $0.27, covered by your subscription)
77
+ saved: runs/2026-07-13T14-16-35_name-3-colours-that-work-well-together.md
78
+ ```
79
+
80
+ Both agents ran at the same time, not one after the other. Swap the goal for something
81
+ real ("research what my competitors charge and write my pricing page") and cast more agents.
82
+
34
83
  ## How it works (the 4 phases)
35
84
 
36
85
  1. **PLAN**: the conductor gets the goal and returns a structured JSON plan (enforced by the API's structured outputs, so it can never come back malformed): a strategy plus one self-contained prompt per sub-agent.
package/orchestra-max.mjs CHANGED
@@ -20,14 +20,34 @@
20
20
  // (an ANTHROPIC_API_KEY in the environment is deliberately stripped so runs
21
21
  // can never silently bill your credit balance).
22
22
 
23
- import { execFile } from "node:child_process";
23
+ import { spawn } from "node:child_process";
24
24
  import fs from "node:fs";
25
25
  import path from "node:path";
26
- import { promisify } from "node:util";
27
26
  import { fileURLToPath } from "node:url";
28
27
  import { ROLES } from "./roles.mjs";
29
28
 
30
- const run = promisify(execFile);
29
+ // Spawn with stdin closed. `claude -p` waits ~3s for piped stdin and warns if it
30
+ // never arrives, which stalls (and can fail) every agent when Orchestra runs
31
+ // without a TTY: cron, CI, or with its output piped anywhere.
32
+ function run(cmd, args, { cwd, env, maxBuffer = 32 * 1024 * 1024, timeout = 15 * 60 * 1000 } = {}) {
33
+ return new Promise((resolve, reject) => {
34
+ const child = spawn(cmd, args, { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
35
+ let stdout = "", stderr = "", settled = false;
36
+ const finish = (fn, arg) => { if (!settled) { settled = true; clearTimeout(timer); fn(arg); } };
37
+ const timer = setTimeout(() => { child.kill("SIGKILL"); finish(reject, Object.assign(new Error(`timed out after ${timeout}ms`), { stdout, stderr })); }, timeout);
38
+
39
+ child.stdout.on("data", (d) => {
40
+ stdout += d;
41
+ if (stdout.length > maxBuffer) { child.kill("SIGKILL"); finish(reject, Object.assign(new Error("stdout exceeded maxBuffer"), { stdout, stderr })); }
42
+ });
43
+ child.stderr.on("data", (d) => { stderr += d; });
44
+ child.on("error", (e) => finish(reject, Object.assign(e, { stdout, stderr })));
45
+ child.on("close", (code) => {
46
+ if (code === 0) finish(resolve, { stdout, stderr });
47
+ else finish(reject, Object.assign(new Error(`${cmd} exited with code ${code}`), { stdout, stderr }));
48
+ });
49
+ });
50
+ }
31
51
  const DIR = path.dirname(fileURLToPath(import.meta.url));
32
52
  const AGENT_HOME = path.join(DIR, "agent-home"); // empty cwd so agents don't inherit a project CLAUDE.md
33
53
  fs.mkdirSync(AGENT_HOME, { recursive: true });
@@ -173,7 +193,9 @@ log("green", "conductor", `casting ${p.tasks.length} sub-agents`);
173
193
 
174
194
  const slug = GOAL.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50).replace(/^-|-$/g, "");
175
195
  const stamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
176
- const runFile = path.join(DIR, "runs", `${stamp}_${slug}.md`);
196
+ const RUNS_DIR = path.join(DIR, "runs");
197
+ fs.mkdirSync(RUNS_DIR, { recursive: true }); // not shipped in the package: a fresh install has no runs/
198
+ const runFile = path.join(RUNS_DIR, `${stamp}_${slug}.md`);
177
199
  let ok = [];
178
200
  let final = null;
179
201
  let runError = null;
package/orchestra.mjs CHANGED
@@ -228,7 +228,9 @@ log("green", "conductor", `casting ${p.tasks.length} sub-agents`);
228
228
  // (rate limit, empty credit balance, network drop) never loses paid-for work.
229
229
  const slug = GOAL.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50).replace(/^-|-$/g, "");
230
230
  const stamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
231
- const runFile = path.join(DIR, "runs", `${stamp}_${slug}.md`);
231
+ const RUNS_DIR = path.join(DIR, "runs");
232
+ fs.mkdirSync(RUNS_DIR, { recursive: true }); // not shipped in the package: a fresh install has no runs/
233
+ const runFile = path.join(RUNS_DIR, `${stamp}_${slug}.md`);
232
234
  let ok = [];
233
235
  let final = null;
234
236
  let runError = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestra-agents",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Multi-agent orchestrator on the Claude API: one conductor plans, sub-agents execute in parallel, adversarial critics attack, the conductor synthesizes one deliverable.",
5
5
  "type": "module",
6
6
  "bin": {