demian-cli 1.0.9 → 1.1.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.
package/dist/tui.mjs CHANGED
@@ -25727,7 +25727,7 @@ var init_store = __esm({
25727
25727
  }
25728
25728
  markTaskFailed(message) {
25729
25729
  const lines = [message];
25730
- if (this.#lastRetryPrompt()) lines.push("Press ctrl+r or type /retry to run the last task again.");
25730
+ if (this.#lastRetryPrompt()) lines.push("Press Esc then r or type /retry to run the last task again.");
25731
25731
  this.#append({ kind: "warning", title: "Task Failed", lines });
25732
25732
  this.#state.status.reason = "error";
25733
25733
  this.#state.inputMode = this.#exitRequested ? "done" : "prompt";
@@ -26987,9 +26987,10 @@ __export(app_exports, {
26987
26987
  });
26988
26988
  import React, { useEffect, useState } from "react";
26989
26989
  import { Box, Text, useApp, useInput } from "ink";
26990
- function TuiApp({ store }) {
26990
+ function TuiApp({ store, version = "dev" }) {
26991
26991
  const [state, setState] = useState(() => store.snapshot());
26992
26992
  const [now2, setNow] = useState(() => Date.now());
26993
+ const [shortcutMode, setShortcutMode] = useState(false);
26993
26994
  const app = useApp();
26994
26995
  useEffect(() => {
26995
26996
  const update = () => setState(store.snapshot());
@@ -27002,27 +27003,64 @@ function TuiApp({ store }) {
27002
27003
  const timer = setInterval(() => setNow(Date.now()), 450);
27003
27004
  return () => clearInterval(timer);
27004
27005
  }, [state.workPlan?.planId, state.inputMode]);
27006
+ useEffect(() => {
27007
+ setShortcutMode(false);
27008
+ }, [state.inputMode, state.permission !== void 0]);
27005
27009
  useInput((input2, key) => {
27006
27010
  const textInput = textInputForKeypress(input2, key);
27007
- if (key.ctrl && input2 === "c") {
27008
- store.requestExit();
27009
- app.exit();
27010
- return;
27011
- }
27012
- if (key.ctrl && input2 === "t") {
27013
- store.toggleLatestToolExpanded();
27014
- return;
27015
- }
27016
- if (key.ctrl && input2 === "p") {
27017
- store.toggleWorkPlanExpanded();
27018
- return;
27019
- }
27020
- if (key.ctrl && input2 === "r") {
27021
- store.retryLastPrompt();
27022
- return;
27023
- }
27024
- if (key.ctrl && input2 === "e") {
27025
- store.usePendingClaudeCodePlan();
27011
+ if (shortcutMode) {
27012
+ setShortcutMode(false);
27013
+ if (key.escape) return;
27014
+ const shortcut = input2.toLowerCase();
27015
+ if (shortcut === "q") {
27016
+ store.requestExit();
27017
+ app.exit();
27018
+ return;
27019
+ }
27020
+ if (shortcut === "s" && state.inputMode === "running") {
27021
+ store.stopActiveTask();
27022
+ return;
27023
+ }
27024
+ if (shortcut === "p" && state.inputMode === "prompt") {
27025
+ store.openProviderSelector();
27026
+ return;
27027
+ }
27028
+ if (shortcut === "a" && state.inputMode === "prompt") {
27029
+ store.openAgentSelector();
27030
+ return;
27031
+ }
27032
+ if (shortcut === "m" && state.inputMode === "prompt") {
27033
+ store.openModelEditor();
27034
+ return;
27035
+ }
27036
+ if (shortcut === "o" && state.inputMode === "prompt") {
27037
+ store.openPermissionPresetSelector();
27038
+ return;
27039
+ }
27040
+ if (shortcut === "u" && state.inputMode === "prompt") {
27041
+ store.clearPromptInput();
27042
+ return;
27043
+ }
27044
+ if (shortcut === "d" && state.turnDiff) {
27045
+ store.toggleDiffExpanded();
27046
+ return;
27047
+ }
27048
+ if (shortcut === "t" && state.blocks.some((block) => block.kind === "tool")) {
27049
+ store.toggleLatestToolExpanded();
27050
+ return;
27051
+ }
27052
+ if (shortcut === "w" && (state.workPlan || state.inputMode === "running")) {
27053
+ store.toggleWorkPlanExpanded();
27054
+ return;
27055
+ }
27056
+ if (shortcut === "e" && state.pendingClaudeCodePlan) {
27057
+ store.usePendingClaudeCodePlan();
27058
+ return;
27059
+ }
27060
+ if (shortcut === "r" && state.canRetryLastPrompt) {
27061
+ store.retryLastPrompt();
27062
+ return;
27063
+ }
27026
27064
  return;
27027
27065
  }
27028
27066
  if (state.permission) {
@@ -27094,10 +27132,6 @@ function TuiApp({ store }) {
27094
27132
  store.closeSettings();
27095
27133
  return;
27096
27134
  }
27097
- if (key.ctrl && input2 === "u") {
27098
- store.clearModelInput();
27099
- return;
27100
- }
27101
27135
  if (key.return) {
27102
27136
  if (textInput) store.appendModelInput(textInput);
27103
27137
  store.submitModelInput();
@@ -27111,10 +27145,6 @@ function TuiApp({ store }) {
27111
27145
  return;
27112
27146
  }
27113
27147
  if (state.inputMode === "prompt" || state.inputMode === "running") {
27114
- if (key.tab && state.turnDiff) {
27115
- store.toggleDiffExpanded();
27116
- return;
27117
- }
27118
27148
  if (state.inputMode === "prompt" && key.upArrow) {
27119
27149
  store.navigatePromptHistory(-1);
27120
27150
  return;
@@ -27123,24 +27153,8 @@ function TuiApp({ store }) {
27123
27153
  store.navigatePromptHistory(1);
27124
27154
  return;
27125
27155
  }
27126
- if (!key.return && state.inputMode === "prompt" && !state.promptInput && input2 === "p") {
27127
- store.openProviderSelector();
27128
- return;
27129
- }
27130
- if (!key.return && state.inputMode === "prompt" && !state.promptInput && input2 === "a") {
27131
- store.openAgentSelector();
27132
- return;
27133
- }
27134
- if (!key.return && state.inputMode === "prompt" && !state.promptInput && input2 === "m") {
27135
- store.openModelEditor();
27136
- return;
27137
- }
27138
- if (!key.return && state.inputMode === "prompt" && !state.promptInput && input2 === "o") {
27139
- store.openPermissionPresetSelector();
27140
- return;
27141
- }
27142
- if (key.ctrl && input2 === "u") {
27143
- store.clearPromptInput();
27156
+ if (key.escape) {
27157
+ setShortcutMode(true);
27144
27158
  return;
27145
27159
  }
27146
27160
  if (key.return) {
@@ -27156,17 +27170,29 @@ function TuiApp({ store }) {
27156
27170
  return;
27157
27171
  }
27158
27172
  });
27173
+ const shellProps = { flexDirection: "column", paddingX: 1, minHeight: terminalHeight() };
27174
+ if (shouldShowEmptyState(state)) {
27175
+ return React.createElement(
27176
+ Box,
27177
+ shellProps,
27178
+ React.createElement(EmptyState, { state, version, shortcutMode })
27179
+ );
27180
+ }
27159
27181
  return React.createElement(
27160
27182
  Box,
27161
- { flexDirection: "column", paddingX: 1 },
27162
- React.createElement(StatusBar, { state, now: now2 }),
27163
- React.createElement(SettingsBar, { state }),
27164
- React.createElement(shouldShowLoading(state) ? LoadingView : TranscriptView, { state }),
27165
- React.createElement(DiffAccordion, { state }),
27166
- React.createElement(WorkPlanPanel, { state, now: now2 }),
27167
- React.createElement(ActivityBar, { state }),
27168
- React.createElement(GoalIsland, { state }),
27169
- React.createElement(BottomBar, { state })
27183
+ shellProps,
27184
+ React.createElement(StatusBar, { state, now: now2, version }),
27185
+ React.createElement(
27186
+ Box,
27187
+ { flexDirection: "column", flexGrow: 1, paddingX: 1 },
27188
+ React.createElement(shouldShowLoading(state) ? LoadingView : TranscriptView, { state }),
27189
+ React.createElement(DiffAccordion, { state }),
27190
+ React.createElement(WorkPlanPanel, { state, now: now2 }),
27191
+ React.createElement(ActivityBar, { state }),
27192
+ React.createElement(GoalIsland, { state }),
27193
+ React.createElement(Box, { flexGrow: 1 }),
27194
+ React.createElement(InteractionPanel, { state, shortcutMode })
27195
+ )
27170
27196
  );
27171
27197
  }
27172
27198
  function textInputForKeypress(input2, key) {
@@ -27179,15 +27205,107 @@ function textInputForKeypress(input2, key) {
27179
27205
  function shouldShowLoading(state) {
27180
27206
  return state.inputMode === "starting" || state.inputMode === "running" && state.blocks.length === 0 && !state.streamingText;
27181
27207
  }
27208
+ function shouldShowEmptyState(state) {
27209
+ return state.inputMode === "prompt" && !state.permission && state.blocks.length === 0 && !state.streamingText && !state.turnDiff && !state.workPlan && !state.goal;
27210
+ }
27211
+ function EmptyState({ state, version, shortcutMode }) {
27212
+ const prompt = state.promptInput || 'Ask anything... "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uAD6C\uC870\uB97C \uC54C\uB824\uC918"';
27213
+ const hint = shortcutMode ? shortcutHelp(state) : "enter send \xB7 esc commands \xB7 / commands";
27214
+ const topGap = Math.max(2, Math.min(8, Math.floor(terminalHeight() * 0.18)));
27215
+ return React.createElement(
27216
+ Box,
27217
+ { flexDirection: "column", flexGrow: 1 },
27218
+ React.createElement(Box, { height: topGap }),
27219
+ React.createElement(DemianWordmark),
27220
+ React.createElement(Box, { height: 2 }),
27221
+ React.createElement(
27222
+ Box,
27223
+ { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", borderStyle: "double", borderColor: shortcutMode ? "cyan" : "blue", paddingX: 2, paddingY: 1 },
27224
+ React.createElement(Text, { color: state.promptInput ? "white" : "gray" }, prompt)
27225
+ ),
27226
+ React.createElement(SessionMetaBox, { state, version }),
27227
+ React.createElement(
27228
+ Box,
27229
+ { alignSelf: "center", width: promptPanelWidth(), justifyContent: "flex-end", marginTop: 1 },
27230
+ React.createElement(Text, { color: shortcutMode ? "cyan" : "gray" }, hint)
27231
+ ),
27232
+ state.promptError ? React.createElement(Box, { alignSelf: "center", width: promptPanelWidth(), marginTop: 1 }, React.createElement(Text, { color: "yellow" }, state.promptError)) : null,
27233
+ React.createElement(Box, { flexGrow: 1 })
27234
+ );
27235
+ }
27236
+ function SessionMetaBox({ state, version, marginTop = 1 }) {
27237
+ const width = promptPanelWidth();
27238
+ const compact = width < 68;
27239
+ const agent = shorten(state.selectedAgent ?? state.status.agent ?? "general", compact ? 14 : 18);
27240
+ const model = shorten(state.selection?.model ?? state.status.model ?? "model", compact ? Math.max(18, width - 14) : 30);
27241
+ const workspace = shorten(state.status.cwd ?? process.cwd(), compact ? Math.max(18, width - 18) : 46);
27242
+ const rows = compact ? [
27243
+ React.createElement(
27244
+ Box,
27245
+ { key: "mode" },
27246
+ React.createElement(MetaItem, { label: "agent", value: agent, color: "green" }),
27247
+ React.createElement(MetaDivider),
27248
+ React.createElement(MetaItem, { label: "perm", value: state.permissionPreset, color: "yellow" })
27249
+ ),
27250
+ React.createElement(Box, { key: "model" }, React.createElement(MetaItem, { label: "model", value: model, color: "cyan" })),
27251
+ React.createElement(Box, { key: "workspace" }, React.createElement(MetaItem, { label: "workspace", value: workspace, color: "white" })),
27252
+ version ? React.createElement(Box, { key: "version" }, React.createElement(MetaItem, { label: "version", value: `v${version}`, color: "gray" })) : null
27253
+ ].filter(Boolean) : [
27254
+ React.createElement(
27255
+ Box,
27256
+ { key: "mode" },
27257
+ React.createElement(MetaItem, { label: "agent", value: agent, color: "green" }),
27258
+ React.createElement(MetaDivider),
27259
+ React.createElement(MetaItem, { label: "model", value: model, color: "cyan" }),
27260
+ React.createElement(MetaDivider),
27261
+ React.createElement(MetaItem, { label: "perm", value: state.permissionPreset, color: "yellow" })
27262
+ ),
27263
+ React.createElement(
27264
+ Box,
27265
+ { key: "workspace" },
27266
+ React.createElement(MetaItem, { label: "workspace", value: workspace, color: "white" }),
27267
+ version ? React.createElement(MetaDivider) : null,
27268
+ version ? React.createElement(MetaItem, { label: "version", value: `v${version}`, color: "gray" }) : null
27269
+ )
27270
+ ];
27271
+ return React.createElement(
27272
+ Box,
27273
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 2, marginTop },
27274
+ ...rows
27275
+ );
27276
+ }
27277
+ function MetaItem({ label, value, color }) {
27278
+ return React.createElement(
27279
+ Text,
27280
+ null,
27281
+ React.createElement(Text, { color: "gray" }, `${label} `),
27282
+ React.createElement(Text, { color }, value)
27283
+ );
27284
+ }
27285
+ function MetaDivider() {
27286
+ return React.createElement(Text, { color: "gray" }, " \xB7 ");
27287
+ }
27288
+ function DemianWordmark() {
27289
+ if (terminalWidth() < 58) {
27290
+ return React.createElement(Box, { alignSelf: "center" }, React.createElement(Text, { color: "gray", bold: true }, "DEMIAN"));
27291
+ }
27292
+ return React.createElement(
27293
+ Box,
27294
+ { alignSelf: "center", flexDirection: "column" },
27295
+ ...DEMIAN_WORDMARK_LINES.map(
27296
+ (line, index) => React.createElement(Text, { key: index, color: index < 2 ? "gray" : "white", bold: true }, line)
27297
+ )
27298
+ );
27299
+ }
27182
27300
  function LoadingView({ state }) {
27183
- const title = state.inputMode === "starting" ? "Loading Demian" : "Preparing session";
27301
+ const title = state.inputMode === "starting" ? "Demian runtime" : "Preparing session";
27184
27302
  const message = state.activity || (state.inputMode === "starting" ? "Loading configuration" : "Starting session");
27185
27303
  return React.createElement(
27186
27304
  Box,
27187
- { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1, paddingY: 1 },
27305
+ { flexDirection: "column", marginTop: 1, borderStyle: "double", borderColor: "cyan", paddingX: 1, paddingY: 1 },
27188
27306
  React.createElement(Text, { color: "cyan", bold: true }, title),
27189
27307
  React.createElement(Text, { color: "white" }, message),
27190
- React.createElement(Text, { color: "gray" }, "Please wait while demian gets ready.")
27308
+ React.createElement(Text, { color: "gray" }, "Loading providers, tools, workspace state, and permissions.")
27191
27309
  );
27192
27310
  }
27193
27311
  function contextEfficiencySummary(state) {
@@ -27207,61 +27325,53 @@ function contextEfficiencySummary(state) {
27207
27325
  ].filter(Boolean);
27208
27326
  return parts.join(" | ");
27209
27327
  }
27210
- function StatusBar({ state, now: now2 }) {
27328
+ function StatusBar({ state, now: now2, version }) {
27211
27329
  const status = state.status;
27212
- const items = [
27213
- claudeCodeBadge(state),
27214
- status.provider ? `provider ${status.provider}` : void 0,
27215
- status.model ? `model ${status.model}` : void 0,
27216
- status.agent ? `agent ${status.agent}` : void 0,
27217
- status.cwd ? `cwd ${shorten(status.cwd, 48)}` : void 0,
27218
- status.externalSessionId ? `cc session ${shorten(status.externalSessionId, 18)}` : void 0,
27219
- status.reason ? `reason ${status.reason}` : void 0
27220
- ].filter(Boolean);
27330
+ const mode = tuiMode(state);
27331
+ const provider = state.selection?.providerName ?? status.provider;
27332
+ const model = state.selection?.model ?? status.model ?? "-";
27333
+ const meta = [provider, shorten(model, 32), state.permissionPreset, `v${version}`].filter(Boolean).join(" \xB7 ");
27221
27334
  return React.createElement(
27222
27335
  Box,
27223
- { borderStyle: "round", paddingX: 1 },
27224
- React.createElement(DemianTuiMark, { active: state.inputMode === "starting" || state.inputMode === "running", now: now2 }),
27225
- React.createElement(Text, { color: "cyan", bold: true }, " demian "),
27226
- React.createElement(Text, { color: "gray" }, items.join(" \xB7 "))
27336
+ { paddingX: 1, marginBottom: 1, justifyContent: "space-between" },
27337
+ React.createElement(
27338
+ Text,
27339
+ null,
27340
+ React.createElement(DemianTuiMark, { active: state.inputMode === "starting" || state.inputMode === "running", now: now2 }),
27341
+ React.createElement(Text, { color: "cyan", bold: true }, " Demian")
27342
+ ),
27343
+ React.createElement(
27344
+ Text,
27345
+ null,
27346
+ React.createElement(Text, { color: mode.color }, mode.label),
27347
+ React.createElement(Text, { color: "gray" }, ` \xB7 ${meta}`)
27348
+ )
27227
27349
  );
27228
27350
  }
27229
27351
  function DemianTuiMark({ active, now: now2 }) {
27230
27352
  const frame = active ? Math.floor(now2 / 450) % 4 : 0;
27231
- const shell = frame === 1 ? "<" : frame === 3 ? ">" : "^";
27232
- const face = frame === 2 ? "o" : "*";
27353
+ const mark = active ? ["D>", "D*", "D+", "D*"][frame] : "D>";
27233
27354
  return React.createElement(
27234
27355
  Text,
27235
27356
  { color: "yellow", bold: true },
27236
- `[${shell}${face}]`
27357
+ `[${mark}]`
27237
27358
  );
27238
27359
  }
27239
- function claudeCodeBadge(state) {
27240
- const option = selectedProviderOption(state);
27241
- const isClaudeCode = state.status.runtime === "claudecode" || option?.type === "claudecode" || state.status.provider === "claudecode" || state.selection?.providerName === "claudecode";
27242
- if (!isClaudeCode) return void 0;
27243
- return option?.ga === true ? "[CC]" : "[CC Preview]";
27244
- }
27245
- function selectedProviderOption(state) {
27246
- const providerName = state.selection?.providerName ?? state.status.provider;
27247
- return providerName ? state.providerOptions.find((option) => option.name === providerName) : void 0;
27248
- }
27249
- function SettingsBar({ state }) {
27250
- const selection = state.selection;
27251
- if (!selection) return null;
27252
- const help = state.inputMode === "provider" ? "up/down select | enter apply | esc cancel" : state.inputMode === "agent" ? "up/down select | enter apply | esc cancel" : state.inputMode === "permissionPreset" ? "up/down select | enter apply | esc cancel" : state.inputMode === "model" ? "type model | enter apply | esc cancel" : "p provider | a agent | m model | o permissions | up/down history | /cowork task | /compact compact | enter send | ctrl+c exit";
27253
- return React.createElement(
27254
- Box,
27255
- { borderStyle: "single", paddingX: 1 },
27256
- React.createElement(Text, null, `agent ${state.selectedAgent ?? state.status.agent ?? "?"} | provider ${selection.providerName} (${selection.providerSource}) | model ${selection.model} (${selection.modelSource}) | permission ${state.permissionPreset} | ${help}`)
27257
- );
27360
+ function tuiMode(state) {
27361
+ if (state.done) return { label: "done", color: "green" };
27362
+ if (state.permission) return { label: "approval", color: "yellow" };
27363
+ if (state.inputMode === "starting") return { label: "booting", color: "cyan" };
27364
+ if (state.inputMode === "running") return { label: "running", color: "cyan" };
27365
+ if (state.inputMode === "provider" || state.inputMode === "agent" || state.inputMode === "model" || state.inputMode === "permissionPreset") return { label: "settings", color: "magenta" };
27366
+ if (state.inputMode === "prompt") return { label: "ready", color: "green" };
27367
+ return { label: state.inputMode, color: "gray" };
27258
27368
  }
27259
27369
  function TranscriptView({ state }) {
27260
27370
  const blocks = state.blocks.slice(-12);
27261
27371
  const streaming = !state.streamingFinalized && state.streamingText ? { kind: "assistant", title: "Assistant streaming", lines: streamingLines(state.streamingText) } : void 0;
27262
27372
  return React.createElement(
27263
27373
  Box,
27264
- { flexDirection: "column", marginTop: 1 },
27374
+ { flexDirection: "column", marginTop: 1, paddingX: 1 },
27265
27375
  ...blocks.map((block) => React.createElement(BlockView, { key: block.id, block })),
27266
27376
  streaming ? React.createElement(BlockView, { key: "streaming", block: streaming }) : null
27267
27377
  );
@@ -27453,21 +27563,26 @@ function toneColor(tone) {
27453
27563
  return void 0;
27454
27564
  }
27455
27565
  function ActivityBar({ state }) {
27566
+ if (!shouldShowActivity(state)) return null;
27456
27567
  const warning = state.warnings.at(-1);
27457
27568
  const context = contextEfficiencySummary(state);
27569
+ const color = state.done ? "green" : state.inputMode === "running" ? "cyan" : "blue";
27458
27570
  return React.createElement(
27459
27571
  Box,
27460
- { borderStyle: "round", borderColor: state.done ? "green" : "blue", paddingX: 1, flexDirection: "column" },
27572
+ { borderStyle: "round", borderColor: color, paddingX: 1, flexDirection: "column" },
27461
27573
  React.createElement(
27462
27574
  Box,
27463
27575
  null,
27464
- React.createElement(Text, { color: state.done ? "green" : "blue", bold: true }, "Progress "),
27576
+ React.createElement(Text, { color, bold: true }, "Activity "),
27465
27577
  React.createElement(Text, { color: state.done ? "green" : "white" }, state.activity),
27466
27578
  context ? React.createElement(Text, { color: "gray" }, ` | ${context}`) : null
27467
27579
  ),
27468
27580
  warning ? React.createElement(Text, { color: "yellow" }, ` \xB7 ${warning}`) : null
27469
27581
  );
27470
27582
  }
27583
+ function shouldShowActivity(state) {
27584
+ return state.inputMode === "running" || state.done || state.warnings.length > 0 || Boolean(state.contextEfficiency);
27585
+ }
27471
27586
  function GoalIsland({ state }) {
27472
27587
  const goal = state.goal;
27473
27588
  if (!goal) return null;
@@ -27523,7 +27638,7 @@ function DiffAccordion({ state }) {
27523
27638
  null,
27524
27639
  React.createElement(Text, { color: "cyan", bold: true }, `${indicator} Diff `),
27525
27640
  React.createElement(Text, { color: "white" }, summary),
27526
- React.createElement(Text, { color: "gray" }, " | tab toggle")
27641
+ React.createElement(Text, { color: "gray" }, " | esc then d toggle")
27527
27642
  ),
27528
27643
  state.diffExpanded ? React.createElement(
27529
27644
  Box,
@@ -27563,7 +27678,7 @@ function WorkPlanPanel({ state, now: now2 }) {
27563
27678
  null,
27564
27679
  React.createElement(Text, { color: "cyan", bold: true }, `${expanded ? "[-]" : "[+]"} Plan `),
27565
27680
  React.createElement(Text, { color: "white" }, "Thinking"),
27566
- React.createElement(Text, { color: "gray" }, " | ctrl+p toggle")
27681
+ React.createElement(Text, { color: "gray" }, " | esc then w toggle")
27567
27682
  ),
27568
27683
  expanded ? React.createElement(Text, { color: "gray" }, "Demian is inspecting context and preparing a step-by-step plan.") : null
27569
27684
  );
@@ -27587,7 +27702,7 @@ function WorkPlanPanel({ state, now: now2 }) {
27587
27702
  elapsed ? React.createElement(Text, { color: "gray" }, ` (${elapsed})`) : null,
27588
27703
  React.createElement(Text, { color: "gray" }, ` | ${planProgressGraph(progress.resolved, progress.total)} ${progress.resolved}/${progress.total}`),
27589
27704
  blocked ? React.createElement(Text, { color: "red" }, ` | ${blocked} blocked`) : null,
27590
- React.createElement(Text, { color: "gray" }, " | ctrl+p toggle")
27705
+ React.createElement(Text, { color: "gray" }, " | esc then w toggle")
27591
27706
  ),
27592
27707
  expanded ? React.createElement(
27593
27708
  Box,
@@ -27708,15 +27823,15 @@ function formatElapsed2(ms2) {
27708
27823
  if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
27709
27824
  return `${seconds}s`;
27710
27825
  }
27711
- function BottomBar({ state }) {
27826
+ function InteractionPanel({ state, shortcutMode }) {
27712
27827
  if (state.inputMode === "starting") return React.createElement(LoadingBar);
27713
27828
  if (state.permission) return React.createElement(PermissionBar, { state });
27714
27829
  if (state.inputMode === "provider") return React.createElement(ProviderSelector, { state });
27715
27830
  if (state.inputMode === "agent") return React.createElement(AgentSelector, { state });
27716
27831
  if (state.inputMode === "permissionPreset") return React.createElement(PermissionPresetSelector, { state });
27717
27832
  if (state.inputMode === "model") return React.createElement(ModelEditor, { state });
27718
- if (state.inputMode === "running") return React.createElement(CommandBar, { state });
27719
- if (state.inputMode === "prompt") return React.createElement(PromptBar, { state });
27833
+ if (state.inputMode === "running") return React.createElement(CommandBar, { state, shortcutMode });
27834
+ if (state.inputMode === "prompt") return React.createElement(PromptBar, { state, shortcutMode });
27720
27835
  return React.createElement(PermissionBar, { state });
27721
27836
  }
27722
27837
  function LoadingBar() {
@@ -27724,58 +27839,72 @@ function LoadingBar() {
27724
27839
  Box,
27725
27840
  { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27726
27841
  React.createElement(Text, { color: "cyan", bold: true }, "loading"),
27727
- React.createElement(Text, { color: "gray" }, "demian is preparing the session | ctrl+c exit")
27842
+ React.createElement(Text, { color: "gray" }, "demian is preparing the session | type /exit after loading to quit")
27728
27843
  );
27729
27844
  }
27730
- function PromptBar({ state }) {
27845
+ function shortcutHelp(state) {
27846
+ const parts = ["esc cancel"];
27847
+ if (state.inputMode === "prompt") parts.push("p provider", "a agent", "m model", "o permissions", "u clear");
27848
+ if (state.inputMode === "running") parts.push("s stop");
27849
+ if (state.turnDiff) parts.push("d diff");
27850
+ if (state.blocks.some((block) => block.kind === "tool")) parts.push("t tools");
27851
+ if (state.workPlan || state.inputMode === "running") parts.push("w plan");
27852
+ if (state.pendingClaudeCodePlan) parts.push("e use CC plan");
27853
+ if (state.canRetryLastPrompt) parts.push("r retry");
27854
+ parts.push("q quit");
27855
+ return `shortcut: ${parts.join(" | ")}`;
27856
+ }
27857
+ function PromptBar({ state, shortcutMode }) {
27731
27858
  const value = state.promptInput || "Type first message and press Enter";
27732
- const diffHelp = state.turnDiff ? " | tab diff" : "";
27733
- const toolHelp = state.blocks.some((block) => block.kind === "tool") ? " | ctrl+t tools" : "";
27734
- const planHelp = state.workPlan || state.inputMode === "running" ? " | ctrl+p plan" : "";
27735
- const claudeCodePlanHelp = state.pendingClaudeCodePlan ? " | ctrl+e use CC plan" : "";
27736
- const retryHelp = state.canRetryLastPrompt ? " | ctrl+r retry" : "";
27859
+ const hint = shortcutMode ? shortcutHelp(state) : "enter send | esc shortcuts | up/down history | /cowork | /compact | /retry | /exit";
27737
27860
  return React.createElement(
27738
27861
  Box,
27739
- { flexDirection: "column", borderStyle: "single", paddingX: 1 },
27862
+ { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", marginTop: 1 },
27863
+ React.createElement(SessionMetaBox, { state, marginTop: 0 }),
27740
27864
  React.createElement(
27741
27865
  Box,
27742
- null,
27743
- React.createElement(Text, { color: "cyan", bold: true }, "> "),
27744
- React.createElement(Text, { color: "gray" }, `[perm:${state.permissionPreset}] `),
27745
- React.createElement(Text, { color: state.promptInput ? "white" : "gray" }, value)
27746
- ),
27747
- state.promptError ? React.createElement(Text, { color: "yellow" }, state.promptError) : React.createElement(Text, { color: "gray" }, `enter send | up/down history | p/a/m/o settings | /cowork | /compact | /retry | /exit | ctrl+u clear${diffHelp}${toolHelp}${planHelp}${claudeCodePlanHelp}${retryHelp}`)
27866
+ { flexDirection: "column", borderStyle: "double", borderColor: shortcutMode ? "cyan" : "blue", paddingX: 2, paddingY: 1, marginTop: 1 },
27867
+ React.createElement(
27868
+ Box,
27869
+ null,
27870
+ React.createElement(Text, { color: "cyan", bold: true }, "> "),
27871
+ React.createElement(Text, { color: state.promptInput ? "white" : "gray" }, value)
27872
+ ),
27873
+ state.promptError ? React.createElement(Text, { color: "yellow" }, state.promptError) : React.createElement(Text, { color: shortcutMode ? "cyan" : "gray" }, hint)
27874
+ )
27748
27875
  );
27749
27876
  }
27750
- function CommandBar({ state }) {
27877
+ function CommandBar({ state, shortcutMode }) {
27751
27878
  const value = state.promptInput || "Type /stop to stop or /exit to quit";
27752
- const diffHelp = state.turnDiff ? " | tab diff" : "";
27753
- const toolHelp = state.blocks.some((block) => block.kind === "tool") ? " | ctrl+t tools" : "";
27754
- const planHelp = state.workPlan || state.inputMode === "running" ? " | ctrl+p plan" : "";
27879
+ const hint = shortcutMode ? shortcutHelp(state) : "running | /stop stop | /exit quit | esc shortcuts";
27755
27880
  return React.createElement(
27756
27881
  Box,
27757
- { flexDirection: "column", borderStyle: "single", paddingX: 1 },
27882
+ { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", marginTop: 1 },
27883
+ React.createElement(SessionMetaBox, { state, marginTop: 0 }),
27758
27884
  React.createElement(
27759
27885
  Box,
27760
- null,
27761
- React.createElement(Text, { color: "yellow", bold: true }, ": "),
27762
- React.createElement(Text, { color: "gray" }, `[perm:${state.permissionPreset}] `),
27763
- React.createElement(Text, { color: state.promptInput ? "white" : "gray" }, value)
27764
- ),
27765
- state.promptError ? React.createElement(Text, { color: "yellow" }, state.promptError) : React.createElement(Text, { color: "gray" }, `running | /stop stop | /exit quit${diffHelp}${toolHelp}${planHelp}`)
27886
+ { flexDirection: "column", borderStyle: "double", borderColor: shortcutMode ? "cyan" : "yellow", paddingX: 2, paddingY: 1, marginTop: 1 },
27887
+ React.createElement(
27888
+ Box,
27889
+ null,
27890
+ React.createElement(Text, { color: "yellow", bold: true }, ": "),
27891
+ React.createElement(Text, { color: state.promptInput ? "white" : "gray" }, value)
27892
+ ),
27893
+ state.promptError ? React.createElement(Text, { color: "yellow" }, state.promptError) : React.createElement(Text, { color: shortcutMode ? "cyan" : "gray" }, hint)
27894
+ )
27766
27895
  );
27767
27896
  }
27768
27897
  function ProviderSelector({ state }) {
27769
27898
  const items = state.providerOptions;
27770
27899
  return React.createElement(
27771
- Box,
27772
- { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27773
- React.createElement(Text, { color: "cyan", bold: true }, "Select provider"),
27900
+ DialogFrame,
27901
+ { title: "Providers", accent: "magenta" },
27902
+ React.createElement(Text, { color: "gray" }, `${"Provider".padEnd(22)} ${"Model".padEnd(30)} Status`),
27774
27903
  ...items.map(
27775
27904
  (item, index) => React.createElement(
27776
27905
  Text,
27777
27906
  { key: item.name, color: index === state.providerCursor ? "cyan" : item.available === false ? "gray" : "white", bold: index === state.providerCursor },
27778
- `${index === state.providerCursor ? ">" : " "} ${item.label ?? item.name} model ${item.modelLabel ?? item.model}`
27907
+ `${index === state.providerCursor ? ">" : " "} ${shorten(item.label ?? item.name, 20).padEnd(20)} ${shorten((item.modelLabel ?? item.model) || "-", 30).padEnd(30)} ${providerStatusLabel(item)}`
27779
27908
  )
27780
27909
  ),
27781
27910
  React.createElement(Text, { color: "gray" }, "up/down select | enter apply | esc cancel")
@@ -27784,9 +27913,8 @@ function ProviderSelector({ state }) {
27784
27913
  function AgentSelector({ state }) {
27785
27914
  const items = state.agentOptions;
27786
27915
  return React.createElement(
27787
- Box,
27788
- { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27789
- React.createElement(Text, { color: "cyan", bold: true }, "Select agent"),
27916
+ DialogFrame,
27917
+ { title: "Agents", accent: "magenta" },
27790
27918
  ...items.map(
27791
27919
  (item, index) => React.createElement(
27792
27920
  Text,
@@ -27799,9 +27927,8 @@ function AgentSelector({ state }) {
27799
27927
  }
27800
27928
  function PermissionPresetSelector({ state }) {
27801
27929
  return React.createElement(
27802
- Box,
27803
- { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27804
- React.createElement(Text, { color: "cyan", bold: true }, "Default permission"),
27930
+ DialogFrame,
27931
+ { title: "Default Permission", accent: "magenta" },
27805
27932
  ...PERMISSION_PRESETS.map(
27806
27933
  (item, index) => React.createElement(
27807
27934
  Text,
@@ -27822,16 +27949,15 @@ function ModelEditor({ state }) {
27822
27949
  const current = state.selection?.model ?? "";
27823
27950
  const value = state.modelInput || `Keep ${current}`;
27824
27951
  return React.createElement(
27825
- Box,
27826
- { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27827
- React.createElement(Text, { color: "cyan", bold: true }, "Model"),
27952
+ DialogFrame,
27953
+ { title: "Model", accent: "magenta" },
27828
27954
  React.createElement(
27829
27955
  Box,
27830
27956
  null,
27831
27957
  React.createElement(Text, { color: "cyan", bold: true }, "> "),
27832
27958
  React.createElement(Text, { color: state.modelInput ? "white" : "gray" }, value)
27833
27959
  ),
27834
- state.settingsError ? React.createElement(Text, { color: "yellow" }, state.settingsError) : React.createElement(Text, { color: "gray" }, "enter apply | ctrl+u clear | esc cancel")
27960
+ state.settingsError ? React.createElement(Text, { color: "yellow" }, state.settingsError) : React.createElement(Text, { color: "gray" }, "enter apply | backspace edit | esc cancel")
27835
27961
  );
27836
27962
  }
27837
27963
  function PermissionBar({ state }) {
@@ -27841,18 +27967,48 @@ function PermissionBar({ state }) {
27841
27967
  }
27842
27968
  const detailLines = describeToolInput(pending.request.tool, pending.request.input);
27843
27969
  return React.createElement(
27844
- Box,
27845
- { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27846
- React.createElement(Text, { color: "yellow", bold: true }, "Permission required"),
27970
+ DialogFrame,
27971
+ { title: "Permission Required", accent: "yellow" },
27847
27972
  React.createElement(Text, null, `Allow ${toolLabel(pending.request.tool, pending.request.input)}?`),
27848
27973
  ...detailLines.map((line, index) => React.createElement(Text, { key: index, color: "white" }, line)),
27849
27974
  ...permissionShortcutLines().map((line, index) => React.createElement(Text, { key: `shortcut-${index}`, color: "cyan", bold: true }, line))
27850
27975
  );
27851
27976
  }
27977
+ function DialogFrame({ title, accent, children }) {
27978
+ return React.createElement(
27979
+ Box,
27980
+ { alignSelf: "center", width: dialogPanelWidth(), flexDirection: "column", borderStyle: "round", borderColor: accent, paddingX: 2, paddingY: 1, marginTop: 1 },
27981
+ React.createElement(Text, { color: accent, bold: true }, title),
27982
+ React.createElement(Box, { height: 1 }),
27983
+ children
27984
+ );
27985
+ }
27986
+ function providerStatusLabel(item) {
27987
+ if (item.available !== false) return "ready";
27988
+ if (item.catalog?.status === "missing-auth") return "auth";
27989
+ if (item.catalog?.status === "unavailable") return "offline";
27990
+ return "n/a";
27991
+ }
27852
27992
  function shorten(value, max) {
27853
27993
  if (value.length <= max) return value;
27854
27994
  return `\u2026${value.slice(value.length - max + 1)}`;
27855
27995
  }
27996
+ function terminalHeight() {
27997
+ const rows = process.stdout.rows;
27998
+ return typeof rows === "number" && Number.isFinite(rows) && rows > 0 ? Math.max(18, rows - 1) : 24;
27999
+ }
28000
+ function terminalWidth() {
28001
+ const columns = process.stdout.columns;
28002
+ return typeof columns === "number" && Number.isFinite(columns) && columns > 0 ? Math.max(48, columns) : 100;
28003
+ }
28004
+ function promptPanelWidth() {
28005
+ const columns = terminalWidth();
28006
+ return Math.max(40, Math.min(96, columns - 8, Math.floor(columns * 0.86)));
28007
+ }
28008
+ function dialogPanelWidth() {
28009
+ const columns = terminalWidth();
28010
+ return Math.max(48, Math.min(92, columns - 10, Math.floor(columns * 0.78)));
28011
+ }
27856
28012
  function formatTokens(value) {
27857
28013
  const tokens = typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0;
27858
28014
  if (tokens >= 1e6) return `${trimDecimal2(tokens / 1e6)}m`;
@@ -27866,12 +28022,20 @@ function clamp01(value) {
27866
28022
  if (!Number.isFinite(value)) return 0;
27867
28023
  return Math.max(0, Math.min(1, value));
27868
28024
  }
28025
+ var DEMIAN_WORDMARK_LINES;
27869
28026
  var init_app = __esm({
27870
28027
  "src/ui/tui/app.ts"() {
27871
28028
  "use strict";
27872
28029
  init_store();
27873
28030
  init_presets();
27874
28031
  init_tool_summary();
28032
+ DEMIAN_WORDMARK_LINES = [
28033
+ " _ _ ",
28034
+ " __| | ___ _ __ ___ (_) __ _ _ __ ",
28035
+ " / _` |/ _ \\ '_ ` _ \\| |/ _` | '_ \\ ",
28036
+ "| (_| | __/ | | | | | | (_| | | | |",
28037
+ " \\__,_|\\___|_| |_| |_|_|\\__,_|_| |_|"
28038
+ ];
27875
28039
  }
27876
28040
  });
27877
28041
 
@@ -34273,6 +34437,7 @@ var init_controller = __esm({
34273
34437
 
34274
34438
  // src/tui.ts
34275
34439
  init_parser();
34440
+ import { readFileSync as readFileSync2 } from "node:fs";
34276
34441
 
34277
34442
  // src/doctor/policies.ts
34278
34443
  import { existsSync } from "node:fs";
@@ -35222,7 +35387,7 @@ async function main(argv = process.argv.slice(2)) {
35222
35387
  ]);
35223
35388
  const React2 = ReactModule.default;
35224
35389
  const store = new TuiStore2();
35225
- const instance = render(React2.createElement(TuiApp2, { store }));
35390
+ const instance = render(React2.createElement(TuiApp2, { store, version: packageVersion() }));
35226
35391
  try {
35227
35392
  const code = await runTuiSession2(flags, store);
35228
35393
  setTimeout(() => instance.unmount(), 100);
@@ -35241,6 +35406,14 @@ async function main(argv = process.argv.slice(2)) {
35241
35406
  throw error;
35242
35407
  }
35243
35408
  }
35409
+ function packageVersion() {
35410
+ try {
35411
+ const metadata = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8"));
35412
+ return typeof metadata.version === "string" && metadata.version.trim() ? metadata.version : "dev";
35413
+ } catch {
35414
+ return "dev";
35415
+ }
35416
+ }
35244
35417
  function parseArgs(argv) {
35245
35418
  const out = { prompt: "", transcript: true, images: [] };
35246
35419
  const rest = [];
@@ -35361,15 +35534,16 @@ Usage:
35361
35534
  Default:
35362
35535
  demian and demian-cli launch the Ink terminal UI.
35363
35536
  The TUI shows provider/model defaults before asking for the first message.
35364
- Press p to select provider, m to edit model, then Enter to send.
35365
- Press a to select the main agent before sending a message.
35366
- Press o to select the default permission preset: deny, ask, auto, or full.
35537
+ Press Esc then p to select provider, Esc then m to edit model, then Enter to send.
35538
+ Press Esc then a to select the main agent before sending a message.
35539
+ Press Esc then o to select the default permission preset: deny, ask, auto, or full.
35367
35540
  Press up/down in the message composer to recall previous prompts.
35368
- Provider/model selections are remembered in .demian/preferences.json.
35541
+ Provider/model selections are remembered in ~/.demian/preferences.json.
35369
35542
  After each answer, the TUI returns to standby for the next message.
35370
35543
  Use /cowork <task> to explicitly ask Demian to coordinate cowork sub agents in multi-agent mode.
35371
35544
  Use /compact to compact current history, /stop to stop active work, and /exit or /quit to close the TUI.
35372
- Tool approvals show the requested command/path in the bottom bar.
35545
+ Press Esc then q to quit, Esc then s to stop a running task, and Esc then u to clear the composer.
35546
+ Tool approvals show the requested command/path in a permission dialog.
35373
35547
  Press y to allow once, a to always allow the grant scope, or n/Enter to deny.
35374
35548
  Prompt arguments are still accepted as a compatibility shortcut.
35375
35549
 
@@ -850,7 +850,7 @@ var TuiStore = class {
850
850
  }
851
851
  markTaskFailed(message) {
852
852
  const lines = [message];
853
- if (this.#lastRetryPrompt()) lines.push("Press ctrl+r or type /retry to run the last task again.");
853
+ if (this.#lastRetryPrompt()) lines.push("Press Esc then r or type /retry to run the last task again.");
854
854
  this.#append({ kind: "warning", title: "Task Failed", lines });
855
855
  this.#state.status.reason = "error";
856
856
  this.#state.inputMode = this.#exitRequested ? "done" : "prompt";
@@ -33180,6 +33180,7 @@ if (process.argv.includes("--config-path")) {
33180
33180
  process.exit(0);
33181
33181
  }
33182
33182
  var options = parseOptions();
33183
+ var SNAPSHOT_DEBOUNCE_MS = 80;
33183
33184
  var store = new TuiStore();
33184
33185
  var diffState = normalizeDiffState(options.initialSnapshot?.diff);
33185
33186
  var restoredSnapshot = normalizeInitialPublicState(options.initialSnapshot);
@@ -33405,7 +33406,7 @@ function scheduleSnapshot() {
33405
33406
  snapshotTimer = setTimeout(() => {
33406
33407
  snapshotTimer = void 0;
33407
33408
  send({ type: "snapshot", state: publicState(store.snapshot()) });
33408
- }, 33);
33409
+ }, SNAPSHOT_DEBOUNCE_MS);
33409
33410
  }
33410
33411
  function sendSnapshotNow() {
33411
33412
  if (snapshotTimer) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "demian-cli",
3
- "version": "1.0.9",
3
+ "version": "1.1.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",