fanout-cli 0.9.1 → 0.9.3
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/{plugin/.claude-plugin → .claude-plugin}/plugin.json +2 -9
- package/README.md +29 -33
- package/{plugin/bin → bin}/fanout +8 -4
- package/{plugin/commands → commands}/fanout.md +13 -7
- package/package.json +13 -15
- package/plugin/README.md +0 -44
- package/src/cli.ts +0 -12
- /package/{plugin/.mcp.json → .mcp.json} +0 -0
- /package/{plugin/commands → commands}/crew.md +0 -0
- /package/{plugin/commands → commands}/watch.md +0 -0
- /package/{plugin/hooks → hooks}/hooks.json +0 -0
- /package/{plugin/skills → skills}/fanout/SKILL.md +0 -0
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fanout",
|
|
3
3
|
"description": "Lead a crew of the coding-agent CLIs you already pay for: plan, fan out into isolated git worktrees, watch, and review before anything merges.",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "elberacasa"
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://github.com/elberacasa/fanout",
|
|
9
9
|
"repository": "https://github.com/elberacasa/fanout",
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"keywords": [
|
|
12
|
-
"agents",
|
|
13
|
-
"orchestration",
|
|
14
|
-
"codex",
|
|
15
|
-
"grok",
|
|
16
|
-
"worktrees",
|
|
17
|
-
"local-first"
|
|
18
|
-
]
|
|
11
|
+
"keywords": ["agents", "orchestration", "codex", "grok", "worktrees", "local-first"]
|
|
19
12
|
}
|
package/README.md
CHANGED
|
@@ -1,48 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# The Fanout plugin for Claude Code
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Claude Code becomes the lead: it plans a mission, fans it out to the other agent CLIs on your machine, watches them
|
|
4
|
+
work in isolated git worktrees, and reviews every diff. Nothing merges without you.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
signed in — Codex, and Claude itself when you opt in — do pieces of it in parallel, each in its own git worktree.
|
|
7
|
-
Nothing merges until it has been reviewed by someone who did not write it, passed your project's own checks, and
|
|
8
|
-
been approved by you.
|
|
6
|
+
## What you get
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
| | |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `/fanout <goal>` | Plan a mission, check it against the safety gate, launch it, and watch it |
|
|
11
|
+
| `/fanout:crew` | Which CLIs are installed, signed in, and ready |
|
|
12
|
+
| `/fanout:watch [mission]` | Subscribe to the live feed and review each run as it finishes |
|
|
13
|
+
| The `fanout` skill | The lead's judgment: when to fan out, how to write a prompt an agent can follow, how to review what comes back |
|
|
14
|
+
| Hooks | The crew at the start of a session; a warning if runs are still going when you stop |
|
|
15
|
+
| Seven MCP tools | `seats`, `repo_overview`, `plan_check`, `launch`, `mission_status`, `run_diff`, `cancel_mission` |
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
npx fanout-cli demo
|
|
14
|
-
```
|
|
17
|
+
## Installing
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
sign-in, no API key, no network. It prints a local URL — open it to watch.
|
|
19
|
+
The plugin runs `fanout mcp`, so the `fanout` command must be on your `PATH`.
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
**From a clone**, while the package is not yet published:
|
|
20
22
|
|
|
21
23
|
```sh
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
fanout
|
|
25
|
-
|
|
24
|
+
git clone https://github.com/elberacasa/fanout.git && cd fanout
|
|
25
|
+
corepack enable && pnpm install
|
|
26
|
+
pnpm --filter fanout-cli link --global # puts `fanout` on your PATH
|
|
27
|
+
claude --plugin-dir "$PWD/plugin" # try it in one session
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
Check it before you rely on it:
|
|
29
31
|
|
|
32
|
+
```sh
|
|
33
|
+
fanout status # your crew
|
|
34
|
+
claude plugin validate plugin
|
|
30
35
|
```
|
|
31
|
-
/plugin marketplace add elberacasa/fanout
|
|
32
|
-
/plugin install fanout@fanout
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
That gives you `/fanout <goal>` inside your session.
|
|
36
36
|
|
|
37
37
|
## What it will not do
|
|
38
38
|
|
|
39
|
-
- **It
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **It
|
|
43
|
-
|
|
44
|
-
- **It
|
|
45
|
-
your secrets, or anything git ignores.
|
|
46
|
-
- **It sends nothing anywhere.** Your code, your prompts and the ledger stay on your machine. There is no telemetry.
|
|
47
|
-
|
|
48
|
-
Requires Node 22.18 or newer. MIT licensed.
|
|
39
|
+
- **It will not merge.** It reads a run's diff from the workspace and hands you the decision. The merge gate, with
|
|
40
|
+
review, your project's checks and proof that a fix fails on the old code, is the next milestone.
|
|
41
|
+
- **It will not work around a usage limit.** A seat that is out of quota is out; the lead says so and uses another.
|
|
42
|
+
- **It will not hand an agent your secrets.** Workspaces exclude ignored and deny-listed files, and a run gets an
|
|
43
|
+
allowlisted environment only — never your shell's.
|
|
44
|
+
- **It sends nothing anywhere.** The daemon listens on `127.0.0.1` with a token only you can read.
|
|
@@ -18,22 +18,26 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
*
|
|
19
19
|
* There are two real layouts and this script is the one place that knows the difference:
|
|
20
20
|
*
|
|
21
|
-
* published <package>/
|
|
22
|
-
* checkout <repo>/plugin/bin/fanout
|
|
21
|
+
* published <package>/bin/fanout → <package>/dist/cli.js
|
|
22
|
+
* checkout <repo>/plugin/bin/fanout → <repo>/packages/cli/src/cli.ts
|
|
23
|
+
*
|
|
24
|
+
* The published path lost a level when the plugin was flattened into the package root, which it had to be:
|
|
25
|
+
* Claude Code treats the installed package's own root as the plugin root, and a manifest one directory down is
|
|
26
|
+
* a plugin that installs, reports itself enabled, and loads nothing.
|
|
23
27
|
*
|
|
24
28
|
* Ordered with the published layout first: that is the one a stranger has, and the one nobody here would notice
|
|
25
29
|
* was broken.
|
|
26
30
|
*/
|
|
27
31
|
|
|
28
32
|
const here = new URL(".", import.meta.url);
|
|
29
|
-
const candidates = ["
|
|
33
|
+
const candidates = ["../dist/cli.js", "../../packages/cli/src/cli.ts"];
|
|
30
34
|
|
|
31
35
|
const cli = candidates.map((path) => fileURLToPath(new URL(path, here))).find((path) => existsSync(path));
|
|
32
36
|
|
|
33
37
|
if (cli === undefined) {
|
|
34
38
|
process.stderr.write(
|
|
35
39
|
"fanout: this plugin cannot find the Fanout CLI beside it.\n" +
|
|
36
|
-
" Looked for dist/cli.js and packages/cli/src/cli.ts
|
|
40
|
+
" Looked for ../dist/cli.js and ../../packages/cli/src/cli.ts from this script.\n" +
|
|
37
41
|
" If you installed from npm, reinstall fanout-cli; if this is a checkout, run pnpm install first.\n",
|
|
38
42
|
);
|
|
39
43
|
process.exit(69);
|
|
@@ -7,24 +7,30 @@ The user wants this done by the crew: **$ARGUMENTS**
|
|
|
7
7
|
|
|
8
8
|
You are the lead. Work in this order, and do not skip the parts that keep it safe.
|
|
9
9
|
|
|
10
|
-
1. **
|
|
10
|
+
1. **Say back what you heard, before anything else.** Restate the goal in one line — `Planning: <goal>` — in the
|
|
11
|
+
words you received it in. A prompt pasted into a terminal can arrive with characters missing, silently: a real
|
|
12
|
+
paste turned "fails on the old code" into "fails on the olcode" and nothing reported it. A goal that lost a
|
|
13
|
+
word still produces a confident plan, and every step after that looks reasonable, so this line is the only
|
|
14
|
+
place the input itself is ever visible. If it reads as truncated, contradictory or half a sentence, ask
|
|
15
|
+
instead of guessing.
|
|
16
|
+
2. **Look before planning.** Call `repo_overview` and `seats`. If no seat is ready, say so and stop: there is no
|
|
11
17
|
crew to lead.
|
|
12
|
-
|
|
18
|
+
3. **Split the work honestly.** One line per piece that can stand alone, each with:
|
|
13
19
|
- a **write scope** narrow enough that no two lines that run at once can touch the same file;
|
|
14
20
|
- a **prompt** that names the exact files, the expected behaviour with an example, the project's check commands,
|
|
15
21
|
and "do not commit";
|
|
16
22
|
- a **seat**: give the risky or subtle piece to the strongest seat, the mechanical piece to the cheapest, and
|
|
17
23
|
read-only work to an auditor. Claude is opt-in; prefer the other subscriptions.
|
|
18
24
|
Dependent work goes in `dependsOn` rather than in one big line.
|
|
19
|
-
|
|
25
|
+
4. **Check before launching.** Call `plan_check`. Fix what it blocks; do not argue with it. Show the user the plan
|
|
20
26
|
as a short list (line, seat, scope) and the dry run if they ask.
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
5. **Launch** with `launch`, then tell the user the mission id and what each line is doing.
|
|
28
|
+
6. **Watch** with `mission_status`. While runs are going, do not start unrelated work in this session: you are the
|
|
23
29
|
lead, and reviewing is your job.
|
|
24
|
-
|
|
30
|
+
7. **Review each finished run** with `run_diff`. Read the risky lines yourself. Anything written outside its scope
|
|
25
31
|
is the first thing you look at. Then tell the user, per run: what it changed, what you would keep, what you would
|
|
26
32
|
not, and what you could not verify.
|
|
27
|
-
|
|
33
|
+
8. **Merging is the user's call, and it is not automatic.** Apply a diff yourself only when they say so, run the
|
|
28
34
|
project's real checks afterwards, and never present an agent's "tests pass" as your own verification.
|
|
29
35
|
|
|
30
36
|
If something fails, say which line, why, and what you propose — never "it didn't work".
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fanout-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "Let Claude Code lead the other coding-agent CLIs you already pay for.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,31 +14,29 @@
|
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/elberacasa/fanout#readme",
|
|
16
16
|
"bin": {
|
|
17
|
-
"fanout": "./
|
|
17
|
+
"fanout": "./dist/cli.js"
|
|
18
18
|
},
|
|
19
19
|
"exports": {
|
|
20
|
-
".":
|
|
20
|
+
".": {
|
|
21
|
+
"default": "./dist/cli.js"
|
|
22
|
+
}
|
|
21
23
|
},
|
|
22
24
|
"files": [
|
|
25
|
+
".claude-plugin",
|
|
26
|
+
".mcp.json",
|
|
27
|
+
"bin",
|
|
28
|
+
"commands",
|
|
23
29
|
"dist",
|
|
24
|
-
"
|
|
30
|
+
"hooks",
|
|
31
|
+
"skills",
|
|
25
32
|
"README.md",
|
|
26
33
|
"LICENSE"
|
|
27
34
|
],
|
|
28
|
-
"publishConfig": {
|
|
29
|
-
"exports": {
|
|
30
|
-
".": {
|
|
31
|
-
"default": "./dist/cli.js"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
"bin": {
|
|
35
|
-
"fanout": "./dist/cli.js"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
35
|
"scripts": {
|
|
39
36
|
"typecheck": "tsc -p tsconfig.json",
|
|
40
37
|
"build": "node ../../scripts/bundle-cli.mjs",
|
|
41
|
-
"prepack": "node ../../scripts/bundle-cli.mjs"
|
|
38
|
+
"prepack": "node ../../scripts/bundle-cli.mjs",
|
|
39
|
+
"prepublishOnly": "node ../../scripts/check-publish.mjs"
|
|
42
40
|
},
|
|
43
41
|
"dependencies": {
|
|
44
42
|
"@modelcontextprotocol/sdk": "1.30.0",
|
package/plugin/README.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# The Fanout plugin for Claude Code
|
|
2
|
-
|
|
3
|
-
Claude Code becomes the lead: it plans a mission, fans it out to the other agent CLIs on your machine, watches them
|
|
4
|
-
work in isolated git worktrees, and reviews every diff. Nothing merges without you.
|
|
5
|
-
|
|
6
|
-
## What you get
|
|
7
|
-
|
|
8
|
-
| | |
|
|
9
|
-
|---|---|
|
|
10
|
-
| `/fanout <goal>` | Plan a mission, check it against the safety gate, launch it, and watch it |
|
|
11
|
-
| `/fanout:crew` | Which CLIs are installed, signed in, and ready |
|
|
12
|
-
| `/fanout:watch [mission]` | Subscribe to the live feed and review each run as it finishes |
|
|
13
|
-
| The `fanout` skill | The lead's judgment: when to fan out, how to write a prompt an agent can follow, how to review what comes back |
|
|
14
|
-
| Hooks | The crew at the start of a session; a warning if runs are still going when you stop |
|
|
15
|
-
| Seven MCP tools | `seats`, `repo_overview`, `plan_check`, `launch`, `mission_status`, `run_diff`, `cancel_mission` |
|
|
16
|
-
|
|
17
|
-
## Installing
|
|
18
|
-
|
|
19
|
-
The plugin runs `fanout mcp`, so the `fanout` command must be on your `PATH`.
|
|
20
|
-
|
|
21
|
-
**From a clone**, while the package is not yet published:
|
|
22
|
-
|
|
23
|
-
```sh
|
|
24
|
-
git clone https://github.com/elberacasa/fanout.git && cd fanout
|
|
25
|
-
corepack enable && pnpm install
|
|
26
|
-
pnpm --filter fanout-cli link --global # puts `fanout` on your PATH
|
|
27
|
-
claude --plugin-dir "$PWD/plugin" # try it in one session
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Check it before you rely on it:
|
|
31
|
-
|
|
32
|
-
```sh
|
|
33
|
-
fanout status # your crew
|
|
34
|
-
claude plugin validate plugin
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## What it will not do
|
|
38
|
-
|
|
39
|
-
- **It will not merge.** It reads a run's diff from the workspace and hands you the decision. The merge gate, with
|
|
40
|
-
review, your project's checks and proof that a fix fails on the old code, is the next milestone.
|
|
41
|
-
- **It will not work around a usage limit.** A seat that is out of quota is out; the lead says so and uses another.
|
|
42
|
-
- **It will not hand an agent your secrets.** Workspaces exclude ignored and deny-listed files, and a run gets an
|
|
43
|
-
allowlisted environment only — never your shell's.
|
|
44
|
-
- **It sends nothing anywhere.** The daemon listens on `127.0.0.1` with a token only you can read.
|
package/src/cli.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// First, and it has to stay first: it silences a warning that fires while the imports below are evaluating.
|
|
3
|
-
import "./quiet.ts";
|
|
4
|
-
import { main } from "./main.ts";
|
|
5
|
-
|
|
6
|
-
/* The thin edge of the CLI: everything testable lives in main.ts, which takes its output as an argument. */
|
|
7
|
-
|
|
8
|
-
const code = await main(process.argv.slice(2), {
|
|
9
|
-
out: (text) => process.stdout.write(text),
|
|
10
|
-
err: (text) => process.stderr.write(text),
|
|
11
|
-
});
|
|
12
|
-
process.exitCode = code;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|