skydive-cli 0.3.0-beta.776 → 0.3.0-beta.794

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/CHANGELOG.md CHANGED
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
35
35
  - Agent-led coding-agent import (`skydive import`).
36
36
  - Per-workspace scoping on the REST client, and cross-org `--agent <uuid>` auto-switches workspace.
37
37
  - `skydive-beta` wrapper for the canary channel.
38
+ - `skydive config` command to view and set the safe, hand-editable CLI preferences (`shareMachineDefault`, `updateCheck`) without opening `config.json`. `config`/`config list` shows each key, its effective value, and whether it comes from the file or the built-in default; `config get`/`set`/`unset` read and change one key; `config path` prints the config file location. It only ever touches those preference keys, never credentials or flow-managed state.
38
39
 
39
40
  ### Fixed
40
41
 
package/README.md CHANGED
@@ -132,6 +132,9 @@ skydive portal grant --agent <id|name>
132
132
  skydive portal revoke --agent <id|name>
133
133
  skydive portal status
134
134
 
135
+ skydive config # list CLI preferences
136
+ skydive config set updateCheck false
137
+
135
138
  skydive completion install # set up TAB completion for your shell
136
139
  ```
137
140
 
@@ -433,9 +436,20 @@ Persistent state lives in a per-user JSON file managed by
433
436
  `SKYDIVE_CONFIG_NAME`). Most keys are written by flows — `auth login`, the
434
437
  device-login flow, the in-TUI theme picker — but the file is plain JSON and
435
438
  read fresh on every invocation, so preference keys can be set by editing it
436
- directly. There is deliberately no `skydive config` command.
439
+ directly.
440
+
441
+ The safe, hand-editable preference keys are also managed by `skydive config`,
442
+ which only ever touches those keys — never credentials or flow-managed state:
443
+
444
+ ```sh
445
+ skydive config # list preferences, values, and source
446
+ skydive config get updateCheck # print the effective value of one key
447
+ skydive config set updateCheck false
448
+ skydive config unset updateCheck # revert to the built-in default
449
+ skydive config path # print the config.json location
450
+ ```
437
451
 
438
- Hand-editable keys:
452
+ Editable keys:
439
453
 
440
454
  | Key | Type | Effect |
441
455
  | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
package/dist/js/bin.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as setActiveWorkspace, S as listWorkspaces, T as version, _ as themes, b as getActiveWorkspaceId, o as brandHelpArt, t as installCrashHandler, w as name, x as getSessionIdentity, y as ensureActiveOrganization } from "./install-CDccuBwm.mjs";
3
- import { A as resolveAppUrl, C as getPromptHistoryPath, D as getStoredApiKeyId, E as getShareMachineDefault, F as saveConfig, I as saveSession, M as resolveManagementAuth, N as resolveSession, O as getStoredApiKeyWorkspaceName, R as setLastSeenVersion, S as getLastSeenVersion, _ as API_KEY_PREFIX, b as deleteConfig, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as resolveConfig, k as getUpdateCheckDisabled, v as DEFAULT_API_URL, x as getConfigPath } from "./print-Cd-bVSEJ.mjs";
2
+ import { C as setActiveWorkspace, S as listWorkspaces, T as version, _ as themes, b as getActiveWorkspaceId, o as brandHelpArt, t as installCrashHandler, w as name, x as getSessionIdentity, y as ensureActiveOrganization } from "./install-DqLgkAHr.mjs";
3
+ import { A as getStoredApiKeyWorkspaceName, B as setLastSeenVersion, C as getLastSeenVersion, F as resolveSession, L as saveConfig, M as resolveAppUrl, N as resolveConfig, O as getShareMachineDefault, P as resolveManagementAuth, R as saveSession, S as getConfigPath, T as getPromptHistoryPath, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig } from "./print-Bmgp3ZT9.mjs";
4
4
  import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
5
5
  import { n as createRestClient, t as HttpError } from "./rest-BlN_uWmL.mjs";
6
6
  import { a as registerPortalDevice, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice, s as machineIdentity } from "./client-Dc7GZ3PG.mjs";
@@ -1056,7 +1056,7 @@ function requireRestClient(argv) {
1056
1056
 
1057
1057
  //#endregion
1058
1058
  //#region src/commands/agents.ts
1059
- const listCommand$4 = {
1059
+ const listCommand$5 = {
1060
1060
  command: "list",
1061
1061
  describe: "List agents in the active workspace",
1062
1062
  builder: (y) => y.option("limit", {
@@ -1131,7 +1131,7 @@ function truncate(value, max) {
1131
1131
  if (trimmed.length <= max) return trimmed || "-";
1132
1132
  return `${trimmed.slice(0, max - 1)}\u2026`;
1133
1133
  }
1134
- const getCommand$1 = {
1134
+ const getCommand$2 = {
1135
1135
  command: "get <id>",
1136
1136
  describe: "Get agent details",
1137
1137
  builder: (y) => y.positional("id", {
@@ -1201,7 +1201,7 @@ const createCommand$1 = {
1201
1201
  const agentsCommand = {
1202
1202
  command: "agents",
1203
1203
  describe: "Manage agents",
1204
- builder: (y) => y.command(listCommand$4).command(getCommand$1).command(createCommand$1).demandCommand(1, "Specify a subcommand: list, get, create"),
1204
+ builder: (y) => y.command(listCommand$5).command(getCommand$2).command(createCommand$1).demandCommand(1, "Specify a subcommand: list, get, create"),
1205
1205
  handler: () => {}
1206
1206
  };
1207
1207
 
@@ -1500,7 +1500,7 @@ const importCommand = {
1500
1500
  process.exit(1);
1501
1501
  }
1502
1502
  }
1503
- const { runChat } = await import("./boot-Dbcys7qY.mjs");
1503
+ const { runChat } = await import("./boot-C-PAidLX.mjs");
1504
1504
  await runChat({
1505
1505
  appUrl,
1506
1506
  sessionToken: session.value.sessionToken,
@@ -1535,7 +1535,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1535
1535
  });
1536
1536
  const extra = (argv.print ?? "").trim();
1537
1537
  const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1538
- const { runPrint } = await import("./print-CPCFci60.mjs");
1538
+ const { runPrint } = await import("./print-CB9kyvkV.mjs");
1539
1539
  try {
1540
1540
  const result = await runPrint({
1541
1541
  appUrl,
@@ -1559,7 +1559,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1559
1559
 
1560
1560
  //#endregion
1561
1561
  //#region src/commands/keys.ts
1562
- const listCommand$3 = {
1562
+ const listCommand$4 = {
1563
1563
  command: "list",
1564
1564
  describe: "List API keys for an agent",
1565
1565
  handler: async (argv) => {
@@ -1648,7 +1648,7 @@ const keysCommand = {
1648
1648
  type: "string",
1649
1649
  demandOption: true,
1650
1650
  describe: "Agent ID"
1651
- }).command(listCommand$3).command(createCommand).command(revokeCommand$1).demandCommand(1, "Specify a subcommand: list, create, revoke"),
1651
+ }).command(listCommand$4).command(createCommand).command(revokeCommand$1).demandCommand(1, "Specify a subcommand: list, create, revoke"),
1652
1652
  handler: () => {}
1653
1653
  };
1654
1654
 
@@ -1660,7 +1660,7 @@ async function readStdin() {
1660
1660
  for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1661
1661
  return Buffer.concat(chunks).toString("utf8").replace(/\n$/, "");
1662
1662
  }
1663
- const listCommand$2 = {
1663
+ const listCommand$3 = {
1664
1664
  command: "list",
1665
1665
  describe: "List secret names for an agent (values are never shown)",
1666
1666
  handler: async (argv) => {
@@ -1681,7 +1681,7 @@ const listCommand$2 = {
1681
1681
  printTable(["Name"], keys.map((k) => [k]));
1682
1682
  }
1683
1683
  };
1684
- const setCommand = {
1684
+ const setCommand$1 = {
1685
1685
  command: "set <key> [value]",
1686
1686
  describe: "Set (create or overwrite) a secret. Reads value from stdin if omitted.",
1687
1687
  builder: (y) => y.positional("key", {
@@ -1746,7 +1746,7 @@ const secretsCommand = {
1746
1746
  type: "string",
1747
1747
  demandOption: true,
1748
1748
  describe: "Agent ID"
1749
- }).command(listCommand$2).command(setCommand).command(rmCommand).demandCommand(1, "Specify a subcommand: list, set, rm"),
1749
+ }).command(listCommand$3).command(setCommand$1).command(rmCommand).demandCommand(1, "Specify a subcommand: list, set, rm"),
1750
1750
  handler: () => {}
1751
1751
  };
1752
1752
 
@@ -1912,7 +1912,7 @@ const chatCommand = {
1912
1912
  sessionToken: session.value.sessionToken,
1913
1913
  agentSelector: argv.agent ?? null
1914
1914
  });
1915
- const { runChat } = await import("./boot-Dbcys7qY.mjs");
1915
+ const { runChat } = await import("./boot-C-PAidLX.mjs");
1916
1916
  await runChat({
1917
1917
  appUrl,
1918
1918
  sessionToken: session.value.sessionToken,
@@ -1978,7 +1978,7 @@ async function runPrintMode({ argv, appUrl }) {
1978
1978
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_SESSION_TOKEN.`);
1979
1979
  process.exit(1);
1980
1980
  }
1981
- const { runPrint, readStdin } = await import("./print-CPCFci60.mjs");
1981
+ const { runPrint, readStdin } = await import("./print-CB9kyvkV.mjs");
1982
1982
  await ensureAgentWorkspace({
1983
1983
  appUrl,
1984
1984
  sessionToken: session.value.sessionToken,
@@ -2042,7 +2042,7 @@ async function runPrintMode({ argv, appUrl }) {
2042
2042
  * Message ids are the currency of this API: `chat -p --json` reports the
2043
2043
  * `messageId` and this command consumes it. Run ids stay server-side.
2044
2044
  */
2045
- const getCommand = {
2045
+ const getCommand$1 = {
2046
2046
  command: "get <message-id>",
2047
2047
  describe: "Fetch a message by id and print the reply (recovers a timed-out -p)",
2048
2048
  builder: (y) => y.positional("message-id", {
@@ -2057,7 +2057,7 @@ const getCommand = {
2057
2057
  printError(`${session.error.message} Run \`skydive auth login\` first, or set SKYDIVE_SESSION_TOKEN.`);
2058
2058
  process.exit(1);
2059
2059
  }
2060
- const { messageGet } = await import("./print-CPCFci60.mjs");
2060
+ const { messageGet } = await import("./print-CB9kyvkV.mjs");
2061
2061
  try {
2062
2062
  const result = await messageGet({
2063
2063
  appUrl,
@@ -2075,7 +2075,7 @@ const getCommand = {
2075
2075
  const messagesCommand = {
2076
2076
  command: "messages",
2077
2077
  describe: "Inspect chat messages and re-fetch agent replies",
2078
- builder: (y) => y.command(getCommand).demandCommand(1, "Specify a subcommand: get"),
2078
+ builder: (y) => y.command(getCommand$1).demandCommand(1, "Specify a subcommand: get"),
2079
2079
  handler: () => {}
2080
2080
  };
2081
2081
 
@@ -2098,7 +2098,7 @@ function messageToText(message) {
2098
2098
  }
2099
2099
  return chunks.join("\n").trim();
2100
2100
  }
2101
- const listCommand$1 = {
2101
+ const listCommand$2 = {
2102
2102
  command: "list",
2103
2103
  describe: "List an agent's conversations",
2104
2104
  builder: (y) => y.option("agent", {
@@ -2188,7 +2188,7 @@ const conversationsCommand = {
2188
2188
  command: "conversations",
2189
2189
  aliases: ["conv"],
2190
2190
  describe: "List and read agent conversations",
2191
- builder: (y) => y.command(listCommand$1).command(showCommand).demandCommand(1, "Specify a subcommand: list, show"),
2191
+ builder: (y) => y.command(listCommand$2).command(showCommand).demandCommand(1, "Specify a subcommand: list, show"),
2192
2192
  handler: () => {}
2193
2193
  };
2194
2194
 
@@ -2230,7 +2230,7 @@ async function runList(argv, { hint }) {
2230
2230
  ]));
2231
2231
  if (hint && !argv.quiet) console.log("\nRun `skydive workspace switch` to pick a workspace, or pass its slug directly.");
2232
2232
  }
2233
- const listCommand = {
2233
+ const listCommand$1 = {
2234
2234
  command: "list",
2235
2235
  describe: "List workspaces on your account",
2236
2236
  handler: (argv) => runList(argv, { hint: false })
@@ -2254,7 +2254,7 @@ const switchCommand = {
2254
2254
  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.");
2255
2255
  process.exit(1);
2256
2256
  }
2257
- const { runWorkspacePicker } = await import("./boot-Dbcys7qY.mjs");
2257
+ const { runWorkspacePicker } = await import("./boot-C-PAidLX.mjs");
2258
2258
  await runWorkspacePicker(session);
2259
2259
  return;
2260
2260
  }
@@ -2287,7 +2287,7 @@ const switchCommand = {
2287
2287
  const workspaceCommand = {
2288
2288
  command: "workspace",
2289
2289
  describe: "List or switch the workspace all `skydive` commands act on",
2290
- builder: (y) => y.command(listCommand).command(switchCommand),
2290
+ builder: (y) => y.command(listCommand$1).command(switchCommand),
2291
2291
  handler: (argv) => runList(argv, { hint: true })
2292
2292
  };
2293
2293
 
@@ -2606,7 +2606,7 @@ const sandboxCommand = {
2606
2606
  handler: async (argv) => {
2607
2607
  const session = requireSession(argv);
2608
2608
  const { createRestClient } = await import("./rest-CDTXCmUb.mjs");
2609
- const { resolveAgent } = await import("./print-CPCFci60.mjs");
2609
+ const { resolveAgent } = await import("./print-CB9kyvkV.mjs");
2610
2610
  const client = createRestClient({
2611
2611
  appUrl: session.appUrl,
2612
2612
  sessionToken: session.sessionToken
@@ -3356,10 +3356,146 @@ const updateCommand = {
3356
3356
  }
3357
3357
  };
3358
3358
 
3359
+ //#endregion
3360
+ //#region src/commands/config.ts
3361
+ /**
3362
+ * Resolve a preference key or exit 1 with a clear message. `choices` on each
3363
+ * positional already rejects unknown keys through yargs' own validation
3364
+ * (routing to the top-level `.fail()` handler), so this is a defensive guard
3365
+ * that also narrows the type. It follows the CLI's convention of
3366
+ * `printError` + `process.exit(1)` rather than throwing, because a throw from
3367
+ * an async yargs handler rejects `parseAsync` instead of being caught by
3368
+ * `.fail()`. `process.exit` returns `never`, so callers can treat the result
3369
+ * as always defined.
3370
+ */
3371
+ function resolvePreference(key) {
3372
+ const pref = getPreference(key ?? "");
3373
+ if (!pref) {
3374
+ printError(`Unknown preference "${key}". Run \`skydive config list\` to see editable keys.`);
3375
+ process.exit(1);
3376
+ }
3377
+ return pref;
3378
+ }
3379
+ /** One row's worth of state for a preference, used by `list` and `get`. */
3380
+ function snapshot(pref) {
3381
+ return {
3382
+ key: pref.key,
3383
+ value: pref.read(),
3384
+ source: pref.isSet() ? "set" : "default",
3385
+ describe: pref.describe
3386
+ };
3387
+ }
3388
+ const listCommand = {
3389
+ command: "list",
3390
+ aliases: ["ls"],
3391
+ describe: "Show every editable preference, its value, and its source",
3392
+ handler: (argv) => {
3393
+ const rows = PREFERENCES.map(snapshot);
3394
+ if (argv.json) {
3395
+ output(argv, rows);
3396
+ return;
3397
+ }
3398
+ printTable([
3399
+ "Key",
3400
+ "Value",
3401
+ "Source",
3402
+ "Description"
3403
+ ], rows.map((r) => [
3404
+ r.key,
3405
+ String(r.value),
3406
+ r.source,
3407
+ r.describe
3408
+ ]));
3409
+ }
3410
+ };
3411
+ const getCommand = {
3412
+ command: "get <key>",
3413
+ describe: "Print the effective value of one preference",
3414
+ builder: (y) => y.positional("key", {
3415
+ type: "string",
3416
+ demandOption: true,
3417
+ choices: PREFERENCES.map((p) => p.key),
3418
+ describe: "Preference key"
3419
+ }),
3420
+ handler: (argv) => {
3421
+ const pref = resolvePreference(argv.key);
3422
+ if (argv.json) {
3423
+ output(argv, snapshot(pref));
3424
+ return;
3425
+ }
3426
+ console.log(String(pref.read()));
3427
+ }
3428
+ };
3429
+ const setCommand = {
3430
+ command: "set <key> <value>",
3431
+ describe: "Set a preference (e.g. `config set updateCheck false`)",
3432
+ builder: (y) => y.positional("key", {
3433
+ type: "string",
3434
+ demandOption: true,
3435
+ choices: PREFERENCES.map((p) => p.key),
3436
+ describe: "Preference key"
3437
+ }).positional("value", {
3438
+ type: "string",
3439
+ demandOption: true,
3440
+ describe: "New value"
3441
+ }),
3442
+ handler: (argv) => {
3443
+ const pref = resolvePreference(argv.key);
3444
+ const parsed = pref.parse(argv.value ?? "");
3445
+ if (parsed.isErr()) {
3446
+ printError(`${pref.key}: ${parsed.error}`);
3447
+ process.exit(1);
3448
+ }
3449
+ pref.write(parsed.value);
3450
+ if (argv.json) {
3451
+ output(argv, snapshot(pref));
3452
+ return;
3453
+ }
3454
+ if (!argv.quiet) console.log(`Set ${pref.key} = ${String(parsed.value)}`);
3455
+ }
3456
+ };
3457
+ const unsetCommand = {
3458
+ command: "unset <key>",
3459
+ aliases: ["reset"],
3460
+ describe: "Remove a preference, reverting it to the built-in default",
3461
+ builder: (y) => y.positional("key", {
3462
+ type: "string",
3463
+ demandOption: true,
3464
+ choices: PREFERENCES.map((p) => p.key),
3465
+ describe: "Preference key"
3466
+ }),
3467
+ handler: (argv) => {
3468
+ const pref = resolvePreference(argv.key);
3469
+ pref.clear();
3470
+ if (argv.json) {
3471
+ output(argv, snapshot(pref));
3472
+ return;
3473
+ }
3474
+ if (!argv.quiet) console.log(`Unset ${pref.key} (now ${String(pref.read())} by default)`);
3475
+ }
3476
+ };
3477
+ const pathCommand = {
3478
+ command: "path",
3479
+ describe: "Print the on-disk config file path",
3480
+ handler: (argv) => {
3481
+ if (argv.json) {
3482
+ output(argv, { path: getConfigPath() });
3483
+ return;
3484
+ }
3485
+ console.log(getConfigPath());
3486
+ }
3487
+ };
3488
+ const configCommand = {
3489
+ command: "config",
3490
+ describe: "View and set CLI preferences (safe, hand-editable keys only)",
3491
+ builder: (y) => y.command(listCommand).command(getCommand).command(setCommand).command(unsetCommand).command(pathCommand),
3492
+ handler: (argv) => listCommand.handler(argv)
3493
+ };
3494
+
3359
3495
  //#endregion
3360
3496
  //#region src/cli.ts
3361
3497
  function createCli(argv) {
3362
- return yargs(argv).scriptName("skydive").usage("$0 <command> [options]").parserConfiguration({ "populate--": true }).example("skydive auth login", "Store your API key").example("skydive chat", "Open the interactive chat TUI (prod)").example("skydive chat --api-url http://localhost:4500", "Chat against a local dev API").example("skydive agents list", "List your agents").example("skydive completion install", "Set up TAB completion for your shell").example("skydive keys create \"my-key\"", "Create an API key").example("skydive secrets set OPENAI_API_KEY --agent-id <id>", "Set a secret (value from stdin)").example("skydive workspace list", "List workspaces on your account").example("skydive workspace switch acme-team", "Switch the workspace all `skydive` commands act on").example("skydive portal open --agent grace", "Open the portal to this machine for an agent, headless (no TUI)").option("json", {
3498
+ return yargs(argv).scriptName("skydive").usage("$0 <command> [options]").parserConfiguration({ "populate--": true }).example("skydive auth login", "Store your API key").example("skydive chat", "Open the interactive chat TUI (prod)").example("skydive chat --api-url http://localhost:4500", "Chat against a local dev API").example("skydive agents list", "List your agents").example("skydive completion install", "Set up TAB completion for your shell").example("skydive keys create \"my-key\"", "Create an API key").example("skydive secrets set OPENAI_API_KEY --agent-id <id>", "Set a secret (value from stdin)").example("skydive workspace list", "List workspaces on your account").example("skydive config set updateCheck false", "Disable the daily update check").example("skydive workspace switch acme-team", "Switch the workspace all `skydive` commands act on").example("skydive portal open --agent grace", "Open the portal to this machine for an agent, headless (no TUI)").option("json", {
3363
3499
  type: "boolean",
3364
3500
  default: false,
3365
3501
  global: true,
@@ -3373,7 +3509,7 @@ function createCli(argv) {
3373
3509
  type: "string",
3374
3510
  global: true,
3375
3511
  describe: "Override API base URL"
3376
- }).command(authCommand).command(chatCommand).command(messagesCommand).command(conversationsCommand).command(agentsCommand).command(importCommand).command(keysCommand).command(secretsCommand).command(workspaceCommand).command(portalCommand).command(sandboxCommand).command(updateCommand).command(completionCommand).demandCommand(1, "Specify a command. Run --help for usage.").strict().wrap(null).version(version).alias("v", "version").alias("h", "help").help().fail((msg, err) => {
3512
+ }).command(authCommand).command(chatCommand).command(messagesCommand).command(conversationsCommand).command(agentsCommand).command(importCommand).command(keysCommand).command(secretsCommand).command(workspaceCommand).command(portalCommand).command(sandboxCommand).command(updateCommand).command(configCommand).command(completionCommand).demandCommand(1, "Specify a command. Run --help for usage.").strict().wrap(null).version(version).alias("v", "version").alias("h", "help").help().fail((msg, err) => {
3377
3513
  printError(err ? err instanceof Error ? err.message : String(err) : msg ?? "Unknown error");
3378
3514
  process.exit(1);
3379
3515
  });
@@ -3586,7 +3722,7 @@ function setupUpdateCheck(argv) {
3586
3722
 
3587
3723
  //#endregion
3588
3724
  //#region src/changelog.generated.ts
3589
- const CHANGELOG_MD = "# Changelog\n\nAll notable changes to the Skydive CLI are documented here.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.3.0] - 2026-08-04\n\n### Added\n\n**Chat TUI**\n\n- `/fork` slash command to fork a conversation at any point and continue in a new thread.\n- `/conversation` switches threads from inside a live session.\n- `/compact` triggers manual conversation compaction.\n- `/portal` and `/copy` slash commands.\n- Archive the current conversation from the chat TUI.\n- `--new` flag on `skydive chat` to start a fresh conversation.\n- Debounced server-side conversation search in the picker, with title matches ranked first.\n- File review pane orientation toggle, and review comments in the file pane.\n- Pinned plan card controls and a jump-to-latest hint.\n- Terminal title (OSC) follows the live conversation title on every screen; inside cmux the workspace is renamed too, and linked PRs are pushed to the cmux sidebar.\n- Every participating agent shows in the conversation list; connect cards each have their own identity, and ctrl+r asks which connect card to act on.\n- Colored Skydive splash art in `skydive --help` and the TUI pickers.\n- New-agent name prefilled from the server's suggestion.\n\n**CLI & platform**\n\n- `skydive update` self-updater, with an update notice when a new version is available.\n- Shell completions for bash, zsh, and fish via `completion install`.\n- Portal daemon: one connection per host with per-conversation working directories.\n- Agent-led coding-agent import (`skydive import`).\n- Per-workspace scoping on the REST client, and cross-org `--agent <uuid>` auto-switches workspace.\n- `skydive-beta` wrapper for the canary channel.\n\n### Fixed\n\n- `skydive update` no longer claims an update is available on every npm install.\n- Chat memory is bounded (payload clamps + windowed rows) with a memory watchdog and OOM diagnostic report; transcript rows are memoized so streaming stops re-rendering the whole chat.\n- Chat-send failures map to real error messages.\n- Notification and sidebar previews use the last text block of the reply and strip inline markdown.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n- Sending to non-web channel conversations is blocked in the TUI.\n- GitHub bot connect opens on the web front door, not api.skydive.com.\n- TUI activity counter times from the run, not the screen.\n\n## [0.2.0] - 2026-07-29\n\n### Added\n\n**Sandbox & machine access**\n\n- Standalone `skydive sandbox` command for direct access to an agent's sandbox.\n- `/sandbox` in the chat TUI — live PTY session and one-shot command execution.\n- Headless machine sharing via `skydive portal`, with self-registering portal grants.\n- `shareMachineDefault` config key for always-on portal sharing.\n\n**Chat TUI**\n\n- File pane (`ctrl+g`) with Changes and Files tabs: a shared file tree plus source viewer and a workspace browser, mouse-resizable and responsive to terminal size.\n- Agent todo list rendered in the chat TUI, above the composer, mirroring the web chat's todo card.\n- Fuzzy finder in the conversation and agent pickers.\n- Slash-command autocomplete menu.\n- Paste or drag-and-drop any file into the composer; paste clipboard images with Cmd+V.\n- Run local shell commands with `!` in the composer.\n- Conversation recaps, streamed live title updates, and per-agent attribution on assistant turns.\n- Working timer rolls up into minutes and hours.\n- Conversation picker paginates past 50 conversations and shows only your own conversations.\n- Esc leaves a live run; typing `exit` quits the chat.\n- Cursor Dark theme.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n\n**Headless & scripting**\n\n- Resume a conversation by id.\n- `conversations list` and `conversations show` for transcript reads.\n- `messages get`, with run recovery keyed on message id.\n- Connect cards surface in headless `-p` mode so a driving agent never gets stuck.\n\n**Authentication**\n\n- `skydive auth login` via the browser now auto-mints an API key, so one login yields both a chat session and a usable management credential. Management commands announce the key's pinned workspace when it drives them, so a workspace mismatch is visible at use time.\n- Workspace picker on the device authorization page.\n- Account and workspace identity shown in `auth status`.\n\n**Platform**\n\n- Standalone binary builds compiling the CLI into a per-target executable.\n- Interactive workspace switcher; management commands follow the active workspace.\n- Terminal host integrations and agent notifications.\n\n### Fixed\n\n- Transcript errors collapse to one line, click to expand (REST and portal errors keep their full body).\n- Dragged/pasted image file paths attach the file instead of inserting path text, including macOS paths with literal parentheses.\n- Bare URLs in chat markdown are hyperlinked so they survive text wrap.\n- Composer draft is preserved across TUI overlays.\n- Relative connect links resolve before opening the browser.\n- Numbered markdown headings render colored in the TUI.\n- Chat transcript pages by 75% of a screen; picker rows stay on one line.\n- Run starts push to the TUI over the conversation stream.\n- Security: remediated high-severity dependency findings and cleared tar/shell-quote CVEs.\n\n## [0.1.0] - 2026-07-21\n\nInitial public release: `skydive chat` TUI, agent and conversation management,\ndevice authorization, and headless `-p` mode.\n";
3725
+ const CHANGELOG_MD = "# Changelog\n\nAll notable changes to the Skydive CLI are documented here.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.3.0] - 2026-08-04\n\n### Added\n\n**Chat TUI**\n\n- `/fork` slash command to fork a conversation at any point and continue in a new thread.\n- `/conversation` switches threads from inside a live session.\n- `/compact` triggers manual conversation compaction.\n- `/portal` and `/copy` slash commands.\n- Archive the current conversation from the chat TUI.\n- `--new` flag on `skydive chat` to start a fresh conversation.\n- Debounced server-side conversation search in the picker, with title matches ranked first.\n- File review pane orientation toggle, and review comments in the file pane.\n- Pinned plan card controls and a jump-to-latest hint.\n- Terminal title (OSC) follows the live conversation title on every screen; inside cmux the workspace is renamed too, and linked PRs are pushed to the cmux sidebar.\n- Every participating agent shows in the conversation list; connect cards each have their own identity, and ctrl+r asks which connect card to act on.\n- Colored Skydive splash art in `skydive --help` and the TUI pickers.\n- New-agent name prefilled from the server's suggestion.\n\n**CLI & platform**\n\n- `skydive update` self-updater, with an update notice when a new version is available.\n- Shell completions for bash, zsh, and fish via `completion install`.\n- Portal daemon: one connection per host with per-conversation working directories.\n- Agent-led coding-agent import (`skydive import`).\n- Per-workspace scoping on the REST client, and cross-org `--agent <uuid>` auto-switches workspace.\n- `skydive-beta` wrapper for the canary channel.\n- `skydive config` command to view and set the safe, hand-editable CLI preferences (`shareMachineDefault`, `updateCheck`) without opening `config.json`. `config`/`config list` shows each key, its effective value, and whether it comes from the file or the built-in default; `config get`/`set`/`unset` read and change one key; `config path` prints the config file location. It only ever touches those preference keys, never credentials or flow-managed state.\n\n### Fixed\n\n- `skydive update` no longer claims an update is available on every npm install.\n- Chat memory is bounded (payload clamps + windowed rows) with a memory watchdog and OOM diagnostic report; transcript rows are memoized so streaming stops re-rendering the whole chat.\n- Chat-send failures map to real error messages.\n- Notification and sidebar previews use the last text block of the reply and strip inline markdown.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n- Sending to non-web channel conversations is blocked in the TUI.\n- GitHub bot connect opens on the web front door, not api.skydive.com.\n- TUI activity counter times from the run, not the screen.\n\n## [0.2.0] - 2026-07-29\n\n### Added\n\n**Sandbox & machine access**\n\n- Standalone `skydive sandbox` command for direct access to an agent's sandbox.\n- `/sandbox` in the chat TUI — live PTY session and one-shot command execution.\n- Headless machine sharing via `skydive portal`, with self-registering portal grants.\n- `shareMachineDefault` config key for always-on portal sharing.\n\n**Chat TUI**\n\n- File pane (`ctrl+g`) with Changes and Files tabs: a shared file tree plus source viewer and a workspace browser, mouse-resizable and responsive to terminal size.\n- Agent todo list rendered in the chat TUI, above the composer, mirroring the web chat's todo card.\n- Fuzzy finder in the conversation and agent pickers.\n- Slash-command autocomplete menu.\n- Paste or drag-and-drop any file into the composer; paste clipboard images with Cmd+V.\n- Run local shell commands with `!` in the composer.\n- Conversation recaps, streamed live title updates, and per-agent attribution on assistant turns.\n- Working timer rolls up into minutes and hours.\n- Conversation picker paginates past 50 conversations and shows only your own conversations.\n- Esc leaves a live run; typing `exit` quits the chat.\n- Cursor Dark theme.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n\n**Headless & scripting**\n\n- Resume a conversation by id.\n- `conversations list` and `conversations show` for transcript reads.\n- `messages get`, with run recovery keyed on message id.\n- Connect cards surface in headless `-p` mode so a driving agent never gets stuck.\n\n**Authentication**\n\n- `skydive auth login` via the browser now auto-mints an API key, so one login yields both a chat session and a usable management credential. Management commands announce the key's pinned workspace when it drives them, so a workspace mismatch is visible at use time.\n- Workspace picker on the device authorization page.\n- Account and workspace identity shown in `auth status`.\n\n**Platform**\n\n- Standalone binary builds compiling the CLI into a per-target executable.\n- Interactive workspace switcher; management commands follow the active workspace.\n- Terminal host integrations and agent notifications.\n\n### Fixed\n\n- Transcript errors collapse to one line, click to expand (REST and portal errors keep their full body).\n- Dragged/pasted image file paths attach the file instead of inserting path text, including macOS paths with literal parentheses.\n- Bare URLs in chat markdown are hyperlinked so they survive text wrap.\n- Composer draft is preserved across TUI overlays.\n- Relative connect links resolve before opening the browser.\n- Numbered markdown headings render colored in the TUI.\n- Chat transcript pages by 75% of a screen; picker rows stay on one line.\n- Run starts push to the TUI over the conversation stream.\n- Security: remediated high-severity dependency findings and cleared tar/shell-quote CVEs.\n\n## [0.1.0] - 2026-07-21\n\nInitial public release: `skydive chat` TUI, agent and conversation management,\ndevice authorization, and headless `-p` mode.\n";
3590
3726
 
3591
3727
  //#endregion
3592
3728
  //#region src/whats-new.ts
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as setActiveWorkspace, S as listWorkspaces, a as WORDMARK, b as getActiveWorkspaceId, c as applyTheme, d as noColorRequested, f as theme, g as themeVersion, h as themeModeFromColorFgBg, i as MARK_CELLS, l as findTheme, m as themeMode, n as buildCrashReport, p as themeForMode, r as writeCrashReport, s as DEFAULT_THEME_ID, t as installCrashHandler, u as monoTheme, v as themesForMode } from "./install-CDccuBwm.mjs";
3
- import { L as saveTheme, P as resolveWebUrl, T as getSavedTheme, 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, v as DEFAULT_API_URL, w as getReviewStateDir, x as getConfigPath, y as DEFAULT_APP_URL } from "./print-Cd-bVSEJ.mjs";
2
+ import { C as setActiveWorkspace, S as listWorkspaces, a as WORDMARK, b as getActiveWorkspaceId, c as applyTheme, d as noColorRequested, f as theme, g as themeVersion, h as themeModeFromColorFgBg, i as MARK_CELLS, l as findTheme, m as themeMode, n as buildCrashReport, p as themeForMode, r as writeCrashReport, s as DEFAULT_THEME_ID, t as installCrashHandler, u as monoTheme, v as themesForMode } from "./install-DqLgkAHr.mjs";
3
+ import { D as getSavedTheme, E as getReviewStateDir, I as resolveWebUrl, S as getConfigPath, 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, v as DEFAULT_API_URL, y as DEFAULT_APP_URL, z as saveTheme } from "./print-Bmgp3ZT9.mjs";
4
4
  import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, r as errorDetail, t as HttpError } from "./rest-BlN_uWmL.mjs";
5
5
  import { t as PortalClient } from "./client-Dc7GZ3PG.mjs";
6
6
  import { d as makeLineParser, f as parseDaemonMessage, l as daemonPaths, n as ensureDaemonRunning, s as LOCAL_PROTOCOL_VERSION, u as encodeLine } from "./daemon-Co4CtpXZ.mjs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { x as getConfigPath } from "./print-Cd-bVSEJ.mjs";
2
+ import { S as getConfigPath } from "./print-Bmgp3ZT9.mjs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { err, ok } from "neverthrow";
@@ -8,7 +8,7 @@ import fs from "node:fs";
8
8
 
9
9
  //#region package.json
10
10
  var name = "skydive-cli";
11
- var version$1 = "0.3.0-beta.776";
11
+ var version$1 = "0.3.0-beta.794";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
@@ -175,6 +175,10 @@ function saveTheme(mode, themeId) {
175
175
  function getShareMachineDefault() {
176
176
  return store.get("shareMachineDefault") ?? false;
177
177
  }
178
+ /** Test-only setter; production writes happen by editing config.json. */
179
+ function saveShareMachineDefault(value) {
180
+ store.set("shareMachineDefault", value);
181
+ }
178
182
  /**
179
183
  * Whether the update check is disabled via config.json (`"updateCheck":
180
184
  * false`). The persistent counterpart to the SKYDIVE_NO_UPDATE_CHECK /
@@ -184,6 +188,52 @@ function getShareMachineDefault() {
184
188
  function getUpdateCheckDisabled() {
185
189
  return store.get("updateCheck") === false;
186
190
  }
191
+ /** Test-only setter; production writes happen by editing config.json. */
192
+ function saveUpdateCheck(value) {
193
+ if (value === void 0) {
194
+ store.delete("updateCheck");
195
+ return;
196
+ }
197
+ store.set("updateCheck", value);
198
+ }
199
+ function parseBoolean(raw) {
200
+ const v = raw.trim().toLowerCase();
201
+ if ([
202
+ "true",
203
+ "1",
204
+ "yes",
205
+ "on"
206
+ ].includes(v)) return ok(true);
207
+ if ([
208
+ "false",
209
+ "0",
210
+ "no",
211
+ "off"
212
+ ].includes(v)) return ok(false);
213
+ return err(`Expected a boolean (true/false), got "${raw}".`);
214
+ }
215
+ const PREFERENCES = [{
216
+ key: "shareMachineDefault",
217
+ type: "boolean",
218
+ describe: "Share this machine over the portal on `skydive chat` launch, as if --share-machine were passed. Default false.",
219
+ read: () => getShareMachineDefault(),
220
+ isSet: () => store.has("shareMachineDefault"),
221
+ parse: parseBoolean,
222
+ write: (value) => saveShareMachineDefault(value),
223
+ clear: () => store.delete("shareMachineDefault")
224
+ }, {
225
+ key: "updateCheck",
226
+ type: "boolean",
227
+ describe: "Run the daily background update check and its \"Update available\" notice. Set false to disable. Default true.",
228
+ read: () => !getUpdateCheckDisabled(),
229
+ isSet: () => store.has("updateCheck"),
230
+ parse: parseBoolean,
231
+ write: (value) => saveUpdateCheck(value),
232
+ clear: () => store.delete("updateCheck")
233
+ }];
234
+ function getPreference(key) {
235
+ return PREFERENCES.find((p) => p.key === key);
236
+ }
187
237
 
188
238
  //#endregion
189
239
  //#region src/chat/tui/chat/card.ts
@@ -677,4 +727,4 @@ async function readStdin() {
677
727
  }
678
728
 
679
729
  //#endregion
680
- export { resolveAppUrl as A, getPromptHistoryPath as C, getStoredApiKeyId as D, getShareMachineDefault as E, saveConfig as F, saveSession as I, saveTheme as L, resolveManagementAuth as M, resolveSession as N, getStoredApiKeyWorkspaceName as O, resolveWebUrl as P, setLastSeenVersion as R, getLastSeenVersion as S, getSavedTheme as T, API_KEY_PREFIX as _, runPrint as a, deleteConfig as b, cardActionErrorMessage as c, reconcileMaskedInput as d, resolveConnectUrl as f, API_KEY_FAMILY_PREFIX as g, API_KEYS_URL as h, resolveAgent as i, resolveConfig as j, getUpdateCheckDisabled as k, parseExternalOauthConnectParams as l, specKeyFor as m, messageGet as n, toPrintError as o, parseConnectCard as p, readStdin as r, MASK_CHAR as s, collectRunText as t, parseOauthConnectParams as u, DEFAULT_API_URL as v, getReviewStateDir as w, getConfigPath as x, DEFAULT_APP_URL as y };
730
+ export { getStoredApiKeyWorkspaceName as A, setLastSeenVersion as B, getLastSeenVersion as C, getSavedTheme as D, getReviewStateDir as E, resolveSession as F, resolveWebUrl as I, saveConfig as L, resolveAppUrl as M, resolveConfig as N, getShareMachineDefault as O, resolveManagementAuth as P, saveSession as R, getConfigPath as S, getPromptHistoryPath as T, API_KEY_PREFIX as _, runPrint as a, PREFERENCES as b, cardActionErrorMessage as c, reconcileMaskedInput as d, resolveConnectUrl as f, API_KEY_FAMILY_PREFIX as g, API_KEYS_URL as h, resolveAgent as i, getUpdateCheckDisabled as j, getStoredApiKeyId as k, parseExternalOauthConnectParams as l, specKeyFor as m, messageGet as n, toPrintError as o, parseConnectCard as p, readStdin as r, MASK_CHAR as s, collectRunText as t, parseOauthConnectParams as u, DEFAULT_API_URL as v, getPreference as w, deleteConfig as x, DEFAULT_APP_URL as y, saveTheme as z };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-Cd-bVSEJ.mjs";
2
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-Bmgp3ZT9.mjs";
3
3
  import "./rest-BlN_uWmL.mjs";
4
4
 
5
5
  export { messageGet, readStdin, resolveAgent, runPrint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.3.0-beta.776",
3
+ "version": "0.3.0-beta.794",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",