skydive-cli 0.2.0-beta.427 → 0.2.0-beta.457

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/js/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { A as getShareMachineDefault, B as saveSession, C as DEFAULT_WEB_URL, D as getPromptHistoryPath, E as getLastSeenVersion, F as resolveConfig, H as setLastSeenVersion, I as resolveManagementAuth, L as resolveSession, M as getStoredApiKeyWorkspaceName, N as getUpdateCheckDisabled, P as resolveAppUrl, T as getConfigPath, _ as setActiveWorkspace, b as API_KEY_PREFIX, d as themes, g as listWorkspaces, h as getSessionIdentity, j as getStoredApiKeyId, m as getActiveWorkspaceId, p as ensureActiveOrganization, v as API_KEYS_URL, w as deleteConfig, x as DEFAULT_API_URL, y as API_KEY_FAMILY_PREFIX, z as saveConfig } from "./theme-CuQhvqzN.mjs";
2
+ import { A as getPromptHistoryPath, B as resolveSession, C as API_KEY_PREFIX, D as deleteConfig, E as DEFAULT_WEB_URL, F as getStoredApiKeyWorkspaceName, G as setLastSeenVersion, H as saveConfig, I as getUpdateCheckDisabled, L as resolveAppUrl, N as getShareMachineDefault, O as getConfigPath, P as getStoredApiKeyId, R as resolveConfig, S as API_KEY_FAMILY_PREFIX, U as saveSession, _ as getActiveWorkspaceId, b as setActiveWorkspace, g as ensureActiveOrganization, k as getLastSeenVersion, m as themes, r as brandHelpArt, v as getSessionIdentity, w as DEFAULT_API_URL, x as API_KEYS_URL, y as listWorkspaces, z as resolveManagementAuth } from "./brand-BVALAEf1.mjs";
3
3
  import { n as printError, r as printTable, t as output } from "./output-B4cW10Ph.mjs";
4
4
  import { n as createRestClient } from "./rest-BBQrPIwq.mjs";
5
5
  import { i as resolveAgent } from "./print-YqNIRwdf.mjs";
@@ -20,7 +20,7 @@ import semver from "semver";
20
20
 
21
21
  //#region package.json
22
22
  var name = "skydive-cli";
23
- var version = "0.2.0-beta.427";
23
+ var version = "0.2.0-beta.457";
24
24
 
25
25
  //#endregion
26
26
  //#region src/types.ts
@@ -1200,7 +1200,7 @@ const importCommand = {
1200
1200
  process.exit(1);
1201
1201
  }
1202
1202
  }
1203
- const { runChat } = await import("./boot-BiMPCqRg.mjs");
1203
+ const { runChat } = await import("./boot-D_H3xGxt.mjs");
1204
1204
  await runChat({
1205
1205
  appUrl,
1206
1206
  sessionToken: session.value.sessionToken,
@@ -1511,7 +1511,7 @@ const chatCommand = {
1511
1511
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
1512
1512
  process.exit(1);
1513
1513
  }
1514
- const { runChat } = await import("./boot-BiMPCqRg.mjs");
1514
+ const { runChat } = await import("./boot-D_H3xGxt.mjs");
1515
1515
  await runChat({
1516
1516
  appUrl,
1517
1517
  sessionToken: session.value.sessionToken,
@@ -1810,7 +1810,7 @@ const switchCommand = {
1810
1810
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
1811
1811
  process.exit(1);
1812
1812
  }
1813
- const { runWorkspacePicker } = await import("./boot-BiMPCqRg.mjs");
1813
+ const { runWorkspacePicker } = await import("./boot-D_H3xGxt.mjs");
1814
1814
  await runWorkspacePicker(session);
1815
1815
  return;
1816
1816
  }
@@ -2608,7 +2608,10 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
2608
2608
  }
2609
2609
  maybePrintWhatsNew();
2610
2610
  setupUpdateCheck(hideBin(process.argv));
2611
- createCli(resolveArgv(hideBin(process.argv))).parse();
2611
+ const resolved = resolveArgv(hideBin(process.argv));
2612
+ const raw = hideBin(process.argv);
2613
+ if ((raw.includes("--help") || raw.includes("-h")) && !raw.some((a) => !a.startsWith("-")) && !raw.includes("--json") && !raw.includes("--quiet")) process.stdout.write(brandHelpArt());
2614
+ createCli(resolved).parse();
2612
2615
 
2613
2616
  //#endregion
2614
2617
  export { };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { O as getReviewStateDir, R as resolveWebUrl, S as DEFAULT_APP_URL, T as getConfigPath, V as saveTheme, _ as setActiveWorkspace, a as noColorRequested, c as themeMode, f as themesForMode, g as listWorkspaces, i as monoTheme, k as getSavedTheme, l as themeModeFromColorFgBg, m as getActiveWorkspaceId, n as applyTheme, o as theme, r as findTheme, s as themeForMode, t as DEFAULT_THEME_ID, u as themeVersion, x as DEFAULT_API_URL } from "./theme-CuQhvqzN.mjs";
2
+ import { M as getSavedTheme, O as getConfigPath, T as DEFAULT_APP_URL, V as resolveWebUrl, W as saveTheme, _ as getActiveWorkspaceId, a as applyTheme, b as setActiveWorkspace, c as noColorRequested, d as themeMode, f as themeModeFromColorFgBg, h as themesForMode, i as DEFAULT_THEME_ID, j as getReviewStateDir, l as theme, n as WORDMARK, o as findTheme, p as themeVersion, s as monoTheme, t as MARK_CELLS, u as themeForMode, w as DEFAULT_API_URL, y as listWorkspaces } from "./brand-BVALAEf1.mjs";
3
3
  import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-BBQrPIwq.mjs";
4
4
  import { n as isRecord, t as errorMessage } from "./util-CeisaZVY.mjs";
5
5
  import { c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams } from "./print-YqNIRwdf.mjs";
@@ -931,6 +931,76 @@ function FuzzyText({ text, indexes, fg }) {
931
931
  }, i)) });
932
932
  }
933
933
 
934
+ //#endregion
935
+ //#region src/chat/tui/skydive-mark.tsx
936
+ /**
937
+ * The Skydive brand splash for the interactive TUI, rendered as OpenTUI React
938
+ * nodes (not raw ANSI). Two variants:
939
+ *
940
+ * - `variant="full"` — the six-petal mark beside the SKYDIVE wordmark.
941
+ * - `variant="mark"` — just the pinwheel mark (compact picker headers).
942
+ *
943
+ * The mark paints fixed brand hexes (a logo is a logo). The wordmark follows
944
+ * the current theme's body text (`theme.fg`) so it reads white-ish on dark
945
+ * themes and dark on light ones. Under NO_COLOR the theme palette is all
946
+ * `undefined`; we detect that and render uncolored so it degrades with the
947
+ * rest of the UI.
948
+ */
949
+ function noColor() {
950
+ return theme.fg === void 0;
951
+ }
952
+ function MarkRows({ mono }) {
953
+ return /* @__PURE__ */ jsx("box", {
954
+ style: { flexDirection: "column" },
955
+ children: MARK_CELLS.map((row, r) => /* @__PURE__ */ jsx("text", { children: row.map((cell, c) => cell === " " ? /* @__PURE__ */ jsx("span", { children: " " }, c) : /* @__PURE__ */ jsx("span", {
956
+ fg: mono ? void 0 : cell[1],
957
+ children: cell[0]
958
+ }, c)) }, r))
959
+ });
960
+ }
961
+ function WordmarkRows({ mono }) {
962
+ const color = mono ? void 0 : theme.fg;
963
+ return /* @__PURE__ */ jsx("box", {
964
+ style: { flexDirection: "column" },
965
+ children: WORDMARK.map((line, r) => /* @__PURE__ */ jsx("text", { children: /* @__PURE__ */ jsx("span", {
966
+ fg: color,
967
+ children: line
968
+ }) }, r))
969
+ });
970
+ }
971
+ /**
972
+ * Height-aware brand header for the pickers. Always shown (including while the
973
+ * list is loading / empty / erroring) so the splash is never gated on data.
974
+ * Full mark + wordmark when the terminal is tall enough; compact one-liner on
975
+ * short terminals so the list still has room.
976
+ */
977
+ function BrandHeader({ height }) {
978
+ if (height >= 24) return /* @__PURE__ */ jsxs("box", {
979
+ style: { flexDirection: "column" },
980
+ children: [/* @__PURE__ */ jsx(SkydiveMark, { variant: "full" }), /* @__PURE__ */ jsx("box", { style: { height: 1 } })]
981
+ });
982
+ return /* @__PURE__ */ jsx("text", {
983
+ fg: theme.fg,
984
+ children: "✦ Skydive"
985
+ });
986
+ }
987
+ /** Row budget the BrandHeader consumes; callers subtract this from visibleRows. */
988
+ function brandHeaderRows(height) {
989
+ return height >= 24 ? 7 : 1;
990
+ }
991
+ function SkydiveMark({ variant = "full" }) {
992
+ const mono = noColor();
993
+ if (variant === "mark") return /* @__PURE__ */ jsx(MarkRows, { mono });
994
+ return /* @__PURE__ */ jsxs("box", {
995
+ style: { flexDirection: "row" },
996
+ children: [
997
+ /* @__PURE__ */ jsx(MarkRows, { mono }),
998
+ /* @__PURE__ */ jsx("box", { style: { width: 3 } }),
999
+ /* @__PURE__ */ jsx(WordmarkRows, { mono })
1000
+ ]
1001
+ });
1002
+ }
1003
+
934
1004
  //#endregion
935
1005
  //#region src/chat/tui/screens/agent-picker.tsx
936
1006
  function AgentPickerScreen() {
@@ -998,17 +1068,36 @@ function AgentPickerScreen() {
998
1068
  scope,
999
1069
  cache
1000
1070
  ]);
1001
- if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1002
- fg: theme.muted,
1003
- children: "loading agents…"
1071
+ const { height } = useTerminalDimensions();
1072
+ if (state.kind === "loading") return /* @__PURE__ */ jsxs("box", {
1073
+ style: {
1074
+ flexDirection: "column",
1075
+ flexGrow: 1
1076
+ },
1077
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1078
+ fg: theme.muted,
1079
+ children: "loading agents…"
1080
+ })]
1004
1081
  });
1005
- if (state.kind === "error") return /* @__PURE__ */ jsxs("text", {
1006
- fg: theme.error,
1007
- children: ["error: ", state.message]
1082
+ if (state.kind === "error") return /* @__PURE__ */ jsxs("box", {
1083
+ style: {
1084
+ flexDirection: "column",
1085
+ flexGrow: 1
1086
+ },
1087
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsxs("text", {
1088
+ fg: theme.error,
1089
+ children: ["error: ", state.message]
1090
+ })]
1008
1091
  });
1009
- if (state.agents.length === 0) return /* @__PURE__ */ jsx("text", {
1010
- fg: theme.muted,
1011
- children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, ctrl+w to switch workspace, or ctrl+n to create one." : "no agents in this workspace. press ctrl+w to switch workspace, or ctrl+n to create one."
1092
+ if (state.agents.length === 0) return /* @__PURE__ */ jsxs("box", {
1093
+ style: {
1094
+ flexDirection: "column",
1095
+ flexGrow: 1
1096
+ },
1097
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1098
+ fg: theme.muted,
1099
+ children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, ctrl+w to switch workspace, or ctrl+n to create one." : "no agents in this workspace. press ctrl+w to switch workspace, or ctrl+n to create one."
1100
+ })]
1012
1101
  });
1013
1102
  return /* @__PURE__ */ jsx(FilterableAgentList, {
1014
1103
  agents: state.agents,
@@ -1026,7 +1115,7 @@ function FilterableAgentList({ agents, scope, onPick }) {
1026
1115
  const hits = fuzzyFilter(query, agents, agentKeys);
1027
1116
  const filtered = hits.map((h) => h.item);
1028
1117
  const clamped = Math.min(highlight, Math.max(0, filtered.length - 1));
1029
- const visibleRows = Math.max(3, height - 6);
1118
+ const visibleRows = Math.max(3, height - 6 - brandHeaderRows(height));
1030
1119
  const start = windowStart(clamped, filtered.length, visibleRows);
1031
1120
  const windowed = hits.slice(start, start + visibleRows);
1032
1121
  useKeyboard((key) => {
@@ -1055,6 +1144,7 @@ function FilterableAgentList({ agents, scope, onPick }) {
1055
1144
  flexGrow: 1
1056
1145
  },
1057
1146
  children: [
1147
+ /* @__PURE__ */ jsx(BrandHeader, { height }),
1058
1148
  /* @__PURE__ */ jsxs("box", {
1059
1149
  style: {
1060
1150
  flexDirection: "row",
@@ -1624,13 +1714,26 @@ function ConversationPickerScreen({ agent }) {
1624
1714
  cacheKey,
1625
1715
  cache
1626
1716
  ]);
1627
- if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1628
- fg: theme.muted,
1629
- children: "loading conversations…"
1717
+ const { height } = useTerminalDimensions();
1718
+ if (state.kind === "loading") return /* @__PURE__ */ jsxs("box", {
1719
+ style: {
1720
+ flexDirection: "column",
1721
+ flexGrow: 1
1722
+ },
1723
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1724
+ fg: theme.muted,
1725
+ children: "loading conversations…"
1726
+ })]
1630
1727
  });
1631
- if (state.kind === "error") return /* @__PURE__ */ jsxs("text", {
1632
- fg: theme.error,
1633
- children: ["error: ", state.message]
1728
+ if (state.kind === "error") return /* @__PURE__ */ jsxs("box", {
1729
+ style: {
1730
+ flexDirection: "column",
1731
+ flexGrow: 1
1732
+ },
1733
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsxs("text", {
1734
+ fg: theme.error,
1735
+ children: ["error: ", state.message]
1736
+ })]
1634
1737
  });
1635
1738
  return /* @__PURE__ */ jsx(ConversationList, {
1636
1739
  agent,
@@ -1687,7 +1790,7 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1687
1790
  "↵ open"
1688
1791
  ], innerWidth)
1689
1792
  };
1690
- const visibleRows = Math.max(3, height - 6);
1793
+ const visibleRows = Math.max(3, height - 6 - brandHeaderRows(height));
1691
1794
  const start = windowStart(clamped, rows.length, visibleRows);
1692
1795
  const windowed = rows.slice(start, start + visibleRows);
1693
1796
  const open = (row) => {
@@ -1786,6 +1889,7 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1786
1889
  flexGrow: 1
1787
1890
  },
1788
1891
  children: [
1892
+ /* @__PURE__ */ jsx(BrandHeader, { height }),
1789
1893
  /* @__PURE__ */ jsxs("box", {
1790
1894
  style: {
1791
1895
  flexDirection: "row",
@@ -4357,6 +4461,10 @@ const keybindGroups = [
4357
4461
  keys: "1 / 2",
4358
4462
  action: "switch Changes / Files"
4359
4463
  },
4464
+ {
4465
+ keys: "\\",
4466
+ action: "toggle side / stacked layout"
4467
+ },
4360
4468
  {
4361
4469
  keys: "↵ / o / l",
4362
4470
  action: "focus selected file preview"
@@ -6747,7 +6855,7 @@ function gcOnce() {
6747
6855
  gcStarted = true;
6748
6856
  store.gc();
6749
6857
  }
6750
- function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHeight, active, shellLayout, tab, onTabChange, selectedChangeFile, onSelectedChangeFileChange, selectedWorkspaceFile, onSelectedWorkspaceFileChange, navigatorSize, onNavigatorResize, onInsertToComposer, onRequestChatFocus, onClose }) {
6858
+ function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHeight, active, shellLayout, tab, onTabChange, selectedChangeFile, onSelectedChangeFileChange, selectedWorkspaceFile, onSelectedWorkspaceFileChange, navigatorSize, onNavigatorResize, onToggleOrientation, onInsertToComposer, onRequestChatFocus, onClose }) {
6751
6859
  const width = viewportWidth;
6752
6860
  const height = viewportHeight;
6753
6861
  const shellHeight = Math.max(6, height - 2);
@@ -6898,6 +7006,10 @@ function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHei
6898
7006
  onTabChange("files");
6899
7007
  return;
6900
7008
  }
7009
+ if (key.name === "\\") {
7010
+ if (onToggleOrientation() === "unavailable") setStatus("no room to change layout in this terminal");
7011
+ return;
7012
+ }
6901
7013
  if (key.name === "s") {
6902
7014
  if (comments.length === 0) {
6903
7015
  setStatus("no pending comments");
@@ -7288,18 +7400,93 @@ function PaneRowView({ row, width, isCursor, inSelection, isAnchor, isCommented
7288
7400
  //#endregion
7289
7401
  //#region src/chat/tui/file-pane-layout.ts
7290
7402
  /**
7403
+ * Terminal cells are about twice as tall as they are wide, so a raw
7404
+ * cols-vs-rows compare would call almost any terminal "wide". Scale rows by
7405
+ * this factor to compare the *visual* aspect: a square-looking terminal has
7406
+ * `width ≈ height * CELL_ASPECT`.
7407
+ */
7408
+ const CELL_ASPECT = 2;
7409
+ /**
7410
+ * The automatic split follows the terminal's visual aspect: wider than tall
7411
+ * places review beside the chat (a vertical divider), taller than wide stacks
7412
+ * it underneath (a horizontal divider). This is the responsive default when
7413
+ * the user hasn't pinned an orientation.
7414
+ */
7415
+ function autoPrefersSide({ width, height }) {
7416
+ return width >= height * CELL_ASPECT;
7417
+ }
7418
+ /**
7419
+ * Bare-minimum room to render a split at all (as opposed to the higher
7420
+ * comfort thresholds the automatic choice uses). A user pin is honored down
7421
+ * to these: chat + review each need a usable strip, but we don't insist on
7422
+ * the roomier auto breakpoints. Mirrors the clamps in clampSidePaneWidth /
7423
+ * clampBelowPaneHeight so a pinned split never renders narrower than those.
7424
+ */
7425
+ function canRenderSide({ width, height }) {
7426
+ return width >= 76 && height >= 8;
7427
+ }
7428
+ function canRenderBelow({ width, height }) {
7429
+ return height >= 24 && width >= 24;
7430
+ }
7431
+ /**
7291
7432
  * Responsive placement for the file pane.
7292
7433
  *
7293
- * Wide terminals keep the conversation readable and place review beside it.
7294
- * Tall terminals without enough horizontal room stack review underneath.
7295
- * Cramped terminals give review the full body rather than rendering two
7296
- * unusably small surfaces.
7434
+ * With no user pin the split follows the terminal's visual aspect (see
7435
+ * autoPrefersSide): wider than tall -> side, taller than wide -> stacked.
7436
+ * The preferred split still has to be renderable; if it isn't we use the
7437
+ * other, and cramped terminals that can render neither get the full body.
7438
+ *
7439
+ * A user `orientation` pin always wins as long as the terminal can render
7440
+ * that split at all (the lower canRender* bar) — so you get the layout you
7441
+ * asked for even on a smallish terminal. Only when the pinned split
7442
+ * genuinely can't fit do we fall back: to the other split if it can render,
7443
+ * else to `fullscreen`.
7297
7444
  */
7298
- function filePanePlacement({ width, height }) {
7299
- if (width >= 140 && height >= 24) return "side";
7300
- if (width >= 50 && height >= 40) return "below";
7445
+ function filePanePlacement({ width, height, orientation }) {
7446
+ const renderSide = canRenderSide({
7447
+ width,
7448
+ height
7449
+ });
7450
+ const renderBelow = canRenderBelow({
7451
+ width,
7452
+ height
7453
+ });
7454
+ if (orientation === "side") {
7455
+ if (renderSide) return "side";
7456
+ if (renderBelow) return "below";
7457
+ return "fullscreen";
7458
+ }
7459
+ if (orientation === "below") {
7460
+ if (renderBelow) return "below";
7461
+ if (renderSide) return "side";
7462
+ return "fullscreen";
7463
+ }
7464
+ if (autoPrefersSide({
7465
+ width,
7466
+ height
7467
+ })) {
7468
+ if (renderSide) return "side";
7469
+ if (renderBelow) return "below";
7470
+ return "fullscreen";
7471
+ }
7472
+ if (renderBelow) return "below";
7473
+ if (renderSide) return "side";
7301
7474
  return "fullscreen";
7302
7475
  }
7476
+ /**
7477
+ * Whether toggling the orientation would actually change anything: both
7478
+ * splits must be renderable. When only one (or neither) can render, the
7479
+ * caller surfaces a hint instead of a silent no-op.
7480
+ */
7481
+ function canToggleOrientation({ width, height }) {
7482
+ return canRenderSide({
7483
+ width,
7484
+ height
7485
+ }) && canRenderBelow({
7486
+ width,
7487
+ height
7488
+ });
7489
+ }
7303
7490
  /** Keep a dragged side pane wide enough to use and leave the chat usable. */
7304
7491
  function clampSidePaneWidth(width, bodyWidth) {
7305
7492
  return Math.max(44, Math.min(bodyWidth - 30, width));
@@ -7827,6 +8014,7 @@ function ChatScreen({ agent, conversation }) {
7827
8014
  side: null,
7828
8015
  stacked: null
7829
8016
  });
8017
+ const [reviewOrientation, setReviewOrientation] = useState(null);
7830
8018
  const [reviewWorkspaceFile, setReviewWorkspaceFile] = useState(null);
7831
8019
  useEffect(() => {
7832
8020
  setReviewChangeFile(null);
@@ -7882,7 +8070,8 @@ function ChatScreen({ agent, conversation }) {
7882
8070
  const bodyHeight = Math.max(1, height - 3);
7883
8071
  const reviewPlacement = filePanePlacement({
7884
8072
  width,
7885
- height
8073
+ height,
8074
+ orientation: reviewOrientation
7886
8075
  });
7887
8076
  const reviewWidth = reviewPlacement === "side" ? clampSidePaneWidth(reviewSizePref.side ?? sidePaneWidth(bodyWidth), bodyWidth) : bodyWidth;
7888
8077
  const reviewHeight = reviewPlacement === "below" ? clampBelowPaneHeight(reviewSizePref.below ?? belowPaneHeight(bodyHeight), bodyHeight) : bodyHeight;
@@ -7925,6 +8114,7 @@ function ChatScreen({ agent, conversation }) {
7925
8114
  helpOpen,
7926
8115
  reviewOpen,
7927
8116
  reviewFocused,
8117
+ reviewPlacement,
7928
8118
  credPromptOpen
7929
8119
  ]);
7930
8120
  useEffect(() => {
@@ -9019,6 +9209,14 @@ function ChatScreen({ agent, conversation }) {
9019
9209
  ...prev,
9020
9210
  side: size
9021
9211
  }),
9212
+ onToggleOrientation: () => {
9213
+ if (!canToggleOrientation({
9214
+ width,
9215
+ height
9216
+ })) return "unavailable";
9217
+ setReviewOrientation(reviewPlacement === "below" ? "side" : "below");
9218
+ return "toggled";
9219
+ },
9022
9220
  onInsertToComposer: (review) => {
9023
9221
  const next = [inputRef.current.trimEnd(), review].filter((part) => part.length > 0).join("\n\n");
9024
9222
  setInput(next);
@@ -987,4 +987,147 @@ function applyTheme(def) {
987
987
  }
988
988
 
989
989
  //#endregion
990
- export { getShareMachineDefault as A, saveSession as B, DEFAULT_WEB_URL as C, getPromptHistoryPath as D, getLastSeenVersion as E, resolveConfig as F, setLastSeenVersion as H, resolveManagementAuth as I, resolveSession as L, getStoredApiKeyWorkspaceName as M, getUpdateCheckDisabled as N, getReviewStateDir as O, resolveAppUrl as P, resolveWebUrl as R, DEFAULT_APP_URL as S, getConfigPath as T, saveTheme as V, setActiveWorkspace as _, noColorRequested as a, API_KEY_PREFIX as b, themeMode as c, themes as d, themesForMode as f, listWorkspaces as g, getSessionIdentity as h, monoTheme as i, getStoredApiKeyId as j, getSavedTheme as k, themeModeFromColorFgBg as l, getActiveWorkspaceId as m, applyTheme as n, theme as o, ensureActiveOrganization as p, findTheme as r, themeForMode as s, DEFAULT_THEME_ID as t, themeVersion as u, API_KEYS_URL as v, deleteConfig as w, DEFAULT_API_URL as x, API_KEY_FAMILY_PREFIX as y, saveConfig as z };
990
+ //#region src/brand.ts
991
+ const MARK_CELLS = [
992
+ [
993
+ " ",
994
+ " ",
995
+ ["▆", "#517a9d"],
996
+ ["█", "#6192bb"],
997
+ ["█", "#6191ba"],
998
+ ["▌", "#4d7494"],
999
+ ["▆", "#90572e"],
1000
+ ["█", "#e18948"],
1001
+ ["█", "#df8747"],
1002
+ ["▅", "#bd733c"],
1003
+ " ",
1004
+ " "
1005
+ ],
1006
+ [
1007
+ " ",
1008
+ ["▂", "#cb5556"],
1009
+ ["▝", "#6699c4"],
1010
+ ["█", "#446683"],
1011
+ ["█", "#527c9f"],
1012
+ ["▘", "#436582"],
1013
+ ["▝", "#ba713b"],
1014
+ ["█", "#be733c"],
1015
+ ["█", "#9c5e31"],
1016
+ ["▚", "#9d6345"],
1017
+ ["▂", "#c1859b"],
1018
+ " "
1019
+ ],
1020
+ [
1021
+ ["█", "#c05152"],
1022
+ ["█", "#e15f60"],
1023
+ ["█", "#d65a5b"],
1024
+ ["▅", "#a24445"],
1025
+ " ",
1026
+ " ",
1027
+ " ",
1028
+ " ",
1029
+ ["▅", "#b17a8e"],
1030
+ ["█", "#d895ad"],
1031
+ ["█", "#df9ab3"],
1032
+ ["▉", "#c3869c"]
1033
+ ],
1034
+ [
1035
+ ["▝", "#d25859"],
1036
+ ["█", "#953f3f"],
1037
+ ["▘", "#df5e5f"],
1038
+ ["▂", "#4d8d6f"],
1039
+ ["▃", "#539877"],
1040
+ " ",
1041
+ " ",
1042
+ ["▃", "#e5aa47"],
1043
+ ["▂", "#d29d41"],
1044
+ ["█", "#7f585e"],
1045
+ ["█", "#916475"],
1046
+ ["▘", "#c6899f"]
1047
+ ],
1048
+ [
1049
+ " ",
1050
+ " ",
1051
+ ["█", "#4e8f70"],
1052
+ ["█", "#569d7b"],
1053
+ ["█", "#569d7b"],
1054
+ ["▌", "#4a876a"],
1055
+ ["▆", "#8f6a2c"],
1056
+ ["█", "#efb24a"],
1057
+ ["█", "#efb24a"],
1058
+ ["▉", "#e2a845"],
1059
+ " ",
1060
+ " "
1061
+ ],
1062
+ [
1063
+ " ",
1064
+ " ",
1065
+ " ",
1066
+ ["▝", "#488467"],
1067
+ ["▘", "#488367"],
1068
+ " ",
1069
+ " ",
1070
+ ["▝", "#ca963e"],
1071
+ ["▘", "#c8953d"],
1072
+ " ",
1073
+ " ",
1074
+ " "
1075
+ ]
1076
+ ];
1077
+ const MARK_WIDTH = 12;
1078
+ /** Accent hex used for the compact one-liner star (matches the blue petal). */
1079
+ const BRAND_ACCENT = "#5b8fc7";
1080
+ /** "Skydive" in an ANSI-shadow block font. */
1081
+ const WORDMARK = [
1082
+ "███████╗██╗ ██╗██╗ ██╗██████╗ ██╗██╗ ██╗███████╗",
1083
+ "██╔════╝██║ ██╔╝╚██╗ ██╔╝██╔══██╗██║██║ ██║██╔════╝",
1084
+ "███████╗█████╔╝ ╚████╔╝ ██║ ██║██║██║ ██║█████╗ ",
1085
+ "╚════██║██╔═██╗ ╚██╔╝ ██║ ██║██║╚██╗ ██╔╝██╔══╝ ",
1086
+ "███████║██║ ██╗ ██║ ██████╔╝██║ ╚████╔╝ ███████╗",
1087
+ "╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝"
1088
+ ];
1089
+ const RESET = "\x1B[0m";
1090
+ function hexToRgb(hex) {
1091
+ const n = Number.parseInt(hex.slice(1), 16);
1092
+ return [
1093
+ n >> 16 & 255,
1094
+ n >> 8 & 255,
1095
+ n & 255
1096
+ ];
1097
+ }
1098
+ function sgr(text, hex) {
1099
+ const [r, g, b] = hexToRgb(hex);
1100
+ return `\x1b[38;2;${r};${g};${b}m${text}${RESET}`;
1101
+ }
1102
+ function markLinesAnsi() {
1103
+ return MARK_CELLS.map((row) => row.map((cell) => cell === " " ? " " : sgr(cell[0], cell[1])).join(""));
1104
+ }
1105
+ /**
1106
+ * Build the splash for the `skydive --help` screen as a string, gated on the
1107
+ * terminal's capabilities. Returns '' when it should be suppressed (non-TTY
1108
+ * target such as a pipe or CI). The wordmark is left uncolored (terminal default
1109
+ * fg = neutral on both light and dark); only the mark uses brand petal hexes.
1110
+ * A narrow terminal collapses to a one-liner.
1111
+ */
1112
+ function brandHelpArt(stream = process.stdout) {
1113
+ if (!stream.isTTY) return "";
1114
+ const truecolor = (typeof stream.getColorDepth === "function" ? stream.getColorDepth() : 1) >= 24;
1115
+ if ((stream.columns ?? 80) < 66) return truecolor ? `\n ${sgr("✦", BRAND_ACCENT)} Skydive\n` : "\n ✦ Skydive\n";
1116
+ if (!truecolor) return `\n${WORDMARK.map((l) => ` ${l}`).join("\n")}\n`;
1117
+ const mark = markLinesAnsi();
1118
+ const word = [...WORDMARK];
1119
+ const height = Math.max(mark.length, word.length);
1120
+ const topPad = Math.max(0, Math.floor((word.length - mark.length) / 2));
1121
+ const blank = " ".repeat(MARK_WIDTH);
1122
+ const lines = [];
1123
+ for (let i = 0; i < height; i++) {
1124
+ const mi = i - topPad;
1125
+ const m = mi >= 0 && mi < mark.length ? mark[mi] : blank;
1126
+ const w = i < word.length ? word[i] : "";
1127
+ lines.push(` ${m} ${w}`);
1128
+ }
1129
+ return `\n${lines.join("\n")}\n`;
1130
+ }
1131
+
1132
+ //#endregion
1133
+ export { getPromptHistoryPath as A, resolveSession as B, API_KEY_PREFIX as C, deleteConfig as D, DEFAULT_WEB_URL as E, getStoredApiKeyWorkspaceName as F, setLastSeenVersion as G, saveConfig as H, getUpdateCheckDisabled as I, resolveAppUrl as L, getSavedTheme as M, getShareMachineDefault as N, getConfigPath as O, getStoredApiKeyId as P, resolveConfig as R, API_KEY_FAMILY_PREFIX as S, DEFAULT_APP_URL as T, saveSession as U, resolveWebUrl as V, saveTheme as W, getActiveWorkspaceId as _, applyTheme as a, setActiveWorkspace as b, noColorRequested as c, themeMode as d, themeModeFromColorFgBg as f, ensureActiveOrganization as g, themesForMode as h, DEFAULT_THEME_ID as i, getReviewStateDir as j, getLastSeenVersion as k, theme as l, themes as m, WORDMARK as n, findTheme as o, themeVersion as p, brandHelpArt as r, monoTheme as s, MARK_CELLS as t, themeForMode as u, getSessionIdentity as v, DEFAULT_API_URL as w, API_KEYS_URL as x, listWorkspaces as y, resolveManagementAuth as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.2.0-beta.427",
3
+ "version": "0.2.0-beta.457",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",