skydive-cli 0.5.0-beta.9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +61 -13
  3. package/dist/js/api-BFQ4PQDA.mjs +315 -0
  4. package/dist/js/{billing-blocked-Dgu5-oDy.mjs → billing-blocked-SE6tySLd.mjs} +1 -1
  5. package/dist/js/bin.mjs +674 -307
  6. package/dist/js/{boot-Q-Kh3nn5.mjs → boot-DD4T-61U.mjs} +4558 -930
  7. package/dist/js/chunk-BbwQpWto.mjs +33 -0
  8. package/dist/js/{client-DabRpc_T.mjs → client--k9cjfkX.mjs} +437 -39
  9. package/dist/js/{client-c4c5MmgN.mjs → client-Btq6bMzX.mjs} +108 -2
  10. package/dist/js/client-Ct0-JZSS.mjs +5 -0
  11. package/dist/js/daemon-CCgNLD0H.mjs +7 -0
  12. package/dist/js/{daemon-D21wQ7DI.mjs → daemon-Do1jU2UF.mjs} +123 -43
  13. package/dist/js/daemon-client-C7nE-lLK.mjs +8 -0
  14. package/dist/js/{daemon-client-DPUNjhBB.mjs → daemon-client-Dvad009G.mjs} +1 -1
  15. package/dist/js/dist-CRtjM7ba.mjs +1750 -0
  16. package/dist/js/forward-C-f04uyE.mjs +208 -0
  17. package/dist/js/{profiler-BkCV__ao.mjs → install-CtAVvERm.mjs} +545 -215
  18. package/dist/js/launcher.mjs +49 -0
  19. package/dist/js/localhost-cert-Bn-UBUmj.mjs +67 -0
  20. package/dist/js/{print-Wakr3GJd.mjs → print-Bx8qUC9U.mjs} +3 -3
  21. package/dist/js/{print-BpuyEfWX.mjs → print-D_UEjdSw.mjs} +257 -35
  22. package/dist/js/{print-share-CKLPmsg0.mjs → print-share-Cz0EO2RK.mjs} +9 -3
  23. package/dist/js/raw-pty-Ci2qFR9F.mjs +5 -0
  24. package/dist/js/{raw-pty-DY4KelZW.mjs → raw-pty-D5PhKZSl.mjs} +1 -1
  25. package/dist/js/{rest-I3imNduB.mjs → rest-B9__Zsuk.mjs} +144 -19
  26. package/dist/js/rest-Dc0EEok3.mjs +6 -0
  27. package/dist/js/tls-cert-BpCaD5AT.mjs +4 -0
  28. package/dist/js/tls-cert-Rua2oV7n.mjs +67 -0
  29. package/package.json +12 -4
  30. package/dist/js/api-DG5W6iwx.mjs +0 -131
  31. package/dist/js/client-BuU34IVE.mjs +0 -5
  32. package/dist/js/daemon-LSDSvMaC.mjs +0 -6
  33. package/dist/js/daemon-client-CUSq-Wuh.mjs +0 -7
  34. package/dist/js/forward-18QoL5dO.mjs +0 -68
  35. package/dist/js/raw-pty-DmdUf4_w.mjs +0 -5
  36. package/dist/js/rest-D29qNkto.mjs +0 -6
  37. /package/dist/js/{billing-blocked-2wju4gC_.mjs → billing-blocked-D3l5kJlX.mjs} +0 -0
  38. /package/dist/js/{http-error-DzyrsLAZ.mjs → http-error-BF2NZZE3.mjs} +0 -0
  39. /package/dist/js/{output-DYzzdXYV.mjs → output-C9mb3sUB.mjs} +0 -0
@@ -1,16 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { S as getConfigPath } from "./print-BpuyEfWX.mjs";
2
+ import { c as version$1 } from "./rest-B9__Zsuk.mjs";
3
+ import { w as getConfigPath } from "./print-D_UEjdSw.mjs";
3
4
  import os from "node:os";
4
5
  import path from "node:path";
5
6
  import { err, ok } from "neverthrow";
6
7
  import { z } from "zod";
7
8
  import fs from "node:fs";
8
9
 
9
- //#region package.json
10
- var name = "skydive-cli";
11
- var version$1 = "0.5.0-beta.9";
12
-
13
- //#endregion
14
10
  //#region src/auth/organization.ts
15
11
  const workspaceSchema = z.object({
16
12
  id: z.string(),
@@ -32,6 +28,32 @@ async function listWorkspaces({ appUrl, sessionToken }) {
32
28
  }
33
29
  }
34
30
  /**
31
+ * Match a workspace selector against a roster the way `workspace switch` does:
32
+ * exact id first, then case-insensitive slug, then case-insensitive name.
33
+ * Returns null when nothing matches. Pure — the caller owns fetching and error
34
+ * messaging.
35
+ */
36
+ function matchWorkspace(workspaces, selector) {
37
+ const needle = selector.trim().toLowerCase();
38
+ return workspaces.find((w) => w.id === selector) ?? workspaces.find((w) => w.slug.toLowerCase() === needle) ?? workspaces.find((w) => w.name.toLowerCase() === needle) ?? null;
39
+ }
40
+ /**
41
+ * Resolve a workspace selector (slug, name, or id) to its id by listing the
42
+ * account's workspaces and matching. Used by any command that takes a
43
+ * `--workspace` flag to scope requests without persisting an active-workspace
44
+ * switch.
45
+ */
46
+ async function resolveWorkspaceId({ appUrl, sessionToken, selector }) {
47
+ const workspaces = await listWorkspaces({
48
+ appUrl,
49
+ sessionToken
50
+ });
51
+ if (workspaces.isErr()) return err(workspaces.error);
52
+ const match = matchWorkspace(workspaces.value, selector);
53
+ if (!match) return err({ message: `No workspace matches "${selector}". Run \`skydive workspace list\` to see available workspaces.` });
54
+ return ok(match);
55
+ }
56
+ /**
35
57
  * Resolve who the current chat session belongs to: the signed-in user's
36
58
  * email/name and the active workspace. Used by `auth status` so a user running
37
59
  * multiple accounts can answer "am I logged into the right one?" without
@@ -125,6 +147,218 @@ async function ensureActiveOrganization({ appUrl, sessionToken }) {
125
147
  });
126
148
  }
127
149
 
150
+ //#endregion
151
+ //#region src/shell/shell-env.ts
152
+ /**
153
+ * Shared, dependency-light shell primitives used by anything that edits the
154
+ * user's shell startup files — `completion install` and `agents alias`. Kept
155
+ * free of yargs and the CLI's output layer so the TUI can import it without
156
+ * dragging command machinery into its bundle or creating a TUI -> commands
157
+ * import cycle.
158
+ */
159
+ const SUPPORTED_SHELLS = [
160
+ "bash",
161
+ "zsh",
162
+ "fish"
163
+ ];
164
+ const defaultInstallEnv = () => ({
165
+ home: os.homedir(),
166
+ platform: process.platform,
167
+ env: process.env,
168
+ exists: fs.existsSync
169
+ });
170
+ /**
171
+ * The shell to act on when the user didn't name one. `$SHELL` is the only
172
+ * signal available: an install runs as a child process, so the invoking
173
+ * shell's own variables (`$ZSH_VERSION`, `$FISH_VERSION`) aren't visible here.
174
+ */
175
+ function detectShell(env = process.env) {
176
+ const shell = env["SHELL"];
177
+ if (!shell) return null;
178
+ const name = path.basename(shell);
179
+ return SUPPORTED_SHELLS.find((candidate) => name === candidate) ?? null;
180
+ }
181
+
182
+ //#endregion
183
+ //#region src/commands/agent-alias.ts
184
+ /**
185
+ * Shell aliases that point a friendly command name at a specific agent's chat.
186
+ *
187
+ * When someone creates an agent named "Ripple", they almost always want to
188
+ * reach it again by typing `ripple`, not `skydive chat --agent <uuid>`. This
189
+ * module writes a managed alias block into the user's shell startup file so
190
+ * `ripple` opens that agent. It reuses the same shell detection and startup-file
191
+ * machinery as `completion install` (imported from ./completion), so the two
192
+ * features agree on which file to touch and how to detect the shell.
193
+ *
194
+ * The alias always targets the agent **id**, never its name: names are not
195
+ * unique and can be renamed, so an id keeps `ripple` pointed at the same agent
196
+ * even if a second "Ripple" is created later or this one is renamed.
197
+ */
198
+ /**
199
+ * Turn a display name into a shell-safe alias token: lowercase, non-alnum runs
200
+ * collapsed to a single hyphen, leading/trailing hyphens trimmed. "Ripple" ->
201
+ * "ripple", "My Agent" -> "my-agent", "Ripple 2.0" -> "ripple-2-0".
202
+ *
203
+ * A leading digit is prefixed with `a-` because bash rejects an alias name that
204
+ * starts with a digit (`alias 2fast=...` is a syntax error). An empty result
205
+ * (a name that was all punctuation) falls back to "agent" so callers always get
206
+ * a usable token to offer or dedupe from.
207
+ */
208
+ function slugifyAliasName(name) {
209
+ const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
210
+ if (!slug) return "agent";
211
+ return /^[0-9]/.test(slug) ? `a-${slug}` : slug;
212
+ }
213
+ /**
214
+ * The alias token to actually install, given a preferred token and the set of
215
+ * names already taken (existing aliases, and — the caller's choice — commands
216
+ * on PATH). Prefers the clean token; on a collision appends the lowest integer
217
+ * that frees it: `ripple` -> `ripple2` -> `ripple3`. Matching is
218
+ * case-insensitive because shells treat `Ripple` and `ripple` as distinct
219
+ * aliases but a user typing one means the other.
220
+ */
221
+ function dedupeAliasName(preferred, taken) {
222
+ const takenLower = new Set(Array.from(taken, (t) => t.trim().toLowerCase()).filter(Boolean));
223
+ if (!takenLower.has(preferred.toLowerCase())) return preferred;
224
+ for (let n = 2;; n += 1) {
225
+ const candidate = `${preferred}${n}`;
226
+ if (!takenLower.has(candidate.toLowerCase())) return candidate;
227
+ }
228
+ }
229
+ /** The startup file an alias block belongs in, per shell. */
230
+ function aliasTarget(shell, deps = defaultInstallEnv()) {
231
+ const { home, platform, env, exists } = deps;
232
+ const join = path.posix.join;
233
+ if (shell === "fish") return join(env["XDG_CONFIG_HOME"] || join(home, ".config"), "fish", "config.fish");
234
+ if (shell === "zsh") return join(env["ZDOTDIR"] || home, ".zshrc");
235
+ const rc = join(home, ".bashrc");
236
+ const profile = join(home, ".bash_profile");
237
+ const preferred = platform === "darwin" ? profile : rc;
238
+ const alternate = platform === "darwin" ? rc : profile;
239
+ if (!exists(preferred) && exists(alternate)) return alternate;
240
+ return preferred;
241
+ }
242
+ const BLOCK_START = "###-begin-skydive-agent-aliases-###";
243
+ const BLOCK_END = "###-end-skydive-agent-aliases-###";
244
+ const aliasLine = (entry) => {
245
+ const ws = entry.workspaceId ? ` --workspace ${entry.workspaceId}` : "";
246
+ return `alias ${entry.alias}='skydive chat --agent ${entry.agentId}${ws} --new' # skydive-agent:${entry.agentId}`;
247
+ };
248
+ /**
249
+ * Parse the alias entries already inside the managed block. Used to merge a new
250
+ * alias with existing ones and to answer "is this token taken?". Tolerant of
251
+ * hand-edits: only well-formed lines carrying our trailing marker are read
252
+ * back; anything else in the block is preserved verbatim on rewrite.
253
+ */
254
+ function parseAliasBlock(existing) {
255
+ const start = existing.indexOf(BLOCK_START);
256
+ const end = existing.indexOf(BLOCK_END);
257
+ if (start === -1 || end <= start) return [];
258
+ const body = existing.slice(start + 35, end);
259
+ const entries = [];
260
+ for (const line of body.split("\n")) {
261
+ const match = line.match(/^alias\s+([^=]+)=.*# skydive-agent:([0-9a-f-]+)\s*$/i);
262
+ if (match?.[1] && match[2]) {
263
+ const wsMatch = line.match(/--workspace\s+(\S+)/);
264
+ entries.push({
265
+ alias: match[1].trim(),
266
+ agentId: match[2],
267
+ workspaceId: wsMatch?.[1]
268
+ });
269
+ }
270
+ }
271
+ return entries;
272
+ }
273
+ /**
274
+ * Merge `entry` into the managed alias block of `existing`, returning the new
275
+ * file contents. Idempotent: re-installing the same alias for the same agent is
276
+ * a no-op edit; a new agent id adds a line; an existing agent id has its alias
277
+ * token refreshed. The block is created if absent.
278
+ */
279
+ function spliceAliasBlock(existing, entry) {
280
+ const current = parseAliasBlock(existing).filter((e) => e.agentId !== entry.agentId);
281
+ current.push(entry);
282
+ current.sort((a, b) => a.alias.localeCompare(b.alias));
283
+ const block = [
284
+ BLOCK_START,
285
+ "# skydive agent aliases, managed by `skydive agents alias`. Edits between",
286
+ "# these markers are overwritten when an alias is added or refreshed.",
287
+ ...current.map(aliasLine),
288
+ BLOCK_END
289
+ ].join("\n");
290
+ const start = existing.indexOf(BLOCK_START);
291
+ const endMarker = BLOCK_END;
292
+ const end = existing.indexOf(endMarker);
293
+ if (start !== -1 && end > start) return `${existing.slice(0, start)}${block}\n${existing.slice(end + 33).replace(/^\n/, "")}`;
294
+ return `${existing}${existing === "" || existing.endsWith("\n\n") ? "" : "\n"}\n${block}\n`;
295
+ }
296
+ /**
297
+ * Write (or refresh) an agent alias in the user's shell startup file. Returns
298
+ * the resolved shell, file, and the alias token actually used. Does not dedupe
299
+ * on its own — pass a token already reconciled with `takenAliasNames` so the
300
+ * caller controls the suggestion UX.
301
+ */
302
+ function installAgentAlias(input, deps = defaultInstallEnv()) {
303
+ const target = aliasTarget(input.shell, deps);
304
+ const existing = deps.exists(target) ? fs.readFileSync(target, "utf8") : "";
305
+ const contents = spliceAliasBlock(existing, {
306
+ alias: input.alias,
307
+ agentId: input.agentId,
308
+ workspaceId: input.workspaceId
309
+ });
310
+ fs.mkdirSync(path.dirname(target), { recursive: true });
311
+ fs.writeFileSync(target, contents, "utf8");
312
+ return {
313
+ shell: input.shell,
314
+ path: target,
315
+ alias: input.alias,
316
+ agentId: input.agentId,
317
+ updated: existing.includes(BLOCK_START)
318
+ };
319
+ }
320
+ /**
321
+ * Alias tokens already defined in our managed block of the given shell's
322
+ * startup file. The dedupe source: a fresh install shouldn't collide with an
323
+ * alias we wrote for another agent. (We don't try to read the user's own
324
+ * hand-written aliases or PATH — a shell child process can't see the parent's
325
+ * live alias table, and probing PATH is noisy; the dedupe covers our own
326
+ * blocks, which is where real collisions come from in practice.)
327
+ */
328
+ function takenAliasNames(shell, deps = defaultInstallEnv()) {
329
+ const target = aliasTarget(shell, deps);
330
+ if (!deps.exists(target)) return [];
331
+ return parseAliasBlock(fs.readFileSync(target, "utf8")).map((e) => e.alias);
332
+ }
333
+ /** What the user must do before the alias works in the shell they're in. */
334
+ function aliasActivationHint(result) {
335
+ return result.shell === "fish" ? "Open a new fish shell to use it." : `Open a new shell, or run: source ${result.path}`;
336
+ }
337
+
338
+ //#endregion
339
+ //#region src/chat/import-seed.ts
340
+ /** Map `process.platform` to the OS family used by the seed prompts. Every
341
+ * non-`win32` platform Node runs on (darwin, linux, the BSDs) is POSIX. */
342
+ function machineOsFromPlatform(platform) {
343
+ return platform === "win32" ? "windows" : "posix";
344
+ }
345
+ /** One clause describing the machine's shell/paths so the agent's discovery
346
+ * sweep uses the right conventions instead of guessing. */
347
+ function osHint(os) {
348
+ return os === "windows" ? "This machine is Windows, so use PowerShell and Windows paths (%USERPROFILE%, backslashes)." : "This machine is POSIX (macOS/Linux), so use a POSIX shell and paths (~, forward slashes).";
349
+ }
350
+ /**
351
+ * The first message of an explicit `skydive import` conversation, sent as the
352
+ * user. Frames the migration and points the agent at its import-config skill.
353
+ */
354
+ function buildImportSeedPrompt(projectDir, os) {
355
+ return [
356
+ "I'm migrating from another coding agent. Import my setup from this machine.",
357
+ "",
358
+ `Use your import-config skill. I ran this from \`${projectDir}\`, so start there and in my home directory. ${osHint(os)} Don't assume one tool — do the discovery sweep so you catch whatever I actually use (Claude Code, Cursor, Codex, Gemini CLI, Copilot, Windsurf, Cline, OpenCode, Aider, and any nested AGENTS.md). Show me the plan first: everything you found, what you'll bring over, where it lands in you, and anything you're leaving out (credentials especially). Then wait for my OK before committing anything.`
359
+ ].join("\n");
360
+ }
361
+
128
362
  //#endregion
129
363
  //#region src/chat/tui/theme.ts
130
364
  const tokyonight = {
@@ -132,6 +366,7 @@ const tokyonight = {
132
366
  label: "Tokyo Night",
133
367
  mode: "dark",
134
368
  palette: {
369
+ cursor: "#ffffff",
135
370
  fg: "#c0caf5",
136
371
  muted: "#7a7a7a",
137
372
  dim: "#565f89",
@@ -160,6 +395,7 @@ const tokyonightDay = {
160
395
  label: "Tokyo Night Day",
161
396
  mode: "light",
162
397
  palette: {
398
+ cursor: "#737373",
163
399
  fg: "#3760bf",
164
400
  muted: "#848cb5",
165
401
  dim: "#9da3c2",
@@ -188,6 +424,7 @@ const catppuccinMocha = {
188
424
  label: "Catppuccin Mocha",
189
425
  mode: "dark",
190
426
  palette: {
427
+ cursor: "#ffffff",
191
428
  fg: "#cdd6f4",
192
429
  muted: "#7f849c",
193
430
  dim: "#6c7086",
@@ -216,6 +453,7 @@ const catppuccinLatte = {
216
453
  label: "Catppuccin Latte",
217
454
  mode: "light",
218
455
  palette: {
456
+ cursor: "#737373",
219
457
  fg: "#4c4f69",
220
458
  muted: "#8c8fa1",
221
459
  dim: "#9ca0b0",
@@ -244,6 +482,7 @@ const gruvboxDark = {
244
482
  label: "Gruvbox Dark",
245
483
  mode: "dark",
246
484
  palette: {
485
+ cursor: "#ffffff",
247
486
  fg: "#ebdbb2",
248
487
  muted: "#928374",
249
488
  dim: "#7c6f64",
@@ -272,6 +511,7 @@ const gruvboxLight = {
272
511
  label: "Gruvbox Light",
273
512
  mode: "light",
274
513
  palette: {
514
+ cursor: "#737373",
275
515
  fg: "#3c3836",
276
516
  muted: "#928374",
277
517
  dim: "#a89984",
@@ -300,6 +540,7 @@ const solarizedDark = {
300
540
  label: "Solarized Dark",
301
541
  mode: "dark",
302
542
  palette: {
543
+ cursor: "#ffffff",
303
544
  fg: "#93a1a1",
304
545
  muted: "#586e75",
305
546
  dim: "#586e75",
@@ -328,6 +569,7 @@ const solarizedLight = {
328
569
  label: "Solarized Light",
329
570
  mode: "light",
330
571
  palette: {
572
+ cursor: "#737373",
331
573
  fg: "#657b83",
332
574
  muted: "#839496",
333
575
  dim: "#93a1a1",
@@ -356,6 +598,7 @@ const nord = {
356
598
  label: "Nord",
357
599
  mode: "dark",
358
600
  palette: {
601
+ cursor: "#ffffff",
359
602
  fg: "#d8dee9",
360
603
  muted: "#616e88",
361
604
  dim: "#4c566a",
@@ -384,6 +627,7 @@ const dracula = {
384
627
  label: "Dracula",
385
628
  mode: "dark",
386
629
  palette: {
630
+ cursor: "#ffffff",
387
631
  fg: "#f8f8f2",
388
632
  muted: "#6272a4",
389
633
  dim: "#6272a4",
@@ -412,6 +656,7 @@ const oneDark = {
412
656
  label: "One Dark",
413
657
  mode: "dark",
414
658
  palette: {
659
+ cursor: "#ffffff",
415
660
  fg: "#abb2bf",
416
661
  muted: "#5c6370",
417
662
  dim: "#5c6370",
@@ -440,6 +685,7 @@ const oneLight = {
440
685
  label: "One Light",
441
686
  mode: "light",
442
687
  palette: {
688
+ cursor: "#737373",
443
689
  fg: "#383a42",
444
690
  muted: "#a0a1a7",
445
691
  dim: "#a0a1a7",
@@ -468,6 +714,7 @@ const rosePine = {
468
714
  label: "Rosé Pine",
469
715
  mode: "dark",
470
716
  palette: {
717
+ cursor: "#ffffff",
471
718
  fg: "#e0def4",
472
719
  muted: "#908caa",
473
720
  dim: "#6e6a86",
@@ -496,6 +743,7 @@ const rosePineDawn = {
496
743
  label: "Rosé Pine Dawn",
497
744
  mode: "light",
498
745
  palette: {
746
+ cursor: "#737373",
499
747
  fg: "#575279",
500
748
  muted: "#797593",
501
749
  dim: "#9893a5",
@@ -524,6 +772,7 @@ const everforestDark = {
524
772
  label: "Everforest Dark",
525
773
  mode: "dark",
526
774
  palette: {
775
+ cursor: "#ffffff",
527
776
  fg: "#d3c6aa",
528
777
  muted: "#859289",
529
778
  dim: "#7a8478",
@@ -552,6 +801,7 @@ const everforestLight = {
552
801
  label: "Everforest Light",
553
802
  mode: "light",
554
803
  palette: {
804
+ cursor: "#737373",
555
805
  fg: "#5c6a72",
556
806
  muted: "#939f91",
557
807
  dim: "#a6b0a0",
@@ -580,6 +830,7 @@ const githubDark = {
580
830
  label: "GitHub Dark",
581
831
  mode: "dark",
582
832
  palette: {
833
+ cursor: "#ffffff",
583
834
  fg: "#c9d1d9",
584
835
  muted: "#8b949e",
585
836
  dim: "#6e7681",
@@ -608,6 +859,7 @@ const githubLight = {
608
859
  label: "GitHub Light",
609
860
  mode: "light",
610
861
  palette: {
862
+ cursor: "#737373",
611
863
  fg: "#24292f",
612
864
  muted: "#57606a",
613
865
  dim: "#8c959f",
@@ -636,6 +888,7 @@ const kanagawa = {
636
888
  label: "Kanagawa",
637
889
  mode: "dark",
638
890
  palette: {
891
+ cursor: "#ffffff",
639
892
  fg: "#dcd7ba",
640
893
  muted: "#727169",
641
894
  dim: "#54546d",
@@ -664,6 +917,7 @@ const cursorDark = {
664
917
  label: "Cursor Dark",
665
918
  mode: "dark",
666
919
  palette: {
920
+ cursor: "#ffffff",
667
921
  background: "#181818",
668
922
  fg: "#d4d4d4",
669
923
  muted: "#898989",
@@ -725,6 +979,7 @@ const monoTheme = {
725
979
  label: "No color",
726
980
  mode: "dark",
727
981
  palette: {
982
+ cursor: void 0,
728
983
  fg: void 0,
729
984
  muted: void 0,
730
985
  dim: void 0,
@@ -807,6 +1062,277 @@ function applyTheme(def) {
807
1062
  version++;
808
1063
  }
809
1064
 
1065
+ //#endregion
1066
+ //#region src/profiling/profiler.ts
1067
+ /**
1068
+ * Session diagnostics. Every invocation records enough context to reconstruct
1069
+ * what happened after the fact. SKYDIVE_PROFILE can select a custom directory
1070
+ * or disable recording with `0`.
1071
+ *
1072
+ * Default sessions live under the CLI's system state directory. Explicit
1073
+ * profiles keep the original cwd/custom-path behavior and add a CPU profile.
1074
+ * Each directory is designed to be handed to an agent (or a human) and read
1075
+ * without special tooling:
1076
+ *
1077
+ * network.ndjson every fetch: method, url, status, timing, content type
1078
+ * state.ndjson TUI state transitions (screen changes, store updates)
1079
+ * commits.ndjson React commits: which subtree rendered, when, how long
1080
+ * render.ndjson OpenTUI renderer samples: fps, frame times, cells drawn
1081
+ * render.json final renderer stats dump (full frame-time series)
1082
+ * cpu-<pid>.cpuprofile V8 CPU profile (Node-run commands only)
1083
+ * meta-<pid>.json argv, versions, runtime, exit code, wall time
1084
+ *
1085
+ * Every ndjson event carries a wall-clock `t` (epoch ms) and `pid`, so
1086
+ * records from the Node parent and the Bun-re-exec'd chat TUI land in the
1087
+ * same files and stay correlatable on one clock. The profile directory is
1088
+ * created by the first process and shared with children through
1089
+ * SKYDIVE_PROFILE_DIR (the chat re-exec inherits the environment).
1090
+ *
1091
+ */
1092
+ const ENV_FLAG = "SKYDIVE_PROFILE";
1093
+ const ENV_DIR = "SKYDIVE_PROFILE_DIR";
1094
+ const DEFAULT_SESSION_LIMIT = 20;
1095
+ let activeDir = null;
1096
+ let startedAtMs = 0;
1097
+ /**
1098
+ * Buffered event lines per stream, flushed asynchronously. Events are
1099
+ * appended to an in-memory buffer and written with fs.promises off the
1100
+ * hot path, so recording never blocks the TUI's event loop — high-rate
1101
+ * streams (React commits, renderer samples) stay cheap. Whatever is
1102
+ * still buffered when the process exits is drained synchronously in the
1103
+ * exit handler, where async I/O would never flush.
1104
+ */
1105
+ const pendingLines = /* @__PURE__ */ new Map();
1106
+ let flushScheduled = false;
1107
+ let flushing = Promise.resolve();
1108
+ function drainBuffersSync() {
1109
+ if (activeDir === null) return;
1110
+ for (const [stream, lines] of pendingLines) {
1111
+ if (lines.length === 0) continue;
1112
+ pendingLines.set(stream, []);
1113
+ try {
1114
+ fs.appendFileSync(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1115
+ } catch (_error) {}
1116
+ }
1117
+ }
1118
+ function scheduleFlush() {
1119
+ if (flushScheduled || activeDir === null) return;
1120
+ flushScheduled = true;
1121
+ setTimeout(() => {
1122
+ flushScheduled = false;
1123
+ flushing = flushing.then(async () => {
1124
+ if (activeDir === null) return;
1125
+ for (const [stream, lines] of pendingLines) {
1126
+ if (lines.length === 0) continue;
1127
+ pendingLines.set(stream, []);
1128
+ try {
1129
+ await fs.promises.appendFile(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1130
+ } catch (_error) {}
1131
+ }
1132
+ });
1133
+ }, 100).unref?.();
1134
+ }
1135
+ function profilingEnabled() {
1136
+ return process.env[ENV_FLAG] !== "0";
1137
+ }
1138
+ function explicitProfilingEnabled() {
1139
+ const value = process.env[ENV_FLAG];
1140
+ return value !== void 0 && value !== "" && value !== "0";
1141
+ }
1142
+ function profileDir() {
1143
+ return activeDir;
1144
+ }
1145
+ function automaticLogsDir(platform, env, home) {
1146
+ const appName = env["SKYDIVE_CONFIG_NAME"] ?? "skydive";
1147
+ if (platform === "darwin") return path.join(home, "Library", "Logs", appName);
1148
+ if (platform === "linux") {
1149
+ const stateHome = env["XDG_STATE_HOME"] || path.join(home, ".local", "state");
1150
+ return path.join(stateHome, appName);
1151
+ }
1152
+ return path.join(path.dirname(getConfigPath()), "logs");
1153
+ }
1154
+ function getAutomaticLogsDir() {
1155
+ return automaticLogsDir(process.platform, process.env, os.homedir());
1156
+ }
1157
+ function sanitizeToken(token) {
1158
+ return token.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
1159
+ }
1160
+ function commandName(argv) {
1161
+ return argv.find((arg) => !arg.startsWith("-")) ?? "chat";
1162
+ }
1163
+ function sessionName(argv) {
1164
+ const command = commandName(argv);
1165
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19)}-${sanitizeToken(command)}-${process.pid}`;
1166
+ }
1167
+ /**
1168
+ * Automatic logs retain command shape, not values. Positional arguments and
1169
+ * flag values can contain prompts, secrets, paths, and other private data.
1170
+ */
1171
+ function safeAutomaticArgv(argv) {
1172
+ return [commandName(argv), ...argv.filter((arg) => arg.startsWith("-")).map((arg) => arg.split("=", 1)[0] ?? arg)];
1173
+ }
1174
+ /** Keeps automatic diagnostics bounded without touching explicit profiles. */
1175
+ function pruneAutomaticSessions(root, activeSession) {
1176
+ try {
1177
+ const sessions = fs.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== activeSession).map((entry) => entry.name).sort().reverse();
1178
+ for (const stale of sessions.slice(DEFAULT_SESSION_LIMIT - 1)) fs.rmSync(path.join(root, stale), {
1179
+ recursive: true,
1180
+ force: true
1181
+ });
1182
+ } catch (_error) {}
1183
+ }
1184
+ /**
1185
+ * Appends one event to a stream file. Buffered and written asynchronously
1186
+ * so recording never blocks the event loop; the exit handler drains any
1187
+ * remainder synchronously so abrupt exits still keep their events.
1188
+ */
1189
+ function record(stream, event) {
1190
+ if (activeDir === null) return;
1191
+ const line = JSON.stringify({
1192
+ t: Date.now(),
1193
+ pid: process.pid,
1194
+ ...event
1195
+ });
1196
+ const lines = pendingLines.get(stream);
1197
+ if (lines === void 0) pendingLines.set(stream, [line]);
1198
+ else lines.push(line);
1199
+ scheduleFlush();
1200
+ }
1201
+ /** Writes a JSON artifact (non-append) into the profile directory. */
1202
+ function writeArtifact(name, data) {
1203
+ if (activeDir === null) return;
1204
+ try {
1205
+ fs.writeFileSync(path.join(activeDir, name), JSON.stringify(data, null, 2));
1206
+ } catch (_error) {}
1207
+ }
1208
+ function safeAutomaticUrl(value) {
1209
+ try {
1210
+ const url = new URL(value);
1211
+ for (const key of url.searchParams.keys()) url.searchParams.set(key, "<redacted>");
1212
+ url.hash = "";
1213
+ return url.href;
1214
+ } catch (_error) {
1215
+ return "<invalid-url>";
1216
+ }
1217
+ }
1218
+ /**
1219
+ * Patches globalThis.fetch to record request timing. A wrapper (not
1220
+ * undici's diagnostics_channel) because chat re-execs under Bun, where
1221
+ * fetch is Bun-native and undici events never fire; the wrapper behaves
1222
+ * identically in both runtimes.
1223
+ */
1224
+ function installFetchRecorder(explicit) {
1225
+ const original = globalThis.fetch;
1226
+ const wrapped = async (...args) => {
1227
+ const [input, init] = args;
1228
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1229
+ const method = init?.method ?? (input instanceof Request ? input.method : "GET");
1230
+ const started = Date.now();
1231
+ try {
1232
+ const res = await original(...args);
1233
+ const contentType = res.headers.get("content-type") ?? "";
1234
+ record("network", {
1235
+ method,
1236
+ url: explicit ? url : safeAutomaticUrl(url),
1237
+ status: res.status,
1238
+ durationMs: Date.now() - started,
1239
+ contentType,
1240
+ streamed: contentType.includes("text/event-stream")
1241
+ });
1242
+ return res;
1243
+ } catch (err) {
1244
+ record("network", {
1245
+ method,
1246
+ url: explicit ? url : safeAutomaticUrl(url),
1247
+ status: 0,
1248
+ durationMs: Date.now() - started,
1249
+ error: err instanceof Error ? err.message : String(err)
1250
+ });
1251
+ throw err;
1252
+ }
1253
+ };
1254
+ globalThis.fetch = Object.assign(wrapped, original);
1255
+ }
1256
+ function isBunRuntime() {
1257
+ return typeof process !== "undefined" && "bun" in process.versions;
1258
+ }
1259
+ /**
1260
+ * Starts the V8 CPU profiler via node:inspector. Node-run commands only —
1261
+ * Bun does not implement the inspector Profiler domain, so the chat TUI
1262
+ * skips this artifact.
1263
+ */
1264
+ function startCpuProfile() {
1265
+ if (isBunRuntime()) return;
1266
+ (async () => {
1267
+ try {
1268
+ const { Session } = await import("node:inspector/promises");
1269
+ const session = new Session();
1270
+ session.connect();
1271
+ await session.post("Profiler.enable");
1272
+ await session.post("Profiler.start");
1273
+ process.once("beforeExit", () => {
1274
+ (async () => {
1275
+ try {
1276
+ const { profile } = await session.post("Profiler.stop");
1277
+ writeArtifact(`cpu-${process.pid}.cpuprofile`, profile);
1278
+ session.disconnect();
1279
+ } catch (_error) {}
1280
+ })();
1281
+ });
1282
+ } catch (_error) {}
1283
+ })();
1284
+ }
1285
+ /**
1286
+ * Starts diagnostics unless SKYDIVE_PROFILE=0. Creates a system-state session
1287
+ * directory (or joins the one a parent process created), patches fetch, and
1288
+ * registers the exit-time metadata dump. Explicit profiles also capture CPU.
1289
+ */
1290
+ function maybeStartProfiling(argv, cliVersion) {
1291
+ if (!profilingEnabled() || activeDir !== null) return;
1292
+ const inherited = process.env[ENV_DIR];
1293
+ const explicit = explicitProfilingEnabled();
1294
+ let automaticRoot = null;
1295
+ let automaticSession = null;
1296
+ if (inherited !== void 0 && inherited !== "") activeDir = inherited;
1297
+ else if (explicit) {
1298
+ const flagValue = process.env[ENV_FLAG] ?? "1";
1299
+ activeDir = flagValue === "1" || flagValue.toLowerCase() === "true" ? path.resolve(process.cwd(), `skydive-profile-${sessionName(argv)}`) : path.resolve(process.cwd(), flagValue);
1300
+ process.env[ENV_DIR] = activeDir;
1301
+ } else {
1302
+ automaticRoot = getAutomaticLogsDir();
1303
+ automaticSession = sessionName(argv);
1304
+ activeDir = path.join(automaticRoot, automaticSession);
1305
+ process.env[ENV_DIR] = activeDir;
1306
+ }
1307
+ try {
1308
+ fs.mkdirSync(activeDir, {
1309
+ recursive: true,
1310
+ mode: 448
1311
+ });
1312
+ if (automaticRoot !== null && automaticSession !== null) pruneAutomaticSessions(automaticRoot, automaticSession);
1313
+ } catch (_error) {
1314
+ activeDir = null;
1315
+ return;
1316
+ }
1317
+ startedAtMs = Date.now();
1318
+ installFetchRecorder(explicit);
1319
+ if (explicit) startCpuProfile();
1320
+ process.on("exit", (code) => {
1321
+ drainBuffersSync();
1322
+ writeArtifact(`meta-${process.pid}.json`, {
1323
+ argv: explicit ? argv : safeAutomaticArgv(argv),
1324
+ cliVersion,
1325
+ runtime: isBunRuntime() ? `bun ${process.versions["bun"]}` : `node ${process.version}`,
1326
+ platform: process.platform,
1327
+ pid: process.pid,
1328
+ exitCode: code,
1329
+ startedAt: new Date(startedAtMs).toISOString(),
1330
+ wallTimeMs: Date.now() - startedAtMs
1331
+ });
1332
+ if (explicit && process.env[ENV_DIR] === activeDir && inherited === void 0) process.stderr.write(`\nprofile written to ${activeDir}\n`);
1333
+ });
1334
+ }
1335
+
810
1336
  //#endregion
811
1337
  //#region src/brand.ts
812
1338
  const MARK_CELLS = [
@@ -907,6 +1433,17 @@ const WORDMARK = [
907
1433
  "███████║██║ ██╗ ██║ ██████╔╝██║ ╚████╔╝ ███████╗",
908
1434
  "╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝"
909
1435
  ];
1436
+ /** Columns between the pinwheel and the wordmark. */
1437
+ const SPLASH_GAP = 3;
1438
+ /** Columns the full mark + wordmark occupies (no chrome). */
1439
+ const SPLASH_WIDTH = MARK_WIDTH + SPLASH_GAP + WORDMARK.reduce((max, line) => Math.max(max, line.length), 0);
1440
+ /** Extra columns around the splash: `skydive --help` indents by 2, the TUI
1441
+ * app pads 1 on each side. Same number, so one threshold covers both. */
1442
+ const SPLASH_CHROME = 2;
1443
+ /** Whether the full splash fits on one line of `columns` without wrapping. */
1444
+ function splashFitsWidth(columns) {
1445
+ return columns >= SPLASH_WIDTH + SPLASH_CHROME;
1446
+ }
910
1447
  const RESET = "\x1B[0m";
911
1448
  function hexToRgb(hex) {
912
1449
  const n = Number.parseInt(hex.slice(1), 16);
@@ -933,7 +1470,7 @@ function markLinesAnsi() {
933
1470
  function brandHelpArt(stream = process.stdout) {
934
1471
  if (!stream.isTTY) return "";
935
1472
  const truecolor = (typeof stream.getColorDepth === "function" ? stream.getColorDepth() : 1) >= 24;
936
- if ((stream.columns ?? 80) < 66) return truecolor ? `\n ${sgr("✦", BRAND_ACCENT)} Skydive\n` : "\n ✦ Skydive\n";
1473
+ if (!splashFitsWidth(stream.columns ?? 80)) return truecolor ? `\n ${sgr("✦", BRAND_ACCENT)} Skydive\n` : "\n ✦ Skydive\n";
937
1474
  if (!truecolor) return `\n${WORDMARK.map((l) => ` ${l}`).join("\n")}\n`;
938
1475
  const mark = markLinesAnsi();
939
1476
  const word = [...WORDMARK];
@@ -1057,211 +1594,4 @@ function printFatalNotice(file, memory) {
1057
1594
  }
1058
1595
 
1059
1596
  //#endregion
1060
- //#region src/profiling/profiler.ts
1061
- /**
1062
- * Opt-in session profiling. When SKYDIVE_PROFILE is set, an invocation
1063
- * records everything needed to reconstruct what happened performance-wise —
1064
- * designed so the whole directory can be handed to an agent (or a human)
1065
- * and read without special tooling:
1066
- *
1067
- * network.ndjson every fetch: method, url, status, timing, content type
1068
- * state.ndjson TUI state transitions (screen changes, store updates)
1069
- * commits.ndjson React commits: which subtree rendered, when, how long
1070
- * render.ndjson OpenTUI renderer samples: fps, frame times, cells drawn
1071
- * render.json final renderer stats dump (full frame-time series)
1072
- * cpu-<pid>.cpuprofile V8 CPU profile (Node-run commands only)
1073
- * meta-<pid>.json argv, versions, runtime, exit code, wall time
1074
- *
1075
- * Every ndjson event carries a wall-clock `t` (epoch ms) and `pid`, so
1076
- * records from the Node parent and the Bun-re-exec'd chat TUI land in the
1077
- * same files and stay correlatable on one clock. The profile directory is
1078
- * created by the first process and shared with children through
1079
- * SKYDIVE_PROFILE_DIR (the chat re-exec inherits the environment).
1080
- *
1081
- * Inert unless SKYDIVE_PROFILE is set: no patched fetch, no subscriptions,
1082
- * no inspector session.
1083
- */
1084
- const ENV_FLAG = "SKYDIVE_PROFILE";
1085
- const ENV_DIR = "SKYDIVE_PROFILE_DIR";
1086
- let activeDir = null;
1087
- let startedAtMs = 0;
1088
- /**
1089
- * Buffered event lines per stream, flushed asynchronously. Events are
1090
- * appended to an in-memory buffer and written with fs.promises off the
1091
- * hot path, so recording never blocks the TUI's event loop — high-rate
1092
- * streams (React commits, renderer samples) stay cheap. Whatever is
1093
- * still buffered when the process exits is drained synchronously in the
1094
- * exit handler, where async I/O would never flush.
1095
- */
1096
- const pendingLines = /* @__PURE__ */ new Map();
1097
- let flushScheduled = false;
1098
- let flushing = Promise.resolve();
1099
- function drainBuffersSync() {
1100
- if (activeDir === null) return;
1101
- for (const [stream, lines] of pendingLines) {
1102
- if (lines.length === 0) continue;
1103
- pendingLines.set(stream, []);
1104
- try {
1105
- fs.appendFileSync(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1106
- } catch (_error) {}
1107
- }
1108
- }
1109
- function scheduleFlush() {
1110
- if (flushScheduled || activeDir === null) return;
1111
- flushScheduled = true;
1112
- setTimeout(() => {
1113
- flushScheduled = false;
1114
- flushing = flushing.then(async () => {
1115
- if (activeDir === null) return;
1116
- for (const [stream, lines] of pendingLines) {
1117
- if (lines.length === 0) continue;
1118
- pendingLines.set(stream, []);
1119
- try {
1120
- await fs.promises.appendFile(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1121
- } catch (_error) {}
1122
- }
1123
- });
1124
- }, 100).unref?.();
1125
- }
1126
- function profilingEnabled() {
1127
- const v = process.env[ENV_FLAG];
1128
- return v !== void 0 && v !== "" && v !== "0";
1129
- }
1130
- function sanitizeToken(token) {
1131
- return token.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
1132
- }
1133
- /**
1134
- * Appends one event to a stream file. Buffered and written asynchronously
1135
- * so recording never blocks the event loop; the exit handler drains any
1136
- * remainder synchronously so abrupt exits still keep their events.
1137
- */
1138
- function record(stream, event) {
1139
- if (activeDir === null) return;
1140
- const line = JSON.stringify({
1141
- t: Date.now(),
1142
- pid: process.pid,
1143
- ...event
1144
- });
1145
- const lines = pendingLines.get(stream);
1146
- if (lines === void 0) pendingLines.set(stream, [line]);
1147
- else lines.push(line);
1148
- scheduleFlush();
1149
- }
1150
- /** Writes a JSON artifact (non-append) into the profile directory. */
1151
- function writeArtifact(name, data) {
1152
- if (activeDir === null) return;
1153
- try {
1154
- fs.writeFileSync(path.join(activeDir, name), JSON.stringify(data, null, 2));
1155
- } catch (_error) {}
1156
- }
1157
- /**
1158
- * Patches globalThis.fetch to record request timing. A wrapper (not
1159
- * undici's diagnostics_channel) because chat re-execs under Bun, where
1160
- * fetch is Bun-native and undici events never fire; the wrapper behaves
1161
- * identically in both runtimes.
1162
- */
1163
- function installFetchRecorder() {
1164
- const original = globalThis.fetch;
1165
- const wrapped = async (...args) => {
1166
- const [input, init] = args;
1167
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1168
- const method = init?.method ?? (input instanceof Request ? input.method : "GET");
1169
- const started = Date.now();
1170
- try {
1171
- const res = await original(...args);
1172
- const contentType = res.headers.get("content-type") ?? "";
1173
- record("network", {
1174
- method,
1175
- url,
1176
- status: res.status,
1177
- durationMs: Date.now() - started,
1178
- contentType,
1179
- streamed: contentType.includes("text/event-stream")
1180
- });
1181
- return res;
1182
- } catch (err) {
1183
- record("network", {
1184
- method,
1185
- url,
1186
- status: 0,
1187
- durationMs: Date.now() - started,
1188
- error: err instanceof Error ? err.message : String(err)
1189
- });
1190
- throw err;
1191
- }
1192
- };
1193
- globalThis.fetch = Object.assign(wrapped, original);
1194
- }
1195
- function isBunRuntime() {
1196
- return typeof process !== "undefined" && "bun" in process.versions;
1197
- }
1198
- /**
1199
- * Starts the V8 CPU profiler via node:inspector. Node-run commands only —
1200
- * Bun does not implement the inspector Profiler domain, so the chat TUI
1201
- * skips this artifact.
1202
- */
1203
- function startCpuProfile() {
1204
- if (isBunRuntime()) return;
1205
- (async () => {
1206
- try {
1207
- const { Session } = await import("node:inspector/promises");
1208
- const session = new Session();
1209
- session.connect();
1210
- await session.post("Profiler.enable");
1211
- await session.post("Profiler.start");
1212
- process.once("beforeExit", () => {
1213
- (async () => {
1214
- try {
1215
- const { profile } = await session.post("Profiler.stop");
1216
- writeArtifact(`cpu-${process.pid}.cpuprofile`, profile);
1217
- session.disconnect();
1218
- } catch (_error) {}
1219
- })();
1220
- });
1221
- } catch (_error) {}
1222
- })();
1223
- }
1224
- /**
1225
- * Activates profiling for this process if SKYDIVE_PROFILE is set. Creates
1226
- * the profile directory (or joins the one a parent process created),
1227
- * patches fetch, starts the CPU profiler, and registers the exit-time
1228
- * meta dump. Call once, as early as possible.
1229
- */
1230
- function maybeStartProfiling(argv, cliVersion) {
1231
- if (!profilingEnabled() || activeDir !== null) return;
1232
- const inherited = process.env[ENV_DIR];
1233
- if (inherited !== void 0 && inherited !== "") activeDir = inherited;
1234
- else {
1235
- const flagValue = process.env[ENV_FLAG] ?? "1";
1236
- const command = argv.find((a) => !a.startsWith("-")) ?? "chat";
1237
- const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
1238
- activeDir = flagValue === "1" || flagValue.toLowerCase() === "true" ? path.resolve(process.cwd(), `skydive-profile-${sanitizeToken(command)}-${stamp}`) : path.resolve(process.cwd(), flagValue);
1239
- process.env[ENV_DIR] = activeDir;
1240
- }
1241
- try {
1242
- fs.mkdirSync(activeDir, { recursive: true });
1243
- } catch (_error) {
1244
- activeDir = null;
1245
- return;
1246
- }
1247
- startedAtMs = Date.now();
1248
- installFetchRecorder();
1249
- startCpuProfile();
1250
- process.on("exit", (code) => {
1251
- drainBuffersSync();
1252
- writeArtifact(`meta-${process.pid}.json`, {
1253
- argv,
1254
- cliVersion,
1255
- runtime: isBunRuntime() ? `bun ${process.versions["bun"]}` : `node ${process.version}`,
1256
- platform: process.platform,
1257
- pid: process.pid,
1258
- exitCode: code,
1259
- startedAt: new Date(startedAtMs).toISOString(),
1260
- wallTimeMs: Date.now() - startedAtMs
1261
- });
1262
- if (process.env[ENV_DIR] === activeDir && inherited === void 0) process.stderr.write(`\nprofile written to ${activeDir}\n`);
1263
- });
1264
- }
1265
-
1266
- //#endregion
1267
- export { ensureActiveOrganization as C, setActiveWorkspace as D, listWorkspaces as E, name as O, themesForMode as S, getSessionIdentity as T, themeForMode as _, installCrashHandler as a, themeVersion as b, MARK_CELLS as c, DEFAULT_THEME_ID as d, applyTheme as f, theme as g, noColorRequested as h, writeArtifact as i, version$1 as k, WORDMARK as l, monoTheme as m, profilingEnabled as n, buildCrashReport as o, findTheme as p, record as r, writeCrashReport as s, maybeStartProfiling as t, brandHelpArt as u, themeMode as v, getActiveWorkspaceId as w, themes as x, themeModeFromColorFgBg as y };
1597
+ export { installAgentAlias as A, resolveWorkspaceId as B, themeVersion as C, machineOsFromPlatform as D, buildImportSeedPrompt as E, detectShell as F, ensureActiveOrganization as I, getActiveWorkspaceId as L, takenAliasNames as M, SUPPORTED_SHELLS as N, aliasActivationHint as O, defaultInstallEnv as P, getSessionIdentity as R, themeModeFromColorFgBg as S, themesForMode as T, setActiveWorkspace as V, monoTheme as _, WORDMARK as a, themeForMode as b, getAutomaticLogsDir as c, profilingEnabled as d, record as f, findTheme as g, applyTheme as h, MARK_CELLS as i, slugifyAliasName as j, dedupeAliasName as k, maybeStartProfiling as l, DEFAULT_THEME_ID as m, buildCrashReport as n, brandHelpArt as o, writeArtifact as p, writeCrashReport as r, splashFitsWidth as s, installCrashHandler as t, profileDir as u, noColorRequested as v, themes as w, themeMode as x, theme as y, listWorkspaces as z };