fanout-cli 0.7.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/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -0
- package/dist/demo.d.ts +32 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +207 -0
- package/dist/demo.js.map +1 -0
- package/dist/format.d.ts +4 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +64 -0
- package/dist/format.js.map +1 -0
- package/dist/home.d.ts +9 -0
- package/dist/home.d.ts.map +1 -0
- package/dist/home.js +13 -0
- package/dist/home.js.map +1 -0
- package/dist/main.d.ts +21 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +602 -0
- package/dist/main.js.map +1 -0
- package/dist/unfinished.d.ts +34 -0
- package/dist/unfinished.d.ts.map +1 -0
- package/dist/unfinished.js +84 -0
- package/dist/unfinished.js.map +1 -0
- package/package.json +44 -0
package/dist/main.js
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import { existsSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createClaudeAdapter, manifest as claude } from "fanout-adapter-claude";
|
|
4
|
+
import { createCodexAdapter, manifest as codex } from "fanout-adapter-codex";
|
|
5
|
+
import { createGrokAdapter, manifest as grok } from "fanout-adapter-grok";
|
|
6
|
+
import { Ledger, missionReport, PlanGraph, project, SeatPosture, stanceFor, routeLine, versionOf, EMPTY_POLICY, } from "fanout-core";
|
|
7
|
+
import { detectSeats, git, lines, buddyReview, checkClaims, createMissionRunner, createWorkspaceManager, missionViewHtml, readOrCreateToken, readSeatPolicy, setPosture, startApi, workSnapshot, writeSeatPolicy, } from "fanout-daemon";
|
|
8
|
+
import { createFanoutServer } from "fanout-mcp";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { createFakeAdapter } from "fanout-adapter-fake";
|
|
11
|
+
import { fanoutHome } from "./home.js";
|
|
12
|
+
import { buildDemoRepo, demoClaims, demoLines, demoScenario, DEMO_GOAL } from "./demo.js";
|
|
13
|
+
import { crewTable, missionLines } from "./format.js";
|
|
14
|
+
import { ownWorkOwed, unfinishedReport, whatIsOwed } from "./unfinished.js";
|
|
15
|
+
/*
|
|
16
|
+
* `fanout` is the terminal half of the product: the daemon the lead talks to, and a straight answer about the crew
|
|
17
|
+
* and the missions. It prints what it knows and says plainly what it doesn't — a CLI that guesses is worse than one
|
|
18
|
+
* that shrugs.
|
|
19
|
+
*/
|
|
20
|
+
export const SEATS = [codex, claude, grok];
|
|
21
|
+
/** Every seat we can drive today. A plan naming anything else is dropped with the reason, never guessed at. */
|
|
22
|
+
export function adapters() {
|
|
23
|
+
return new Map([
|
|
24
|
+
["codex", createCodexAdapter()],
|
|
25
|
+
["claude", createClaudeAdapter()],
|
|
26
|
+
["grok", createGrokAdapter()],
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
/** What a run is allowed before the supervisor stops it. Generous: a real agent thinks for minutes. */
|
|
30
|
+
export const DEFAULT_LIMITS = {
|
|
31
|
+
startTimeoutMs: 90_000,
|
|
32
|
+
timeoutMs: 30 * 60_000,
|
|
33
|
+
killGraceMs: 5_000,
|
|
34
|
+
maxLogBytes: 16 * 1024 * 1024,
|
|
35
|
+
maxLineBytes: 200_000,
|
|
36
|
+
};
|
|
37
|
+
const HELP = `fanout — Claude Code leads, your other agents build
|
|
38
|
+
|
|
39
|
+
fanout demo watch a whole mission run, offline, with no accounts at all (--once to exit at the end)
|
|
40
|
+
fanout status the crew on this machine, and any missions on the go
|
|
41
|
+
fanout seat how freely to spend a seat: preferred | normal | sparing | off
|
|
42
|
+
fanout owed what is waiting on you before anything can merge (the Stop hook runs this)
|
|
43
|
+
fanout review ask a second vendor to read your own uncommitted changes
|
|
44
|
+
fanout check state what you believe; a cold reader tries to disprove each claim
|
|
45
|
+
fanout daemon run the daemon the lead and the mission view talk to
|
|
46
|
+
fanout clean remove the worktrees and branches finished missions left behind
|
|
47
|
+
fanout mcp speak MCP on stdin/stdout, for Claude Code to drive (the plugin runs this)
|
|
48
|
+
fanout version what you are running
|
|
49
|
+
fanout help this
|
|
50
|
+
|
|
51
|
+
Everything lives in ~/.fanout (move it with FANOUT_HOME). Nothing leaves your machine.
|
|
52
|
+
`;
|
|
53
|
+
export async function main(argv, io) {
|
|
54
|
+
const [command = "help"] = argv;
|
|
55
|
+
const home = fanoutHome(io.env ?? process.env);
|
|
56
|
+
switch (command) {
|
|
57
|
+
case "demo":
|
|
58
|
+
return demo(home, io, argv.slice(1));
|
|
59
|
+
case "status":
|
|
60
|
+
return status(home, io);
|
|
61
|
+
case "seat":
|
|
62
|
+
return seat(home, argv.slice(1), io);
|
|
63
|
+
case "owed":
|
|
64
|
+
return owed(home, io);
|
|
65
|
+
case "review":
|
|
66
|
+
return buddy(home, io);
|
|
67
|
+
case "check":
|
|
68
|
+
return check(home, argv.slice(1), io);
|
|
69
|
+
case "daemon":
|
|
70
|
+
return daemon(home, io);
|
|
71
|
+
case "clean":
|
|
72
|
+
return clean(home, io);
|
|
73
|
+
case "mcp":
|
|
74
|
+
return mcp(home, io);
|
|
75
|
+
case "version":
|
|
76
|
+
io.out(`fanout ${versionOf(import.meta.url)}\n`);
|
|
77
|
+
return 0;
|
|
78
|
+
case "help":
|
|
79
|
+
case "--help":
|
|
80
|
+
case "-h":
|
|
81
|
+
io.out(HELP);
|
|
82
|
+
return 0;
|
|
83
|
+
default:
|
|
84
|
+
io.err(`fanout: there is no "${command}" command.\n\n${HELP}`);
|
|
85
|
+
return 64;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The demo's crew: the simulated seat, said plainly, and nothing else.
|
|
90
|
+
*
|
|
91
|
+
* One list, used both by the page that shows the crew and by the router that decides on it. Two copies would let
|
|
92
|
+
* the screen say one thing while the routing did another, which on this particular screen is the whole product.
|
|
93
|
+
*
|
|
94
|
+
* Reporting the machine's real CLIs here would make the demo look like it was using them, and reporting nothing
|
|
95
|
+
* makes a working demo look broken.
|
|
96
|
+
*/
|
|
97
|
+
const DEMO_CREW = [
|
|
98
|
+
{
|
|
99
|
+
id: "fake",
|
|
100
|
+
displayName: "Simulated agent",
|
|
101
|
+
binary: "fake",
|
|
102
|
+
version: "demo",
|
|
103
|
+
supported: true,
|
|
104
|
+
signedIn: "yes",
|
|
105
|
+
models: ["demo"],
|
|
106
|
+
efforts: [],
|
|
107
|
+
billing: "unknown",
|
|
108
|
+
plan: { name: "no account needed", source: "detected" },
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* `fanout demo` — the whole thing, on a machine with nothing signed in.
|
|
113
|
+
*
|
|
114
|
+
* Real worktrees, the real safety gate, the real ledger, real diffs from real files. Only the agents are
|
|
115
|
+
* simulated, by the `fake` seat: a genuine CLI speaking the genuine protocol from a script. Nothing inside the
|
|
116
|
+
* daemon takes a special path, because a demo of a special path is a demo of something nobody ships.
|
|
117
|
+
*/
|
|
118
|
+
async function demo(home, io, argv = []) {
|
|
119
|
+
const root = join(home.root, "demo");
|
|
120
|
+
const repo = buildDemoRepo(join(root, "shop"));
|
|
121
|
+
const ledgerPath = join(root, "ledger.db");
|
|
122
|
+
rmSync(ledgerPath, { force: true });
|
|
123
|
+
// The feed exists only once the API is listening, and the ledger is open before that; this holder is the join.
|
|
124
|
+
const feed = {};
|
|
125
|
+
const ledger = Ledger.open(ledgerPath, {
|
|
126
|
+
onAppend: (event) => {
|
|
127
|
+
feed.publish?.(event);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const adapter = createFakeAdapter({ scenarioFor: demoScenario });
|
|
131
|
+
const workspaces = createWorkspaceManager({ repoRoot: repo, workspaceRoot: join(root, "workspaces") });
|
|
132
|
+
const runner = createMissionRunner({
|
|
133
|
+
ledger,
|
|
134
|
+
workspaces,
|
|
135
|
+
adapters: new Map([["fake", adapter]]),
|
|
136
|
+
runsRoot: join(root, "runs"),
|
|
137
|
+
limits: DEFAULT_LIMITS,
|
|
138
|
+
/*
|
|
139
|
+
* The real router, over the demo's real crew — which is the simulated seat and nothing else. The `ui` line
|
|
140
|
+
* asks for Codex, so it is moved and the reason on screen is the router's own sentence rather than a caption
|
|
141
|
+
* we wrote. A demo that faked this would be demonstrating a code path nobody ships.
|
|
142
|
+
*/
|
|
143
|
+
route: (line) => routeLine({
|
|
144
|
+
wanted: line.seat.id,
|
|
145
|
+
seats: DEMO_CREW,
|
|
146
|
+
policy: EMPTY_POLICY,
|
|
147
|
+
headroom: {},
|
|
148
|
+
now: new Date(),
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
const api = await startApi({
|
|
152
|
+
ledger,
|
|
153
|
+
token: readOrCreateToken(home.token),
|
|
154
|
+
view: missionViewHtml,
|
|
155
|
+
/*
|
|
156
|
+
* The demo's crew is the simulated seat, said plainly. Reporting the machine's real CLIs here would make the
|
|
157
|
+
* demo look like it was using them, and reporting nothing makes a working demo look broken.
|
|
158
|
+
*/
|
|
159
|
+
crew: () => Promise.resolve(DEMO_CREW),
|
|
160
|
+
});
|
|
161
|
+
feed.publish = (event) => {
|
|
162
|
+
api.publish(event);
|
|
163
|
+
};
|
|
164
|
+
const plan = PlanGraph.parse({ lines: demoLines() });
|
|
165
|
+
const head = (await git(["rev-parse", "HEAD"], { cwd: repo })).trim();
|
|
166
|
+
const missionId = "demo-csv-export";
|
|
167
|
+
ledger.appendAll([
|
|
168
|
+
{
|
|
169
|
+
type: "mission.created",
|
|
170
|
+
missionId,
|
|
171
|
+
goal: DEMO_GOAL,
|
|
172
|
+
repo: { root: repo, baseCommit: head },
|
|
173
|
+
limits: { maxParallel: 3, timeoutMinutes: 10 },
|
|
174
|
+
},
|
|
175
|
+
{ type: "plan.proposed", missionId, plan, by: "lead" },
|
|
176
|
+
]);
|
|
177
|
+
io.out(`\n ${DEMO_GOAL}\n\n`);
|
|
178
|
+
io.out(` Watch it at ${api.url}/\n`);
|
|
179
|
+
io.out(` The repo it is changing is ${repo}\n`);
|
|
180
|
+
io.out(` Three simulated agents, three worktrees, no accounts. Ctrl-C when you have seen enough.\n\n`);
|
|
181
|
+
const handle = runner.launch({ missionId, plan, baseCommit: head, maxParallel: 3 });
|
|
182
|
+
await handle.finished;
|
|
183
|
+
/*
|
|
184
|
+
* The claim check the demo shows is written, not read: real verdicts need a real second vendor. It is recorded
|
|
185
|
+
* with `simulated: true` so every surface says so, because inventing a second opinion and presenting it as one
|
|
186
|
+
* would fake the only thing this product claims to do.
|
|
187
|
+
*/
|
|
188
|
+
ledger.appendAll([
|
|
189
|
+
{
|
|
190
|
+
type: "claims.checked",
|
|
191
|
+
repoRoot: repo,
|
|
192
|
+
revision: "d3".repeat(32),
|
|
193
|
+
by: { id: "fake", model: "demo" },
|
|
194
|
+
claims: demoClaims(),
|
|
195
|
+
ran: true,
|
|
196
|
+
simulated: true,
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
const state = project(ledger.read({ missionId }));
|
|
200
|
+
const mission = state.missions[missionId];
|
|
201
|
+
if (mission !== undefined)
|
|
202
|
+
io.out(`\n${missionReport(mission, new Date())}\n`);
|
|
203
|
+
/*
|
|
204
|
+
* `--once` exits when the mission does, instead of holding the view open. It is what a script wants: the
|
|
205
|
+
* packaging check runs this to prove an installed Fanout actually works, and a command that never returns
|
|
206
|
+
* cannot be checked by anything.
|
|
207
|
+
*/
|
|
208
|
+
if (argv.includes("--once")) {
|
|
209
|
+
await api.close();
|
|
210
|
+
ledger.close();
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
io.out(` Still watching at ${api.url}/ — Ctrl-C to stop.\n`);
|
|
214
|
+
await (io.until ?? new Promise(() => undefined));
|
|
215
|
+
await api.close();
|
|
216
|
+
ledger.close();
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
async function status(home, io) {
|
|
220
|
+
const seats = await detectSeats({
|
|
221
|
+
manifests: SEATS,
|
|
222
|
+
...(io.execute === undefined ? {} : { execute: io.execute }),
|
|
223
|
+
});
|
|
224
|
+
const { policy, problem } = readSeatPolicy(home.root);
|
|
225
|
+
// A policy we could not read is not the same as no policy, and the difference is whose money it is.
|
|
226
|
+
if (problem !== null)
|
|
227
|
+
io.err(`fanout: ${problem}\n Until it is fixed, every seat falls back to its default.\n\n`);
|
|
228
|
+
io.out(crewTable(seats, policy));
|
|
229
|
+
if (!existsSync(home.ledger)) {
|
|
230
|
+
io.out("\nNo missions yet. The ledger appears the first time the lead plans one.\n");
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
const ledger = Ledger.open(home.ledger);
|
|
234
|
+
try {
|
|
235
|
+
const state = project(ledger.read());
|
|
236
|
+
io.out(`\n${missionLines(state)}`);
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
ledger.close();
|
|
240
|
+
}
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* `fanout check` — the lead writes down what it believes; a cold reader tries to disprove each claim.
|
|
245
|
+
*
|
|
246
|
+
* Sharper and far cheaper than a broad review, because the value was never the volume of reading. The lead
|
|
247
|
+
* carries the plan and the reasoning, and that is exactly what hides its mistakes from it; a reader with only the
|
|
248
|
+
* diff is not smarter, it is differently placed. Three specific claims buy that difference for almost nothing.
|
|
249
|
+
*
|
|
250
|
+
* Exits non-zero when a claim is refuted, so this can sit in a script or a hook.
|
|
251
|
+
*/
|
|
252
|
+
async function check(home, claims, io) {
|
|
253
|
+
if (claims.length === 0) {
|
|
254
|
+
io.err(`fanout: check needs something to check.\n\n${CHECK_HELP}`);
|
|
255
|
+
return 64;
|
|
256
|
+
}
|
|
257
|
+
const ready = await reviewerFor(home, io);
|
|
258
|
+
if (typeof ready === "number")
|
|
259
|
+
return ready;
|
|
260
|
+
const { event, refuted } = await checkClaims({
|
|
261
|
+
repoRoot: io.cwd ?? process.cwd(),
|
|
262
|
+
claims,
|
|
263
|
+
manifest: ready.manifest,
|
|
264
|
+
...(io.execute === undefined ? {} : { execute: reviewWith(io.execute) }),
|
|
265
|
+
});
|
|
266
|
+
const ledger = Ledger.open(home.ledger);
|
|
267
|
+
try {
|
|
268
|
+
ledger.appendAll([event]);
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
ledger.close();
|
|
272
|
+
}
|
|
273
|
+
if (!event.ran) {
|
|
274
|
+
// Never let "we could not ask" read as "nothing was refuted".
|
|
275
|
+
io.err(`fanout: ${ready.manifest.displayName} did not check your claims.\n ${event.claims[0]?.evidence ?? ""}\n`);
|
|
276
|
+
return 69;
|
|
277
|
+
}
|
|
278
|
+
const mark = { confirmed: "✓", refuted: "✗", unclear: "?" };
|
|
279
|
+
io.out(`${ready.manifest.displayName} read your changes cold:\n\n`);
|
|
280
|
+
for (const claim of event.claims) {
|
|
281
|
+
io.out(` ${mark[claim.verdict]} ${claim.claim}\n ${claim.evidence}\n`);
|
|
282
|
+
}
|
|
283
|
+
io.out(`\n${summarise(event.claims)}\n`);
|
|
284
|
+
// A refuted claim is the only outcome worth interrupting someone for.
|
|
285
|
+
return refuted.length > 0 ? 1 : 0;
|
|
286
|
+
}
|
|
287
|
+
function summarise(claims) {
|
|
288
|
+
const count = (verdict) => claims.filter((claim) => claim.verdict === verdict).length;
|
|
289
|
+
const refuted = count("refuted");
|
|
290
|
+
const unclear = count("unclear");
|
|
291
|
+
if (refuted > 0)
|
|
292
|
+
return `${String(refuted)} refuted. Nothing here is settled until those are.`;
|
|
293
|
+
if (unclear > 0)
|
|
294
|
+
return `Nothing refuted, but ${String(unclear)} could not be checked — that is not the same as fine.`;
|
|
295
|
+
return "All confirmed.";
|
|
296
|
+
}
|
|
297
|
+
const CHECK_HELP = ` fanout check "<claim>" ["<claim>" ...]
|
|
298
|
+
|
|
299
|
+
Write claims a reader could disprove. "It works" cannot be checked; "no caller of
|
|
300
|
+
total() passes fewer than two arguments" can.
|
|
301
|
+
`;
|
|
302
|
+
/**
|
|
303
|
+
* `fanout review` — a second vendor reads the lead's own uncommitted work.
|
|
304
|
+
*
|
|
305
|
+
* The one command that earns its keep in a session where no agent ran at all. Most of the code in a Claude Code
|
|
306
|
+
* session is written by the lead and reviewed by the lead, which is how a confident mistake ships; this is the
|
|
307
|
+
* call that breaks that loop. The findings are printed verbatim, because a second opinion summarised by the
|
|
308
|
+
* author it is about is not a second opinion.
|
|
309
|
+
*/
|
|
310
|
+
async function buddy(home, io) {
|
|
311
|
+
const ready = await reviewerFor(home, io);
|
|
312
|
+
if (typeof ready === "number")
|
|
313
|
+
return ready;
|
|
314
|
+
const { snapshot, event } = await buddyReview({
|
|
315
|
+
repoRoot: io.cwd ?? process.cwd(),
|
|
316
|
+
manifest: ready.manifest,
|
|
317
|
+
...(io.execute === undefined ? {} : { execute: reviewWith(io.execute) }),
|
|
318
|
+
});
|
|
319
|
+
const ledger = Ledger.open(home.ledger);
|
|
320
|
+
try {
|
|
321
|
+
ledger.appendAll([event]);
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
ledger.close();
|
|
325
|
+
}
|
|
326
|
+
if (snapshot.clean) {
|
|
327
|
+
io.out("Nothing uncommitted to review.\n");
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
if (!event.ran) {
|
|
331
|
+
// Never let "the reviewer broke" read as "the reviewer found nothing".
|
|
332
|
+
io.err(`fanout: ${ready.manifest.displayName} could not review your changes.\n ${event.findings}\n`);
|
|
333
|
+
return 69;
|
|
334
|
+
}
|
|
335
|
+
const files = `${String(snapshot.files.length)} file${snapshot.files.length === 1 ? "" : "s"}`;
|
|
336
|
+
io.out(event.findings.trim() === ""
|
|
337
|
+
? `${ready.manifest.displayName} read ${files} and had nothing to say.\n`
|
|
338
|
+
: `${ready.manifest.displayName} read ${files}:\n\n${event.findings.trim()}\n`);
|
|
339
|
+
return 0;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* The seat that will read your work, or the exit code explaining why nobody will.
|
|
343
|
+
*
|
|
344
|
+
* Every check here is about not spending someone's subscription behind their back — a posture they set, a version
|
|
345
|
+
* this adapter was never verified against, a policy file we could not read. Shared by both readers so that the
|
|
346
|
+
* next one cannot forget any of them, which is exactly how `fanout review` shipped ignoring the seat policy.
|
|
347
|
+
*/
|
|
348
|
+
async function reviewerFor(home, io) {
|
|
349
|
+
const manifest = SEATS.find((seat) => seat.id === "codex");
|
|
350
|
+
if (manifest?.capabilities.review == null) {
|
|
351
|
+
io.err("fanout: no seat on this machine has a non-interactive review command.\n");
|
|
352
|
+
return 69;
|
|
353
|
+
}
|
|
354
|
+
const { policy, problem } = readSeatPolicy(home.root);
|
|
355
|
+
if (problem !== null) {
|
|
356
|
+
io.err(`fanout: ${problem}\n Fix or delete that file before spending a seat.\n`);
|
|
357
|
+
return 65;
|
|
358
|
+
}
|
|
359
|
+
const [detected] = await detectSeats({
|
|
360
|
+
manifests: [manifest],
|
|
361
|
+
...(io.execute === undefined ? {} : { execute: io.execute }),
|
|
362
|
+
});
|
|
363
|
+
if (detected === undefined) {
|
|
364
|
+
io.err("fanout: could not detect the reviewing seat.\n");
|
|
365
|
+
return 69;
|
|
366
|
+
}
|
|
367
|
+
const stance = stanceFor(detected, policy);
|
|
368
|
+
if (stance.posture === "off") {
|
|
369
|
+
io.err(`fanout: ${manifest.displayName} is off (${stance.reason}). Turn it on with:\n`);
|
|
370
|
+
io.err(` fanout seat ${manifest.id} normal\n`);
|
|
371
|
+
return 69;
|
|
372
|
+
}
|
|
373
|
+
if (!detected.supported) {
|
|
374
|
+
// An unverified build would be driven with flags we have not confirmed and read as a stream we have not seen.
|
|
375
|
+
io.err(`fanout: ${manifest.displayName} ${detected.version ?? "is not installed"} is outside the versions this ` +
|
|
376
|
+
`adapter was verified against (${manifest.supportedVersions}).\n`);
|
|
377
|
+
return 69;
|
|
378
|
+
}
|
|
379
|
+
if (detected.signedIn !== "yes") {
|
|
380
|
+
io.err(`fanout: ${manifest.displayName} is ${detected.signedIn === "no" ? "not signed in" : "unknown"}.\n`);
|
|
381
|
+
return 69;
|
|
382
|
+
}
|
|
383
|
+
if (stance.posture === "sparing") {
|
|
384
|
+
// Sparing means "only when nothing else fits, and say so first". This is the saying so.
|
|
385
|
+
io.out(`Using ${manifest.displayName}, which you marked sparing${stance.note === undefined ? "" : ` — ${stance.note}`}.\n`);
|
|
386
|
+
}
|
|
387
|
+
return { manifest };
|
|
388
|
+
}
|
|
389
|
+
/** The CLI's injected executor takes no options; the buddy's takes cwd and a deadline. Bridge them for tests. */
|
|
390
|
+
function reviewWith(execute) {
|
|
391
|
+
return (binary, args) => execute(binary, args);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* `fanout owed` — what the lead still owes before anything can merge.
|
|
395
|
+
*
|
|
396
|
+
* Run by the Stop hook on every turn, which is the point: a tool the lead chooses to call cannot catch a lead who
|
|
397
|
+
* believes the work is already finished. Prints nothing and exits 0 when there is nothing owed, so a quiet session
|
|
398
|
+
* stays quiet, and it never blocks — walking away from unfinished work is allowed, doing it unknowingly is not.
|
|
399
|
+
*/
|
|
400
|
+
async function owed(home, io) {
|
|
401
|
+
const repoRoot = io.cwd ?? process.cwd();
|
|
402
|
+
// The working tree is asked about first, because that is where the lead's own unread code lives.
|
|
403
|
+
let own = "";
|
|
404
|
+
try {
|
|
405
|
+
const snapshot = await workSnapshot({ cwd: repoRoot });
|
|
406
|
+
if (!existsSync(home.ledger)) {
|
|
407
|
+
own = ownWorkOwed({ revision: snapshot.revision, files: snapshot.files.length, checked: undefined });
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const ledger = Ledger.open(home.ledger);
|
|
411
|
+
try {
|
|
412
|
+
const state = project(ledger.read());
|
|
413
|
+
own = ownWorkOwed({
|
|
414
|
+
revision: snapshot.revision,
|
|
415
|
+
files: snapshot.files.length,
|
|
416
|
+
checked: state.claims[snapshot.repoRoot],
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
finally {
|
|
420
|
+
ledger.close();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
// Not a repository, or git is unhappy. A hook that runs every turn must never be the reason a turn fails.
|
|
426
|
+
}
|
|
427
|
+
if (!existsSync(home.ledger)) {
|
|
428
|
+
if (own !== "")
|
|
429
|
+
io.out(unfinishedReport([], own));
|
|
430
|
+
return 0;
|
|
431
|
+
}
|
|
432
|
+
const ledger = Ledger.open(home.ledger);
|
|
433
|
+
try {
|
|
434
|
+
const state = project(ledger.read());
|
|
435
|
+
const report = unfinishedReport(whatIsOwed(Object.values(state.missions)), own);
|
|
436
|
+
if (report !== "")
|
|
437
|
+
io.out(report);
|
|
438
|
+
}
|
|
439
|
+
finally {
|
|
440
|
+
ledger.close();
|
|
441
|
+
}
|
|
442
|
+
return 0;
|
|
443
|
+
}
|
|
444
|
+
/** `fanout seat <id> <posture> [note]` — the one setting, and it is always the owner's to make. */
|
|
445
|
+
function seat(home, args, io) {
|
|
446
|
+
const [seatId, posture, ...rest] = args;
|
|
447
|
+
if (seatId === undefined || posture === undefined) {
|
|
448
|
+
io.err(`fanout: seat needs which seat and how freely to spend it.\n\n${SEAT_HELP}`);
|
|
449
|
+
return 64;
|
|
450
|
+
}
|
|
451
|
+
const wanted = SeatPosture.safeParse(posture);
|
|
452
|
+
if (!wanted.success) {
|
|
453
|
+
io.err(`fanout: "${posture}" is not a posture.\n\n${SEAT_HELP}`);
|
|
454
|
+
return 64;
|
|
455
|
+
}
|
|
456
|
+
if (!SEATS.some((manifest) => manifest.id === seatId)) {
|
|
457
|
+
const known = SEATS.map((manifest) => manifest.id).join(", ");
|
|
458
|
+
io.err(`fanout: there is no seat called "${seatId}". Seats: ${known}.\n`);
|
|
459
|
+
return 64;
|
|
460
|
+
}
|
|
461
|
+
const { policy, problem } = readSeatPolicy(home.root);
|
|
462
|
+
if (problem !== null) {
|
|
463
|
+
// Writing on top of a file we could not read would silently discard preferences the owner did set.
|
|
464
|
+
io.err(`fanout: ${problem}\n Fix or delete that file before changing a seat.\n`);
|
|
465
|
+
return 65;
|
|
466
|
+
}
|
|
467
|
+
const note = rest.join(" ");
|
|
468
|
+
writeSeatPolicy(home.root, setPosture(policy, seatId, wanted.data, note));
|
|
469
|
+
io.out(`${seatId} is now ${wanted.data}${note === "" ? "" : ` — ${note}`}.\n`);
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
const SEAT_HELP = ` fanout seat <id> <preferred|normal|sparing|off> [why]
|
|
473
|
+
|
|
474
|
+
preferred reach for this one first
|
|
475
|
+
normal use it when the plan calls for it
|
|
476
|
+
sparing only when nothing else fits, and say so first
|
|
477
|
+
off never, until you say otherwise
|
|
478
|
+
`;
|
|
479
|
+
async function daemon(home, io) {
|
|
480
|
+
const ledger = Ledger.open(home.ledger);
|
|
481
|
+
const token = readOrCreateToken(home.token);
|
|
482
|
+
const api = await startApi({
|
|
483
|
+
ledger,
|
|
484
|
+
token,
|
|
485
|
+
crew: () => detectSeats({ manifests: SEATS, ...(io.execute === undefined ? {} : { execute: io.execute }) }),
|
|
486
|
+
view: missionViewHtml,
|
|
487
|
+
});
|
|
488
|
+
io.out(`fanout daemon listening on ${api.url}\n` +
|
|
489
|
+
` token ${home.token} (read by the plugin and the CLI; keep it to yourself)\n` +
|
|
490
|
+
` ledger ${home.ledger}\n` +
|
|
491
|
+
` live ${api.url.replace("http", "ws")}/events?for=lead\n\nStop it with Ctrl-C.\n`);
|
|
492
|
+
await (io.until ?? interrupted());
|
|
493
|
+
await api.close();
|
|
494
|
+
ledger.close();
|
|
495
|
+
io.out("fanout daemon stopped.\n");
|
|
496
|
+
return 0;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Removes what a run leaves on disk: its worktree and its throwaway branch. It only ever touches workspaces under
|
|
500
|
+
* FANOUT_HOME and branches under `fanout/`, so a clean can never take the user's own work with it.
|
|
501
|
+
*/
|
|
502
|
+
async function clean(home, io) {
|
|
503
|
+
const cwd = io.cwd ?? process.cwd();
|
|
504
|
+
let repoRoot;
|
|
505
|
+
try {
|
|
506
|
+
repoRoot = (await git(["rev-parse", "--show-toplevel"], { cwd })).trim();
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
io.err("fanout clean: run this inside the repository whose missions you want to clean up.\n");
|
|
510
|
+
return 64;
|
|
511
|
+
}
|
|
512
|
+
// git reports resolved paths (/private/var/… on macOS) while FANOUT_HOME may be the symlinked form (/var/…),
|
|
513
|
+
// so compare both spellings; otherwise a clean silently removes nothing and then fails to delete the branch.
|
|
514
|
+
const roots = [
|
|
515
|
+
home.workspaces,
|
|
516
|
+
existsSync(home.workspaces) ? realpathSync(home.workspaces) : home.workspaces,
|
|
517
|
+
];
|
|
518
|
+
const worktrees = lines(await git(["worktree", "list", "--porcelain"], { cwd: repoRoot }))
|
|
519
|
+
.filter((line) => line.startsWith("worktree "))
|
|
520
|
+
.map((line) => line.slice("worktree ".length))
|
|
521
|
+
.filter((path) => roots.some((root) => path.startsWith(root)));
|
|
522
|
+
const branches = lines(await git(["for-each-ref", "--format=%(refname:short)", "refs/heads/fanout/"], { cwd: repoRoot }));
|
|
523
|
+
if (worktrees.length === 0 && branches.length === 0) {
|
|
524
|
+
io.out("Nothing to clean: no Fanout worktrees or branches in this repository.\n");
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
for (const path of worktrees)
|
|
528
|
+
await git(["worktree", "remove", "--force", path], { cwd: repoRoot });
|
|
529
|
+
await git(["worktree", "prune"], { cwd: repoRoot });
|
|
530
|
+
const kept = [];
|
|
531
|
+
for (const branch of branches) {
|
|
532
|
+
try {
|
|
533
|
+
await git(["branch", "-D", branch], { cwd: repoRoot });
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
kept.push(branch); // still checked out somewhere: say so rather than pretending it is gone
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
rmSync(home.workspaces, { recursive: true, force: true });
|
|
540
|
+
const removed = branches.length - kept.length;
|
|
541
|
+
io.out(`Cleaned ${worktrees.length} worktree${worktrees.length === 1 ? "" : "s"} and ` +
|
|
542
|
+
`${removed} branch${removed === 1 ? "" : "es"}. Your own branches were not touched.\n`);
|
|
543
|
+
if (kept.length > 0) {
|
|
544
|
+
io.err(`Still in use elsewhere, so left alone: ${kept.join(", ")}.\n`);
|
|
545
|
+
}
|
|
546
|
+
return kept.length === 0 ? 0 : 1;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Speaks MCP on stdin and stdout so Claude Code can drive the crew, and runs the daemon in the same process so the
|
|
550
|
+
* mission view and the lead's live feed have something to subscribe to.
|
|
551
|
+
*
|
|
552
|
+
* Nothing but MCP may touch stdout here: a stray line would corrupt the protocol, which is why every message this
|
|
553
|
+
* command prints goes to stderr.
|
|
554
|
+
*/
|
|
555
|
+
async function mcp(home, io) {
|
|
556
|
+
// The feed exists only once the API is listening, and the ledger is open before that; this holder is the join.
|
|
557
|
+
const feed = {};
|
|
558
|
+
const ledger = Ledger.open(home.ledger, {
|
|
559
|
+
onAppend: (event) => {
|
|
560
|
+
feed.publish?.(event);
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
const token = readOrCreateToken(home.token);
|
|
564
|
+
const api = await startApi({
|
|
565
|
+
ledger,
|
|
566
|
+
token,
|
|
567
|
+
crew: () => detectSeats({ manifests: SEATS, ...(io.execute === undefined ? {} : { execute: io.execute }) }),
|
|
568
|
+
view: missionViewHtml,
|
|
569
|
+
});
|
|
570
|
+
feed.publish = (event) => {
|
|
571
|
+
api.publish(event);
|
|
572
|
+
};
|
|
573
|
+
// Where the daemon is, for the mission view and any other client. Private, like everything else here.
|
|
574
|
+
writeFileSync(join(home.root, "daemon.json"), `${JSON.stringify({ url: api.url, pid: process.pid })}\n`, {
|
|
575
|
+
mode: 0o600,
|
|
576
|
+
});
|
|
577
|
+
const server = createFanoutServer({
|
|
578
|
+
ledger,
|
|
579
|
+
repoRoot: io.cwd ?? process.cwd(),
|
|
580
|
+
paths: { runs: home.runs, workspaces: home.workspaces, home: home.root },
|
|
581
|
+
adapters: adapters(),
|
|
582
|
+
manifests: SEATS,
|
|
583
|
+
limits: DEFAULT_LIMITS,
|
|
584
|
+
});
|
|
585
|
+
io.err(`fanout mcp ready. Live feed: ${api.url.replace("http", "ws")}/events?for=lead\n`);
|
|
586
|
+
await server.connect(new StdioServerTransport());
|
|
587
|
+
await (io.until ?? interrupted());
|
|
588
|
+
await server.close();
|
|
589
|
+
await api.close();
|
|
590
|
+
ledger.close();
|
|
591
|
+
return 0;
|
|
592
|
+
}
|
|
593
|
+
function interrupted() {
|
|
594
|
+
return new Promise((resolve) => {
|
|
595
|
+
const stop = () => {
|
|
596
|
+
resolve();
|
|
597
|
+
};
|
|
598
|
+
process.once("SIGINT", stop);
|
|
599
|
+
process.once("SIGTERM", stop);
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ClaimCheck, type MissionView } from "fanout-core";
|
|
2
|
+
/** What the lead's own uncommitted work still owes, if anything. */
|
|
3
|
+
export interface OwnWork {
|
|
4
|
+
/** The revision the working tree is at now. */
|
|
5
|
+
revision: string;
|
|
6
|
+
files: number;
|
|
7
|
+
/** The most recent claim check, if any, whatever revision it was about. */
|
|
8
|
+
checked: ClaimCheck | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What to say about the lead's own changes.
|
|
12
|
+
*
|
|
13
|
+
* The runs a crew produced are the obvious thing to guard, and they are not where most of a session's code comes
|
|
14
|
+
* from: the lead writes it, and the lead is its only reader. A check nobody is reminded of is a check nobody runs,
|
|
15
|
+
* which is why this asks about the working tree and not only about the mission.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ownWorkOwed(work: OwnWork): string;
|
|
18
|
+
export interface Owed {
|
|
19
|
+
missionId: string;
|
|
20
|
+
runId: string;
|
|
21
|
+
/** One line, written for someone about to close their laptop. */
|
|
22
|
+
what: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Every run that is waiting on the lead: finished agents nobody has reviewed, checks nobody has run, fixes with
|
|
26
|
+
* no proof, work reviewed at a revision that has since moved.
|
|
27
|
+
*
|
|
28
|
+
* Runs still working are deliberately not here. They are not owed by anyone; they are simply not done, and the
|
|
29
|
+
* mission view says so already.
|
|
30
|
+
*/
|
|
31
|
+
export declare function whatIsOwed(missions: readonly MissionView[]): Owed[];
|
|
32
|
+
/** The hook's whole output. Empty string when nothing is owed, so a quiet session stays quiet. */
|
|
33
|
+
export declare function unfinishedReport(owed: readonly Owed[], own?: string): string;
|
|
34
|
+
//# sourceMappingURL=unfinished.d.ts.map
|