skydive-cli 0.2.0-beta.497 → 0.2.0-beta.509
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/README.md +7 -0
- package/dist/js/bin.mjs +4 -4
- package/dist/js/{boot-DlptSLP6.mjs → boot-C-wL4WoP.mjs} +108 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -176,6 +176,13 @@ internal API. On launch it walks you through:
|
|
|
176
176
|
formatting, collapsed reasoning, and rich rendering of tool calls
|
|
177
177
|
(`bash`, `edit`/`write` diffs, `read`, `grep`/`glob`).
|
|
178
178
|
|
|
179
|
+
You can change conversation without leaving chat. `/conversation` (aliases
|
|
180
|
+
`/conversations`, `/conv`) reopens the conversation list, and
|
|
181
|
+
`/conversation <id>` — an id, its `/c/<id>` link, or a pasted
|
|
182
|
+
`skydive chat --resume <id>` line — jumps straight into that thread, including
|
|
183
|
+
one belonging to a **different** agent. Either way the run you leave behind
|
|
184
|
+
keeps going server-side; reopening it reattaches to the stream.
|
|
185
|
+
|
|
179
186
|
Runs under **Node** like every other command. The TUI renders through
|
|
180
187
|
OpenTUI, whose native core needs the [Bun](https://bun.sh) runtime, so the
|
|
181
188
|
first `chat` run downloads a private, pinned Bun and re-execs under it
|
package/dist/js/bin.mjs
CHANGED
|
@@ -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.
|
|
23
|
+
var version = "0.2.0-beta.509";
|
|
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-
|
|
1203
|
+
const { runChat } = await import("./boot-C-wL4WoP.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-
|
|
1514
|
+
const { runChat } = await import("./boot-C-wL4WoP.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-
|
|
1813
|
+
const { runWorkspacePicker } = await import("./boot-C-wL4WoP.mjs");
|
|
1814
1814
|
await runWorkspacePicker(session);
|
|
1815
1815
|
return;
|
|
1816
1816
|
}
|
|
@@ -2003,6 +2003,33 @@ function relativeTime(iso) {
|
|
|
2003
2003
|
return `${Math.floor(diffMs / 864e5)}d ago`;
|
|
2004
2004
|
}
|
|
2005
2005
|
|
|
2006
|
+
//#endregion
|
|
2007
|
+
//#region src/chat/conversation-ref.ts
|
|
2008
|
+
/**
|
|
2009
|
+
* Parsing the conversation reference a user hands `/conversation`.
|
|
2010
|
+
*
|
|
2011
|
+
* The whole point of the command is that you paste whatever you already have
|
|
2012
|
+
* in front of you, so every form the platform hands out has to work: a bare
|
|
2013
|
+
* id, the `https://skydive.com/c/<id>` link an agent replies with, or the
|
|
2014
|
+
* `skydive chat --resume <id>` line the TUI prints on exit. All three carry
|
|
2015
|
+
* exactly one id, so we pull the first uuid out of the string rather than
|
|
2016
|
+
* teaching this about URL shapes and command syntax.
|
|
2017
|
+
*
|
|
2018
|
+
* Validating here (instead of letting the API answer) is what turns a typo
|
|
2019
|
+
* into "not a conversation id" in the composer rather than a request that
|
|
2020
|
+
* cannot succeed.
|
|
2021
|
+
*/
|
|
2022
|
+
const uuidPattern = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
2023
|
+
/**
|
|
2024
|
+
* The conversation id in `input`, lowercased, or null when there isn't one.
|
|
2025
|
+
* Trailing punctuation, query strings and surrounding prose are tolerated —
|
|
2026
|
+
* a pasted link or command line is the expected input, not the exception.
|
|
2027
|
+
*/
|
|
2028
|
+
function parseConversationRef(input) {
|
|
2029
|
+
const match = uuidPattern.exec(input);
|
|
2030
|
+
return match ? match[0].toLowerCase() : null;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2006
2033
|
//#endregion
|
|
2007
2034
|
//#region src/chat/paste.ts
|
|
2008
2035
|
const MAX_PATH_PASTE_CHARS = 4096;
|
|
@@ -4887,6 +4914,13 @@ const slashCommands = [
|
|
|
4887
4914
|
description: "start a new conversation with this agent",
|
|
4888
4915
|
action: { kind: "new-conversation" }
|
|
4889
4916
|
},
|
|
4917
|
+
{
|
|
4918
|
+
name: "conversation",
|
|
4919
|
+
aliases: ["conversations", "conv"],
|
|
4920
|
+
argsHint: "[id or link]",
|
|
4921
|
+
description: "switch conversation (no id: pick from the list)",
|
|
4922
|
+
action: { kind: "conversation" }
|
|
4923
|
+
},
|
|
4890
4924
|
{
|
|
4891
4925
|
name: "model",
|
|
4892
4926
|
aliases: [],
|
|
@@ -4951,7 +4985,7 @@ const slashCommands = [
|
|
|
4951
4985
|
*
|
|
4952
4986
|
* An argument-less command only matches as the exact line — `/help me write
|
|
4953
4987
|
* a plan` stays an ordinary message rather than swallowing the user's text
|
|
4954
|
-
* into the help modal. Only `sandbox`
|
|
4988
|
+
* into the help modal. Only `sandbox` and `conversation` accept a rest.
|
|
4955
4989
|
*/
|
|
4956
4990
|
function matchSlashCommand(line) {
|
|
4957
4991
|
if (!line.startsWith("/")) return null;
|
|
@@ -7969,6 +8003,10 @@ function routeInput(raw) {
|
|
|
7969
8003
|
kind: "sandbox-exec",
|
|
7970
8004
|
command: match.rest
|
|
7971
8005
|
} : { kind: "sandbox-pty" };
|
|
8006
|
+
case "conversation": return match.rest ? {
|
|
8007
|
+
kind: "open-conversation",
|
|
8008
|
+
ref: match.rest
|
|
8009
|
+
} : { kind: "conversation-picker" };
|
|
7972
8010
|
case "new-conversation":
|
|
7973
8011
|
case "model-picker":
|
|
7974
8012
|
case "theme-picker":
|
|
@@ -9017,6 +9055,64 @@ function ChatScreen({ agent, conversation }) {
|
|
|
9017
9055
|
agent,
|
|
9018
9056
|
goTo
|
|
9019
9057
|
]);
|
|
9058
|
+
/**
|
|
9059
|
+
* Leave this conversation for another screen without ending its run: drop
|
|
9060
|
+
* our own SSE subscription and nothing else — never `rest.cancelRun` — so
|
|
9061
|
+
* the agent keeps working server-side and reopening the thread reattaches.
|
|
9062
|
+
* Same contract as `esc`, which is why `/conversation` is allowed mid-run
|
|
9063
|
+
* where `/new` is not: what you land on still lists the thread you left,
|
|
9064
|
+
* so the run is never stranded out of reach.
|
|
9065
|
+
*/
|
|
9066
|
+
const leaveForScreen = useCallback((next) => {
|
|
9067
|
+
const current = runRef.current;
|
|
9068
|
+
if (current.kind === "streaming") current.abort.abort();
|
|
9069
|
+
if (current.kind !== "idle") useStore.getState().showToast({ message: `${agent.name} keeps working — reopen to reattach` });
|
|
9070
|
+
goTo(next);
|
|
9071
|
+
}, [agent.name, goTo]);
|
|
9072
|
+
/**
|
|
9073
|
+
* `/conversation <id or link>`: open that conversation without leaving the
|
|
9074
|
+
* TUI — the point being that the id usually arrives *from* an agent, in the
|
|
9075
|
+
* session you started to go find it.
|
|
9076
|
+
*
|
|
9077
|
+
* Resolution goes through `resolveInitialScreen`, the same path
|
|
9078
|
+
* `--resume <id>` takes, so an id belonging to a different agent opens
|
|
9079
|
+
* under that agent and the not-found / no-access wording lives in one
|
|
9080
|
+
* place.
|
|
9081
|
+
*/
|
|
9082
|
+
const openConversationRef = useCallback(async (ref) => {
|
|
9083
|
+
const target = parseConversationRef(ref);
|
|
9084
|
+
if (!target) {
|
|
9085
|
+
setItems((prev) => [...prev, {
|
|
9086
|
+
kind: "error",
|
|
9087
|
+
id: crypto.randomUUID(),
|
|
9088
|
+
text: `not a conversation id: ${ref} — pass an id, its /c/<id> link, or run /conversation on its own to pick from the list`
|
|
9089
|
+
}]);
|
|
9090
|
+
return;
|
|
9091
|
+
}
|
|
9092
|
+
if (!rest) return;
|
|
9093
|
+
if (target === conversationId) {
|
|
9094
|
+
useStore.getState().showToast({ message: "already in this conversation" });
|
|
9095
|
+
return;
|
|
9096
|
+
}
|
|
9097
|
+
try {
|
|
9098
|
+
leaveForScreen(await resolveInitialScreen({
|
|
9099
|
+
rest,
|
|
9100
|
+
agentSelector: null,
|
|
9101
|
+
conversationId: target,
|
|
9102
|
+
newConversation: false
|
|
9103
|
+
}));
|
|
9104
|
+
} catch (err) {
|
|
9105
|
+
setItems((prev) => [...prev, {
|
|
9106
|
+
kind: "error",
|
|
9107
|
+
id: crypto.randomUUID(),
|
|
9108
|
+
text: `couldn't open ${target}: ${errorMessage(err)}`
|
|
9109
|
+
}]);
|
|
9110
|
+
}
|
|
9111
|
+
}, [
|
|
9112
|
+
rest,
|
|
9113
|
+
conversationId,
|
|
9114
|
+
leaveForScreen
|
|
9115
|
+
]);
|
|
9020
9116
|
const toggleShare = useCallback(() => {
|
|
9021
9117
|
if (!portalClient) return;
|
|
9022
9118
|
if (portal.status === "off") {
|
|
@@ -9088,6 +9184,15 @@ function ChatScreen({ agent, conversation }) {
|
|
|
9088
9184
|
}
|
|
9089
9185
|
});
|
|
9090
9186
|
break;
|
|
9187
|
+
case "conversation-picker":
|
|
9188
|
+
leaveForScreen({
|
|
9189
|
+
kind: "conversation-picker",
|
|
9190
|
+
agent
|
|
9191
|
+
});
|
|
9192
|
+
break;
|
|
9193
|
+
case "open-conversation":
|
|
9194
|
+
openConversationRef(routed.ref);
|
|
9195
|
+
break;
|
|
9091
9196
|
case "model-picker":
|
|
9092
9197
|
setModelPickerOpen(true);
|
|
9093
9198
|
break;
|
|
@@ -9146,6 +9251,8 @@ function ChatScreen({ agent, conversation }) {
|
|
|
9146
9251
|
runLocalShell,
|
|
9147
9252
|
runSandboxExec,
|
|
9148
9253
|
runSandboxPty,
|
|
9254
|
+
leaveForScreen,
|
|
9255
|
+
openConversationRef,
|
|
9149
9256
|
toggleShare,
|
|
9150
9257
|
openInBrowser,
|
|
9151
9258
|
quit,
|