@herdingchats/friday 0.1.1 → 0.1.525

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.
Files changed (2) hide show
  1. package/herd.mjs +275 -105
  2. package/package.json +2 -2
package/herd.mjs CHANGED
@@ -18,7 +18,7 @@ var __export = (target, all) => {
18
18
  var define_HERD_PACKAGE_default;
19
19
  var init_define_HERD_PACKAGE = __esm({
20
20
  "<define:HERD_PACKAGE>"() {
21
- define_HERD_PACKAGE_default = { version: "0.1.1", gitHead: "91b08aebd285af82cf2ea35b4b380b52acf2a33f" };
21
+ define_HERD_PACKAGE_default = { version: "0.1.525", gitHead: "cbd227dfdbed52c13e4cb3a2b388d6e7d4a6866e" };
22
22
  }
23
23
  });
24
24
 
@@ -513,6 +513,9 @@ import { z as z8 } from "zod";
513
513
  function agentShortName(cli) {
514
514
  return AGENTS[cli]?.short ?? cli;
515
515
  }
516
+ function shareOf(harness) {
517
+ return harness?.share ?? (harness?.enabled === false ? "off" : "some");
518
+ }
516
519
  function runnerCapacity(setting) {
517
520
  if (setting === void 0 || setting === true) return 1;
518
521
  return setting.max;
@@ -528,7 +531,7 @@ function workerKey(machineId, cli) {
528
531
  function machineShortName(name) {
529
532
  return name.trim().toLowerCase().replace(/\.local$/, "").replace(/\s+/g, "-");
530
533
  }
531
- var CAPABILITIES, capabilitySchema, PLATFORMS, platformSchema, TOOLS, toolSchema, CAPABILITY_WORDING, AGENTS, PHRASE_MAX, PHRASES_MAX, ABOUT_MAX, phrase, phrases, harnessConfigSchema, BUILDS_MAX, buildsSettingSchema, CONFIG_REPOS_MAX, configRepoSchema, herdConfigSchema, HERD_CONFIG_TYPE, PROFILE_LEVELS, profileEntrySchema, workerSchema;
534
+ var CAPABILITIES, capabilitySchema, PLATFORMS, platformSchema, TOOLS, toolSchema, CAPABILITY_WORDING, AGENTS, PHRASE_MAX, PHRASES_MAX, ABOUT_MAX, phrase, phrases, SHARES, shareSchema, SHARE_LABELS, harnessConfigSchema, BUILDS_MAX, buildsSettingSchema, CONFIG_REPOS_MAX, configRepoSchema, herdConfigSchema, HERD_CONFIG_TYPE, PROFILE_LEVELS, profileEntrySchema, workerSchema;
532
535
  var init_workers = __esm({
533
536
  "../common-chat/src/workers.ts"() {
534
537
  "use strict";
@@ -566,7 +569,17 @@ var init_workers = __esm({
566
569
  ABOUT_MAX = 500;
567
570
  phrase = z8.string().trim().min(1).max(PHRASE_MAX, `at most ${PHRASE_MAX} characters`);
568
571
  phrases = z8.array(phrase).max(PHRASES_MAX, `at most ${PHRASES_MAX} phrases`);
572
+ SHARES = ["off", "little", "some", "lot"];
573
+ shareSchema = z8.enum(SHARES);
574
+ SHARE_LABELS = {
575
+ off: "off",
576
+ little: "a little",
577
+ some: "some",
578
+ lot: "a lot"
579
+ };
569
580
  harnessConfigSchema = z8.object({
581
+ share: shareSchema.optional(),
582
+ /** Read as `share: "off"` when false, from files written before shares. */
570
583
  enabled: z8.boolean().optional(),
571
584
  only: phrases.optional(),
572
585
  great: phrases.optional(),
@@ -619,10 +632,12 @@ var init_workers = __esm({
619
632
  /** Each harness by its short name, such as claude, codex or gemini. */
620
633
  harnesses?: Record<string, {
621
634
  /**
622
- * Whether this machine runs the agent at all, as chosen in \`friday start\`. false means it's no
623
- * worker here. Without a configuration file, every installed agent is.
635
+ * How much of this agent its owner has, as chosen in \`friday start\`: "off", "little", "some"
636
+ * or "lot". Routing gives each its share of the work several agents suit equally, weighted
637
+ * 1, 2 and 4. "off" means it's no worker here. Without a configuration file, every installed
638
+ * agent is at "some".
624
639
  */
625
- enabled?: boolean;
640
+ share?: "off" | "little" | "some" | "lot";
626
641
  /** It takes only work about one of these, however good it is at other work. */
627
642
  only?: string[];
628
643
  /**
@@ -664,6 +679,8 @@ export default {
664
679
  /** What it's great, good and okay at, and can't do: its agent's defaults with its owner's word. */
665
680
  profile: z8.array(profileEntrySchema),
666
681
  priority: z8.number().int(),
682
+ /** How much of its agent its owner has. Servers from before shares send none, which is "some". */
683
+ share: shareSchema.default("some"),
667
684
  enabled: z8.boolean(),
668
685
  /** Whether its agent checked in from herd's terminal on this version, so it's offered work. */
669
686
  activated: z8.boolean(),
@@ -1002,6 +1019,7 @@ var init_events = __esm({
1002
1019
  init_prompts();
1003
1020
  init_usage();
1004
1021
  init_work_objects();
1022
+ init_workers();
1005
1023
  routeSchema = z13.enum(["direct", "mention", "joined"]);
1006
1024
  deliveryModeSchema = z13.enum(["interrupt", "defer"]);
1007
1025
  eventSourceSchema = z13.enum(["slack", "discord", "web", "api", "cli", "mcp", "system"]);
@@ -1056,7 +1074,18 @@ var init_events = __esm({
1056
1074
  // The repos the work hinted, and the workers whose machine had cloned every one, which go first
1057
1075
  // among workers that tie on fit and priority.
1058
1076
  hints: z13.array(nonEmpty6).optional(),
1059
- warm: z13.array(nonEmpty6).optional()
1077
+ warm: z13.array(nonEmpty6).optional(),
1078
+ // Each ordered worker's share, how its weekly usage is pacing, and the weight that gives it. The
1079
+ // first worker was drawn by weight from those in `drawn`, which suited the work equally.
1080
+ shares: z13.array(
1081
+ z13.object({
1082
+ key: nonEmpty6,
1083
+ share: shareSchema,
1084
+ pacing: z13.enum(["ahead", "even", "under", "unknown"]),
1085
+ weight: z13.number().nonnegative()
1086
+ })
1087
+ ).optional(),
1088
+ drawn: z13.array(nonEmpty6).optional()
1060
1089
  });
1061
1090
  conversationEventSchema = z13.discriminatedUnion("type", [
1062
1091
  event("message.created", {
@@ -3171,9 +3200,9 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
3171
3200
  // src/friday/main.ts
3172
3201
  init_define_HERD_PACKAGE();
3173
3202
  import { spawn as spawn5 } from "node:child_process";
3174
- import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
3203
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
3175
3204
  import { homedir as homedir4 } from "node:os";
3176
- import { dirname as dirname8, join as join13 } from "node:path";
3205
+ import { dirname as dirname9, join as join13 } from "node:path";
3177
3206
  import { z as z22 } from "zod";
3178
3207
 
3179
3208
  // src/auth.ts
@@ -3369,6 +3398,89 @@ async function apiFetch(path, init = {}) {
3369
3398
  return response;
3370
3399
  }
3371
3400
 
3401
+ // src/cli-flags.ts
3402
+ init_define_HERD_PACKAGE();
3403
+ var HERD_FLAGS = {
3404
+ login: [],
3405
+ logout: [],
3406
+ daemon: ["--repo"],
3407
+ attach: [],
3408
+ builds: [],
3409
+ inbox: [],
3410
+ send: ["--json"],
3411
+ automations: ["--json", "--wait"],
3412
+ channels: [],
3413
+ watch: [],
3414
+ unwatch: [],
3415
+ watching: [],
3416
+ conversations: ["--about", "--json"],
3417
+ github: ["--on", "--off", "--json"],
3418
+ setup: ["--json"],
3419
+ repo: [],
3420
+ repos: ["--json"],
3421
+ work: ["--kind", "--title", "--status", "--detail", "--phase", "--made", "--remove", "--json"],
3422
+ check: [],
3423
+ handoff: [],
3424
+ delegate: ["--repo"],
3425
+ tell: ["--json"],
3426
+ workers: ["--json", "--types"],
3427
+ status: ["--clear"],
3428
+ title: [],
3429
+ handled: [],
3430
+ pass: [],
3431
+ exit: [],
3432
+ persona: []
3433
+ };
3434
+ var FRIDAY_FLAGS = {
3435
+ start: ["--agents"],
3436
+ stop: [],
3437
+ status: [],
3438
+ logs: ["--follow", "-f", "-n"]
3439
+ };
3440
+ var HERD_OWN_HELP = /* @__PURE__ */ new Set([
3441
+ "builds",
3442
+ "send",
3443
+ "automations",
3444
+ "watch",
3445
+ "unwatch",
3446
+ "watching",
3447
+ "github",
3448
+ "repo",
3449
+ "repos",
3450
+ "work",
3451
+ "check",
3452
+ "handoff",
3453
+ "delegate",
3454
+ "tell",
3455
+ "workers",
3456
+ "status",
3457
+ "title"
3458
+ ]);
3459
+ var FLAG = /^--?[A-Za-z][\w-]*(=.*)?$/;
3460
+ function unknownFlag(program, command, args, known) {
3461
+ for (const arg of args) {
3462
+ if (arg === "--") break;
3463
+ if (!FLAG.test(arg)) continue;
3464
+ const name = arg.split("=")[0] ?? arg;
3465
+ if (name === "--help" || name === "-h" || known.includes(name)) continue;
3466
+ const takes = known.length ? `It takes ${known.join(", ")}.` : "It takes no options.";
3467
+ return `${program} ${command} doesn't have a "${name}" option, so nothing ran. ${takes} See ${program} ${command} --help.`;
3468
+ }
3469
+ return null;
3470
+ }
3471
+ var asksForHelp = (args) => args.some((arg) => arg === "--help" || arg === "-h");
3472
+ function commandHelp(overview, command) {
3473
+ const lines = overview.split("\n");
3474
+ const start = lines.findIndex((line2) => new RegExp(`^ ${command}(\\s|$)`).test(line2));
3475
+ if (start === -1) return null;
3476
+ const out = [lines[start] ?? ""];
3477
+ for (const line2 of lines.slice(start + 1)) {
3478
+ if (!/^ {4,}\S/.test(line2)) break;
3479
+ out.push(line2);
3480
+ }
3481
+ return out.join("\n");
3482
+ }
3483
+
3372
3484
  // src/commands/login.ts
3373
3485
  init_define_HERD_PACKAGE();
3374
3486
  import { spawn } from "node:child_process";
@@ -3402,9 +3514,9 @@ async function runLogout() {
3402
3514
  init_define_HERD_PACKAGE();
3403
3515
  import { execFileSync as execFileSync3 } from "node:child_process";
3404
3516
  import { randomUUID } from "node:crypto";
3405
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
3517
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
3406
3518
  import { homedir as homedir3, hostname } from "node:os";
3407
- import { basename, join as join5, resolve } from "node:path";
3519
+ import { basename, dirname as dirname3, join as join5, resolve } from "node:path";
3408
3520
  import { z as z18 } from "zod";
3409
3521
 
3410
3522
  // src/machine-facts.ts
@@ -3649,7 +3761,7 @@ var ADAPTERS = [
3649
3761
  { cli: "agy", capabilities: ["image_generation"] }
3650
3762
  ];
3651
3763
  function chosenClis(clis, config) {
3652
- return clis.filter((cli) => config?.harnesses?.[agentShortName(cli.name)]?.enabled !== false);
3764
+ return clis.filter((cli) => shareOf(config?.harnesses?.[agentShortName(cli.name)]) !== "off");
3653
3765
  }
3654
3766
  var runCommand = (command, args) => {
3655
3767
  try {
@@ -4111,6 +4223,20 @@ function readRoot() {
4111
4223
  function setRoot(path) {
4112
4224
  return save({ ...loadConfig(), root: resolve(path) });
4113
4225
  }
4226
+ async function waitForRoot(root, {
4227
+ pollMs = 2e3,
4228
+ exists = existsSync2,
4229
+ sleep: sleep8 = (ms) => new Promise((done) => setTimeout(done, ms)),
4230
+ log = (line2) => console.log(line2)
4231
+ } = {}) {
4232
+ const parent = dirname3(root);
4233
+ if (exists(parent)) return;
4234
+ log(
4235
+ `Waiting for ${parent}, which holds this machine's root, to be there, as when its drive mounts.`
4236
+ );
4237
+ while (!exists(parent)) await sleep8(pollMs);
4238
+ log(`${parent} is there.`);
4239
+ }
4114
4240
  var machineRoot = () => readRoot() ?? defaultRoot();
4115
4241
  function describeRepo(path) {
4116
4242
  const full = resolve(path);
@@ -4267,15 +4393,15 @@ async function readSetup(fetch2, init) {
4267
4393
  // src/friday/agents.ts
4268
4394
  init_define_HERD_PACKAGE();
4269
4395
  init_src();
4270
- import { existsSync as existsSync3, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
4271
- import { dirname as dirname4, join as join7 } from "node:path";
4396
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
4397
+ import { dirname as dirname5, join as join7 } from "node:path";
4272
4398
 
4273
4399
  // src/activation.ts
4274
4400
  init_define_HERD_PACKAGE();
4275
4401
  init_src();
4276
4402
  import { randomBytes as randomBytes4, randomUUID as randomUUID3 } from "node:crypto";
4277
4403
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
4278
- import { dirname as dirname3, join as join6 } from "node:path";
4404
+ import { dirname as dirname4, join as join6 } from "node:path";
4279
4405
  import { z as z21 } from "zod";
4280
4406
 
4281
4407
  // src/permissions.ts
@@ -4287,7 +4413,7 @@ import { z as z20 } from "zod";
4287
4413
  // src/local-socket.ts
4288
4414
  init_define_HERD_PACKAGE();
4289
4415
  import { randomBytes as randomBytes3 } from "node:crypto";
4290
- import { chmodSync as chmodSync2, existsSync as existsSync2, rmSync as rmSync4 } from "node:fs";
4416
+ import { chmodSync as chmodSync2, existsSync as existsSync3, rmSync as rmSync4 } from "node:fs";
4291
4417
  import { connect, createServer as createServer2 } from "node:net";
4292
4418
  import { z as z19 } from "zod";
4293
4419
  var localRequestSchema = z19.object({
@@ -4423,7 +4549,7 @@ var LocalSocket = class {
4423
4549
  }
4424
4550
  }
4425
4551
  async listen() {
4426
- if (existsSync2(this.path)) rmSync4(this.path);
4552
+ if (existsSync3(this.path)) rmSync4(this.path);
4427
4553
  this.server = createServer2((socket) => this.serve(socket));
4428
4554
  await new Promise((resolve6, reject) => {
4429
4555
  this.server?.once("error", reject);
@@ -4505,7 +4631,7 @@ function askDaemon(path, request) {
4505
4631
  }
4506
4632
  function isAnswering(path) {
4507
4633
  return new Promise((resolve6) => {
4508
- if (!existsSync2(path)) return resolve6(false);
4634
+ if (!existsSync3(path)) return resolve6(false);
4509
4635
  const socket = connect(path);
4510
4636
  socket.on("connect", () => {
4511
4637
  socket.destroy();
@@ -4744,7 +4870,7 @@ var Activator = class {
4744
4870
  }
4745
4871
  record(agent, record) {
4746
4872
  const agents = { ...readActivations(this.path), [agent]: record };
4747
- mkdirSync4(dirname3(this.path), { recursive: true, mode: 448 });
4873
+ mkdirSync4(dirname4(this.path), { recursive: true, mode: 448 });
4748
4874
  const temporary = `${this.path}.${process.pid}`;
4749
4875
  writeFileSync4(temporary, JSON.stringify({ agents }, null, 2), { mode: 384 });
4750
4876
  renameSync2(temporary, this.path);
@@ -4956,36 +5082,42 @@ var ACTIVATION_READ_MS = 500;
4956
5082
  var label2 = (cli) => AGENTS[cli]?.label ?? cli;
4957
5083
  function agentChoices(installed, saved) {
4958
5084
  const harnesses = saved?.harnesses ?? {};
4959
- const chosenBefore = Object.values(harnesses).some((harness) => harness.enabled !== void 0);
5085
+ const chosenBefore = Object.values(harnesses).some(
5086
+ (harness) => harness.enabled !== void 0 || harness.share !== void 0
5087
+ );
4960
5088
  return ADAPTERS.map(({ cli }) => {
4961
5089
  const isInstalled = installed.includes(cli);
4962
- const enabled = harnesses[agentShortName(cli)]?.enabled;
5090
+ const harness = harnesses[agentShortName(cli)];
5091
+ const before = harness?.share ?? (harness?.enabled ? "some" : "off");
4963
5092
  return {
4964
5093
  cli,
4965
5094
  label: label2(cli),
4966
5095
  installed: isInstalled,
4967
- checked: chosenBefore ? enabled === true : isInstalled
5096
+ share: chosenBefore ? before : isInstalled ? "some" : "off"
4968
5097
  };
4969
5098
  });
4970
5099
  }
4971
5100
  function parseAgents(value) {
4972
- const names = value.split(",").map((name) => name.trim().toLowerCase()).filter(Boolean);
4973
- const agents = [];
5101
+ const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
5102
+ const shares = Object.fromEntries(ADAPTERS.map(({ cli }) => [cli, "off"]));
4974
5103
  const unknown = [];
4975
- for (const name of names) {
5104
+ for (const part of parts) {
5105
+ const [name = "", share = "some"] = part.split("=", 2).map((piece) => piece.trim());
4976
5106
  const adapter = ADAPTERS.find(
4977
5107
  ({ cli }) => cli === name || agentShortName(cli) === name || label2(cli).toLowerCase() === name
4978
5108
  );
4979
- if (adapter) agents.push(adapter.cli);
4980
- else unknown.push(name);
5109
+ if (adapter && SHARES.includes(share)) {
5110
+ shares[adapter.cli] = share;
5111
+ } else unknown.push(part);
4981
5112
  }
4982
- return unknown.length > 0 ? { unknown } : { agents: [...new Set(agents)] };
5113
+ return unknown.length > 0 ? { unknown } : { shares };
4983
5114
  }
4984
- function withChoice(config, chosen) {
5115
+ function withChoice(config, shares) {
4985
5116
  const harnesses = { ...config?.harnesses ?? {} };
4986
5117
  for (const { cli } of ADAPTERS) {
4987
5118
  const short = agentShortName(cli);
4988
- harnesses[short] = { ...harnesses[short], enabled: chosen.includes(cli) };
5119
+ const { enabled: _, ...rest } = harnesses[short] ?? {};
5120
+ harnesses[short] = { ...rest, share: shares[cli] ?? "off" };
4989
5121
  }
4990
5122
  return { ...config, harnesses };
4991
5123
  }
@@ -4994,9 +5126,10 @@ function configSource(config) {
4994
5126
  export default ${JSON.stringify(config, null, 2)};
4995
5127
  `;
4996
5128
  }
4997
- var sameChoice = (config, chosen) => ADAPTERS.every(
4998
- ({ cli }) => config?.harnesses?.[agentShortName(cli)]?.enabled === chosen.includes(cli)
4999
- );
5129
+ var sameChoice = (config, shares) => ADAPTERS.every(({ cli }) => {
5130
+ const harness = config?.harnesses?.[agentShortName(cli)];
5131
+ return harness?.enabled === void 0 && harness?.share === (shares[cli] ?? "off");
5132
+ });
5000
5133
  async function chooseAgents(options, print) {
5001
5134
  const [installed, loaded] = await Promise.all([options.installed(), options.load()]);
5002
5135
  if (loaded.status === "error") {
@@ -5006,14 +5139,15 @@ async function chooseAgents(options, print) {
5006
5139
  const saved = loaded.status === "ok" ? loaded.config : void 0;
5007
5140
  const choices = agentChoices(installed, saved);
5008
5141
  const asked = options.given ? null : await (options.pick?.(choices) ?? Promise.resolve(null));
5009
- const chosen = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
5010
- choices.filter((choice) => choice.checked).map((choice) => choice.cli);
5011
- if (!sameChoice(saved, chosen) || !existsSync3(options.configPath)) {
5012
- mkdirSync5(dirname4(options.configPath), { recursive: true, mode: 448 });
5013
- writeFileSync5(options.configPath, configSource(withChoice(saved, chosen)), { mode: 384 });
5142
+ const shares = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
5143
+ Object.fromEntries(choices.map((choice) => [choice.cli, choice.share]));
5144
+ if (!sameChoice(saved, shares) || !existsSync4(options.configPath)) {
5145
+ mkdirSync5(dirname5(options.configPath), { recursive: true, mode: 448 });
5146
+ writeFileSync5(options.configPath, configSource(withChoice(saved, shares)), { mode: 384 });
5014
5147
  }
5148
+ const chosen = ADAPTERS.map(({ cli }) => cli).filter((cli) => (shares[cli] ?? "off") !== "off");
5015
5149
  print(
5016
- chosen.length > 0 ? `This machine runs ${chosen.map(label2).join(", ")}.` : "This machine runs no agents, so it takes no work. Run this again to choose some."
5150
+ chosen.length > 0 ? `This machine runs ${chosen.map((cli) => `${label2(cli)} (${SHARE_LABELS[shares[cli]]})`).join(", ")}.` : "This machine runs no agents, so it takes no work. Run this again to choose some."
5017
5151
  );
5018
5152
  return chosen;
5019
5153
  }
@@ -5081,7 +5215,7 @@ init_define_HERD_PACKAGE();
5081
5215
  import { spawnSync } from "node:child_process";
5082
5216
  import {
5083
5217
  closeSync as closeSync2,
5084
- existsSync as existsSync4,
5218
+ existsSync as existsSync5,
5085
5219
  mkdirSync as mkdirSync6,
5086
5220
  openSync as openSync2,
5087
5221
  readFileSync as readFileSync6,
@@ -5089,7 +5223,7 @@ import {
5089
5223
  statSync as statSync5,
5090
5224
  writeFileSync as writeFileSync6
5091
5225
  } from "node:fs";
5092
- import { dirname as dirname5, join as join8, resolve as resolve2 } from "node:path";
5226
+ import { dirname as dirname6, join as join8, resolve as resolve2 } from "node:path";
5093
5227
  var PACKAGE_NAME = "@herdingchats/friday";
5094
5228
  function fridayPaths(herdHome2) {
5095
5229
  const logs = join8(herdHome2, "logs");
@@ -5118,8 +5252,8 @@ function alive2(pid) {
5118
5252
  }
5119
5253
  }
5120
5254
  function takeLock(path, staleMs = 10 * 6e4) {
5121
- mkdirSync6(dirname5(path), { recursive: true });
5122
- if (existsSync4(path)) {
5255
+ mkdirSync6(dirname6(path), { recursive: true });
5256
+ if (existsSync5(path)) {
5123
5257
  const holder2 = Number(readFileSync6(path, "utf8").trim());
5124
5258
  const old = Date.now() - statSync5(path).mtimeMs > staleMs;
5125
5259
  if (old || !alive2(holder2)) rmSync6(path, { force: true });
@@ -5169,7 +5303,7 @@ function writeRoot(paths, root) {
5169
5303
  }
5170
5304
  function insideWorkTree(folder) {
5171
5305
  let dir = resolve2(folder);
5172
- while (!existsSync4(dir) && dirname5(dir) !== dir) dir = dirname5(dir);
5306
+ while (!existsSync5(dir) && dirname6(dir) !== dir) dir = dirname6(dir);
5173
5307
  const result = spawnSync("git", ["-C", dir, "rev-parse", "--show-toplevel"], {
5174
5308
  encoding: "utf8",
5175
5309
  stdio: ["ignore", "pipe", "ignore"]
@@ -5189,6 +5323,9 @@ var VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
5189
5323
 
5190
5324
  // src/friday/picker.ts
5191
5325
  init_define_HERD_PACKAGE();
5326
+ init_src();
5327
+ var LESS = /* @__PURE__ */ new Set(["-", "_", "\x1B[D"]);
5328
+ var MORE = /* @__PURE__ */ new Set(["+", "=", "\x1B[C"]);
5192
5329
  function pickerKey(state, key) {
5193
5330
  const { choices, cursor } = state;
5194
5331
  if (key === "" || key === "\x1B") return null;
@@ -5197,22 +5334,28 @@ function pickerKey(state, key) {
5197
5334
  return { ...state, cursor: (cursor + choices.length - 1) % choices.length };
5198
5335
  }
5199
5336
  if (key === "\x1B[B" || key === "j") return { ...state, cursor: (cursor + 1) % choices.length };
5200
- if (key === " ") {
5201
- return {
5202
- ...state,
5203
- choices: choices.map(
5204
- (choice, at) => at === cursor ? { ...choice, checked: !choice.checked } : choice
5205
- )
5206
- };
5207
- }
5208
- return state;
5337
+ const step = LESS.has(key) ? -1 : MORE.has(key) ? 1 : 0;
5338
+ if (step === 0) return state;
5339
+ return {
5340
+ ...state,
5341
+ choices: choices.map((choice, at) => {
5342
+ if (at !== cursor) return choice;
5343
+ const next = Math.min(SHARES.length - 1, Math.max(0, SHARES.indexOf(choice.share) + step));
5344
+ return { ...choice, share: SHARES[next] ?? choice.share };
5345
+ })
5346
+ };
5209
5347
  }
5210
5348
  function pickerLines(state) {
5349
+ const width = Math.max(...state.choices.map((choice) => choice.label.length));
5350
+ const cells = SHARES.length - 1;
5211
5351
  return [
5212
- "Which agents do you use on this computer? (\u2191/\u2193 to move, space to choose, Enter to accept)",
5213
- ...state.choices.map(
5214
- (choice, at) => `${at === state.cursor ? "\u276F" : " "} ${choice.checked ? "\u25C9" : "\u25CB"} ${choice.label}${choice.installed ? "" : " (not installed here)"}`
5215
- )
5352
+ "How much of each agent do you have on this computer? (\u2191/\u2193 to move, -/+ to change, Enter to accept)",
5353
+ ...state.choices.map((choice, at) => {
5354
+ const filled = SHARES.indexOf(choice.share);
5355
+ const bar = "\u25A0".repeat(filled) + "\u25A1".repeat(cells - filled);
5356
+ const missing = choice.installed ? "" : " (not installed here)";
5357
+ return `${at === state.cursor ? "\u276F" : " "} ${choice.label.padEnd(width)} ${bar} ${SHARE_LABELS[choice.share]}${missing}`;
5358
+ })
5216
5359
  ];
5217
5360
  }
5218
5361
  function pickInTerminal(choices, input = process.stdin, output2 = process.stdout) {
@@ -5229,18 +5372,18 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
5229
5372
  input.setRawMode(true);
5230
5373
  input.resume();
5231
5374
  draw(true);
5232
- const finish = (chosen) => {
5375
+ const finish = (shares) => {
5233
5376
  input.off("data", onData);
5234
5377
  input.setRawMode(wasRaw);
5235
5378
  input.pause();
5236
- resolve6(chosen);
5379
+ resolve6(shares);
5237
5380
  };
5238
5381
  const onData = (data) => {
5239
5382
  const next = pickerKey(state, data.toString("utf8"));
5240
5383
  if (!next) return finish(null);
5241
5384
  state = next;
5242
5385
  draw(false);
5243
- if (state.done) finish(state.choices.filter((c) => c.checked).map((c) => c.cli));
5386
+ if (state.done) finish(Object.fromEntries(state.choices.map((c) => [c.cli, c.share])));
5244
5387
  };
5245
5388
  input.on("data", onData);
5246
5389
  });
@@ -5249,9 +5392,9 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
5249
5392
  // src/friday/service.ts
5250
5393
  init_define_HERD_PACKAGE();
5251
5394
  import { spawn as spawn2 } from "node:child_process";
5252
- import { existsSync as existsSync5, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
5395
+ import { existsSync as existsSync6, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
5253
5396
  import { userInfo } from "node:os";
5254
- import { dirname as dirname6, join as join9 } from "node:path";
5397
+ import { dirname as dirname7, join as join9 } from "node:path";
5255
5398
  var LAUNCH_AGENT = "com.herdingchats.friday";
5256
5399
  var SYSTEMD_UNIT = "herdingchats-friday.service";
5257
5400
  async function detectService(run4, platform2 = process.platform, override = process.env.HERD_SERVICE) {
@@ -5266,7 +5409,7 @@ async function detectService(run4, platform2 = process.platform, override = proc
5266
5409
  }
5267
5410
  function servicePath(node) {
5268
5411
  return [
5269
- dirname6(node),
5412
+ dirname7(node),
5270
5413
  "/opt/homebrew/bin",
5271
5414
  "/usr/local/bin",
5272
5415
  "/usr/bin",
@@ -5337,8 +5480,8 @@ WantedBy=default.target
5337
5480
  `;
5338
5481
  }
5339
5482
  function writeIfChanged(path, text4) {
5340
- if (existsSync5(path) && readFileSync7(path, "utf8") === text4) return false;
5341
- mkdirSync7(dirname6(path), { recursive: true });
5483
+ if (existsSync6(path) && readFileSync7(path, "utf8") === text4) return false;
5484
+ mkdirSync7(dirname7(path), { recursive: true });
5342
5485
  writeFileSync7(path, text4);
5343
5486
  return true;
5344
5487
  }
@@ -5393,7 +5536,7 @@ function systemd(context2) {
5393
5536
  };
5394
5537
  }
5395
5538
  function spawnInBackground(command, env, log) {
5396
- mkdirSync7(dirname6(log), { recursive: true });
5539
+ mkdirSync7(dirname7(log), { recursive: true });
5397
5540
  const out = openSync3(log, "a");
5398
5541
  const [program = process.execPath, ...args] = command;
5399
5542
  spawn2(program, args, {
@@ -5442,20 +5585,20 @@ import { join as join12, resolve as resolve3 } from "node:path";
5442
5585
 
5443
5586
  // src/friday/legacy.ts
5444
5587
  init_define_HERD_PACKAGE();
5445
- import { existsSync as existsSync6, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
5588
+ import { existsSync as existsSync7, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
5446
5589
  import { userInfo as userInfo2 } from "node:os";
5447
5590
  import { join as join10 } from "node:path";
5448
5591
  var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
5449
5592
  var OLD_APPS = ["herd-daemon", "herd-updater"];
5450
5593
  function oldPm2s(paths) {
5451
5594
  const dir = join10(paths.herdHome, "daemon");
5452
- if (!existsSync6(dir)) return [];
5453
- return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync6(pm2));
5595
+ if (!existsSync7(dir)) return [];
5596
+ return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync7(pm2));
5454
5597
  }
5455
5598
  async function retireOldDaemon(paths, { home, run: run4 }) {
5456
5599
  const removed = [];
5457
5600
  const plist = join10(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
5458
- if (existsSync6(plist)) {
5601
+ if (existsSync7(plist)) {
5459
5602
  await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
5460
5603
  rmSync8(plist, { force: true });
5461
5604
  removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
@@ -5481,7 +5624,7 @@ async function retireOldDaemon(paths, { home, run: run4 }) {
5481
5624
  init_define_HERD_PACKAGE();
5482
5625
  import { spawn as spawn3 } from "node:child_process";
5483
5626
  import {
5484
- existsSync as existsSync7,
5627
+ existsSync as existsSync8,
5485
5628
  mkdirSync as mkdirSync8,
5486
5629
  readdirSync as readdirSync4,
5487
5630
  readFileSync as readFileSync8,
@@ -5492,7 +5635,7 @@ import {
5492
5635
  symlinkSync,
5493
5636
  writeFileSync as writeFileSync8
5494
5637
  } from "node:fs";
5495
- import { basename as basename2, dirname as dirname7, join as join11 } from "node:path";
5638
+ import { basename as basename2, dirname as dirname8, join as join11 } from "node:path";
5496
5639
  var exec = (command, args, options = {}) => new Promise((resolve6) => {
5497
5640
  let stdout = "";
5498
5641
  let stderr = "";
@@ -5514,8 +5657,8 @@ var exec = (command, args, options = {}) => new Promise((resolve6) => {
5514
5657
  });
5515
5658
  });
5516
5659
  function npmCommand() {
5517
- const beside = join11(dirname7(process.execPath), "npm");
5518
- return existsSync7(beside) ? beside : "npm";
5660
+ const beside = join11(dirname8(process.execPath), "npm");
5661
+ return existsSync8(beside) ? beside : "npm";
5519
5662
  }
5520
5663
  function currentVersion(paths) {
5521
5664
  try {
@@ -5525,7 +5668,7 @@ function currentVersion(paths) {
5525
5668
  }
5526
5669
  }
5527
5670
  function installedVersions(paths) {
5528
- if (!existsSync7(paths.versions)) return [];
5671
+ if (!existsSync8(paths.versions)) return [];
5529
5672
  return readdirSync4(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
5530
5673
  }
5531
5674
  var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
@@ -5556,7 +5699,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5556
5699
  } } = {}) {
5557
5700
  if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
5558
5701
  const dir = join11(paths.versions, version);
5559
- if (existsSync7(dir)) {
5702
+ if (existsSync8(dir)) {
5560
5703
  const installed2 = recordedIntegrity(dir);
5561
5704
  if (integrity && installed2 !== integrity)
5562
5705
  return { ok: false, reason: `the installed ${version} doesn't have the integrity named.` };
@@ -5609,7 +5752,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5609
5752
  return { ok: true, dir };
5610
5753
  }
5611
5754
  function sweepStaging(paths) {
5612
- if (!existsSync7(paths.versions)) return;
5755
+ if (!existsSync8(paths.versions)) return;
5613
5756
  for (const name of readdirSync4(paths.versions)) {
5614
5757
  const match = /^\.staging-.+-(\d+)$/.exec(name);
5615
5758
  if (match && !alive2(Number(match[1])))
@@ -5815,7 +5958,7 @@ init_define_HERD_PACKAGE();
5815
5958
  import { spawn as spawn4 } from "node:child_process";
5816
5959
  import {
5817
5960
  closeSync as closeSync3,
5818
- existsSync as existsSync8,
5961
+ existsSync as existsSync9,
5819
5962
  mkdirSync as mkdirSync10,
5820
5963
  openSync as openSync4,
5821
5964
  readFileSync as readFileSync9,
@@ -5900,7 +6043,7 @@ var Supervisor = class {
5900
6043
  const { daemonLog, logs } = this.options.paths;
5901
6044
  mkdirSync10(logs, { recursive: true });
5902
6045
  try {
5903
- if (existsSync8(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
6046
+ if (existsSync9(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
5904
6047
  renameSync4(daemonLog, `${daemonLog}.1`);
5905
6048
  } catch {
5906
6049
  }
@@ -6034,9 +6177,10 @@ Usage: friday <command>
6034
6177
 
6035
6178
  start Make this computer a machine, with the folder this runs in as its root, where
6036
6179
  Friday keeps its repos and conversations. Signs in through your browser when
6037
- needed, asks which agents you use, starts the daemon now and whenever you log
6038
- in, waits for each agent to check in, then shows what's left to set up. Safe to
6039
- run again. --agents claude,codex chooses the agents without asking
6180
+ needed, asks how much of each agent you have, starts the daemon now and whenever
6181
+ you log in, waits for each agent to check in, then shows what's left to set up.
6182
+ Safe to run again. --agents claude=lot,codex=little sets the shares without
6183
+ asking: off, little, some or lot, where a bare name is some and one left out off
6040
6184
  stop Stop the daemon until you log in again, or run friday start
6041
6185
  status Whether the daemon is running, which version, and its root
6042
6186
  logs [-f] Show the daemon's latest log lines. -f keeps following them
@@ -6072,6 +6216,18 @@ function passedEnv() {
6072
6216
  }
6073
6217
  async function friday(args, deps = defaults()) {
6074
6218
  const [command = "help", ...rest] = args;
6219
+ const known = FRIDAY_FLAGS[command];
6220
+ if (known) {
6221
+ if (asksForHelp(rest)) {
6222
+ deps.print(commandHelp(HELP, command) ?? HELP);
6223
+ return 0;
6224
+ }
6225
+ const wrong = unknownFlag("friday", command, rest, known);
6226
+ if (wrong) {
6227
+ deps.print(wrong);
6228
+ return 1;
6229
+ }
6230
+ }
6075
6231
  try {
6076
6232
  switch (command) {
6077
6233
  case "help":
@@ -6138,13 +6294,13 @@ function agentsFlag(args) {
6138
6294
  const parsed = parseAgents(value ?? "");
6139
6295
  if ("unknown" in parsed) {
6140
6296
  throw new StartError(
6141
- `friday start doesn't know the agent ${parsed.unknown.join(", ")}. Choose from claude, codex and agy.`
6297
+ `friday start doesn't understand ${parsed.unknown.join(", ")}. Name claude, codex or agy, each with =off, =little, =some or =lot if you like.`
6142
6298
  );
6143
6299
  }
6144
- return parsed.agents;
6300
+ return parsed.shares;
6145
6301
  }
6146
6302
  function attachTo(paths, terminal) {
6147
- const herd = existsSync9(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join13(dirname8(process.argv[1] ?? ""), "herd.mjs");
6303
+ const herd = existsSync10(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join13(dirname9(process.argv[1] ?? ""), "herd.mjs");
6148
6304
  const child = spawn5(process.execPath, [herd, "attach", terminal], {
6149
6305
  stdio: "inherit",
6150
6306
  env: { ...process.env, HERD_HOME: paths.herdHome }
@@ -6200,7 +6356,7 @@ function runStatus(deps) {
6200
6356
  return running ? 0 : 1;
6201
6357
  }
6202
6358
  function tail(path, count) {
6203
- if (!existsSync9(path)) return [];
6359
+ if (!existsSync10(path)) return [];
6204
6360
  const lines = readFileSync10(path, "utf8").split("\n");
6205
6361
  if (lines.at(-1) === "") lines.pop();
6206
6362
  return lines.slice(-count);
@@ -6208,7 +6364,7 @@ function tail(path, count) {
6208
6364
  async function runLogs(args, deps) {
6209
6365
  const { paths, print } = deps;
6210
6366
  const follow = args.includes("-f") || args.includes("--follow");
6211
- const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync9(file));
6367
+ const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync10(file));
6212
6368
  if (files.length === 0) {
6213
6369
  print(`There are no logs in ${paths.logs} yet.`);
6214
6370
  return 0;
@@ -6258,7 +6414,7 @@ init_define_HERD_PACKAGE();
6258
6414
  init_define_HERD_PACKAGE();
6259
6415
  import { spawn as spawn6 } from "node:child_process";
6260
6416
  import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
6261
- import { dirname as dirname9, join as join14 } from "node:path";
6417
+ import { dirname as dirname10, join as join14 } from "node:path";
6262
6418
  var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
6263
6419
  function buildPolicy(input) {
6264
6420
  const platform2 = input.platform ?? process.platform;
@@ -6288,7 +6444,7 @@ function describePolicy(policy, platform2 = process.platform) {
6288
6444
  const hosts = policy.network.allowedDomains.join(", ");
6289
6445
  return `Sandboxed with ${kind}: the owner's home is unreadable, writes go only to the worktree and the build's home, and the only hosts it reaches are ${hosts}.`;
6290
6446
  }
6291
- var nodePrefix = (execPath = process.execPath) => dirname9(dirname9(execPath));
6447
+ var nodePrefix = (execPath = process.execPath) => dirname10(dirname10(execPath));
6292
6448
  var shellQuote = (args) => args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).join(" ");
6293
6449
  var BLOCKED_LOG = "blocked.log";
6294
6450
  async function runSandboxed(args) {
@@ -6779,7 +6935,7 @@ init_define_HERD_PACKAGE();
6779
6935
  // src/build-runner-key.ts
6780
6936
  init_define_HERD_PACKAGE();
6781
6937
  init_src();
6782
- import { chmodSync as chmodSync3, existsSync as existsSync10, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
6938
+ import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
6783
6939
  import { join as join15 } from "node:path";
6784
6940
  import { z as z24 } from "zod";
6785
6941
  var BUILDS_URL = "https://builds.herdingchats.com";
@@ -6791,7 +6947,7 @@ var runnerKeySchema = z24.object({
6791
6947
  var runnerKeyPath = () => join15(herdHome(), "build-runner.json");
6792
6948
  function readRunnerKey() {
6793
6949
  const path = runnerKeyPath();
6794
- if (!existsSync10(path)) return null;
6950
+ if (!existsSync11(path)) return null;
6795
6951
  try {
6796
6952
  return runnerKeySchema.parse(JSON.parse(readFileSync13(path, "utf8")));
6797
6953
  } catch {
@@ -7765,7 +7921,7 @@ init_define_HERD_PACKAGE();
7765
7921
  import { execFile as execFile2, execFileSync as execFileSync4, spawn as spawn7 } from "node:child_process";
7766
7922
  import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
7767
7923
  import {
7768
- existsSync as existsSync11,
7924
+ existsSync as existsSync12,
7769
7925
  mkdirSync as mkdirSync15,
7770
7926
  mkdtempSync,
7771
7927
  readdirSync as readdirSync6,
@@ -7859,7 +8015,7 @@ var real2 = (path) => {
7859
8015
  }
7860
8016
  };
7861
8017
  function worktreeOf(dir, store) {
7862
- if (!existsSync11(join20(dir, ".git"))) return false;
8018
+ if (!existsSync12(join20(dir, ".git"))) return false;
7863
8019
  try {
7864
8020
  return real2(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real2(store);
7865
8021
  } catch {
@@ -7871,10 +8027,10 @@ function cloneFolder(from, to) {
7871
8027
  execFileSync4("cp", [...args, from, to]);
7872
8028
  }
7873
8029
  function moduleFolders(dir) {
7874
- const found = existsSync11(join20(dir, "node_modules")) ? ["node_modules"] : [];
8030
+ const found = existsSync12(join20(dir, "node_modules")) ? ["node_modules"] : [];
7875
8031
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
7876
8032
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
7877
- if (existsSync11(join20(dir, entry.name, "node_modules")))
8033
+ if (existsSync12(join20(dir, entry.name, "node_modules")))
7878
8034
  found.push(join20(entry.name, "node_modules"));
7879
8035
  }
7880
8036
  }
@@ -7970,11 +8126,11 @@ var BuildRunner = class _BuildRunner {
7970
8126
  async ensureModules(dir) {
7971
8127
  const wanted = lockHash(dir);
7972
8128
  const marker = join20(dir, "node_modules", ".herd-lock");
7973
- if (existsSync11(marker) && readFileSync16(marker, "utf8") === wanted) return;
8129
+ if (existsSync12(marker) && readFileSync16(marker, "utf8") === wanted) return;
7974
8130
  const cache = join20(this.home(), "node_modules", wanted);
7975
8131
  for (const folder of moduleFolders(dir))
7976
8132
  rmSync12(join20(dir, folder), { recursive: true, force: true });
7977
- if (existsSync11(join20(cache, ".complete"))) {
8133
+ if (existsSync12(join20(cache, ".complete"))) {
7978
8134
  for (const folder of readFileSync16(join20(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
7979
8135
  cloneFolder(join20(cache, folder), join20(dir, folder));
7980
8136
  }
@@ -8156,7 +8312,7 @@ var BuildRunner = class _BuildRunner {
8156
8312
  clearTimeout(timer);
8157
8313
  if (status2 === 10)
8158
8314
  return { ok: false, reason: "the sandbox let a build read the owner's files" };
8159
- if (status2 !== 0 || !existsSync11(join20(dir, "home", "ok"))) {
8315
+ if (status2 !== 0 || !existsSync12(join20(dir, "home", "ok"))) {
8160
8316
  const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
8161
8317
  return { ok: false, reason: why.slice(0, 300) };
8162
8318
  }
@@ -8929,7 +9085,7 @@ init_define_HERD_PACKAGE();
8929
9085
  import { execFileSync as execFileSync5 } from "node:child_process";
8930
9086
  import {
8931
9087
  appendFileSync as appendFileSync2,
8932
- existsSync as existsSync12,
9088
+ existsSync as existsSync13,
8933
9089
  mkdirSync as mkdirSync16,
8934
9090
  readFileSync as readFileSync17,
8935
9091
  renameSync as renameSync5,
@@ -8945,7 +9101,7 @@ var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
8945
9101
  function writeWorktreeSettings(worktree, settings2, ownCommand) {
8946
9102
  const path = join22(worktree, LOCAL_SETTINGS);
8947
9103
  let current = {};
8948
- if (existsSync12(path)) {
9104
+ if (existsSync13(path)) {
8949
9105
  try {
8950
9106
  const parsed = JSON.parse(readFileSync17(path, "utf8"));
8951
9107
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
@@ -8990,7 +9146,7 @@ function ignoreLocalSettings(worktree) {
8990
9146
  const info = join22(isAbsolute3(common) ? common : join22(worktree, common), "info");
8991
9147
  const exclude = join22(info, "exclude");
8992
9148
  const line2 = "/.claude/settings.local.json";
8993
- const existing = existsSync12(exclude) ? readFileSync17(exclude, "utf8") : "";
9149
+ const existing = existsSync13(exclude) ? readFileSync17(exclude, "utf8") : "";
8994
9150
  if (existing.split("\n").includes(line2)) return;
8995
9151
  mkdirSync16(info, { recursive: true });
8996
9152
  appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
@@ -9039,7 +9195,7 @@ function loginShellEnv() {
9039
9195
 
9040
9196
  // src/machine-checks.ts
9041
9197
  init_define_HERD_PACKAGE();
9042
- import { existsSync as existsSync13, rmSync as rmSync13 } from "node:fs";
9198
+ import { existsSync as existsSync14, rmSync as rmSync13 } from "node:fs";
9043
9199
  var REQUEST_POLL_MS = 2e3;
9044
9200
  var MachineChecks = class {
9045
9201
  constructor(options) {
@@ -9085,7 +9241,7 @@ var MachineChecks = class {
9085
9241
  /** `friday start` asked for every agent to be activated again. */
9086
9242
  takeRequest() {
9087
9243
  const path = activationRequestPath(this.options.herdHome);
9088
- if (!existsSync13(path)) return;
9244
+ if (!existsSync14(path)) return;
9089
9245
  rmSync13(path, { force: true });
9090
9246
  this.activateChosen(true);
9091
9247
  }
@@ -10623,7 +10779,7 @@ import {
10623
10779
  statSync as statSync8,
10624
10780
  writeFileSync as writeFileSync16
10625
10781
  } from "node:fs";
10626
- import { dirname as dirname10, join as join29, sep as sep2 } from "node:path";
10782
+ import { dirname as dirname11, join as join29, sep as sep2 } from "node:path";
10627
10783
  import { Readable } from "node:stream";
10628
10784
  import { pipeline } from "node:stream/promises";
10629
10785
 
@@ -10811,6 +10967,7 @@ function describe(worker, current) {
10811
10967
  );
10812
10968
  lines.push(` ${label3}: ${listed.join(", ")}`);
10813
10969
  }
10970
+ lines.push(` share: ${SHARE_LABELS[worker.share]}`);
10814
10971
  if (worker.priority !== 0) lines.push(` priority: ${worker.priority}`);
10815
10972
  const repos = worker.repos.map(
10816
10973
  (repo) => `${normalizeRemote(repo.remote) ?? repo.name} (${isCloned(repo) ? "cloned" : "not cloned"})`
@@ -11673,8 +11830,8 @@ async function download(ws, workspace, file) {
11673
11830
  if (lstatSync2(herd, { throwIfNoEntry: false })?.isSymbolicLink()) {
11674
11831
  throw new Error("its folder leads outside the worktree");
11675
11832
  }
11676
- mkdirSync20(dirname10(target), { recursive: true });
11677
- if (!realpathSync8(dirname10(target)).startsWith(realpathSync8(workspace.folder) + sep2)) {
11833
+ mkdirSync20(dirname11(target), { recursive: true });
11834
+ if (!realpathSync8(dirname11(target)).startsWith(realpathSync8(workspace.folder) + sep2)) {
11678
11835
  throw new Error("its folder leads outside the worktree");
11679
11836
  }
11680
11837
  writeFileSync16(join29(herd, ".gitignore"), "*\n");
@@ -12187,6 +12344,7 @@ async function runDaemon(args) {
12187
12344
  const root = setRoot(defaultRoot()).root;
12188
12345
  console.log(`No root folder is set for this machine, so it works from ${root}.`);
12189
12346
  }
12347
+ await waitForRoot(machineRoot(), { pollMs: Number(process.env.HERD_ROOT_POLL_MS) || 2e3 });
12190
12348
  const api = apiUrl();
12191
12349
  const terminals = new HostTerminals({ log: (line2) => console.log(line2) });
12192
12350
  await terminals.ensureHost();
@@ -13837,6 +13995,18 @@ Inside a terminal the daemon started:
13837
13995
  Set HERD_API_URL to use another API, such as http://localhost:5174 for the local dev server.`;
13838
13996
  async function main(args) {
13839
13997
  const [command = "help", ...rest] = args;
13998
+ const known = HERD_FLAGS[command];
13999
+ if (known) {
14000
+ if (asksForHelp(rest) && !HERD_OWN_HELP.has(command)) {
14001
+ console.log(commandHelp(HELP5, command) ?? HELP5);
14002
+ return 0;
14003
+ }
14004
+ const wrong = unknownFlag("herd", command, rest, known);
14005
+ if (wrong) {
14006
+ console.error(wrong);
14007
+ return 1;
14008
+ }
14009
+ }
13840
14010
  try {
13841
14011
  switch (command) {
13842
14012
  case "help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herdingchats/friday",
3
- "version": "0.1.1",
3
+ "version": "0.1.525",
4
4
  "description": "Friday, HerdingChats' agent daemon, and the herd command it runs agents with.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,5 +22,5 @@
22
22
  "satori": "0.32.0",
23
23
  "zod": "4.6.7"
24
24
  },
25
- "gitHead": "91b08aebd285af82cf2ea35b4b380b52acf2a33f"
25
+ "gitHead": "cbd227dfdbed52c13e4cb3a2b388d6e7d4a6866e"
26
26
  }