@logic-joe/fleek 0.2.15 → 0.2.16

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/dist/fleek.mjs +595 -251
  2. package/package.json +1 -1
package/dist/fleek.mjs CHANGED
@@ -2395,6 +2395,7 @@ var require_tools = __commonJS({
2395
2395
  fleek_get_whiteboard: "whiteboards.get",
2396
2396
  whiteboard_scene: "whiteboards.get",
2397
2397
  fleek_list_attachments: "attachments.list",
2398
+ fleek_get_attachment: "attachments.get",
2398
2399
  // DOCFMT stage 1 (DOC-P2) reads, registered in INTEG-4 (coordinator 12.10.2026, IP10 option a)
2399
2400
  fleek_read_document_fmd: "documents.readFmd",
2400
2401
  fleek_list_space_documents: "documents.listInSpace",
@@ -4652,7 +4653,7 @@ var require_setup_session = __commonJS({
4652
4653
  exports.SETUP_SESSION_TTL_MINUTES = 30;
4653
4654
  exports.SETUP_SESSION_OPEN_MAX = 10;
4654
4655
  exports.SETUP_SESSION_CLAIM_LIMIT = 10;
4655
- exports.FLEEK_CLI_SETUP_VERSION_DEFAULT = "0.2.15";
4656
+ exports.FLEEK_CLI_SETUP_VERSION_DEFAULT = "0.2.16";
4656
4657
  exports.FLEEK_CLI_PACKAGE = "@logic-joe/fleek";
4657
4658
  exports.FLEEK_CANONICAL_SERVER = "https://api.onfleek.app";
4658
4659
  exports.SETUP_CLIENT_APPS = ["claude-code", "codex", "orca", "cursor", "other"];
@@ -5594,7 +5595,7 @@ var require_util = __commonJS({
5594
5595
  else
5595
5596
  target[key] = value;
5596
5597
  }
5597
- function mirrorShape(target, source, keys, wrap) {
5598
+ function mirrorShape(target, source, keys, wrap2) {
5598
5599
  const raw = sourceShape(source);
5599
5600
  for (const key of keys) {
5600
5601
  const desc = Object.getOwnPropertyDescriptor(raw, key);
@@ -5603,10 +5604,10 @@ var require_util = __commonJS({
5603
5604
  if (desc.get) {
5604
5605
  deferProp(target, key, () => {
5605
5606
  const value = source._zod.def.shape[key];
5606
- return wrap ? wrap(value, key) : value;
5607
+ return wrap2 ? wrap2(value, key) : value;
5607
5608
  });
5608
5609
  } else
5609
- putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);
5610
+ putProp(target, key, wrap2 ? wrap2(desc.value, key) : desc.value);
5610
5611
  }
5611
5612
  }
5612
5613
  function mirrorProps(target, source) {
@@ -36471,7 +36472,7 @@ var require_dist2 = __commonJS({
36471
36472
  var import_types11 = __toESM(require_dist(), 1);
36472
36473
  import { spawn as spawn9 } from "node:child_process";
36473
36474
  import { createHash as createHash9, randomBytes as randomBytes12 } from "node:crypto";
36474
- import { rm as rm13 } from "node:fs/promises";
36475
+ import { rm as rm13, stat as stat16 } from "node:fs/promises";
36475
36476
  import { hostname as osHostname2 } from "node:os";
36476
36477
  import { join as join33 } from "node:path";
36477
36478
  import { parseArgs } from "node:util";
@@ -36944,6 +36945,11 @@ var Output = class {
36944
36945
  if (this.activities.size) this.progress(null);
36945
36946
  this.stderr.write(`${this.render(text6, "err")}
36946
36947
  `);
36948
+ }
36949
+ /** The keyboard list (tui.ts): CLI-owned cursor control plus rendered lines in one write, so a redraw never flickers. */
36950
+ screen(control, lines) {
36951
+ this.stdout.write(control + lines.map((line) => `${this.render(line, "out")}
36952
+ `).join(""));
36947
36953
  }
36948
36954
  /** CLI-owned OSC-8 link. The label/URI are neutralized; JSON, pipes and dumb terminals get a plain URL. */
36949
36955
  link(label, uri, stream = "out") {
@@ -37270,20 +37276,32 @@ var EN = {
37270
37276
  "menu.setupServer": "Server: {server}",
37271
37277
  "menu.resume": "Continue {key}",
37272
37278
  "menu.ticketDefault": "Ticket key (Enter = {key}):",
37273
- "menu.fix": "Setup needs attention: {command} (option 3).",
37274
- "menu.recommend": "Recommended: {action}",
37279
+ "menu.fix": "Setup needs attention: {command}. Open status and repair.",
37280
+ "menu.recommended": "Recommended",
37281
+ "menu.quit": "Quit",
37282
+ "menu.loading": "Loading …",
37283
+ "menu.yes": "Yes",
37284
+ "menu.no": "No",
37285
+ "menu.yesNo": "(y/N)",
37286
+ "menu.setupComputer": "Set up this computer",
37287
+ "menu.switchCheckout": "Switch checkout",
37288
+ "menu.checkoutTitle": "Which checkout do you want to work in?",
37289
+ "menu.key.move": "↑↓ move",
37290
+ "menu.key.open": "Enter open",
37291
+ "menu.key.confirm": "Enter confirm",
37292
+ "menu.key.back": "Esc back",
37293
+ "menu.key.cancel": "Esc cancel",
37294
+ "menu.key.quit": "q quit",
37275
37295
  "menu.connect": "Connect this computer",
37276
- "menu.bind": "Choose a checkout or set up this computer",
37277
37296
  "menu.pick": "My next ticket",
37278
37297
  "menu.continue": "Continue a ticket",
37279
37298
  "menu.doctor": "Status and repair",
37280
37299
  "menu.connections": "Connect ChatGPT / Claude · Settings › Connections",
37281
37300
  "menu.help": "All commands",
37282
- "menu.choose": "Number (Enter cancels):",
37283
- "menu.checkout": "Checkout number (0 = setup, Enter cancels):",
37301
+ "menu.choose": "Number (Enter quits):",
37284
37302
  "menu.code": "Paste fleek setup <code> from Settings › Connections (Enter cancels):",
37285
37303
  "menu.ticket": "Ticket key (Enter cancels):",
37286
- "menu.repair": "Run {command} to review repair offers? (y/N)",
37304
+ "menu.repair": "Run {command} to review repair offers?",
37287
37305
  "read.more": "More details: add --json.",
37288
37306
  "read.empty": "No entries.",
37289
37307
  "read.items": "Entries",
@@ -37660,6 +37678,7 @@ var EN = {
37660
37678
  "orca.rateWait": "Rate limit: waiting {seconds} s, then continuing at the same page.",
37661
37679
  "orca.partial": "Partial list after {pages} pages. Narrow it with --project or --mine.",
37662
37680
  "orca.listCount": "{count} open tickets · mine first, priority, recently updated",
37681
+ "orca.pickTitle": "{count} tickets · mine first, priority, recently updated",
37663
37682
  "orca.pickPage": "Tickets {first}–{last} of {count} · mine first, priority, recently updated",
37664
37683
  "orca.pickPages": "n: next page · p: previous page · enter any ticket number",
37665
37684
  "errors.proxyProfileAmbiguous": "Several sign-ins are bound to this checkout. Pin one with --profile <key> or FLEEK_PROFILE.",
@@ -38045,20 +38064,32 @@ var DE = {
38045
38064
  "menu.setupServer": "Server: {server}",
38046
38065
  "menu.resume": "{key} weiterarbeiten",
38047
38066
  "menu.ticketDefault": "Ticket-Schlüssel (Enter = {key}):",
38048
- "menu.fix": "Einrichtung prüfen: {command} (Option 3).",
38049
- "menu.recommend": "Empfohlen: {action}",
38067
+ "menu.fix": "Einrichtung prüfen: {command}. Öffne Status und Reparatur.",
38068
+ "menu.recommended": "Empfohlen",
38069
+ "menu.quit": "Beenden",
38070
+ "menu.loading": "Lädt …",
38071
+ "menu.yes": "Ja",
38072
+ "menu.no": "Nein",
38073
+ "menu.yesNo": "(j/N)",
38074
+ "menu.setupComputer": "Rechner einrichten",
38075
+ "menu.switchCheckout": "Checkout wechseln",
38076
+ "menu.checkoutTitle": "In welchem Checkout möchtest du arbeiten?",
38077
+ "menu.key.move": "↑↓ wählen",
38078
+ "menu.key.open": "Enter öffnen",
38079
+ "menu.key.confirm": "Enter bestätigen",
38080
+ "menu.key.back": "Esc zurück",
38081
+ "menu.key.cancel": "Esc abbrechen",
38082
+ "menu.key.quit": "q beenden",
38050
38083
  "menu.connect": "Rechner verbinden",
38051
- "menu.bind": "Checkout wählen oder Rechner einrichten",
38052
38084
  "menu.pick": "Mein nächstes Ticket",
38053
38085
  "menu.continue": "Ticket fortsetzen",
38054
38086
  "menu.doctor": "Status und Reparatur",
38055
38087
  "menu.connections": "ChatGPT / Claude verbinden · Einstellungen › Verbindungen",
38056
38088
  "menu.help": "Alle Befehle",
38057
- "menu.choose": "Nummer (Enter bricht ab):",
38058
- "menu.checkout": "Checkout-Nummer (0 = Einrichtung, Enter bricht ab):",
38089
+ "menu.choose": "Nummer (Enter beendet):",
38059
38090
  "menu.code": "fleek setup <code> aus Einstellungen › Verbindungen einfügen (Enter bricht ab):",
38060
38091
  "menu.ticket": "Ticket-Schlüssel (Enter bricht ab):",
38061
- "menu.repair": "Mit {command} Reparaturangebote prüfen? (j/N)",
38092
+ "menu.repair": "Mit {command} Reparaturangebote prüfen?",
38062
38093
  "read.more": "Weitere Details: --json ergänzen.",
38063
38094
  "read.empty": "Keine Einträge.",
38064
38095
  "read.items": "Einträge",
@@ -38435,6 +38466,7 @@ var DE = {
38435
38466
  "orca.rateWait": "Anfragelimit: {seconds} s warten, dann auf derselben Seite fortsetzen.",
38436
38467
  "orca.partial": "Teilliste nach {pages} Seiten. Mit --project oder --mine eingrenzen.",
38437
38468
  "orca.listCount": "{count} offene Tickets · eigene zuerst, Priorität, zuletzt aktualisiert",
38469
+ "orca.pickTitle": "{count} Tickets · eigene zuerst, Priorität, zuletzt aktualisiert",
38438
38470
  "orca.pickPage": "Tickets {first}–{last} von {count} · eigene zuerst, Priorität, zuletzt aktualisiert",
38439
38471
  "orca.pickPages": "n: nächste Seite · p: vorherige Seite · beliebige Ticket-Nummer eingeben",
38440
38472
  "errors.proxyProfileAmbiguous": "Mehrere Anmeldungen sind an dieses Repo gebunden. Leg eine fest mit --profile <schlüssel> oder FLEEK_PROFILE.",
@@ -38807,8 +38839,8 @@ async function processStart(pid) {
38807
38839
  if (process.platform === "win32") return null;
38808
38840
  if (process.platform === "linux") {
38809
38841
  try {
38810
- const stat16 = await readFile2(`/proc/${pid}/stat`, "utf8");
38811
- const fields = stat16.slice(stat16.lastIndexOf(")") + 2).split(" ");
38842
+ const stat17 = await readFile2(`/proc/${pid}/stat`, "utf8");
38843
+ const fields = stat17.slice(stat17.lastIndexOf(")") + 2).split(" ");
38812
38844
  const ticks = Number(fields[19]);
38813
38845
  return Number.isSafeInteger(ticks) ? ticks : null;
38814
38846
  } catch {
@@ -49716,7 +49748,7 @@ async function reloadProxyProfile(store, started) {
49716
49748
  // src/orca.ts
49717
49749
  import { execFile as nodeExecFile } from "node:child_process";
49718
49750
  import { realpath as realpath4 } from "node:fs/promises";
49719
- import { posix, win32 } from "node:path";
49751
+ import { posix as posix2, win32 as win322 } from "node:path";
49720
49752
  import { setTimeout as sleep4 } from "node:timers/promises";
49721
49753
 
49722
49754
  // src/tools.ts
@@ -49773,12 +49805,277 @@ var WIRE = {
49773
49805
  reportServiceHost: "fleek_report_service_host"
49774
49806
  };
49775
49807
 
49808
+ // src/header.ts
49809
+ import { win32, posix } from "node:path";
49810
+
49811
+ // src/uuid.ts
49812
+ var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
49813
+ function isUuid(value) {
49814
+ return UUID2.test(value);
49815
+ }
49816
+ function canonicalId(value) {
49817
+ if (!UUID2.test(value)) throw new Error("INVALID_UUID");
49818
+ return value.toLowerCase();
49819
+ }
49820
+
49821
+ // src/header.ts
49822
+ var LOGO2 = [" ▄██████▀", " ▄█████▀ ", " ██▀ "];
49823
+ var ASCII_LOGO = [" ######/", " #####/ ", " ##/ "];
49824
+ var BRAND = "\x1B[38;2;69;94;255m";
49825
+ function shouldShowHeader(positionals, options, terminal) {
49826
+ if (options.json || options.version || terminal.stream.isTTY !== true) return false;
49827
+ const active = (value) => value !== void 0 && value !== "" && value !== "0" && value !== "false";
49828
+ if (["CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "BUILD_NUMBER", "TF_BUILD"].some((key) => active(terminal.env[key]))) return false;
49829
+ const [command, sub] = positionals;
49830
+ return !command || command === "help" || !!options.help || command === "setup" || command === "doctor" || command === "init" || command === "kit" && ["setup", "doctor", "pull"].includes(sub ?? "") || command === "service" && sub === "setup";
49831
+ }
49832
+ function cells(value) {
49833
+ let width = 0;
49834
+ for (const char of value) {
49835
+ const code = char.codePointAt(0);
49836
+ if (new RegExp("\\p{Mark}", "u").test(char)) continue;
49837
+ width += code >= 4352 && (code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744) ? 2 : 1;
49838
+ }
49839
+ return width;
49840
+ }
49841
+ function fit(value, width, ascii) {
49842
+ if (cells(value) <= width) return value;
49843
+ const suffix = ascii ? "..." : "…";
49844
+ if (width <= suffix.length) return suffix.slice(0, Math.max(0, width));
49845
+ let out = "";
49846
+ for (const char of value) {
49847
+ if (cells(out + char) > width - suffix.length) break;
49848
+ out += char;
49849
+ }
49850
+ return out + suffix;
49851
+ }
49852
+ var safe = (value) => neutralizeLine(redact(value));
49853
+ function directory(context, windows, env) {
49854
+ const path = windows ? win32 : posix;
49855
+ const home = context.home ?? userHome(env, windows ? "win32" : process.platform);
49856
+ const relative3 = path.relative(home, context.cwd);
49857
+ const inside = relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path.sep}`) && !path.isAbsolute(relative3);
49858
+ return safe(inside ? relative3 ? `~${path.sep}${relative3}` : "~" : context.cwd);
49859
+ }
49860
+ function displayPath(cwd, home, env) {
49861
+ return directory({ version: "", cwd, home }, process.platform === "win32", env);
49862
+ }
49863
+ function headerServer(value) {
49864
+ if (!value) return void 0;
49865
+ try {
49866
+ const url = new URL(value);
49867
+ return ["http:", "https:"].includes(url.protocol) ? url.host : void 0;
49868
+ } catch {
49869
+ return void 0;
49870
+ }
49871
+ }
49872
+ function renderHeader(context, terminal, t) {
49873
+ const windows = (terminal.platform ?? process.platform) === "win32";
49874
+ const ascii = windows || terminal.env.TERM === "dumb";
49875
+ const separator = ascii ? " - " : " · ";
49876
+ const requested = terminal.stream.columns ?? Number(terminal.env.COLUMNS);
49877
+ const columns = Number.isFinite(requested) && requested > 0 ? Math.floor(requested) : 80;
49878
+ const title = t("header.title", { version: safe(context.version) });
49879
+ const cwd = directory(context, windows, terminal.env);
49880
+ const parts = [context.workspace, context.project && !isUuid(context.project) ? context.project : void 0, headerServer(context.server), context.displayName].filter((value) => !!value);
49881
+ const details = parts.length ? parts.map(safe).join(separator) : t("header.noContext");
49882
+ if (columns < 60) return ["", fit([title, cwd].join(separator), columns, ascii)];
49883
+ const logo = ascii ? ASCII_LOGO : LOGO2;
49884
+ const width = Math.max(...logo.map(cells));
49885
+ const color = colorEnabled(terminal.env, terminal.stream);
49886
+ const available2 = columns - width - 6;
49887
+ const tagline = separator + t("header.tagline");
49888
+ const attribution = separator + t("header.attribution");
49889
+ const secondary = cells(title + tagline) <= available2 ? tagline + (cells(title + tagline + attribution) <= available2 ? attribution : "") : "";
49890
+ const text6 = [title, details, cwd];
49891
+ const rows = logo.map((row, index) => {
49892
+ const mark = row.padEnd(width);
49893
+ const label = fit(text6[index], available2, ascii);
49894
+ if (index === 0) {
49895
+ const heading = color ? `\x1B[1m${label}\x1B[22m` : label;
49896
+ const branding = color && secondary ? `\x1B[2m${secondary}\x1B[22m` : secondary;
49897
+ return `${color ? `${BRAND}${mark}\x1B[39m` : mark} ${heading}${branding}`;
49898
+ }
49899
+ const styled = color ? `\x1B[2m${label}\x1B[22m` : label;
49900
+ return `${color ? `${BRAND}${mark}\x1B[39m` : mark} ${styled}`;
49901
+ });
49902
+ const inner = Math.max(...rows.map((row) => cells(stripAnsi(row))));
49903
+ const edge = (value) => color ? `${BRAND}${value}\x1B[39m` : value;
49904
+ const vertical = ascii ? "|" : "│";
49905
+ return [
49906
+ "",
49907
+ edge(`${ascii ? "+" : "╭"}${(ascii ? "-" : "─").repeat(inner + 2)}${ascii ? "+" : "╮"}`),
49908
+ ...rows.map((row) => `${edge(vertical)} ${row}${" ".repeat(inner - cells(stripAnsi(row)))} ${edge(vertical)}`),
49909
+ edge(`${ascii ? "+" : "╰"}${(ascii ? "-" : "─").repeat(inner + 2)}${ascii ? "+" : "╯"}`)
49910
+ ];
49911
+ }
49912
+ function printHeader(out, context, terminal, t) {
49913
+ for (const line of renderHeader(context, terminal, t)) out.line(line);
49914
+ out.line();
49915
+ out.indentHumanOutput();
49916
+ }
49917
+
49918
+ // src/tui.ts
49919
+ var yesNoList = (question, t) => ({
49920
+ title: [question],
49921
+ items: [{ id: "no", label: t("menu.no") }, { id: "yes", label: t("menu.yes") }],
49922
+ initial: "no",
49923
+ back: true,
49924
+ confirm: true,
49925
+ echo: (item2) => `${question} ${item2.label}`
49926
+ });
49927
+ var isYes = (result) => result.kind === "select" && result.id === "yes";
49928
+ function isKeyInput(stream) {
49929
+ const input = stream;
49930
+ return !!input && input.isTTY === true && typeof input.setRawMode === "function";
49931
+ }
49932
+ var SEQUENCE = /\u001b\[[0-9;]*[A-Za-z~]|\u001bO[A-Za-z]|[\s\S]/g;
49933
+ var NAMED = {
49934
+ "\x1B[A": "up",
49935
+ "\x1BOA": "up",
49936
+ k: "up",
49937
+ "\x1B[B": "down",
49938
+ "\x1BOB": "down",
49939
+ j: "down",
49940
+ "\x1B[H": "home",
49941
+ "\x1BOH": "home",
49942
+ "\x1B[1~": "home",
49943
+ "\x1B[F": "end",
49944
+ "\x1BOF": "end",
49945
+ "\x1B[4~": "end",
49946
+ "\x1B[5~": "pageUp",
49947
+ "\x1B[6~": "pageDown",
49948
+ "\r": "enter",
49949
+ "\n": "enter",
49950
+ "\x1B[C": "enter",
49951
+ "\x1BOC": "enter",
49952
+ "\x1B": "back",
49953
+ "\x1B[D": "back",
49954
+ "\x1BOD": "back",
49955
+ q: "quit",
49956
+ "": "abort",
49957
+ "": "abort"
49958
+ };
49959
+ function parseKeys(chunk) {
49960
+ return (chunk.match(SEQUENCE) ?? []).flatMap((part) => {
49961
+ const named = NAMED[part];
49962
+ if (named) return [named];
49963
+ return /^[1-9]$/.test(part) ? [`digit${Number(part)}`] : [];
49964
+ });
49965
+ }
49966
+ function wrap(text6, width) {
49967
+ const lines = [];
49968
+ let line = "";
49969
+ for (const word of text6.split(" ")) {
49970
+ if (line && cells(`${line} ${word}`) > width) {
49971
+ lines.push(line);
49972
+ line = "";
49973
+ }
49974
+ line = line ? `${line} ${word}` : word;
49975
+ }
49976
+ return [...lines, line].map((entry) => fit(entry, width, false));
49977
+ }
49978
+ function selectList(terminal, options) {
49979
+ const { stdin, stdout, out, t } = terminal;
49980
+ const s = out.style, items = options.items;
49981
+ const pointer = s.unicode ? "›" : ">";
49982
+ let cursor = Math.max(0, items.findIndex((item2) => item2.id === options.initial));
49983
+ let top = 0, drawn = 0;
49984
+ const footer = () => [
49985
+ t("menu.key.move"),
49986
+ t(options.confirm ? "menu.key.confirm" : "menu.key.open"),
49987
+ ...options.back ? [t(options.confirm ? "menu.key.cancel" : "menu.key.back")] : [],
49988
+ ...options.quit ? [t("menu.key.quit")] : []
49989
+ ].join(` ${s.glyph.bullet} `);
49990
+ const view = () => {
49991
+ const width = Math.max(20, (stdout.columns ?? 80) - 3);
49992
+ const title = [
49993
+ ...options.context ? [s.dim(fit(neutralizeLine(options.context), width, !s.unicode))] : [],
49994
+ ...(options.title ?? []).flatMap((line) => wrap(neutralizeLine(line), width)).map((line) => s.bold(line))
49995
+ ];
49996
+ const room = Math.max(3, (stdout.rows ?? 24) - title.length - 5);
49997
+ const size = Math.min(items.length, room);
49998
+ if (cursor < top) top = cursor;
49999
+ if (cursor >= top + size) top = cursor - size + 1;
50000
+ top = Math.min(top, items.length - size);
50001
+ const shown3 = items.slice(top, top + size).map((item2) => ({ item: item2, label: neutralizeLine(item2.label), hint: item2.hint ? neutralizeLine(item2.hint) : "" }));
50002
+ const column = Math.max(...shown3.map(({ label, hint }) => hint ? cells(label) : 0));
50003
+ const rows = shown3.map(({ item: item2, label, hint }) => {
50004
+ const room2 = hint ? width - 2 - cells(hint) - 2 : width - 2;
50005
+ const text6 = fit(label, room2, !s.unicode);
50006
+ const pad = hint ? " ".repeat(Math.max(2, column - cells(text6) + 2)) : "";
50007
+ const chosen = items[cursor] === item2;
50008
+ return `${chosen ? s.cmd(`${pointer} `) : " "}${chosen ? s.bold(s.cmd(text6)) : text6}${pad}${hint ? s.dim(hint) : ""}`;
50009
+ });
50010
+ const position = items.length > size ? `${cursor + 1}/${items.length} ${s.glyph.bullet} ` : "";
50011
+ return [...title, ...title.length ? [""] : [], ...rows, "", s.dim(`${position}${footer()}`)];
50012
+ };
50013
+ const draw = () => {
50014
+ const lines = view();
50015
+ out.screen(`${drawn ? `\x1B[${drawn}A` : ""}\r\x1B[J`, lines);
50016
+ drawn = lines.length;
50017
+ };
50018
+ return new Promise((resolve9) => {
50019
+ const showCursor = () => out.screen("\x1B[?25h", []);
50020
+ const finish2 = (result) => {
50021
+ stdin.off("data", onData);
50022
+ stdin.off("end", onEnd);
50023
+ stdout.off?.("resize", draw);
50024
+ process.off("exit", showCursor);
50025
+ stdin.setRawMode(false);
50026
+ stdin.pause();
50027
+ out.screen(`\x1B[${drawn}A\r\x1B[J\x1B[?25h`, []);
50028
+ if (result.kind === "select") {
50029
+ const item2 = items[cursor];
50030
+ out.line(s.dim(`${pointer} ${neutralizeLine(options.echo ? options.echo(item2) : item2.label)}`));
50031
+ }
50032
+ resolve9(result);
50033
+ };
50034
+ const onEnd = () => finish2({ kind: "abort" });
50035
+ const onData = (chunk) => {
50036
+ for (const key of parseKeys(String(chunk))) {
50037
+ if (key === "abort") return finish2({ kind: "abort" });
50038
+ if (key === "quit" && options.quit) return finish2({ kind: "quit" });
50039
+ if (key === "quit" && options.back) return finish2({ kind: "back" });
50040
+ if (key === "back" && options.back) return finish2({ kind: "back" });
50041
+ if (key === "enter") return finish2({ kind: "select", id: items[cursor].id });
50042
+ if (key === "up") cursor = (cursor - 1 + items.length) % items.length;
50043
+ else if (key === "down") cursor = (cursor + 1) % items.length;
50044
+ else if (key === "home") cursor = 0;
50045
+ else if (key === "end") cursor = items.length - 1;
50046
+ else if (key === "pageUp") cursor = Math.max(0, cursor - 10);
50047
+ else if (key === "pageDown") cursor = Math.min(items.length - 1, cursor + 10);
50048
+ else if (key.startsWith("digit") && !options.confirm) {
50049
+ const index = Number(key.slice(5)) - 1;
50050
+ if (items[index]) {
50051
+ cursor = index;
50052
+ return finish2({ kind: "select", id: items[index].id });
50053
+ }
50054
+ }
50055
+ }
50056
+ draw();
50057
+ };
50058
+ process.once("exit", showCursor);
50059
+ stdin.setRawMode(true);
50060
+ stdin.resume();
50061
+ stdin.on("data", onData);
50062
+ stdin.once("end", onEnd);
50063
+ stdout.on?.("resize", draw);
50064
+ out.screen("\x1B[?25l", []);
50065
+ draw();
50066
+ });
50067
+ }
50068
+
49776
50069
  // src/orca.ts
49777
50070
  var ORCA_LIMITS = { items: 200, projects: 200, title: 1e3, status: 120, id: 200 };
49778
50071
  var KEY = /^[A-Z0-9]{2,10}-[1-9][0-9]*$/;
49779
- var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
50072
+ var UUID3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
49780
50073
  var CLOSED = /* @__PURE__ */ new Set(["completed", "cancelled", "canceled"]);
49781
- var terminalConfirm = (deps) => async (question) => deps.isTTY && deps.ask ? /^(y|yes|j|ja)$/i.test((await deps.ask(`${question} (y/N)`)).trim()) : null;
50074
+ var terminalConfirm = (deps) => async (question) => {
50075
+ if (!deps.isTTY || !deps.ask) return null;
50076
+ if (deps.select) return isYes(await deps.select(yesNoList(question, deps.t)));
50077
+ return /^(y|yes|j|ja)$/i.test((await deps.ask(`${question} (y/N)`)).trim());
50078
+ };
49782
50079
  var notBound = () => new CliError("errors.orcaNotBound", EXIT.failed, {}, "NOT_BOUND");
49783
50080
  var usage = () => new CliError("errors.usage", EXIT.usage, {}, "USAGE");
49784
50081
  var bounded = (value, max) => String(value ?? "").slice(0, max);
@@ -49845,7 +50142,7 @@ async function readTool(client, tool, args, read2) {
49845
50142
  }
49846
50143
  async function claimableIds(client, teamId, read2) {
49847
50144
  const candidates = await pages(client, WIRE.findWork, { ...teamId ? { teamId } : {} }, void 0, read2);
49848
- return new Set(candidates.filter((row) => Array.isArray(row.claims) && !row.claims.some((claim) => object3(claim) && claim.purpose === "work") && (!object3(row.eligibility) || row.eligibility.allowAgents === true && !row.eligibility.blockedBy) && (!object3(row.request) || row.request.canTake !== false)).map((row) => String(row.issueId ?? "").toLowerCase()).filter((id) => UUID2.test(id)));
50145
+ return new Set(candidates.filter((row) => Array.isArray(row.claims) && !row.claims.some((claim) => object3(claim) && claim.purpose === "work") && (!object3(row.eligibility) || row.eligibility.allowAgents === true && !row.eligibility.blockedBy) && (!object3(row.request) || row.request.canTake !== false)).map((row) => String(row.issueId ?? "").toLowerCase()).filter((id) => UUID3.test(id)));
49849
50146
  }
49850
50147
  async function pages(client, tool, args, max = Number.POSITIVE_INFINITY, read2) {
49851
50148
  await available(client, tool);
@@ -49886,7 +50183,7 @@ function sortIssues(issues, mine) {
49886
50183
  return [...issues].sort((a, b) => Number(mine.has(b.id)) - Number(mine.has(a.id)) || priority(a) - priority(b) || updated(b) - updated(a) || String(a.identifier ?? "").localeCompare(String(b.identifier ?? ""), "en", { numeric: true }));
49887
50184
  }
49888
50185
  async function teams(client, read2) {
49889
- return (await pages(client, "fleek_list_teams", {}, ORCA_LIMITS.projects, read2)).filter((team) => typeof team.id === "string" && UUID2.test(team.id)).map((team) => ({ id: team.id.toLowerCase(), key: bounded(team.key, 32), name: bounded(team.name, 512) }));
50186
+ return (await pages(client, "fleek_list_teams", {}, ORCA_LIMITS.projects, read2)).filter((team) => typeof team.id === "string" && UUID3.test(team.id)).map((team) => ({ id: team.id.toLowerCase(), key: bounded(team.key, 32), name: bounded(team.name, 512) }));
49890
50187
  }
49891
50188
  async function statesOf(client, teamIds, read2) {
49892
50189
  const states = /* @__PURE__ */ new Map();
@@ -49898,9 +50195,9 @@ async function statesOf(client, teamIds, read2) {
49898
50195
  return states;
49899
50196
  }
49900
50197
  function item(profile, issue, teamsById, states) {
49901
- const id = typeof issue.id === "string" && UUID2.test(issue.id) ? issue.id.toLowerCase() : null;
50198
+ const id = typeof issue.id === "string" && UUID3.test(issue.id) ? issue.id.toLowerCase() : null;
49902
50199
  const key = typeof issue.identifier === "string" && KEY.test(issue.identifier) ? issue.identifier : null;
49903
- const teamId = typeof issue.teamId === "string" && UUID2.test(issue.teamId) ? issue.teamId.toLowerCase() : null;
50200
+ const teamId = typeof issue.teamId === "string" && UUID3.test(issue.teamId) ? issue.teamId.toLowerCase() : null;
49904
50201
  if (!id || !key || !teamId) return null;
49905
50202
  const status = typeof issue.stateId === "string" ? states.get(issue.stateId.toLowerCase())?.name ?? "" : "";
49906
50203
  return { id, key, title: bounded(issue.title, ORCA_LIMITS.title), status, url: ticketUrl(profile, teamsById.get(teamId)?.key ?? null, key), projectId: teamId };
@@ -49908,14 +50205,14 @@ function item(profile, issue, teamsById, states) {
49908
50205
  function projectOf(values, found, list2) {
49909
50206
  if (values.project === void 0) return found.projectId;
49910
50207
  const value = values.project.trim();
49911
- const match = UUID2.test(value) ? list2.find((team) => team.id === value.toLowerCase()) : list2.find((team) => team.key.toUpperCase() === value.toUpperCase());
50208
+ const match = UUID3.test(value) ? list2.find((team) => team.id === value.toLowerCase()) : list2.find((team) => team.key.toUpperCase() === value.toUpperCase());
49912
50209
  if (!match) throw new CliError("errors.orcaProjectUnknown", EXIT.usage, {}, "NOT_FOUND");
49913
50210
  return match.id;
49914
50211
  }
49915
50212
  async function myUserId(client, read2) {
49916
50213
  const who = (await readTool(client, WIRE.whoami, {}, read2)).structured;
49917
50214
  const id = who.principal?.userId;
49918
- if (typeof id !== "string" || !UUID2.test(id)) throw new CliError("errors.metadata", EXIT.failed, { reason: "context" }, "METADATA_INVALID");
50215
+ if (typeof id !== "string" || !UUID3.test(id)) throw new CliError("errors.metadata", EXIT.failed, { reason: "context" }, "METADATA_INVALID");
49919
50216
  return id.toLowerCase();
49920
50217
  }
49921
50218
  async function orcaContext(deps, values = {}) {
@@ -50013,7 +50310,7 @@ var defaultExec = (program, args, options) => new Promise((resolve9) => nodeExec
50013
50310
  (error, stdout, stderr) => resolve9({ code: error ? typeof error.code === "number" ? error.code : 1 : 0, stdout: String(stdout), stderr: String(stderr) })
50014
50311
  ));
50015
50312
  async function orcaProgram(deps, runtime = { platform: process.platform }) {
50016
- const paths = runtime.platform === "win32" ? win32 : posix;
50313
+ const paths = runtime.platform === "win32" ? win322 : posix2;
50017
50314
  const custom = deps.env.ORCA_CLI_COMMAND || void 0;
50018
50315
  const found = custom ?? (runtime.platform === "linux" ? await deps.which("orca-ide") : null) ?? await deps.which("orca");
50019
50316
  if (!found || !paths.isAbsolute(found) || paths.resolve(found) === "/usr/bin/orca" || await (runtime.realpath ?? realpath4)(found).catch(() => found) === "/usr/bin/orca") {
@@ -50053,6 +50350,11 @@ async function orcaPick(deps, values, start2) {
50053
50350
  deps.out.line(deps.out.style.note(deps.t("orca.none")));
50054
50351
  return EXIT.ok;
50055
50352
  }
50353
+ if (deps.select) {
50354
+ const rows = deps.out.style.columns(items.map((entry) => [entry.key, mineIds.has(entry.id) ? deps.t("orca.mine") : "", neutralizeLine(entry.status), neutralizeLine(entry.title).slice(0, 100)]), "");
50355
+ const choice = await deps.select({ title: [deps.t("orca.pickTitle", { count: items.length })], items: items.map((entry, index) => ({ id: entry.key, label: rows[index] })), back: true });
50356
+ return choice.kind === "select" ? start2(choice.id) : EXIT.ok;
50357
+ }
50056
50358
  let offset = 0;
50057
50359
  while (true) {
50058
50360
  const end = Math.min(offset + 50, items.length);
@@ -50158,7 +50460,7 @@ async function liveFacts(client, task) {
50158
50460
  const claims = (Array.isArray(work2.claims) ? work2.claims : []).filter(object3).map((claim) => ({
50159
50461
  purpose: string3(claim.purpose),
50160
50462
  isMine: claim.isMine === true,
50161
- claimId: typeof claim.claimId === "string" && UUID2.test(claim.claimId) ? claim.claimId.toLowerCase() : null,
50463
+ claimId: typeof claim.claimId === "string" && UUID3.test(claim.claimId) ? claim.claimId.toLowerCase() : null,
50162
50464
  holder: object3(claim.holder) ? string3(claim.holder.text) : "",
50163
50465
  expiresAt: string3(claim.expiresAt)
50164
50466
  }));
@@ -50264,7 +50566,7 @@ var foreignClaim = (key, holder, expiresAt) => new CliError(
50264
50566
  async function takeClaim(deps, client, task, purpose, runLabel) {
50265
50567
  try {
50266
50568
  const claim = (await mutate(client, WIRE.claim, { issueId: task.id, purpose, runLabel: runLabel.slice(0, 120) }, { backoffMs: deps.backoffMs })).result;
50267
- return typeof claim.claimId === "string" && UUID2.test(claim.claimId) ? claim.claimId.toLowerCase() : null;
50569
+ return typeof claim.claimId === "string" && UUID3.test(claim.claimId) ? claim.claimId.toLowerCase() : null;
50268
50570
  } catch (error) {
50269
50571
  if (error instanceof ToolError && error.code === "ALREADY_CLAIMED") {
50270
50572
  const details = object3(error.details) ? error.details : {};
@@ -51751,7 +52053,7 @@ var USER_SKILL_MARKER = "<!-- Generated by scripts/agent-guidance/generate.mjs f
51751
52053
  var USER_SKILLS = [
51752
52054
  {
51753
52055
  "name": "fleek-orca-coordinator",
51754
- "content": "---\nname: fleek-orca-coordinator\ndescription: \"Coordinate Fleek tickets in Orca: find claimable work, claim it for this machine, start a builder in its own worktree with the project kit, review with the other model, leave gates to people and deliver to review. Use when you coordinate Orca agents for a Fleek project.\"\nmetadata:\n protocol: fleek-work-protocol/1.13\n---\n\n<!-- Generated by scripts/agent-guidance/generate.mjs from scripts/agent-guidance/protocol.json - do not edit by hand. -->\n\n# Fleek in Orca: coordinator recipe\n\nFleek holds the tickets and every human decision; Orca runs worktrees and agents. Follow the fleek-work skill for every Fleek call. Never change Orca's code or settings.\n\n## 1. Ready check\n- `fleek doctor` in the repo: Fleek MCP current for Claude Code, Codex and every Orca account, repo bound to its Fleek project. Otherwise ask the person to run the setup command from Fleek once per machine (Settings › Connections › Set up this computer: `fleek setup <code>`). Never sign in for them.\n\n## 2. Find work\n- fleek_find_work and fleek_my_work_inbox (claimable: agent-ready or assigned to your person; addressed work first); `fleek orca tasks` lists the project's open tickets. Take only status roles where agents work: intake_review, ready, analysis, design, implementation, review. Skip tickets another holder claims.\n- Read stage in fleek_get_work_context before starting a worker, then use this role table. Do not infer a role from the displayed status name or from the ticket title.\n\n| Status role | Work and delivery |\n| --- | --- |\n| intake_review | One triage agent: duplicates, criteria and readiness; no product code or PR. |\n| ready | Claim, then re-read the live stage and use its row. A claim is not permission to skip analysis or design. |\n| analysis / design | One agent: concept or design proposal with evidence; no product code or PR. |\n| implementation | Builder, other-model review, then deliver the work claim (sections 3-5). |\n| review | One reviewer with a review claim (`fleek orca start KEY --yes --agent OTHER-MODEL` takes it on the delivered branch in its own worktree): checklist, acceptance and evidence; record the verdict, then release that review claim. No builder, implementation, PR or complete_work outcome done loop. Findings go back to the existing implementation work or to the person. |\n| collecting / clarification / acceptance / customer gate | Start no builder; prepare a draft only if requested and wait for the responsible person or customer. Never decide their gate. |\n| done / cancelled | Start nothing. |\n\n- Only implementation uses sections 3-5. `fleek orca tasks` is an overview; `fleek orca pick` works only in a person's terminal and offers only server candidates without an existing work claim. Re-read the live context after claiming: availability is not a status-role decision.\n\n## 3. Claim, worktree, builder\n- `fleek orca start KEY --yes` (the core of `fleek start` for coordinators: Orca required; a new or restarted agent stays in the background, one already running comes to the front) takes or reuses the claim for this machine, creates the Orca worktree named after the key from the project's base branch with the Fleek ticket as reference and starts the agent with a first prompt naming key and claim. You run without a terminal: only `--yes` replaces the person's confirmation, without it the command refuses with CONFIRMATION_REQUIRED. Use it only for tickets your person lets you start; the web link and a person's own terminal keep asking. With the Fleek start command as Orca's agent command (`fleek setup` names it), the agent loads the project's kit. Your own claim is reused, no error. Another holder's claim or NOT_READY: pick another ticket.\n- By hand: fleek_claim_issue (purpose work, runLabel \"orca\" plus the worktree name), then `orca worktree create --name eng-123-slug --reference TICKET-URL --agent claude` from the base branch of fleek_get_project_brief.\n- The builder's task is a pointer: key, claimId, worktree, branch, role builder. Never paste ticket text, criteria or sign-in data; the agent reads them live from Fleek.\n- Models only from providers conventions.aiProviders allows (fleek_get_work_context); a customer's code never goes to another provider.\n- Card: `orca worktree set --worktree id:WORKTREE-ID --workspace-status in-progress`.\n\n## 4. Review by the other model\n- After the builder's worker_done verify its report in two reads before starting a reviewer: the fetched feature branch head equals the reported SHA and the worktree has no uncommitted changes; the test note is on the ticket when the project kit asks for one. A mismatch goes back to the builder, not to the reviewer.\n- Then start a reviewer with the other model (Claude and Codex swap; only one provider allowed: a fresh session of it) in the same worktree, role reviewer. It claims purpose review, checks criteria and checklist and records its verdict at the ticket.\n- Check the review's shape (fleek-work, In Orca, reviewer): verdict on the first line, blockers each with a reproduction, anchors only inside the reviewed range, each finding read or measured, questions for a person at the ticket. A review that misses it goes back to the reviewer once with the exact points.\n- Verdict not ready or a criterion not_met: builder round 2 in the same worktree, then review again. After two rounds ask the person.\n\n## 5. Deliver\n- Clean review: load the guidance (fleek_get_work_context), then fleek_complete_work on the work claim, outcome done, with SHA, branch, PR, checks and result, acceptance per criterion and nextStep. The ticket moves to the project's review state, never Done; check moved and toStateId.\n- A coordination or review claim that only records a status writes fleek_post_progress, not a result. One completion = one fleek_complete_work; no extra summary or analysis comment. Put the recommendation in nextStep; do not add a note about your own draft or publication request.\n- Parts open: outcome partial. Blocked: fleek_release_issue outcome not_doable. Interrupted: outcome paused. A failed dispatch is never done.\n- Card in-review with the existing result; no extra ticket comment.\n\n## 6. People decide\n- Questions only a person can answer go to the ticket (fleek_ask_question); the worker waits, the card gets `--unread`. Gates, acceptance, customer text and Done are decided by a person in Fleek, by an agent only where the project rule allows it and the person approved or gave a standing mandate. Never orca gate-resolve or a terminal yes on their behalf.\n- Answer an orca ask only when a rule or a decision recorded at the ticket settles it, and cite it. Anything else a person decides goes back as \"ask at the ticket\": the asking agent calls fleek_ask_question with the deciding person as addressee and waits. Your own person's answer in chat or terminal is recorded verbatim as an internal ticket comment (\"Answer NAME in chat, DATE: ...\"); pass it on only together with that comment, never a chat answer alone. The comment only records the factual answer of your own person; it decides no gate, customer acceptance or publication and grants or widens no mandate or agent_git_* right - those stay decisions in Fleek.\n\n## 7. Keep claims alive\n- Renew by expiresAt (about every 20 minutes) while a worker runs or waits; an Orca heartbeat is no renewal. The claim belongs to the machine, so a switched account or a usage-limit pause keeps it: renew and resume. Nobody can continue before expiry: release paused.\n- After a crash read your live claims (fleek_my_work_inbox) and `orca worktree list`, then continue on the same claim (`fleek orca start KEY --yes` restarts an ended agent) or release it. Never claim or complete twice.\n\n## 8. Limits\n- One builder per ticket, one ticket per worktree. Parallel tickets: as many as the person allows, default 2, each with its reviewer; queue the rest. QUOTA_EXCEEDED: the machine's claims are used up - finish or release one first.\n- No Orca code changes, forks or pull requests on Orca. Git effects only as far as the project brief allows.\n"
52056
+ "content": "---\nname: fleek-orca-coordinator\ndescription: \"Coordinate Fleek tickets in Orca: find claimable work, claim it for this machine, start a builder in its own worktree with the project kit, review with the other model, leave gates to people and deliver to review. Use when you coordinate Orca agents for a Fleek project.\"\nmetadata:\n protocol: fleek-work-protocol/1.13\n---\n\n<!-- Generated by scripts/agent-guidance/generate.mjs from scripts/agent-guidance/protocol.json - do not edit by hand. -->\n\n# Fleek in Orca: coordinator recipe\n\nFleek holds the tickets and every human decision; Orca runs worktrees and agents. Follow the fleek-work skill for every Fleek call. Never change Orca's code or settings.\n\n## 1. Ready check\n- `fleek doctor` in the repo: Fleek MCP current for Claude Code, Codex and every Orca account, repo bound to its Fleek project. Otherwise ask the person to run the setup command from Fleek once per machine (Settings › Connections › Set up this computer: `fleek setup <code>`). Never sign in for them.\n\n## 2. Find work\n- fleek_find_work and fleek_my_work_inbox (claimable: agent-ready or assigned to your person; addressed work first); `fleek orca tasks` lists the project's open tickets. Take only status roles where agents work: intake_review, ready, analysis, design, implementation, review. Skip tickets another holder claims.\n- Read stage in fleek_get_work_context before starting a worker, then use this role table. Do not infer a role from the displayed status name or from the ticket title.\n\n| Status role | Work and delivery |\n| --- | --- |\n| intake_review | One triage agent: duplicates, criteria and readiness; no product code or PR. |\n| ready | Claim, then re-read the live stage and use its row. A claim is not permission to skip analysis or design. |\n| analysis / design | One agent: concept or design proposal with evidence; no product code or PR. |\n| implementation | Builder, other-model review, then deliver the work claim (sections 3-5). |\n| review | One reviewer with a review claim (`fleek orca start KEY --yes --agent OTHER-MODEL` takes it on the delivered branch in its own worktree): checklist, acceptance and evidence; record the verdict, then release that review claim. No builder, implementation, PR or complete_work outcome done loop. Findings go back to the existing implementation work or to the person. |\n| collecting / clarification / acceptance / customer gate | Start no builder; prepare a draft only if requested and wait for the responsible person or customer. Never decide their gate. |\n| done / cancelled | Start nothing. |\n\n- Only implementation uses sections 3-5. `fleek orca tasks` is an overview; `fleek orca pick` works only in a person's terminal and offers only server candidates without an existing work claim. Re-read the live context after claiming: availability is not a status-role decision.\n\n## 3. Claim, worktree, builder\n- `fleek orca start KEY --yes` (the core of `fleek start` for coordinators: Orca required; a new or restarted agent stays in the background, one already running comes to the front) takes or reuses the claim for this machine, creates the Orca worktree named after the key from the project's base branch with the Fleek ticket as reference and starts the agent with a first prompt naming key and claim. You run without a terminal: only `--yes` replaces the person's confirmation, without it the command refuses with CONFIRMATION_REQUIRED. Use it only for tickets your person lets you start; the web link and a person's own terminal keep asking. With the Fleek start command as Orca's agent command (`fleek setup` names it), the agent loads the project's kit. Your own claim is reused, no error. Another holder's claim or NOT_READY: pick another ticket.\n- By hand: fleek_claim_issue (purpose work, runLabel \"orca\" plus the worktree name), then `orca worktree create --name eng-123-slug --reference TICKET-URL --agent claude` from the base branch of fleek_get_project_brief.\n- The builder's task is a pointer: key, claimId, worktree, branch, role builder. Never paste ticket text, criteria or sign-in data; the agent reads them live from Fleek.\n- Models only from providers conventions.aiProviders allows (fleek_get_work_context); a customer's code never goes to another provider.\n- Card: `orca worktree set --worktree id:WORKTREE-ID --workspace-status in-progress`.\n\n## 4. Review by the other model\n- After the builder's worker_done verify its report in two reads before starting a reviewer: the fetched feature branch head equals the reported SHA and the worktree has no uncommitted changes; the test note is on the ticket when the project kit asks for one. A mismatch goes back to the builder, not to the reviewer.\n- Then start a reviewer with the other model (Claude and Codex swap; only one provider allowed: a fresh session of it) in the same worktree, role reviewer. It claims purpose review, checks criteria and checklist and records its verdict at the ticket.\n- Check the review's shape (fleek-work, In Orca, reviewer): verdict on the first line, blockers each with a reproduction, anchors only inside the reviewed range, each finding read or measured, questions for a person at the ticket. A review that misses it goes back to the reviewer once with the exact points.\n- Verdict not ready or a criterion not_met: builder round 2 in the same worktree, then review again. After two rounds ask the person.\n\n## 5. Deliver\n- Clean review: load the guidance (fleek_get_work_context), then fleek_complete_work on the work claim, outcome done, with SHA, branch, PR, checks and result, acceptance per criterion and nextStep. The ticket moves to the project's review state, never Done; check moved and toStateId.\n- A coordination or review claim that only records a status writes fleek_post_progress, not a result. One completion = one fleek_complete_work; no extra summary or analysis comment. Put the recommendation in nextStep; do not add a note about your own draft or publication request.\n- Parts open: outcome partial. Blocked: fleek_release_issue outcome not_doable. Interrupted: outcome paused. A failed dispatch is never done.\n- Card in-review with the existing result; no extra ticket comment.\n\n## 6. People decide\n- Questions only a person can answer go to the ticket (fleek_ask_question); the worker waits, the card gets `--unread`. Gates, acceptance, customer text and Done are decided by a person in Fleek, by an agent only where the project rule allows it and the person approved or gave a standing mandate. Never orca gate-resolve or a terminal yes on their behalf.\n- Answer an orca ask only when a rule or a decision recorded at the ticket settles it, and cite it. Anything else a person decides goes back as \"ask at the ticket\": the asking agent calls fleek_ask_question with the deciding person as addressee and waits. Your own person's answer in chat or terminal is recorded verbatim as an internal ticket comment (\"Answer NAME in chat, DATE: ...\"); pass it on only together with that comment, never a chat answer alone. The comment only records the factual answer of your own person; it decides no gate, customer acceptance or publication and grants or widens no mandate or agent_git_* right - those stay decisions in Fleek.\n\n## 7. Keep claims alive\n- Renew by expiresAt (about every 20 minutes) while a worker runs or waits; an Orca heartbeat is no renewal. The claim belongs to the machine, so a switched account or a usage-limit pause keeps it: renew and resume. Nobody can continue before expiry: release paused. A ticket you paused stays claimable for your agent under your person's work mandate until someone else changes its status or assignee.\n- After a crash read your live claims (fleek_my_work_inbox) and `orca worktree list`, then continue on the same claim (`fleek orca start KEY --yes` restarts an ended agent) or release it. Never claim or complete twice.\n\n## 8. Limits\n- One builder per ticket, one ticket per worktree. Parallel tickets: as many as the person allows, default 2, each with its reviewer; queue the rest. QUOTA_EXCEEDED: the machine's claims are used up - finish or release one first.\n- No Orca code changes, forks or pull requests on Orca. Git effects only as far as the project brief allows.\n"
51755
52057
  }
51756
52058
  ];
51757
52059
 
@@ -51981,8 +52283,9 @@ function initAgentArgs(prompt) {
51981
52283
  JSON.stringify({ disableAllHooks: true })
51982
52284
  ];
51983
52285
  }
51984
- function initAgentEnv(env) {
51985
- const result = { PATH: dirname9(process.execPath) };
52286
+ function initAgentEnv(env, platform = process.platform) {
52287
+ const path = platform === "darwin" ? [dirname9(process.execPath), "/usr/bin"] : [dirname9(process.execPath)];
52288
+ const result = { PATH: path.join(delimiter2) };
51986
52289
  for (const key of [
51987
52290
  "HOME",
51988
52291
  "USER",
@@ -54265,113 +54568,6 @@ var Session = class {
54265
54568
  }
54266
54569
  };
54267
54570
 
54268
- // src/header.ts
54269
- import { win32 as win322, posix as posix2 } from "node:path";
54270
-
54271
- // src/uuid.ts
54272
- var UUID3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
54273
- function isUuid(value) {
54274
- return UUID3.test(value);
54275
- }
54276
- function canonicalId(value) {
54277
- if (!UUID3.test(value)) throw new Error("INVALID_UUID");
54278
- return value.toLowerCase();
54279
- }
54280
-
54281
- // src/header.ts
54282
- var LOGO2 = [" ▄██████▀", " ▄█████▀ ", " ██▀ "];
54283
- var ASCII_LOGO = [" ######/", " #####/ ", " ##/ "];
54284
- var BRAND = "\x1B[38;2;69;94;255m";
54285
- function shouldShowHeader(positionals, options, terminal) {
54286
- if (options.json || options.version || terminal.stream.isTTY !== true) return false;
54287
- const active = (value) => value !== void 0 && value !== "" && value !== "0" && value !== "false";
54288
- if (["CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "BUILD_NUMBER", "TF_BUILD"].some((key) => active(terminal.env[key]))) return false;
54289
- const [command, sub] = positionals;
54290
- return !command || command === "help" || !!options.help || command === "setup" || command === "doctor" || command === "init" || command === "kit" && ["setup", "doctor", "pull"].includes(sub ?? "") || command === "service" && sub === "setup";
54291
- }
54292
- function cells(value) {
54293
- let width = 0;
54294
- for (const char of value) {
54295
- const code = char.codePointAt(0);
54296
- if (new RegExp("\\p{Mark}", "u").test(char)) continue;
54297
- width += code >= 4352 && (code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744) ? 2 : 1;
54298
- }
54299
- return width;
54300
- }
54301
- function fit(value, width, ascii) {
54302
- if (cells(value) <= width) return value;
54303
- const suffix = ascii ? "..." : "…";
54304
- if (width <= suffix.length) return suffix.slice(0, Math.max(0, width));
54305
- let out = "";
54306
- for (const char of value) {
54307
- if (cells(out + char) > width - suffix.length) break;
54308
- out += char;
54309
- }
54310
- return out + suffix;
54311
- }
54312
- var safe = (value) => neutralizeLine(redact(value));
54313
- function directory(context, windows, env) {
54314
- const path = windows ? win322 : posix2;
54315
- const home = context.home ?? userHome(env, windows ? "win32" : process.platform);
54316
- const relative3 = path.relative(home, context.cwd);
54317
- const inside = relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path.sep}`) && !path.isAbsolute(relative3);
54318
- return safe(inside ? relative3 ? `~${path.sep}${relative3}` : "~" : context.cwd);
54319
- }
54320
- function headerServer(value) {
54321
- if (!value) return void 0;
54322
- try {
54323
- const url = new URL(value);
54324
- return ["http:", "https:"].includes(url.protocol) ? url.host : void 0;
54325
- } catch {
54326
- return void 0;
54327
- }
54328
- }
54329
- function renderHeader(context, terminal, t) {
54330
- const windows = (terminal.platform ?? process.platform) === "win32";
54331
- const ascii = windows || terminal.env.TERM === "dumb";
54332
- const separator = ascii ? " - " : " · ";
54333
- const requested = terminal.stream.columns ?? Number(terminal.env.COLUMNS);
54334
- const columns = Number.isFinite(requested) && requested > 0 ? Math.floor(requested) : 80;
54335
- const title = t("header.title", { version: safe(context.version) });
54336
- const cwd = directory(context, windows, terminal.env);
54337
- const parts = [context.workspace, context.project && !isUuid(context.project) ? context.project : void 0, headerServer(context.server), context.displayName].filter((value) => !!value);
54338
- const details = parts.length ? parts.map(safe).join(separator) : t("header.noContext");
54339
- if (columns < 60) return ["", fit([title, cwd].join(separator), columns, ascii)];
54340
- const logo = ascii ? ASCII_LOGO : LOGO2;
54341
- const width = Math.max(...logo.map(cells));
54342
- const color = colorEnabled(terminal.env, terminal.stream);
54343
- const available2 = columns - width - 6;
54344
- const tagline = separator + t("header.tagline");
54345
- const attribution = separator + t("header.attribution");
54346
- const secondary = cells(title + tagline) <= available2 ? tagline + (cells(title + tagline + attribution) <= available2 ? attribution : "") : "";
54347
- const text6 = [title, details, cwd];
54348
- const rows = logo.map((row, index) => {
54349
- const mark = row.padEnd(width);
54350
- const label = fit(text6[index], available2, ascii);
54351
- if (index === 0) {
54352
- const heading = color ? `\x1B[1m${label}\x1B[22m` : label;
54353
- const branding = color && secondary ? `\x1B[2m${secondary}\x1B[22m` : secondary;
54354
- return `${color ? `${BRAND}${mark}\x1B[39m` : mark} ${heading}${branding}`;
54355
- }
54356
- const styled = color ? `\x1B[2m${label}\x1B[22m` : label;
54357
- return `${color ? `${BRAND}${mark}\x1B[39m` : mark} ${styled}`;
54358
- });
54359
- const inner = Math.max(...rows.map((row) => cells(stripAnsi(row))));
54360
- const edge = (value) => color ? `${BRAND}${value}\x1B[39m` : value;
54361
- const vertical = ascii ? "|" : "│";
54362
- return [
54363
- "",
54364
- edge(`${ascii ? "+" : "╭"}${(ascii ? "-" : "─").repeat(inner + 2)}${ascii ? "+" : "╮"}`),
54365
- ...rows.map((row) => `${edge(vertical)} ${row}${" ".repeat(inner - cells(stripAnsi(row)))} ${edge(vertical)}`),
54366
- edge(`${ascii ? "+" : "╰"}${(ascii ? "-" : "─").repeat(inner + 2)}${ascii ? "+" : "╯"}`)
54367
- ];
54368
- }
54369
- function printHeader(out, context, terminal, t) {
54370
- for (const line of renderHeader(context, terminal, t)) out.line(line);
54371
- out.line();
54372
- out.indentHumanOutput();
54373
- }
54374
-
54375
54571
  // src/project-reference.ts
54376
54572
  async function visibleProject(client, ref) {
54377
54573
  let cursor;
@@ -59288,6 +59484,20 @@ async function guidedMenu(d) {
59288
59484
  out.line(`$ fleek ${args[0] === "setup" && args[1] && !args[1].startsWith("--") ? "setup <code>" : args.map(neutralizeLine).join(" ")}`);
59289
59485
  return d.run(args, cwd2);
59290
59486
  };
59487
+ const choose = async (options) => {
59488
+ if (d.select) return d.select(options);
59489
+ for (const line of [...options.context ? [options.context] : [], ...options.title ?? []]) out.line(line);
59490
+ for (const [i, item2] of options.items.entries()) out.line(`${i + 1} ${item2.label}${item2.hint ? ` · ${item2.hint}` : ""}`);
59491
+ const answer2 = (await d.ask(t("menu.choose"))).trim();
59492
+ if (!answer2) return { kind: "back" };
59493
+ const chosen = /^[1-9][0-9]*$/.test(answer2) ? options.items[Number(answer2) - 1] : void 0;
59494
+ if (!chosen) throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59495
+ return { kind: "select", id: chosen.id };
59496
+ };
59497
+ const confirm2 = async (question) => {
59498
+ if (!d.select) return /^(y|yes|j|ja)$/i.test((await d.ask(`${question} ${t("menu.yesNo")}`)).trim());
59499
+ return isYes(await d.select(yesNoList(question, t)));
59500
+ };
59291
59501
  const connect = async (setup) => {
59292
59502
  await d.connect();
59293
59503
  if (!setup) return EXIT.ok;
@@ -59311,53 +59521,121 @@ async function guidedMenu(d) {
59311
59521
  }
59312
59522
  return run2(["setup", code, ...language ? ["--language", language] : []]);
59313
59523
  };
59314
- out.line(t("menu.recommend", { action: d.resume ? t("menu.resume", { key: d.resume }) : t(!d.signedIn ? "menu.connect" : d.stateError ? "menu.doctor" : !d.bound ? "menu.bind" : "menu.pick") }));
59315
- if (d.stateError) out.line(t("menu.stateUnreadable"));
59316
- if (!d.signedIn) {
59317
- out.line(`1 ${t("menu.connect")}`);
59318
- out.line(`2 ${t("menu.help")}`);
59319
- const choice2 = (await d.ask(t("menu.choose"))).trim();
59320
- if (!choice2) return EXIT.ok;
59321
- if (choice2 === "1") return connect(true);
59322
- if (choice2 === "2") return run2(["help"]);
59323
- throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59324
- }
59325
59524
  let cwd;
59326
- if (!d.bound && !d.stateError) {
59327
- for (const [i, path] of d.checkouts.entries()) out.line(`${i + 1} ${neutralizeLine(path)}`);
59328
- out.line(`0 ${t("menu.connect")}`);
59329
- const choice2 = (await d.ask(t("menu.checkout"))).trim();
59330
- if (!choice2) return EXIT.ok;
59331
- if (choice2 === "0") return connect(true);
59332
- const index = Number(choice2) - 1;
59333
- if (!/^\d+$/.test(choice2) || !d.checkouts[index]) throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59334
- cwd = d.checkouts[index];
59335
- }
59336
- if (d.repair) out.line(t("menu.fix", { command: `fleek ${repair.join(" ")}` }));
59337
- for (const [i, key] of ["pick", "continue", "doctor", "connections", "help"].entries()) out.line(`${i + 1} ${t(`menu.${key}`)}`);
59338
- const choice = (await d.ask(t("menu.choose"))).trim();
59339
- switch (choice) {
59340
- case "":
59525
+ let state;
59526
+ let cursor;
59527
+ while (true) {
59528
+ if (!state) {
59529
+ out.progress(t("menu.loading"));
59530
+ try {
59531
+ state = await d.state(cwd);
59532
+ } catch {
59533
+ state = { signedIn: false, bound: false, stateError: true, checkouts: [] };
59534
+ }
59535
+ out.progress(null);
59536
+ if (cwd && !state.bound && !state.stateError) {
59537
+ cwd = void 0;
59538
+ continue;
59539
+ }
59540
+ }
59541
+ const current2 = state;
59542
+ const recommended = (id2, wanted2) => id2 === wanted2 ? { hint: t("menu.recommended") } : {};
59543
+ const chooser2 = current2.signedIn && !current2.bound && !current2.stateError;
59544
+ const canSwitch = !chooser2 && !!cwd;
59545
+ const wanted = !current2.signedIn ? "connect" : current2.resume ? "resume" : current2.stateError ? "doctor" : "pick";
59546
+ const items = !current2.signedIn ? [{ id: "connect", label: t("menu.connect"), ...recommended("connect", wanted) }, { id: "help", label: t("menu.help") }, { id: "quit", label: t("menu.quit") }] : chooser2 ? [
59547
+ ...current2.checkouts.map((checkout, i) => ({ id: `checkout:${i}`, label: checkout.label ?? checkout.path, ...checkout.project ? { hint: checkout.project } : {} })),
59548
+ { id: "connect", label: t("menu.setupComputer") },
59549
+ { id: "help", label: t("menu.help") },
59550
+ { id: "quit", label: t("menu.quit") }
59551
+ ] : [
59552
+ ...current2.resume ? [{ id: "resume", label: t("menu.resume", { key: current2.resume }), ...recommended("resume", wanted) }] : [],
59553
+ { id: "pick", label: t("menu.pick"), ...recommended("pick", wanted) },
59554
+ { id: "continue", label: t("menu.continue") },
59555
+ { id: "doctor", label: t("menu.doctor"), ...recommended("doctor", wanted) },
59556
+ { id: "connections", label: t("menu.connections") },
59557
+ ...canSwitch ? [{ id: "switch", label: t("menu.switchCheckout") }] : [],
59558
+ { id: "help", label: t("menu.help") },
59559
+ { id: "quit", label: t("menu.quit") }
59560
+ ];
59561
+ const title = [
59562
+ ...chooser2 ? [t("menu.checkoutTitle")] : [],
59563
+ ...current2.stateError && current2.signedIn ? [t("menu.stateUnreadable")] : [],
59564
+ ...current2.repair && current2.signedIn && !chooser2 ? [t("menu.fix", { command: `fleek ${repair.join(" ")}` })] : []
59565
+ ];
59566
+ let choice;
59567
+ try {
59568
+ choice = await choose({ ...current2.context ? { context: current2.context } : {}, title, items, initial: cursor && items.some((item2) => item2.id === cursor) ? cursor : wanted, quit: true, back: canSwitch });
59569
+ } catch (error) {
59570
+ d.report(error);
59571
+ out.line();
59572
+ continue;
59573
+ }
59574
+ if (choice.kind !== "select") {
59575
+ if (choice.kind === "back" && canSwitch) {
59576
+ cwd = void 0;
59577
+ state = void 0;
59578
+ cursor = void 0;
59579
+ continue;
59580
+ }
59341
59581
  return EXIT.ok;
59342
- case "1":
59343
- return run2(["start", "--mine"], cwd);
59344
- case "2": {
59345
- const key = (await d.ask(d.resume ? t("menu.ticketDefault", { key: d.resume }) : t("menu.ticket"))).trim().toUpperCase() || d.resume;
59346
- if (!key) return EXIT.ok;
59347
- if (!/^[A-Z0-9]{2,10}-[1-9][0-9]*$/.test(key)) throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59348
- return run2(["start", key], cwd);
59349
- }
59350
- case "3": {
59351
- const code = await run2(["doctor"], cwd);
59352
- if ((code !== EXIT.ok || d.repair || d.stateError) && /^(y|yes|j|ja)$/i.test((await d.ask(t("menu.repair", { command: `fleek ${repair.join(" ")}` }))).trim())) return run2(repair, cwd);
59353
- return code;
59354
59582
  }
59355
- case "4":
59356
- return connect(false);
59357
- case "5":
59358
- return run2(["help"]);
59359
- default:
59360
- throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59583
+ const id = choice.id;
59584
+ if (id === "quit") return EXIT.ok;
59585
+ cursor = id;
59586
+ if (id === "switch") {
59587
+ cwd = void 0;
59588
+ state = void 0;
59589
+ cursor = void 0;
59590
+ continue;
59591
+ }
59592
+ if (id.startsWith("checkout:")) {
59593
+ cwd = current2.checkouts[Number(id.slice(9))]?.path;
59594
+ state = void 0;
59595
+ cursor = void 0;
59596
+ continue;
59597
+ }
59598
+ let refresh = true, code = EXIT.ok;
59599
+ try {
59600
+ switch (id) {
59601
+ case "connect":
59602
+ code = await connect(true);
59603
+ break;
59604
+ case "resume":
59605
+ code = await run2(["start", current2.resume], cwd);
59606
+ break;
59607
+ case "pick":
59608
+ code = await run2(["start", "--mine"], cwd);
59609
+ break;
59610
+ case "continue": {
59611
+ const key = (await d.ask(current2.resume ? t("menu.ticketDefault", { key: current2.resume }) : t("menu.ticket"))).trim().toUpperCase() || current2.resume;
59612
+ if (!key) {
59613
+ refresh = false;
59614
+ break;
59615
+ }
59616
+ if (!/^[A-Z0-9]{2,10}-[1-9][0-9]*$/.test(key)) throw new CliError("errors.usage", EXIT.usage, {}, "USAGE");
59617
+ code = await run2(["start", key], cwd);
59618
+ break;
59619
+ }
59620
+ case "doctor":
59621
+ code = await run2(["doctor"], cwd);
59622
+ if (!d.leaving?.() && (code !== EXIT.ok || current2.repair || current2.stateError) && await confirm2(t("menu.repair", { command: `fleek ${repair.join(" ")}` }))) code = await run2(repair, cwd);
59623
+ break;
59624
+ case "connections":
59625
+ refresh = false;
59626
+ code = await connect(false);
59627
+ break;
59628
+ case "help":
59629
+ refresh = false;
59630
+ code = await run2(["help"]);
59631
+ break;
59632
+ }
59633
+ } catch (error) {
59634
+ d.report(error);
59635
+ }
59636
+ if (d.leaving?.()) return code;
59637
+ out.line();
59638
+ if (refresh) state = void 0;
59361
59639
  }
59362
59640
  }
59363
59641
 
@@ -59731,6 +60009,10 @@ var Context = class {
59731
60009
  fetch;
59732
60010
  now;
59733
60011
  headerShown = false;
60012
+ /** Bare `fleek` is an application: an update hint once, and these two end it after the current action. */
60013
+ updateChecked = false;
60014
+ agentRan = false;
60015
+ appQuit = false;
59734
60016
  store;
59735
60017
  boundProfile;
59736
60018
  /** The look of human lines (colours only for a TTY, never with --json). */
@@ -60034,7 +60316,7 @@ async function confirmLocal(ctx, lines) {
60034
60316
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
60035
60317
  try {
60036
60318
  for (const line of lines.slice(0, -1)) ctx.out.error(line);
60037
- const answer2 = await rl.question(`${ctx.out.render(ctx.s.question(lines.at(-1) ?? ""), "err")} `);
60319
+ const answer2 = await askLine(rl, `${ctx.out.render(ctx.s.question(lines.at(-1) ?? ""), "err")} `);
60038
60320
  return /^(?:j|ja|y|yes)$/i.test(answer2.trim());
60039
60321
  } finally {
60040
60322
  rl.close();
@@ -60394,7 +60676,8 @@ async function dispatch(ctx, positionals, values, exec, passthrough) {
60394
60676
  displayName: profile?.needsLogin ? void 0 : profile?.displayName
60395
60677
  }, terminal, ctx.t);
60396
60678
  }
60397
- if (!["update", "uninstall", "mcp", "listen", "help", "login", "setup"].includes(command ?? "") && !(command === "kit" && sub === "run") && !values.help) {
60679
+ if (!ctx.updateChecked && !["update", "uninstall", "mcp", "listen", "help", "login", "setup"].includes(command ?? "") && !(command === "kit" && sub === "run") && !values.help) {
60680
+ ctx.updateChecked = true;
60398
60681
  await updateHint({
60399
60682
  configDir: ctx.configDir,
60400
60683
  env: ctx.env,
@@ -60408,47 +60691,19 @@ async function dispatch(ctx, positionals, values, exec, passthrough) {
60408
60691
  }, ctx.deps.isTTY ?? (process.stderr.isTTY === true && process.stdin.isTTY === true));
60409
60692
  }
60410
60693
  if (!command && !values.help && !ctx.json && (ctx.deps.isTTY ?? (process.stdin.isTTY === true && (ctx.deps.stdout ?? process.stdout).isTTY === true))) {
60411
- let stateError = false;
60412
- const found = await resolveBinding(ctx.profiles, ctx.configDir, { flag: ctx.profileFlag, env: ctx.env, cwd: ctx.deps.cwd ?? process.cwd(), allowCurrent: true }).catch(() => {
60413
- stateError = true;
60414
- return null;
60415
- });
60416
- const profile = found?.profile ?? await ctx.profiles.current().catch(() => null);
60417
- const checkouts = profile ? await new CheckoutIndex(ctx.configDir, profile.key).entries().catch(() => {
60418
- stateError = true;
60419
- return [];
60420
- }) : [];
60421
- const domain = profile && found?.projectId ? new KitDomain(ctx.configDir, profile.key, found.projectId) : null;
60422
- let kitNeedsRepair = false;
60423
- if (domain) {
60424
- try {
60425
- kitNeedsRepair = !await domain.current() && await domain.pulledAt() === null;
60426
- } catch {
60427
- stateError = true;
60428
- kitNeedsRepair = true;
60429
- }
60430
- }
60431
- const handler = await inspectSetupHandler(kitContext(ctx, values)).catch(() => {
60432
- stateError = true;
60433
- return null;
60434
- });
60435
- const repair = handler?.state === "missing" || kitNeedsRepair ? ["kit", "setup"] : void 0;
60436
- const resume = profile && !profile.needsLogin && found?.projectId ? await ctx.session().then(async ({ client }) => {
60437
- const inbox = (await client.callTool(WIRE.inbox, { limit: 25, kinds: ["claim"], teamId: found.projectId })).structured;
60438
- const rows = Array.isArray(inbox.items) ? inbox.items : [];
60439
- return rows.find((row) => row.kind === "claim" && typeof row.identifier === "string" && /^[A-Z0-9]{2,10}-[1-9][0-9]*$/.test(row.identifier))?.identifier;
60440
- }).catch(() => void 0) : void 0;
60694
+ let leaving = false;
60441
60695
  return guidedMenu({
60442
- resume,
60443
- stateError,
60444
60696
  out: ctx.out,
60445
- t: ctx.t,
60697
+ t: (key, params) => ctx.t(key, params),
60446
60698
  ask: orcaDeps(ctx).ask,
60447
- signedIn: !!profile && !profile.needsLogin,
60448
- repair,
60449
- bound: !!found?.projectId,
60450
- checkouts: checkouts.map(([path]) => path),
60699
+ ...selectFor(ctx),
60700
+ state: (cwd) => menuState(ctx, values, cwd),
60701
+ leaving: () => leaving,
60702
+ report: (error) => {
60703
+ reportFailure(ctx.out, ctx.t, error, false);
60704
+ },
60451
60705
  connect: async () => {
60706
+ const profile = await ctx.profiles.current().catch(() => null);
60452
60707
  const origin = profile ? webOrigin(profile) : "https://app.onfleek.app";
60453
60708
  if (!origin) {
60454
60709
  ctx.out.line(ctx.t("menu.setupExternal"));
@@ -60462,7 +60717,13 @@ async function dispatch(ctx, positionals, values, exec, passthrough) {
60462
60717
  const target = cwd ? new Context({ ...ctx.deps, cwd }, false, ctx.profileFlag, ctx.languageFlag) : ctx;
60463
60718
  const parsed = parseArgs({ args, options: OPTIONS, allowPositionals: true });
60464
60719
  target.headerShown = ctx.headerShown;
60465
- return dispatch(target, parsed.positionals, { profile: values.profile, language: values.language, ...parsed.values, yes: false }, null, null);
60720
+ target.updateChecked = true;
60721
+ if (target !== ctx && ctx.headerShown) target.out.indentHumanOutput();
60722
+ try {
60723
+ return await dispatch(target, parsed.positionals, { profile: values.profile, language: values.language, ...parsed.values, yes: false }, null, null);
60724
+ } finally {
60725
+ leaving = target.agentRan || target.appQuit;
60726
+ }
60466
60727
  }
60467
60728
  });
60468
60729
  }
@@ -60750,6 +61011,82 @@ async function mcpProxy(ctx, args, values) {
60750
61011
  ...ctx.deps.proxyRetryDelaysMs ? { retryDelaysMs: ctx.deps.proxyRetryDelaysMs } : {}
60751
61012
  });
60752
61013
  }
61014
+ function selectFor(ctx) {
61015
+ const stdin = ctx.deps.stdin ?? process.stdin, stdout = ctx.deps.stdout ?? process.stdout;
61016
+ if (ctx.json || !isKeyInput(stdin) || stdout.isTTY !== true || ctx.env.TERM === "dumb") return {};
61017
+ return { select: async (options) => {
61018
+ const result = await selectList({ stdin, stdout, out: ctx.out, t: ctx.t }, options);
61019
+ if (result.kind === "abort") ctx.appQuit = true;
61020
+ return result;
61021
+ } };
61022
+ }
61023
+ function askLine(rl, question) {
61024
+ return new Promise((resolve9, reject) => {
61025
+ rl.once("close", () => resolve9(""));
61026
+ rl.question(question).then(resolve9, reject);
61027
+ });
61028
+ }
61029
+ var RESUME_READ_MS = 3e3;
61030
+ async function menuState(ctx, values, cwd) {
61031
+ let stateError = false;
61032
+ const folder = cwd ?? ctx.deps.cwd ?? process.cwd();
61033
+ const found = await resolveBinding(ctx.profiles, ctx.configDir, { flag: ctx.profileFlag, env: ctx.env, cwd: folder, allowCurrent: true }).catch(() => {
61034
+ stateError = true;
61035
+ return null;
61036
+ });
61037
+ const profile = found?.profile ?? await ctx.profiles.current().catch(() => null);
61038
+ if (profile?.language && !ctx.languageFlag) ctx.t = translator(profile.language);
61039
+ const keys = profile ? new ProjectKeyIndex(ctx.configDir, profile.key) : null;
61040
+ const keyOf = async (project2) => await keys?.keyFor(project2).catch(() => null) ?? void 0;
61041
+ const entries = profile ? await new CheckoutIndex(ctx.configDir, profile.key).entries().catch(() => {
61042
+ stateError = true;
61043
+ return [];
61044
+ }) : [];
61045
+ const checkouts = (await Promise.all(entries.map(async ([path, project2]) => {
61046
+ if (!await stat16(path).then((info) => info.isDirectory(), () => false)) return null;
61047
+ const key = await keyOf(project2);
61048
+ return { path, label: displayPath(path, ctx.home, ctx.env), ...key ? { project: key } : {} };
61049
+ }))).filter((entry) => entry !== null);
61050
+ const domain = profile && found?.projectId ? new KitDomain(ctx.configDir, profile.key, found.projectId) : null;
61051
+ let kitNeedsRepair = false;
61052
+ if (domain) {
61053
+ try {
61054
+ kitNeedsRepair = !await domain.current() && await domain.pulledAt() === null;
61055
+ } catch {
61056
+ stateError = true;
61057
+ kitNeedsRepair = true;
61058
+ }
61059
+ }
61060
+ const handler = await inspectSetupHandler(kitContext(ctx, values)).catch(() => {
61061
+ stateError = true;
61062
+ return null;
61063
+ });
61064
+ const signedIn = !!profile && !profile.needsLogin;
61065
+ let resume;
61066
+ if (signedIn && found?.projectId) {
61067
+ const read2 = ctx.sessionFor(profile).then(async ({ client }) => {
61068
+ const inbox = (await client.callTool(WIRE.inbox, { limit: 25, kinds: ["claim"], teamId: found.projectId })).structured;
61069
+ const rows = Array.isArray(inbox.items) ? inbox.items : [];
61070
+ return rows.find((row) => row.kind === "claim" && typeof row.identifier === "string" && /^[A-Z0-9]{2,10}-[1-9][0-9]*$/.test(row.identifier))?.identifier;
61071
+ }).catch(() => void 0);
61072
+ let timer;
61073
+ resume = await Promise.race([read2, new Promise((resolve9) => {
61074
+ timer = setTimeout(() => resolve9(void 0), RESUME_READ_MS);
61075
+ })]);
61076
+ clearTimeout(timer);
61077
+ }
61078
+ const project = found?.projectId ? await keyOf(found.projectId) : void 0;
61079
+ const context = signedIn ? [profile.workspace.name, project, displayPath(folder, ctx.home, ctx.env)].filter(Boolean).map((part) => neutralizeLine(part)).join(" · ") : void 0;
61080
+ return {
61081
+ signedIn,
61082
+ bound: !!found?.projectId,
61083
+ stateError,
61084
+ checkouts,
61085
+ ...resume ? { resume } : {},
61086
+ ...context ? { context } : {},
61087
+ ...handler?.state === "missing" || kitNeedsRepair ? { repair: true } : {}
61088
+ };
61089
+ }
60753
61090
  function orcaDeps(ctx) {
60754
61091
  return {
60755
61092
  out: ctx.out,
@@ -60768,11 +61105,12 @@ function orcaDeps(ctx) {
60768
61105
  const { createInterface: createInterface2 } = await import("node:readline/promises");
60769
61106
  const rl = createInterface2({ input: process.stdin, output: ctx.json ? process.stderr : process.stdout });
60770
61107
  try {
60771
- return await rl.question(`${ctx.out.style.question(question)} `);
61108
+ return await askLine(rl, `${ctx.out.render(ctx.out.style.question(question), ctx.json ? "err" : "out")} `);
60772
61109
  } finally {
60773
61110
  rl.close();
60774
61111
  }
60775
61112
  }),
61113
+ ...selectFor(ctx),
60776
61114
  ...ctx.deps.mutationBackoffMs !== void 0 ? { backoffMs: ctx.deps.mutationBackoffMs } : {}
60777
61115
  };
60778
61116
  }
@@ -60789,7 +61127,11 @@ async function start(ctx, key, values) {
60789
61127
  ...orcaDeps(ctx),
60790
61128
  ...ctx.deps.linkDialog ? { dialog: ctx.deps.linkDialog } : {},
60791
61129
  kitReady: async (agent, found) => !!(await new KitDomain(ctx.configDir, found.profile.key, found.projectId).current())?.state.targets[agent],
60792
- runAgent: (agent, args, found) => kitRun(kitContext(ctx, values, { profile: found.profile, projectId: found.projectId, source: "repo", root: found.root }), agent, { project: found.projectId }, args)
61130
+ runAgent: async (agent, args, found) => {
61131
+ const code = await kitRun(kitContext(ctx, values, { profile: found.profile, projectId: found.projectId, source: "repo", root: found.root }), agent, { project: found.projectId }, args);
61132
+ ctx.agentRan = code !== 127;
61133
+ return code;
61134
+ }
60793
61135
  };
60794
61136
  return key ? fleekStart(deps, key, own4) : orcaPick(deps, own4, (chosen) => fleekStart(deps, chosen, own4));
60795
61137
  }
@@ -61080,6 +61422,31 @@ async function setupDeveloper(ctx, code, values) {
61080
61422
  }
61081
61423
  }, code, values);
61082
61424
  }
61425
+ function reportFailure(out, t, error, json2) {
61426
+ const errno = error?.code;
61427
+ const failure2 = error instanceof CliError ? error : typeof errno === "string" && /^E[A-Z]{2,16}$/.test(errno) ? new CliError("errors.internalCode", EXIT.failed, { code: errno }, "INTERNAL") : new CliError("errors.internal", EXIT.failed, {}, "INTERNAL");
61428
+ if (json2) out.json({ error: failure2.code ?? failure2.key, ...Object.keys(failure2.params).length ? { details: failure2.params } : {} });
61429
+ else {
61430
+ const params = Object.fromEntries(Object.entries(failure2.params).map(([key, value]) => [key, typeof value === "string" ? neutralizeLine(value) : value]));
61431
+ let friendlyKey = failure2 instanceof ToolError && ["RATE_LIMITED", "ACCESS_DENIED", "CONFLICT", "TRANSITION_DENIED"].includes(failure2.code ?? "") ? `errors.server.${failure2.code}` : failure2.key;
61432
+ if (failure2.code === "KIT_INVALID" || failure2.code === "KIT_UNSIGNED") {
61433
+ const reason = String(failure2.params.reason ?? "");
61434
+ const kind = kitRefusalCategory(failure2.code, reason);
61435
+ friendlyKey = `errors.kitRefused.${kind}`;
61436
+ }
61437
+ out.error(out.style.fail(redact(t(friendlyKey, params))));
61438
+ if (failure2 instanceof ToolError && failure2.code === "TRANSITION_DENIED") {
61439
+ const details = failure2.details;
61440
+ const key = typeof details?.reason === "string" ? `errors.transition.${details.reason}` : "";
61441
+ if (Object.hasOwn(EN, key)) out.error(t(key));
61442
+ }
61443
+ const suggestion = failure2.suggestion;
61444
+ if (suggestion) out.error(t("errors.didYouMean", { command: out.style.cmd(suggestion) }));
61445
+ if (failure2.code === "KIT_INVALID" || failure2.code === "KIT_UNSIGNED") out.error(out.style.dim(redact(t("errors.kitReason", { ...params, reason: params.reason ?? failure2.code }))));
61446
+ if (`${friendlyKey}.next` in EN) out.error(` ${t("next.step", { command: out.style.cmd(redact(t(`${friendlyKey}.next`, params))) })}`);
61447
+ }
61448
+ return failure2;
61449
+ }
61083
61450
  async function main(argv, deps = {}) {
61084
61451
  const env = deps.env ?? process.env;
61085
61452
  let t = translator(locale(env));
@@ -61110,30 +61477,7 @@ async function main(argv, deps = {}) {
61110
61477
  context = new Context(deps, json2, parsed.values.profile, parsed.values.language === "en" || parsed.values.language === "de" ? parsed.values.language : void 0);
61111
61478
  return await dispatch(context, parsed.positionals, parsed.values, exec, passthrough);
61112
61479
  } catch (error) {
61113
- t = context?.t ?? t;
61114
- const errorOut = context?.out ?? out;
61115
- const errno = error?.code;
61116
- const failure2 = error instanceof CliError ? error : typeof errno === "string" && /^E[A-Z]{2,16}$/.test(errno) ? new CliError("errors.internalCode", EXIT.failed, { code: errno }, "INTERNAL") : new CliError("errors.internal", EXIT.failed, {}, "INTERNAL");
61117
- if (json2) errorOut.json({ error: failure2.code ?? failure2.key, ...Object.keys(failure2.params).length ? { details: failure2.params } : {} });
61118
- else {
61119
- const params = Object.fromEntries(Object.entries(failure2.params).map(([key, value]) => [key, typeof value === "string" ? neutralizeLine(value) : value]));
61120
- let friendlyKey = failure2 instanceof ToolError && ["RATE_LIMITED", "ACCESS_DENIED", "CONFLICT", "TRANSITION_DENIED"].includes(failure2.code ?? "") ? `errors.server.${failure2.code}` : failure2.key;
61121
- if (failure2.code === "KIT_INVALID" || failure2.code === "KIT_UNSIGNED") {
61122
- const reason = String(failure2.params.reason ?? "");
61123
- const kind = kitRefusalCategory(failure2.code, reason);
61124
- friendlyKey = `errors.kitRefused.${kind}`;
61125
- }
61126
- errorOut.error(errorOut.style.fail(redact(t(friendlyKey, params))));
61127
- if (failure2 instanceof ToolError && failure2.code === "TRANSITION_DENIED") {
61128
- const details = failure2.details;
61129
- const key = typeof details?.reason === "string" ? `errors.transition.${details.reason}` : "";
61130
- if (Object.hasOwn(EN, key)) errorOut.error(t(key));
61131
- }
61132
- const suggestion = failure2.suggestion;
61133
- if (suggestion) errorOut.error(t("errors.didYouMean", { command: errorOut.style.cmd(suggestion) }));
61134
- if (failure2.code === "KIT_INVALID" || failure2.code === "KIT_UNSIGNED") errorOut.error(errorOut.style.dim(redact(t("errors.kitReason", { ...params, reason: params.reason ?? failure2.code }))));
61135
- if (`${friendlyKey}.next` in EN) errorOut.error(` ${t("next.step", { command: errorOut.style.cmd(redact(t(`${friendlyKey}.next`, params))) })}`);
61136
- }
61480
+ const failure2 = reportFailure(context?.out ?? out, context?.t ?? t, error, json2);
61137
61481
  if (["listen", "mcp"].includes(commandName ?? "") && failure2.code === "ACCESS_DENIED") return EXIT.accessDenied;
61138
61482
  return failure2.exitCode;
61139
61483
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-joe/fleek",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "fleek - command line client for the Fleek agent hub (MCP over OAuth, OS keychain, signed role/project kits)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",