demian-cli 1.1.2 → 1.2.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.
|
Binary file
|
|
Binary file
|
package/dist/tui.mjs
CHANGED
|
@@ -23189,8 +23189,8 @@ function permissionLabel(req) {
|
|
|
23189
23189
|
if ((req.tool === "write_file" || req.tool === "edit_file" || req.tool === "read_file") && typeof inputObject.path === "string") {
|
|
23190
23190
|
return `${req.tool}: ${inputObject.path}`;
|
|
23191
23191
|
}
|
|
23192
|
-
const
|
|
23193
|
-
if (
|
|
23192
|
+
const path38 = typeof inputObject.path === "string" ? inputObject.path : typeof inputObject.file_path === "string" ? inputObject.file_path : void 0;
|
|
23193
|
+
if (path38) return `${tool}: ${path38}`;
|
|
23194
23194
|
return tool;
|
|
23195
23195
|
}
|
|
23196
23196
|
var init_prompt = __esm({
|
|
@@ -26675,6 +26675,12 @@ var init_store = __esm({
|
|
|
26675
26675
|
}
|
|
26676
26676
|
if (option.id) this.#resolveSession({ kind: "session", id: option.id });
|
|
26677
26677
|
}
|
|
26678
|
+
submitDeleteSessionSelection() {
|
|
26679
|
+
if (this.#state.inputMode !== "session") return;
|
|
26680
|
+
const option = this.#state.sessionOptions[this.#state.sessionCursor];
|
|
26681
|
+
if (!option || option.kind === "new" || !option.id) return;
|
|
26682
|
+
this.#resolveSession({ kind: "delete", id: option.id });
|
|
26683
|
+
}
|
|
26678
26684
|
submitNewSessionSelection() {
|
|
26679
26685
|
if (this.#state.inputMode !== "session") return;
|
|
26680
26686
|
this.#resolveSession({ kind: "new" });
|
|
@@ -28029,6 +28035,7 @@ var app_exports = {};
|
|
|
28029
28035
|
__export(app_exports, {
|
|
28030
28036
|
TuiApp: () => TuiApp,
|
|
28031
28037
|
commandPaletteMatches: () => commandPaletteMatches,
|
|
28038
|
+
initialSessionCommandLines: () => initialSessionCommandLines,
|
|
28032
28039
|
inputFrameRows: () => inputFrameRows,
|
|
28033
28040
|
isCtrlCKeypress: () => isCtrlCKeypress,
|
|
28034
28041
|
isPromptNewlineKeypress: () => isPromptNewlineKeypress,
|
|
@@ -28219,6 +28226,10 @@ function TuiApp({ store, version = "dev" }) {
|
|
|
28219
28226
|
store.submitNewSessionSelection();
|
|
28220
28227
|
return;
|
|
28221
28228
|
}
|
|
28229
|
+
if (normalized === "d") {
|
|
28230
|
+
store.submitDeleteSessionSelection();
|
|
28231
|
+
return;
|
|
28232
|
+
}
|
|
28222
28233
|
if (key.upArrow) {
|
|
28223
28234
|
store.moveSessionCursor(-1);
|
|
28224
28235
|
return;
|
|
@@ -28623,6 +28634,7 @@ function buildCommandPaletteItems(mode, availability) {
|
|
|
28623
28634
|
items.push({ id: "permission-preset", title: "Permission preset", description: "Choose the next-turn permission behavior", group: "Suggested", shortcut: "esc o", action: "open-permissions", keywords: ["permission", "policy", "approval"] });
|
|
28624
28635
|
items.push({ id: "configure-providers", title: "Configure providers", description: "Persist default providers and model profiles", group: "Suggested", shortcut: "esc c", action: "open-config", keywords: ["config", "settings", "provider", "model"] });
|
|
28625
28636
|
items.push({ id: "select-session", title: "Select session", description: "Open the saved session picker", group: "Session", shortcut: "esc s", action: "open-session", keywords: ["session", "sessions", "conversation", "switch"] });
|
|
28637
|
+
items.push({ id: "delete-current-session", title: "Delete current session", description: "Remove the current saved conversation", group: "Session", shortcut: "/session delete", action: "delete-session", keywords: ["delete", "remove", "session", "conversation"] });
|
|
28626
28638
|
}
|
|
28627
28639
|
for (const command of SLASH_COMMANDS) {
|
|
28628
28640
|
if (!slashCommandActiveInMode(command, mode)) continue;
|
|
@@ -28745,6 +28757,12 @@ function executeCommandPaletteItem({
|
|
|
28745
28757
|
case "open-session":
|
|
28746
28758
|
store.submitSessionSelectionShortcut();
|
|
28747
28759
|
return;
|
|
28760
|
+
case "delete-session":
|
|
28761
|
+
store.clearPromptInput({ notify: false });
|
|
28762
|
+
store.appendPromptInput("/session delete", { notify: false });
|
|
28763
|
+
promptDraft.setValue("/session delete");
|
|
28764
|
+
store.submitPromptInput();
|
|
28765
|
+
return;
|
|
28748
28766
|
case "clear-prompt":
|
|
28749
28767
|
store.clearPromptInput({ notify: false });
|
|
28750
28768
|
promptDraft.setValue("");
|
|
@@ -28879,7 +28897,7 @@ function SessionStart({ state, version }) {
|
|
|
28879
28897
|
React.createElement(
|
|
28880
28898
|
Box,
|
|
28881
28899
|
{ alignSelf: "center", width, justifyContent: "space-between", marginTop: 1 },
|
|
28882
|
-
React.createElement(Text, { color: "white" }, "enter open \xB7 n new \xB7 q quit"),
|
|
28900
|
+
React.createElement(Text, { color: "white" }, "enter open \xB7 n new \xB7 d delete \xB7 esc cancel \xB7 q quit"),
|
|
28883
28901
|
React.createElement(Text, { color: "white" }, `v${version}`)
|
|
28884
28902
|
),
|
|
28885
28903
|
React.createElement(Box, { flexGrow: 1 })
|
|
@@ -28915,12 +28933,41 @@ function EmptyState({
|
|
|
28915
28933
|
}),
|
|
28916
28934
|
React.createElement(CommandGuide, { hint, color: shortcutMode ? "cyan" : "gray", error: state.promptError }),
|
|
28917
28935
|
hasOverlay ? null : React.createElement(SessionMetaBox, { state, version, marginTop: 2 }),
|
|
28936
|
+
hasOverlay ? null : React.createElement(SessionCommandsBox, { marginTop: 1 }),
|
|
28918
28937
|
React.createElement(
|
|
28919
28938
|
Box,
|
|
28920
28939
|
{ flexGrow: 1 }
|
|
28921
28940
|
)
|
|
28922
28941
|
);
|
|
28923
28942
|
}
|
|
28943
|
+
function initialSessionCommandLines(width) {
|
|
28944
|
+
const target = Math.max(24, width);
|
|
28945
|
+
if (target < 48) {
|
|
28946
|
+
return [
|
|
28947
|
+
"Session commands",
|
|
28948
|
+
"esc s sessions",
|
|
28949
|
+
"/session list",
|
|
28950
|
+
"/session new [title]",
|
|
28951
|
+
"/session switch <target>",
|
|
28952
|
+
"/session rename <title>",
|
|
28953
|
+
"/session delete [target]"
|
|
28954
|
+
].map((line) => fitToWidth(line, target));
|
|
28955
|
+
}
|
|
28956
|
+
const commandWidth = Math.min(28, Math.max(24, Math.floor(target * 0.42)));
|
|
28957
|
+
const descriptionWidth = Math.max(0, target - commandWidth - 1);
|
|
28958
|
+
const rows = [
|
|
28959
|
+
["esc s", "open saved sessions"],
|
|
28960
|
+
["/session list", "list saved sessions"],
|
|
28961
|
+
["/session new [title]", "create a session"],
|
|
28962
|
+
["/session switch <target>", "switch by number, id, or title"],
|
|
28963
|
+
["/session rename <title>", "rename current session"],
|
|
28964
|
+
["/session delete [target]", "delete current or saved session"]
|
|
28965
|
+
];
|
|
28966
|
+
return [
|
|
28967
|
+
fitToWidth("Session commands", target),
|
|
28968
|
+
...rows.map(([command, description]) => `${padCell(command, commandWidth)} ${truncateEndToWidth(description, descriptionWidth)}`)
|
|
28969
|
+
];
|
|
28970
|
+
}
|
|
28924
28971
|
function LoadingView({ state }) {
|
|
28925
28972
|
const title = state.inputMode === "starting" ? "Demian runtime" : "Preparing session";
|
|
28926
28973
|
const message = state.activity || (state.inputMode === "starting" ? "Loading configuration" : "Starting session");
|
|
@@ -29718,7 +29765,8 @@ function SessionSelector({ state }) {
|
|
|
29718
29765
|
sessionOptionLine(item, index, selected, contentWidth)
|
|
29719
29766
|
);
|
|
29720
29767
|
}),
|
|
29721
|
-
items.length === 0 ? React.createElement(Text, { color: "gray" }, "No saved sessions. Press n to create one.") : null
|
|
29768
|
+
items.length === 0 ? React.createElement(Text, { color: "gray" }, "No saved sessions. Press n to create one.") : null,
|
|
29769
|
+
React.createElement(Text, { color: "gray" }, "enter switch | n new | d delete | esc cancel")
|
|
29722
29770
|
);
|
|
29723
29771
|
}
|
|
29724
29772
|
function sessionSelectorColumns(width) {
|
|
@@ -30134,7 +30182,7 @@ function clamp01(value) {
|
|
|
30134
30182
|
if (!Number.isFinite(value)) return 0;
|
|
30135
30183
|
return Math.max(0, Math.min(1, value));
|
|
30136
30184
|
}
|
|
30137
|
-
var SURFACE, CSI_U_INPUT, XTERM_MODIFIED_ENTER_INPUT, SLASH_COMMANDS, PromptDraftController, SessionMetaBox, DEMIAN_WORDMARK_LINES, DemianWordmark, TranscriptView, BlockView, CommandGuide;
|
|
30185
|
+
var SURFACE, CSI_U_INPUT, XTERM_MODIFIED_ENTER_INPUT, SLASH_COMMANDS, PromptDraftController, SessionMetaBox, SessionCommandsBox, DEMIAN_WORDMARK_LINES, DemianWordmark, TranscriptView, BlockView, CommandGuide;
|
|
30138
30186
|
var init_app = __esm({
|
|
30139
30187
|
"src/ui/tui/app.ts"() {
|
|
30140
30188
|
"use strict";
|
|
@@ -30248,6 +30296,25 @@ var init_app = __esm({
|
|
|
30248
30296
|
...rows.map((line, index) => React.createElement(SurfaceLine, { key: index, text: line ?? "", width: width - 4, backgroundColor: SURFACE.status, color: "white" }))
|
|
30249
30297
|
);
|
|
30250
30298
|
});
|
|
30299
|
+
SessionCommandsBox = React.memo(function SessionCommandsBox2({ marginTop = 1 }) {
|
|
30300
|
+
const width = promptPanelWidth();
|
|
30301
|
+
const contentWidth = Math.max(24, width - 4);
|
|
30302
|
+
const lines = initialSessionCommandLines(contentWidth);
|
|
30303
|
+
return React.createElement(
|
|
30304
|
+
Box,
|
|
30305
|
+
{ alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "#334155", paddingX: 1, marginTop },
|
|
30306
|
+
...lines.map(
|
|
30307
|
+
(line, index) => React.createElement(SurfaceLine, {
|
|
30308
|
+
key: index,
|
|
30309
|
+
text: line,
|
|
30310
|
+
width: contentWidth,
|
|
30311
|
+
backgroundColor: SURFACE.status,
|
|
30312
|
+
color: index === 0 ? "magenta" : "gray",
|
|
30313
|
+
bold: index === 0
|
|
30314
|
+
})
|
|
30315
|
+
)
|
|
30316
|
+
);
|
|
30317
|
+
});
|
|
30251
30318
|
DEMIAN_WORDMARK_LINES = [
|
|
30252
30319
|
" _ _ ",
|
|
30253
30320
|
" __| | ___ _ __ ___ (_) __ _ _ __ ",
|
|
@@ -35032,17 +35099,17 @@ function findDependencyCycle(group) {
|
|
|
35032
35099
|
const byId = new Map(group.map((member) => [member.memberId, member]));
|
|
35033
35100
|
const visiting = /* @__PURE__ */ new Set();
|
|
35034
35101
|
const visited = /* @__PURE__ */ new Set();
|
|
35035
|
-
const
|
|
35102
|
+
const path38 = [];
|
|
35036
35103
|
const visit = (id) => {
|
|
35037
|
-
if (visiting.has(id)) return [...
|
|
35104
|
+
if (visiting.has(id)) return [...path38.slice(path38.indexOf(id)), id];
|
|
35038
35105
|
if (visited.has(id)) return void 0;
|
|
35039
35106
|
visiting.add(id);
|
|
35040
|
-
|
|
35107
|
+
path38.push(id);
|
|
35041
35108
|
for (const dep of byId.get(id)?.dependsOn ?? []) {
|
|
35042
35109
|
const cycle = visit(dep);
|
|
35043
35110
|
if (cycle) return cycle;
|
|
35044
35111
|
}
|
|
35045
|
-
|
|
35112
|
+
path38.pop();
|
|
35046
35113
|
visiting.delete(id);
|
|
35047
35114
|
visited.add(id);
|
|
35048
35115
|
return void 0;
|
|
@@ -36988,22 +37055,13 @@ ${sessionListMessage()}`);
|
|
|
36988
37055
|
return;
|
|
36989
37056
|
}
|
|
36990
37057
|
if (command.action === "delete") {
|
|
36991
|
-
const target = findConversation(sortedConversations(), command.target);
|
|
37058
|
+
const target = command.target ? findConversation(sortedConversations(), command.target) : currentConversation;
|
|
36992
37059
|
if (!target) {
|
|
36993
37060
|
store.showSystemMessage("Sessions", `No session matched "${command.target ?? ""}".`);
|
|
36994
37061
|
store.prepareForPrompt("waiting for next message");
|
|
36995
37062
|
return;
|
|
36996
37063
|
}
|
|
36997
|
-
|
|
36998
|
-
runtimeByConversation.delete(target.id);
|
|
36999
|
-
await deleteConversationRecord(flags.conversationStorageDir, target.id);
|
|
37000
|
-
if (currentConversation?.id === target.id) {
|
|
37001
|
-
const next = sortedConversations()[0] ?? createConversationRecord({ id: createRootSessionId(), cwd });
|
|
37002
|
-
conversationRecords.set(next.id, next);
|
|
37003
|
-
await switchConversation(next);
|
|
37004
|
-
}
|
|
37005
|
-
store.prepareForPrompt(`deleted session: ${target.title}`);
|
|
37006
|
-
await persistCurrentConversation();
|
|
37064
|
+
await deleteConversationById(target.id);
|
|
37007
37065
|
return;
|
|
37008
37066
|
}
|
|
37009
37067
|
if (command.action === "rename") {
|
|
@@ -37047,6 +37105,10 @@ ${sessionListMessage()}`);
|
|
|
37047
37105
|
return;
|
|
37048
37106
|
}
|
|
37049
37107
|
if (selection.kind === "exit" || store.exitRequested()) return;
|
|
37108
|
+
if (selection.kind === "delete") {
|
|
37109
|
+
await deleteConversationById(selection.id);
|
|
37110
|
+
return;
|
|
37111
|
+
}
|
|
37050
37112
|
await applyStartupSessionSelection(selection);
|
|
37051
37113
|
}
|
|
37052
37114
|
function startupSessionOptions() {
|
|
@@ -37067,6 +37129,10 @@ ${sessionListMessage()}`);
|
|
|
37067
37129
|
store.prepareForPrompt("waiting for next message");
|
|
37068
37130
|
return;
|
|
37069
37131
|
}
|
|
37132
|
+
if (selection.kind === "delete") {
|
|
37133
|
+
await deleteConversationById(selection.id);
|
|
37134
|
+
return;
|
|
37135
|
+
}
|
|
37070
37136
|
if (selection.kind === "new") {
|
|
37071
37137
|
const next = createConversationRecord({ id: createRootSessionId(), cwd });
|
|
37072
37138
|
conversationRecords.set(next.id, next);
|
|
@@ -37088,6 +37154,24 @@ ${sessionListMessage()}`);
|
|
|
37088
37154
|
store.prepareForPrompt(`session ready: ${target.title}`);
|
|
37089
37155
|
await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], target.id);
|
|
37090
37156
|
}
|
|
37157
|
+
async function deleteConversationById(id) {
|
|
37158
|
+
const target = conversationRecords.get(id) ?? findConversation(sortedConversations(), id);
|
|
37159
|
+
if (!target) {
|
|
37160
|
+
store.showSystemMessage("Sessions", `No session matched "${id}".`);
|
|
37161
|
+
store.prepareForPrompt("waiting for next message");
|
|
37162
|
+
return;
|
|
37163
|
+
}
|
|
37164
|
+
conversationRecords.delete(target.id);
|
|
37165
|
+
runtimeByConversation.delete(target.id);
|
|
37166
|
+
await deleteConversationRecord(flags.conversationStorageDir, target.id);
|
|
37167
|
+
if (currentConversation?.id === target.id) {
|
|
37168
|
+
const next = sortedConversations()[0] ?? createConversationRecord({ id: createRootSessionId(), cwd });
|
|
37169
|
+
conversationRecords.set(next.id, next);
|
|
37170
|
+
await switchConversation(next);
|
|
37171
|
+
}
|
|
37172
|
+
store.prepareForPrompt(`deleted session: ${target.title}`);
|
|
37173
|
+
await persistCurrentConversation();
|
|
37174
|
+
}
|
|
37091
37175
|
async function switchConversation(record) {
|
|
37092
37176
|
currentConversation = record;
|
|
37093
37177
|
rootSessionId = record.id;
|
|
@@ -37124,17 +37208,17 @@ ${sessionListMessage()}`);
|
|
|
37124
37208
|
savedSelectionKey = key;
|
|
37125
37209
|
}
|
|
37126
37210
|
async function handleConfigAction(action) {
|
|
37127
|
-
const
|
|
37211
|
+
const path38 = configMutationPath(flags);
|
|
37128
37212
|
try {
|
|
37129
37213
|
let message = "Config updated.";
|
|
37130
37214
|
let nextSelection = {};
|
|
37131
37215
|
if (action.type === "setDefaultProvider") {
|
|
37132
|
-
await updateConfigDefaults({ path:
|
|
37216
|
+
await updateConfigDefaults({ path: path38, defaultProvider: action.provider });
|
|
37133
37217
|
message = `Default provider saved: ${action.provider}`;
|
|
37134
37218
|
nextSelection = { provider: action.provider };
|
|
37135
37219
|
} else if (action.type === "setDefaultModel") {
|
|
37136
37220
|
await setDefaultModelProfile({
|
|
37137
|
-
path:
|
|
37221
|
+
path: path38,
|
|
37138
37222
|
provider: action.provider,
|
|
37139
37223
|
profileName: action.profileName,
|
|
37140
37224
|
name: action.name,
|
|
@@ -37144,12 +37228,12 @@ ${sessionListMessage()}`);
|
|
|
37144
37228
|
message = `Default model saved for ${action.provider}: ${action.displayName ?? action.model}`;
|
|
37145
37229
|
nextSelection = { provider: action.provider, model: action.displayName ?? action.model };
|
|
37146
37230
|
} else if (action.type === "addProvider") {
|
|
37147
|
-
await addProvider({ path:
|
|
37231
|
+
await addProvider({ path: path38, name: action.name, preset: action.preset, apiKeyEnv: action.apiKeyEnv });
|
|
37148
37232
|
message = `Provider added: ${action.name}`;
|
|
37149
37233
|
nextSelection = { provider: action.name };
|
|
37150
37234
|
} else if (action.type === "updateProvider") {
|
|
37151
37235
|
await updateProviderSettings({
|
|
37152
|
-
path:
|
|
37236
|
+
path: path38,
|
|
37153
37237
|
provider: action.provider,
|
|
37154
37238
|
...action.field === "baseURL" ? { baseURL: action.value } : {},
|
|
37155
37239
|
...action.field === "apiKeyEnv" ? { apiKeyEnv: action.value } : {},
|
|
@@ -37230,7 +37314,7 @@ function sessionUsage() {
|
|
|
37230
37314
|
" /session new [title]",
|
|
37231
37315
|
" /session switch <number|id|title>",
|
|
37232
37316
|
" /session rename <title>",
|
|
37233
|
-
" /session delete
|
|
37317
|
+
" /session delete [number|id|title]",
|
|
37234
37318
|
" /session clear",
|
|
37235
37319
|
"",
|
|
37236
37320
|
"Aliases: /sessions, /conversation, /conversations, /conv"
|
|
@@ -37262,6 +37346,151 @@ var init_controller = __esm({
|
|
|
37262
37346
|
}
|
|
37263
37347
|
});
|
|
37264
37348
|
|
|
37349
|
+
// src/ui/tui/engine/ink-engine.ts
|
|
37350
|
+
var ink_engine_exports = {};
|
|
37351
|
+
__export(ink_engine_exports, {
|
|
37352
|
+
runInkTuiEngine: () => runInkTuiEngine
|
|
37353
|
+
});
|
|
37354
|
+
async function runInkTuiEngine(options) {
|
|
37355
|
+
const [{ render }, ReactModule, { TuiApp: TuiApp2 }, { TuiStore: TuiStore2 }, { runTuiSession: runTuiSession2 }] = await Promise.all([
|
|
37356
|
+
dynamicImport4("ink"),
|
|
37357
|
+
dynamicImport4("react"),
|
|
37358
|
+
Promise.resolve().then(() => (init_app(), app_exports)),
|
|
37359
|
+
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
37360
|
+
Promise.resolve().then(() => (init_controller(), controller_exports))
|
|
37361
|
+
]);
|
|
37362
|
+
const React2 = ReactModule.default;
|
|
37363
|
+
const store = new TuiStore2();
|
|
37364
|
+
const instance = render(React2.createElement(TuiApp2, { store, version: options.version }));
|
|
37365
|
+
try {
|
|
37366
|
+
const code = await runTuiSession2({ ...options.flags, conversationManagement: true }, store);
|
|
37367
|
+
setTimeout(() => instance.unmount(), 100);
|
|
37368
|
+
await instance.waitUntilExit();
|
|
37369
|
+
return code;
|
|
37370
|
+
} catch (error) {
|
|
37371
|
+
instance.unmount();
|
|
37372
|
+
await instance.waitUntilExit().catch(() => void 0);
|
|
37373
|
+
throw error;
|
|
37374
|
+
}
|
|
37375
|
+
}
|
|
37376
|
+
var dynamicImport4;
|
|
37377
|
+
var init_ink_engine = __esm({
|
|
37378
|
+
"src/ui/tui/engine/ink-engine.ts"() {
|
|
37379
|
+
"use strict";
|
|
37380
|
+
dynamicImport4 = new Function("specifier", "return import(specifier)");
|
|
37381
|
+
}
|
|
37382
|
+
});
|
|
37383
|
+
|
|
37384
|
+
// src/ui/tui/engine/bubbletea-engine.ts
|
|
37385
|
+
var bubbletea_engine_exports = {};
|
|
37386
|
+
__export(bubbletea_engine_exports, {
|
|
37387
|
+
BubbleTeaSidecarUnavailableError: () => BubbleTeaSidecarUnavailableError,
|
|
37388
|
+
bubbleTeaSidecarCandidates: () => bubbleTeaSidecarCandidates,
|
|
37389
|
+
bubbleTeaSidecarPlatformKey: () => bubbleTeaSidecarPlatformKey,
|
|
37390
|
+
resolveBubbleTeaSidecarBinary: () => resolveBubbleTeaSidecarBinary,
|
|
37391
|
+
runBubbleTeaTuiEngine: () => runBubbleTeaTuiEngine
|
|
37392
|
+
});
|
|
37393
|
+
import { access } from "node:fs/promises";
|
|
37394
|
+
import { constants } from "node:fs";
|
|
37395
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
37396
|
+
import path37 from "node:path";
|
|
37397
|
+
import { fileURLToPath } from "node:url";
|
|
37398
|
+
function bubbleTeaSidecarPlatformKey(platform = process.platform, arch = process.arch) {
|
|
37399
|
+
if (platform === "darwin" && arch === "arm64") return "darwin-arm64";
|
|
37400
|
+
if (platform === "linux" && arch === "x64") return "linux-x64";
|
|
37401
|
+
return void 0;
|
|
37402
|
+
}
|
|
37403
|
+
function bubbleTeaSidecarCandidates(platformKey = bubbleTeaSidecarPlatformKey(), moduleUrl = import.meta.url) {
|
|
37404
|
+
if (!platformKey) return [];
|
|
37405
|
+
const names = [
|
|
37406
|
+
new URL(`./tui-sidecar/${platformKey}/demian-tui`, moduleUrl),
|
|
37407
|
+
new URL(`../../../../dist/tui-sidecar/${platformKey}/demian-tui`, moduleUrl)
|
|
37408
|
+
].map((url) => fileURLToPath(url));
|
|
37409
|
+
return [...new Set(names.map((name) => path37.resolve(name)))];
|
|
37410
|
+
}
|
|
37411
|
+
async function resolveBubbleTeaSidecarBinary(options = {}) {
|
|
37412
|
+
const platformKey = options.platformKey ?? bubbleTeaSidecarPlatformKey();
|
|
37413
|
+
if (!platformKey) {
|
|
37414
|
+
throw new BubbleTeaSidecarUnavailableError(`Bubble Tea sidecar is not packaged for ${process.platform}-${process.arch}.`);
|
|
37415
|
+
}
|
|
37416
|
+
const candidates = options.candidates ?? bubbleTeaSidecarCandidates(platformKey);
|
|
37417
|
+
for (const candidate of candidates) {
|
|
37418
|
+
try {
|
|
37419
|
+
await access(candidate, constants.X_OK);
|
|
37420
|
+
return candidate;
|
|
37421
|
+
} catch {
|
|
37422
|
+
}
|
|
37423
|
+
}
|
|
37424
|
+
throw new BubbleTeaSidecarUnavailableError(`Bubble Tea sidecar binary was not found for ${platformKey}. Run npm run build:tui-sidecar.`);
|
|
37425
|
+
}
|
|
37426
|
+
async function runBubbleTeaTuiEngine(options) {
|
|
37427
|
+
try {
|
|
37428
|
+
const binaryPath = await resolveBubbleTeaSidecarBinary();
|
|
37429
|
+
const probe = await runSidecarProbe(binaryPath);
|
|
37430
|
+
process.stderr.write(`Bubble Tea sidecar ${probe.version ?? "unknown"} is available, but IPC rendering is still gated; falling back to Ink for this run.
|
|
37431
|
+
`);
|
|
37432
|
+
} catch (error) {
|
|
37433
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)} Falling back to Ink TUI.
|
|
37434
|
+
`);
|
|
37435
|
+
}
|
|
37436
|
+
return runInkTuiEngine(options);
|
|
37437
|
+
}
|
|
37438
|
+
async function runSidecarProbe(binaryPath, timeoutMs = 1500) {
|
|
37439
|
+
return await new Promise((resolve, reject) => {
|
|
37440
|
+
const child = spawn8(binaryPath, ["--probe"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
37441
|
+
let stdout = "";
|
|
37442
|
+
let stderr = "";
|
|
37443
|
+
let settled = false;
|
|
37444
|
+
const timer = setTimeout(() => {
|
|
37445
|
+
if (settled) return;
|
|
37446
|
+
settled = true;
|
|
37447
|
+
child.kill("SIGTERM");
|
|
37448
|
+
reject(new BubbleTeaSidecarUnavailableError(`Bubble Tea sidecar startup timed out after ${timeoutMs}ms.`));
|
|
37449
|
+
}, timeoutMs);
|
|
37450
|
+
child.stdout?.setEncoding("utf8");
|
|
37451
|
+
child.stderr?.setEncoding("utf8");
|
|
37452
|
+
child.stdout?.on("data", (chunk) => {
|
|
37453
|
+
stdout += chunk;
|
|
37454
|
+
});
|
|
37455
|
+
child.stderr?.on("data", (chunk) => {
|
|
37456
|
+
stderr += chunk;
|
|
37457
|
+
});
|
|
37458
|
+
child.on("error", (error) => {
|
|
37459
|
+
if (settled) return;
|
|
37460
|
+
settled = true;
|
|
37461
|
+
clearTimeout(timer);
|
|
37462
|
+
reject(new BubbleTeaSidecarUnavailableError(`Bubble Tea sidecar failed to start: ${error.message}`, { cause: error }));
|
|
37463
|
+
});
|
|
37464
|
+
child.on("close", (code) => {
|
|
37465
|
+
if (settled) return;
|
|
37466
|
+
settled = true;
|
|
37467
|
+
clearTimeout(timer);
|
|
37468
|
+
if (code !== 0) {
|
|
37469
|
+
reject(new BubbleTeaSidecarUnavailableError(`Bubble Tea sidecar probe exited with ${code ?? "unknown"}${stderr ? `: ${stderr.trim()}` : ""}`));
|
|
37470
|
+
return;
|
|
37471
|
+
}
|
|
37472
|
+
try {
|
|
37473
|
+
resolve(JSON.parse(stdout));
|
|
37474
|
+
} catch (error) {
|
|
37475
|
+
reject(new BubbleTeaSidecarUnavailableError("Bubble Tea sidecar probe returned invalid JSON.", { cause: error }));
|
|
37476
|
+
}
|
|
37477
|
+
});
|
|
37478
|
+
});
|
|
37479
|
+
}
|
|
37480
|
+
var BubbleTeaSidecarUnavailableError;
|
|
37481
|
+
var init_bubbletea_engine = __esm({
|
|
37482
|
+
"src/ui/tui/engine/bubbletea-engine.ts"() {
|
|
37483
|
+
"use strict";
|
|
37484
|
+
init_ink_engine();
|
|
37485
|
+
BubbleTeaSidecarUnavailableError = class extends Error {
|
|
37486
|
+
constructor(message, options) {
|
|
37487
|
+
super(message, options);
|
|
37488
|
+
this.name = "BubbleTeaSidecarUnavailableError";
|
|
37489
|
+
}
|
|
37490
|
+
};
|
|
37491
|
+
}
|
|
37492
|
+
});
|
|
37493
|
+
|
|
37265
37494
|
// src/tui.ts
|
|
37266
37495
|
init_parser();
|
|
37267
37496
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
@@ -37960,8 +38189,23 @@ async function checkCodexAuth() {
|
|
|
37960
38189
|
return { ok: false, source: "none", message: "no codex auth file or OPENAI_API_KEY; run `demian auth login codex`" };
|
|
37961
38190
|
}
|
|
37962
38191
|
|
|
38192
|
+
// src/ui/tui/engine/index.ts
|
|
38193
|
+
var TUI_ENGINE_NAMES = /* @__PURE__ */ new Set(["ink", "bubbletea"]);
|
|
38194
|
+
function resolveTuiEngineName(options = {}) {
|
|
38195
|
+
const raw = (options.cliEngine ?? options.env?.DEMIAN_TUI_ENGINE ?? "ink").trim().toLowerCase();
|
|
38196
|
+
if (TUI_ENGINE_NAMES.has(raw)) return raw;
|
|
38197
|
+
throw new Error(`Unsupported TUI engine "${raw}". Use "ink" or "bubbletea".`);
|
|
38198
|
+
}
|
|
38199
|
+
async function runTuiEngine(engine, options) {
|
|
38200
|
+
if (engine === "ink") {
|
|
38201
|
+
const { runInkTuiEngine: runInkTuiEngine2 } = await Promise.resolve().then(() => (init_ink_engine(), ink_engine_exports));
|
|
38202
|
+
return runInkTuiEngine2(options);
|
|
38203
|
+
}
|
|
38204
|
+
const { runBubbleTeaTuiEngine: runBubbleTeaTuiEngine2 } = await Promise.resolve().then(() => (init_bubbletea_engine(), bubbletea_engine_exports));
|
|
38205
|
+
return runBubbleTeaTuiEngine2(options);
|
|
38206
|
+
}
|
|
38207
|
+
|
|
37963
38208
|
// src/tui.ts
|
|
37964
|
-
var dynamicImport4 = new Function("specifier", "return import(specifier)");
|
|
37965
38209
|
async function main(argv = process.argv.slice(2)) {
|
|
37966
38210
|
const configCommandResult = await maybeRunConfigCommand(argv);
|
|
37967
38211
|
if (configCommandResult !== void 0) return configCommandResult;
|
|
@@ -37978,26 +38222,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
37978
38222
|
return 1;
|
|
37979
38223
|
}
|
|
37980
38224
|
try {
|
|
37981
|
-
const
|
|
37982
|
-
|
|
37983
|
-
dynamicImport4("react"),
|
|
37984
|
-
Promise.resolve().then(() => (init_app(), app_exports)),
|
|
37985
|
-
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
37986
|
-
Promise.resolve().then(() => (init_controller(), controller_exports))
|
|
37987
|
-
]);
|
|
37988
|
-
const React2 = ReactModule.default;
|
|
37989
|
-
const store = new TuiStore2();
|
|
37990
|
-
const instance = render(React2.createElement(TuiApp2, { store, version: packageVersion() }));
|
|
37991
|
-
try {
|
|
37992
|
-
const code = await runTuiSession2({ ...flags, conversationManagement: true }, store);
|
|
37993
|
-
setTimeout(() => instance.unmount(), 100);
|
|
37994
|
-
await instance.waitUntilExit();
|
|
37995
|
-
return code;
|
|
37996
|
-
} catch (error) {
|
|
37997
|
-
instance.unmount();
|
|
37998
|
-
await instance.waitUntilExit().catch(() => void 0);
|
|
37999
|
-
throw error;
|
|
38000
|
-
}
|
|
38225
|
+
const engine = resolveTuiEngineName({ cliEngine: flags.engine, env: process.env });
|
|
38226
|
+
return await runTuiEngine(engine, { flags, version: packageVersion() });
|
|
38001
38227
|
} catch (error) {
|
|
38002
38228
|
if (isMissingDependency(error)) {
|
|
38003
38229
|
process.stderr.write("TUI dependencies are not installed. Run `npm install` in demian, or use `demian-plain`.\n");
|
|
@@ -38059,6 +38285,14 @@ function parseArgs(argv) {
|
|
|
38059
38285
|
out.goal = true;
|
|
38060
38286
|
continue;
|
|
38061
38287
|
}
|
|
38288
|
+
if (arg === "--engine") {
|
|
38289
|
+
out.engine = takeValue2(argv, ++i, arg);
|
|
38290
|
+
continue;
|
|
38291
|
+
}
|
|
38292
|
+
if (arg.startsWith("--engine=")) {
|
|
38293
|
+
out.engine = arg.slice("--engine=".length);
|
|
38294
|
+
continue;
|
|
38295
|
+
}
|
|
38062
38296
|
if (arg === "--image") {
|
|
38063
38297
|
out.images.push(takeValue2(argv, ++i, arg));
|
|
38064
38298
|
continue;
|
|
@@ -38145,7 +38379,7 @@ Default:
|
|
|
38145
38379
|
Use /cowork <task> to explicitly ask Demian to coordinate cowork sub agents in multi-agent mode.
|
|
38146
38380
|
Use /compact to compact current history, /stop to stop active work, and /exit or /quit to close the TUI.
|
|
38147
38381
|
Use /session list, /session new, /session switch <number|id|title>, /session rename <title>,
|
|
38148
|
-
and /session delete
|
|
38382
|
+
and /session delete [number|id|title] to manage saved CLI conversations.
|
|
38149
38383
|
Press Esc then q to quit, Esc then s while running to stop the task, and Esc then u to clear the composer.
|
|
38150
38384
|
Tool approvals show the requested command/path in a permission dialog.
|
|
38151
38385
|
Press y to allow once, a to always allow the grant scope, or n/Enter to deny.
|
|
@@ -38174,6 +38408,8 @@ Flags:
|
|
|
38174
38408
|
--no-wizard skip the first-run config wizard
|
|
38175
38409
|
--goal run the initial prompt as an explicit /goal objective
|
|
38176
38410
|
/ralph-loop --fresh-context is single-agent only
|
|
38411
|
+
--engine <ink|bubbletea>
|
|
38412
|
+
select the TUI renderer; defaults to ink during migration
|
|
38177
38413
|
--config <path> load an additional config file
|
|
38178
38414
|
--help, -h show this help
|
|
38179
38415
|
|
package/dist/vscode-worker.mjs
CHANGED
|
@@ -1139,6 +1139,12 @@ var TuiStore = class {
|
|
|
1139
1139
|
}
|
|
1140
1140
|
if (option.id) this.#resolveSession({ kind: "session", id: option.id });
|
|
1141
1141
|
}
|
|
1142
|
+
submitDeleteSessionSelection() {
|
|
1143
|
+
if (this.#state.inputMode !== "session") return;
|
|
1144
|
+
const option = this.#state.sessionOptions[this.#state.sessionCursor];
|
|
1145
|
+
if (!option || option.kind === "new" || !option.id) return;
|
|
1146
|
+
this.#resolveSession({ kind: "delete", id: option.id });
|
|
1147
|
+
}
|
|
1142
1148
|
submitNewSessionSelection() {
|
|
1143
1149
|
if (this.#state.inputMode !== "session") return;
|
|
1144
1150
|
this.#resolveSession({ kind: "new" });
|
|
@@ -33833,22 +33839,13 @@ ${sessionListMessage()}`);
|
|
|
33833
33839
|
return;
|
|
33834
33840
|
}
|
|
33835
33841
|
if (command.action === "delete") {
|
|
33836
|
-
const target = findConversation(sortedConversations(), command.target);
|
|
33842
|
+
const target = command.target ? findConversation(sortedConversations(), command.target) : currentConversation;
|
|
33837
33843
|
if (!target) {
|
|
33838
33844
|
store2.showSystemMessage("Sessions", `No session matched "${command.target ?? ""}".`);
|
|
33839
33845
|
store2.prepareForPrompt("waiting for next message");
|
|
33840
33846
|
return;
|
|
33841
33847
|
}
|
|
33842
|
-
|
|
33843
|
-
runtimeByConversation.delete(target.id);
|
|
33844
|
-
await deleteConversationRecord(flags.conversationStorageDir, target.id);
|
|
33845
|
-
if (currentConversation?.id === target.id) {
|
|
33846
|
-
const next = sortedConversations()[0] ?? createConversationRecord({ id: createRootSessionId(), cwd });
|
|
33847
|
-
conversationRecords.set(next.id, next);
|
|
33848
|
-
await switchConversation(next);
|
|
33849
|
-
}
|
|
33850
|
-
store2.prepareForPrompt(`deleted session: ${target.title}`);
|
|
33851
|
-
await persistCurrentConversation();
|
|
33848
|
+
await deleteConversationById(target.id);
|
|
33852
33849
|
return;
|
|
33853
33850
|
}
|
|
33854
33851
|
if (command.action === "rename") {
|
|
@@ -33892,6 +33889,10 @@ ${sessionListMessage()}`);
|
|
|
33892
33889
|
return;
|
|
33893
33890
|
}
|
|
33894
33891
|
if (selection.kind === "exit" || store2.exitRequested()) return;
|
|
33892
|
+
if (selection.kind === "delete") {
|
|
33893
|
+
await deleteConversationById(selection.id);
|
|
33894
|
+
return;
|
|
33895
|
+
}
|
|
33895
33896
|
await applyStartupSessionSelection(selection);
|
|
33896
33897
|
}
|
|
33897
33898
|
function startupSessionOptions() {
|
|
@@ -33912,6 +33913,10 @@ ${sessionListMessage()}`);
|
|
|
33912
33913
|
store2.prepareForPrompt("waiting for next message");
|
|
33913
33914
|
return;
|
|
33914
33915
|
}
|
|
33916
|
+
if (selection.kind === "delete") {
|
|
33917
|
+
await deleteConversationById(selection.id);
|
|
33918
|
+
return;
|
|
33919
|
+
}
|
|
33915
33920
|
if (selection.kind === "new") {
|
|
33916
33921
|
const next = createConversationRecord({ id: createRootSessionId(), cwd });
|
|
33917
33922
|
conversationRecords.set(next.id, next);
|
|
@@ -33933,6 +33938,24 @@ ${sessionListMessage()}`);
|
|
|
33933
33938
|
store2.prepareForPrompt(`session ready: ${target.title}`);
|
|
33934
33939
|
await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], target.id);
|
|
33935
33940
|
}
|
|
33941
|
+
async function deleteConversationById(id) {
|
|
33942
|
+
const target = conversationRecords.get(id) ?? findConversation(sortedConversations(), id);
|
|
33943
|
+
if (!target) {
|
|
33944
|
+
store2.showSystemMessage("Sessions", `No session matched "${id}".`);
|
|
33945
|
+
store2.prepareForPrompt("waiting for next message");
|
|
33946
|
+
return;
|
|
33947
|
+
}
|
|
33948
|
+
conversationRecords.delete(target.id);
|
|
33949
|
+
runtimeByConversation.delete(target.id);
|
|
33950
|
+
await deleteConversationRecord(flags.conversationStorageDir, target.id);
|
|
33951
|
+
if (currentConversation?.id === target.id) {
|
|
33952
|
+
const next = sortedConversations()[0] ?? createConversationRecord({ id: createRootSessionId(), cwd });
|
|
33953
|
+
conversationRecords.set(next.id, next);
|
|
33954
|
+
await switchConversation(next);
|
|
33955
|
+
}
|
|
33956
|
+
store2.prepareForPrompt(`deleted session: ${target.title}`);
|
|
33957
|
+
await persistCurrentConversation();
|
|
33958
|
+
}
|
|
33936
33959
|
async function switchConversation(record) {
|
|
33937
33960
|
currentConversation = record;
|
|
33938
33961
|
rootSessionId = record.id;
|
|
@@ -34075,7 +34098,7 @@ function sessionUsage() {
|
|
|
34075
34098
|
" /session new [title]",
|
|
34076
34099
|
" /session switch <number|id|title>",
|
|
34077
34100
|
" /session rename <title>",
|
|
34078
|
-
" /session delete
|
|
34101
|
+
" /session delete [number|id|title]",
|
|
34079
34102
|
" /session clear",
|
|
34080
34103
|
"",
|
|
34081
34104
|
"Aliases: /sessions, /conversation, /conversations, /conv"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "demian-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Local terminal coding agent with TUI, goals, cowork agents, permissions, and provider switching.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"icon": "media/demian.svg",
|
|
@@ -26,7 +26,10 @@
|
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "esbuild src/index.ts src/tui.ts src/cli.ts src/vscode-worker.ts --bundle --platform=node --format=esm --target=node22 --outdir=dist --out-extension:.js=.mjs --loader:.ts=ts --define:process.env.DEV=\\\"false\\\" --external:@anthropic-ai/claude-agent-sdk-* --external:ink --external:react --external:react-devtools-core --banner:js=\"import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);\"",
|
|
29
|
-
"
|
|
29
|
+
"build:tui-sidecar": "npm run build:tui-sidecar:darwin-arm64 && npm run build:tui-sidecar:linux-x64",
|
|
30
|
+
"build:tui-sidecar:darwin-arm64": "mkdir -p dist/tui-sidecar/darwin-arm64 && cd tui-sidecar && GOOS=darwin GOARCH=arm64 go build -o ../dist/tui-sidecar/darwin-arm64/demian-tui ./cmd/demian-tui",
|
|
31
|
+
"build:tui-sidecar:linux-x64": "mkdir -p dist/tui-sidecar/linux-x64 && cd tui-sidecar && GOOS=linux GOARCH=amd64 go build -o ../dist/tui-sidecar/linux-x64/demian-tui ./cmd/demian-tui",
|
|
32
|
+
"prepack": "npm run build && npm run build:tui-sidecar",
|
|
30
33
|
"test": "node --experimental-strip-types --test test/*.test.ts",
|
|
31
34
|
"smoke": "npm run smoke:tui && npm run smoke:plain",
|
|
32
35
|
"smoke:tui": "bin/demian --help",
|
|
@@ -41,9 +44,9 @@
|
|
|
41
44
|
"chalk": "^5.4.1",
|
|
42
45
|
"cli-highlight": "^2.1.11",
|
|
43
46
|
"highlight.js": "^11.11.1",
|
|
44
|
-
"ink": "^
|
|
47
|
+
"ink": "^7.0.3",
|
|
45
48
|
"marked": "^15.0.7",
|
|
46
|
-
"react": "^
|
|
49
|
+
"react": "^19.2.0",
|
|
47
50
|
"slice-ansi": "^7.1.0",
|
|
48
51
|
"string-width": "^7.2.0",
|
|
49
52
|
"wrap-ansi": "^9.0.0"
|