demian-cli 1.1.0 → 1.1.2

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
@@ -1,13 +1,36 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
8
  var __esm = (fn, res) => function __init() {
5
9
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
10
  };
11
+ var __commonJS = (cb, mod) => function __require() {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ };
7
14
  var __export = (target, all) => {
8
15
  for (var name in all)
9
16
  __defProp(target, name, { get: all[name], enumerable: true });
10
17
  };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
27
+ // If the importer is in node compatibility mode or this is not an ESM
28
+ // file that has been converted to a CommonJS file using a Babel-
29
+ // compatible transform (i.e. "__esModule" has not been set), then set
30
+ // "default" to the CommonJS "module.exports" for node compatibility.
31
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
32
+ mod
33
+ ));
11
34
 
12
35
  // src/goals/parser.ts
13
36
  function parseGoalCommand(input2) {
@@ -124,6 +147,382 @@ var init_path_expansion = __esm({
124
147
  }
125
148
  });
126
149
 
150
+ // src/config-scaffold.ts
151
+ import { chmod, mkdir, readFile as readFile2, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
152
+ import os3 from "node:os";
153
+ import path3 from "node:path";
154
+ function defaultUserConfigPath() {
155
+ return path3.join(os3.homedir(), ".demian", "config.json");
156
+ }
157
+ function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
158
+ return {
159
+ version: 2,
160
+ defaultProvider,
161
+ providers: {
162
+ openai: {
163
+ type: "openai-compatible",
164
+ baseURL: "https://api.openai.com/v1",
165
+ apiKey: "",
166
+ apiKeyEnv: "OPENAI_API_KEY",
167
+ catalog: { type: "openai-models", endpoint: "https://api.openai.com/v1/models" }
168
+ },
169
+ anthropic: {
170
+ type: "anthropic",
171
+ baseURL: "https://api.anthropic.com/v1",
172
+ apiKey: "",
173
+ apiKeyEnv: "ANTHROPIC_API_KEY",
174
+ catalog: { type: "anthropic-models", endpoint: "https://api.anthropic.com/v1/models" }
175
+ },
176
+ gemini: {
177
+ type: "openai-compatible",
178
+ baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
179
+ apiKey: "",
180
+ apiKeyEnv: "GEMINI_API_KEY",
181
+ apiKeyEnvAliases: ["GOOGLE_API_KEY"],
182
+ catalog: { type: "gemini-openai-models", endpoint: "https://generativelanguage.googleapis.com/v1beta/openai/models" }
183
+ },
184
+ groq: {
185
+ type: "openai-compatible",
186
+ baseURL: "https://api.groq.com/openai/v1",
187
+ apiKey: "",
188
+ apiKeyEnv: "GROQ_API_KEY",
189
+ catalog: { type: "groq-models", endpoint: "https://api.groq.com/openai/v1/models" }
190
+ },
191
+ azure: {
192
+ type: "openai-compatible",
193
+ auth: { type: "api-key", header: "api-key" },
194
+ modelProfiles: [
195
+ {
196
+ name: "azure-example",
197
+ displayName: "Azure example",
198
+ model: "azure-deployment-name",
199
+ baseURL: "https://example.openai.azure.com/openai/v1",
200
+ apiKey: "",
201
+ apiKeyEnv: "AZURE_OPENAI_API_KEY"
202
+ }
203
+ ]
204
+ },
205
+ lmstudio: {
206
+ type: "openai-compatible",
207
+ baseURL: "http://localhost:1234/v1",
208
+ apiKey: "lm-studio",
209
+ modelProfiles: [],
210
+ catalog: { type: "openai-compatible-models", endpoint: "http://localhost:1234/v1/models" }
211
+ },
212
+ "ollama-local": {
213
+ type: "openai-compatible",
214
+ baseURL: "http://localhost:11434/v1",
215
+ apiKey: "ollama",
216
+ modelProfiles: [],
217
+ quirks: { omitTemperature: true },
218
+ catalog: { type: "openai-compatible-models", endpoint: "http://localhost:11434/v1/models" }
219
+ },
220
+ "ollama-cloud": {
221
+ type: "ollama",
222
+ baseURL: "https://ollama.com/api",
223
+ apiKey: "",
224
+ apiKeyEnv: "OLLAMA_API_KEY",
225
+ modelProfiles: [],
226
+ catalog: { type: "ollama-tags", endpoint: "https://ollama.com/api/tags" }
227
+ },
228
+ llamacpp: {
229
+ type: "openai-compatible",
230
+ baseURL: "http://localhost:8080/v1",
231
+ apiKey: "llama.cpp",
232
+ modelProfiles: [],
233
+ catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8080/v1/models" }
234
+ },
235
+ vllm: {
236
+ type: "openai-compatible",
237
+ baseURL: "http://localhost:8000/v1",
238
+ apiKey: "vllm",
239
+ modelProfiles: [],
240
+ catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8000/v1/models" }
241
+ },
242
+ codex: {
243
+ type: "codex",
244
+ baseURL: "https://chatgpt.com/backend-api/codex",
245
+ authStore: "auto",
246
+ allowApiKeyFallback: false,
247
+ promptCacheKey: "root-session",
248
+ catalog: { type: "codex-oauth-models" }
249
+ },
250
+ claudecode: {
251
+ type: "claudecode",
252
+ runtime: "agent-sdk",
253
+ cliPath: "~/.local/bin/claude",
254
+ cwdMode: "session",
255
+ historyPolicy: "passthrough-resume",
256
+ onInvalidResume: "fresh",
257
+ attachmentFallback: "block",
258
+ allowSubagents: false,
259
+ sanitizeApiKeyEnv: true,
260
+ authPreflight: true,
261
+ useBareMode: false,
262
+ usageLedgerScope: "process",
263
+ sessionLock: true,
264
+ abortPolicy: "record-only",
265
+ catalog: { type: "claudecode-supported-models" }
266
+ }
267
+ }
268
+ };
269
+ }
270
+ async function createUserConfig(options = {}) {
271
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
272
+ const content = `${JSON.stringify(defaultUserConfig(options.defaultProvider), null, 2)}
273
+ `;
274
+ if (options.print) return { path: filePath, created: false, content };
275
+ const existed = await exists(filePath);
276
+ if (existed && !options.force) return { path: filePath, created: false, content: await readFile2(filePath, "utf8"), existed: true };
277
+ await writeJsonAtomic(filePath, content);
278
+ return { path: filePath, created: true, content, existed };
279
+ }
280
+ async function addProvider(options) {
281
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
282
+ const config = await readConfigObject(filePath);
283
+ const providers = objectValue(config.providers);
284
+ const name = options.name;
285
+ if (providers[name] && !options.force) throw new Error(`Provider ${name} already exists. Use --force to overwrite it.`);
286
+ providers[name] = providerPreset(options);
287
+ config.providers = providers;
288
+ const content = `${JSON.stringify(config, null, 2)}
289
+ `;
290
+ await writeJsonAtomic(filePath, content);
291
+ return { path: filePath, created: true, content };
292
+ }
293
+ async function addModelProfile(options) {
294
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
295
+ const config = await readConfigObject(filePath);
296
+ const providers = objectValue(config.providers);
297
+ const provider = objectValue(providers[options.provider]);
298
+ const profiles = Array.isArray(provider.modelProfiles) ? [...provider.modelProfiles] : [];
299
+ const displayName = options.displayName ?? options.name;
300
+ const previousName = normalizeOptionalName(options.previousName);
301
+ const existingByPreviousName = previousName ? profiles.findIndex((entry) => entry.name === previousName) : -1;
302
+ const existingByName = profiles.findIndex((entry) => entry.name === options.name);
303
+ const existingByDisplay = profiles.findIndex((entry) => entry.displayName === displayName);
304
+ const updateIndex = existingByPreviousName >= 0 ? existingByPreviousName : existingByName;
305
+ if (existingByName >= 0 && existingByName !== updateIndex) throw new Error(`Profile name "${options.name}" already exists on provider ${options.provider}. Pick a different profile name.`);
306
+ if (existingByName >= 0 && existingByPreviousName < 0 && !options.force) throw new Error(`Profile name "${options.name}" already exists on provider ${options.provider}. Use --force to overwrite it.`);
307
+ if (existingByDisplay >= 0 && existingByDisplay !== updateIndex && !options.force) {
308
+ throw new Error(`Profile displayName "${displayName}" already exists on provider ${options.provider} (profile name: ${profiles[existingByDisplay].name}). Use --force or pick a different --display-name.`);
309
+ }
310
+ const next = {
311
+ name: options.name,
312
+ displayName,
313
+ model: options.model,
314
+ ...options.baseURL ? { baseURL: options.baseURL } : {},
315
+ ...options.apiKey !== void 0 || options.apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
316
+ ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {}
317
+ };
318
+ if (updateIndex >= 0) profiles[updateIndex] = next;
319
+ else profiles.push(next);
320
+ provider.modelProfiles = profiles;
321
+ providers[options.provider] = provider;
322
+ config.providers = providers;
323
+ const content = `${JSON.stringify(config, null, 2)}
324
+ `;
325
+ await writeJsonAtomic(filePath, content);
326
+ return { path: filePath, created: true, content };
327
+ }
328
+ async function updateConfigDefaults(options) {
329
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
330
+ const config = await readConfigObject(filePath);
331
+ const defaultProvider = normalizeOptionalName(options.defaultProvider);
332
+ const defaultAgent = normalizeOptionalName(options.defaultAgent);
333
+ if (!defaultProvider && !defaultAgent) throw new Error("At least one of defaultProvider or defaultAgent is required.");
334
+ if (defaultProvider) config.defaultProvider = defaultProvider;
335
+ if (defaultAgent) config.defaultAgent = defaultAgent;
336
+ const content = `${JSON.stringify(config, null, 2)}
337
+ `;
338
+ await writeJsonAtomic(filePath, content);
339
+ return { path: filePath, created: true, content };
340
+ }
341
+ async function setDefaultModelProfile(options) {
342
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
343
+ const config = await readConfigObject(filePath);
344
+ const providers = objectValue(config.providers);
345
+ const provider = providers[options.provider];
346
+ if (!provider || typeof provider !== "object" || Array.isArray(provider)) throw new Error(`Provider ${options.provider} does not exist.`);
347
+ const providerConfig = { ...provider };
348
+ const profiles = Array.isArray(providerConfig.modelProfiles) ? [...providerConfig.modelProfiles] : [];
349
+ const requestedName = normalizeOptionalName(options.profileName ?? options.name);
350
+ const requestedModel = normalizeOptionalName(options.model);
351
+ const requestedDisplayName = normalizeOptionalName(options.displayName) ?? requestedModel ?? requestedName;
352
+ let index = profiles.findIndex((entry) => requestedName && entry.name === requestedName);
353
+ if (index < 0 && requestedModel) index = profiles.findIndex((entry) => entry.model === requestedModel);
354
+ if (index < 0 && requestedDisplayName) index = profiles.findIndex((entry) => entry.displayName === requestedDisplayName);
355
+ const profile = index >= 0 ? {
356
+ ...profiles[index],
357
+ ...requestedName ? { name: requestedName } : {},
358
+ ...requestedDisplayName ? { displayName: requestedDisplayName } : {},
359
+ ...requestedModel ? { model: requestedModel } : {},
360
+ ...options.baseURL ? { baseURL: options.baseURL } : {},
361
+ ...options.apiKey !== void 0 || options.apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
362
+ ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {}
363
+ } : newModelProfile({
364
+ name: requestedName,
365
+ displayName: requestedDisplayName,
366
+ model: requestedModel ?? requestedName,
367
+ baseURL: options.baseURL,
368
+ apiKey: options.apiKey,
369
+ apiKeyEnv: options.apiKeyEnv
370
+ });
371
+ if (!profile.name || typeof profile.name !== "string") throw new Error("Model profile name is required.");
372
+ if (!profile.model || typeof profile.model !== "string") throw new Error("Model ID is required.");
373
+ if (index >= 0) profiles.splice(index, 1);
374
+ profiles.unshift(profile);
375
+ providerConfig.modelProfiles = profiles;
376
+ providers[options.provider] = providerConfig;
377
+ config.providers = providers;
378
+ const content = `${JSON.stringify(config, null, 2)}
379
+ `;
380
+ await writeJsonAtomic(filePath, content);
381
+ return { path: filePath, created: true, content };
382
+ }
383
+ async function updateProviderSettings(options) {
384
+ const filePath = expandHome(options.path ?? defaultUserConfigPath());
385
+ const config = await readConfigObject(filePath);
386
+ const providers = objectValue(config.providers);
387
+ const provider = providers[options.provider];
388
+ if (!provider || typeof provider !== "object" || Array.isArray(provider)) throw new Error(`Provider ${options.provider} does not exist.`);
389
+ const providerConfig = { ...provider };
390
+ if (options.baseURL !== void 0) setOrDelete(providerConfig, "baseURL", options.baseURL);
391
+ if (options.apiKey !== void 0) setOrDelete(providerConfig, "apiKey", options.apiKey);
392
+ if (options.apiKeyEnv !== void 0) setOrDelete(providerConfig, "apiKeyEnv", options.apiKeyEnv);
393
+ if (options.authHeader !== void 0) {
394
+ const header = options.authHeader.trim();
395
+ if (header) providerConfig.auth = { type: "api-key", header };
396
+ else delete providerConfig.auth;
397
+ }
398
+ providers[options.provider] = providerConfig;
399
+ config.providers = providers;
400
+ const content = `${JSON.stringify(config, null, 2)}
401
+ `;
402
+ await writeJsonAtomic(filePath, content);
403
+ return { path: filePath, created: true, content };
404
+ }
405
+ async function readConfigObject(filePath) {
406
+ if (!await exists(filePath)) await createUserConfig({ path: filePath });
407
+ const raw = JSON.parse(await readFile2(filePath, "utf8"));
408
+ raw.version ??= 2;
409
+ raw.providers = objectValue(raw.providers);
410
+ return raw;
411
+ }
412
+ function providerPreset(options) {
413
+ const preset = options.preset ?? options.name;
414
+ const auth = apiKeyAuthFields(options);
415
+ const openAIAuth = options.authHeader ? { auth: { type: "api-key", header: options.authHeader } } : {};
416
+ if (preset === "openai") return { type: "openai-compatible", baseURL: options.baseURL ?? "https://api.openai.com/v1", ...apiKeyAuthFields(options, "OPENAI_API_KEY"), ...openAIAuth, catalog: { type: "openai-models", endpoint: `${(options.baseURL ?? "https://api.openai.com/v1").replace(/\/+$/, "")}/models` } };
417
+ if (preset === "anthropic") return { type: "anthropic", baseURL: options.baseURL ?? "https://api.anthropic.com/v1", ...apiKeyAuthFields(options, "ANTHROPIC_API_KEY"), catalog: { type: "anthropic-models", endpoint: `${(options.baseURL ?? "https://api.anthropic.com/v1").replace(/\/+$/, "")}/models` } };
418
+ if (preset === "gemini") {
419
+ const geminiBase = options.baseURL ?? "https://generativelanguage.googleapis.com/v1beta/openai/";
420
+ return { type: "openai-compatible", baseURL: geminiBase, ...apiKeyAuthFields(options, "GEMINI_API_KEY"), apiKeyEnvAliases: ["GOOGLE_API_KEY"], ...openAIAuth, catalog: { type: "gemini-openai-models", endpoint: `${geminiBase.replace(/\/+$/, "")}/models` } };
421
+ }
422
+ if (preset === "groq") return { type: "openai-compatible", baseURL: options.baseURL ?? "https://api.groq.com/openai/v1", ...apiKeyAuthFields(options, "GROQ_API_KEY"), ...openAIAuth, catalog: { type: "groq-models", endpoint: `${(options.baseURL ?? "https://api.groq.com/openai/v1").replace(/\/+$/, "")}/models` } };
423
+ if (preset === "azure") {
424
+ return {
425
+ type: "openai-compatible",
426
+ auth: { type: "api-key", header: options.authHeader ?? "api-key" },
427
+ ...auth,
428
+ modelProfiles: [
429
+ {
430
+ name: "azure-example",
431
+ displayName: "Azure example",
432
+ model: "azure-deployment-name",
433
+ baseURL: options.baseURL ?? "https://example.openai.azure.com/openai/v1",
434
+ ...options.apiKey ? {} : { apiKey: "" },
435
+ apiKeyEnv: options.apiKeyEnv ?? "AZURE_OPENAI_API_KEY"
436
+ }
437
+ ]
438
+ };
439
+ }
440
+ if (preset === "lmstudio") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:1234/v1", apiKey: options.apiKey ?? "lm-studio", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:1234/v1").replace(/\/+$/, "")}/models` } };
441
+ if (preset === "ollama-local" || preset === "ollama") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:11434/v1", apiKey: options.apiKey ?? "ollama", modelProfiles: [], quirks: { omitTemperature: true }, catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:11434/v1").replace(/\/+$/, "")}/models` } };
442
+ if (preset === "ollama-cloud") return { type: "ollama", baseURL: options.baseURL ?? "https://ollama.com/api", ...apiKeyAuthFields(options, "OLLAMA_API_KEY"), modelProfiles: [], catalog: { type: "ollama-tags", endpoint: `${(options.baseURL ?? "https://ollama.com/api").replace(/\/+$/, "")}/tags` } };
443
+ if (preset === "llamacpp") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:8080/v1", apiKey: options.apiKey ?? "llama.cpp", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:8080/v1").replace(/\/+$/, "")}/models` } };
444
+ if (preset === "vllm") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:8000/v1", apiKey: options.apiKey ?? "vllm", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:8000/v1").replace(/\/+$/, "")}/models` } };
445
+ if (preset === "codex") return { type: "codex", baseURL: options.baseURL ?? "https://chatgpt.com/backend-api/codex", authStore: "auto", allowApiKeyFallback: false, promptCacheKey: "root-session", catalog: { type: "codex-oauth-models" } };
446
+ if (preset === "claudecode") return { type: "claudecode", runtime: "agent-sdk", cliPath: "~/.local/bin/claude", cwdMode: "session", historyPolicy: "passthrough-resume", onInvalidResume: "fresh", attachmentFallback: "block", allowSubagents: false, sanitizeApiKeyEnv: true, authPreflight: true, useBareMode: false, usageLedgerScope: "process", sessionLock: true, abortPolicy: "record-only", catalog: { type: "claudecode-supported-models" } };
447
+ if ((options.type ?? preset) === "openai-compatible") {
448
+ const baseURL = options.baseURL;
449
+ if (!baseURL) throw new Error("openai-compatible provider requires --base-url.");
450
+ return {
451
+ type: "openai-compatible",
452
+ baseURL,
453
+ ...auth,
454
+ ...openAIAuth,
455
+ catalog: { type: "openai-compatible-models", endpoint: `${baseURL.replace(/\/+$/, "")}/models` }
456
+ };
457
+ }
458
+ throw new Error(`Unknown provider preset: ${preset}`);
459
+ }
460
+ function apiKeyAuthFields(options, defaultApiKeyEnv) {
461
+ const apiKeyEnv = options.apiKeyEnv ?? defaultApiKeyEnv;
462
+ return {
463
+ ...options.apiKey !== void 0 || apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
464
+ ...apiKeyEnv ? { apiKeyEnv } : {}
465
+ };
466
+ }
467
+ async function writeJsonAtomic(filePath, content) {
468
+ await mkdir(path3.dirname(filePath), { recursive: true, mode: 448 });
469
+ const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
470
+ await writeFile2(temp, content, { mode: 384 });
471
+ await rename(temp, filePath);
472
+ await chmod(filePath, 384).catch(() => void 0);
473
+ }
474
+ function detectDefaultProvider() {
475
+ if (process.env.OPENAI_API_KEY) return "openai";
476
+ if (process.env.ANTHROPIC_API_KEY) return "anthropic";
477
+ if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) return "gemini";
478
+ if (process.env.GROQ_API_KEY) return "groq";
479
+ return "openai";
480
+ }
481
+ function objectValue(value) {
482
+ return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
483
+ }
484
+ function normalizeOptionalName(value) {
485
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
486
+ }
487
+ function newModelProfile(options) {
488
+ const model = options.model?.trim();
489
+ if (!model) throw new Error("Model ID is required.");
490
+ const name = options.name?.trim() || modelProfileNameFromModel(model);
491
+ return {
492
+ name,
493
+ displayName: options.displayName?.trim() || name,
494
+ model,
495
+ ...options.baseURL ? { baseURL: options.baseURL } : {},
496
+ ...options.apiKey !== void 0 || options.apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
497
+ ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {}
498
+ };
499
+ }
500
+ function modelProfileNameFromModel(model) {
501
+ return model.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "model";
502
+ }
503
+ function setOrDelete(target, key, value) {
504
+ const trimmed = value.trim();
505
+ if (trimmed) target[key] = trimmed;
506
+ else delete target[key];
507
+ }
508
+ async function exists(filePath) {
509
+ try {
510
+ await stat(filePath);
511
+ return true;
512
+ } catch {
513
+ return false;
514
+ }
515
+ }
516
+ function expandHome(value) {
517
+ return resolveExpandedPath(value);
518
+ }
519
+ var init_config_scaffold = __esm({
520
+ "src/config-scaffold.ts"() {
521
+ "use strict";
522
+ init_path_expansion();
523
+ }
524
+ });
525
+
127
526
  // src/providers/retry.ts
128
527
  async function chatWithRetry(operation, options) {
129
528
  const maxRetries = options.maxRetries ?? 5;
@@ -22790,8 +23189,8 @@ function permissionLabel(req) {
22790
23189
  if ((req.tool === "write_file" || req.tool === "edit_file" || req.tool === "read_file") && typeof inputObject.path === "string") {
22791
23190
  return `${req.tool}: ${inputObject.path}`;
22792
23191
  }
22793
- const path36 = typeof inputObject.path === "string" ? inputObject.path : typeof inputObject.file_path === "string" ? inputObject.file_path : void 0;
22794
- if (path36) return `${tool}: ${path36}`;
23192
+ const path37 = typeof inputObject.path === "string" ? inputObject.path : typeof inputObject.file_path === "string" ? inputObject.file_path : void 0;
23193
+ if (path37) return `${tool}: ${path37}`;
22795
23194
  return tool;
22796
23195
  }
22797
23196
  var init_prompt = __esm({
@@ -24615,6 +25014,214 @@ var init_catalog = __esm({
24615
25014
  }
24616
25015
  });
24617
25016
 
25017
+ // node_modules/ansi-regex/index.js
25018
+ function ansiRegex({ onlyFirst = false } = {}) {
25019
+ const ST2 = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
25020
+ const osc = `(?:\\u001B\\][\\s\\S]*?${ST2})`;
25021
+ const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
25022
+ const pattern = `${osc}|${csi}`;
25023
+ return new RegExp(pattern, onlyFirst ? void 0 : "g");
25024
+ }
25025
+ var init_ansi_regex = __esm({
25026
+ "node_modules/ansi-regex/index.js"() {
25027
+ }
25028
+ });
25029
+
25030
+ // node_modules/strip-ansi/index.js
25031
+ function stripAnsi(string) {
25032
+ if (typeof string !== "string") {
25033
+ throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
25034
+ }
25035
+ if (!string.includes("\x1B") && !string.includes("\x9B")) {
25036
+ return string;
25037
+ }
25038
+ return string.replace(regex, "");
25039
+ }
25040
+ var regex;
25041
+ var init_strip_ansi = __esm({
25042
+ "node_modules/strip-ansi/index.js"() {
25043
+ init_ansi_regex();
25044
+ regex = ansiRegex();
25045
+ }
25046
+ });
25047
+
25048
+ // node_modules/get-east-asian-width/lookup-data.js
25049
+ var ambiguousRanges, fullwidthRanges, halfwidthRanges, narrowRanges, wideRanges;
25050
+ var init_lookup_data = __esm({
25051
+ "node_modules/get-east-asian-width/lookup-data.js"() {
25052
+ ambiguousRanges = [161, 161, 164, 164, 167, 168, 170, 170, 173, 174, 176, 180, 182, 186, 188, 191, 198, 198, 208, 208, 215, 216, 222, 225, 230, 230, 232, 234, 236, 237, 240, 240, 242, 243, 247, 250, 252, 252, 254, 254, 257, 257, 273, 273, 275, 275, 283, 283, 294, 295, 299, 299, 305, 307, 312, 312, 319, 322, 324, 324, 328, 331, 333, 333, 338, 339, 358, 359, 363, 363, 462, 462, 464, 464, 466, 466, 468, 468, 470, 470, 472, 472, 474, 474, 476, 476, 593, 593, 609, 609, 708, 708, 711, 711, 713, 715, 717, 717, 720, 720, 728, 731, 733, 733, 735, 735, 768, 879, 913, 929, 931, 937, 945, 961, 963, 969, 1025, 1025, 1040, 1103, 1105, 1105, 8208, 8208, 8211, 8214, 8216, 8217, 8220, 8221, 8224, 8226, 8228, 8231, 8240, 8240, 8242, 8243, 8245, 8245, 8251, 8251, 8254, 8254, 8308, 8308, 8319, 8319, 8321, 8324, 8364, 8364, 8451, 8451, 8453, 8453, 8457, 8457, 8467, 8467, 8470, 8470, 8481, 8482, 8486, 8486, 8491, 8491, 8531, 8532, 8539, 8542, 8544, 8555, 8560, 8569, 8585, 8585, 8592, 8601, 8632, 8633, 8658, 8658, 8660, 8660, 8679, 8679, 8704, 8704, 8706, 8707, 8711, 8712, 8715, 8715, 8719, 8719, 8721, 8721, 8725, 8725, 8730, 8730, 8733, 8736, 8739, 8739, 8741, 8741, 8743, 8748, 8750, 8750, 8756, 8759, 8764, 8765, 8776, 8776, 8780, 8780, 8786, 8786, 8800, 8801, 8804, 8807, 8810, 8811, 8814, 8815, 8834, 8835, 8838, 8839, 8853, 8853, 8857, 8857, 8869, 8869, 8895, 8895, 8978, 8978, 9312, 9449, 9451, 9547, 9552, 9587, 9600, 9615, 9618, 9621, 9632, 9633, 9635, 9641, 9650, 9651, 9654, 9655, 9660, 9661, 9664, 9665, 9670, 9672, 9675, 9675, 9678, 9681, 9698, 9701, 9711, 9711, 9733, 9734, 9737, 9737, 9742, 9743, 9756, 9756, 9758, 9758, 9792, 9792, 9794, 9794, 9824, 9825, 9827, 9829, 9831, 9834, 9836, 9837, 9839, 9839, 9886, 9887, 9919, 9919, 9926, 9933, 9935, 9939, 9941, 9953, 9955, 9955, 9960, 9961, 9963, 9969, 9972, 9972, 9974, 9977, 9979, 9980, 9982, 9983, 10045, 10045, 10102, 10111, 11094, 11097, 12872, 12879, 57344, 63743, 65024, 65039, 65533, 65533, 127232, 127242, 127248, 127277, 127280, 127337, 127344, 127373, 127375, 127376, 127387, 127404, 917760, 917999, 983040, 1048573, 1048576, 1114109];
25053
+ fullwidthRanges = [12288, 12288, 65281, 65376, 65504, 65510];
25054
+ halfwidthRanges = [8361, 8361, 65377, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65512, 65518];
25055
+ narrowRanges = [32, 126, 162, 163, 165, 166, 172, 172, 175, 175, 10214, 10221, 10629, 10630];
25056
+ wideRanges = [4352, 4447, 8986, 8987, 9001, 9002, 9193, 9196, 9200, 9200, 9203, 9203, 9725, 9726, 9748, 9749, 9776, 9783, 9800, 9811, 9855, 9855, 9866, 9871, 9875, 9875, 9889, 9889, 9898, 9899, 9917, 9918, 9924, 9925, 9934, 9934, 9940, 9940, 9962, 9962, 9970, 9971, 9973, 9973, 9978, 9978, 9981, 9981, 9989, 9989, 9994, 9995, 10024, 10024, 10060, 10060, 10062, 10062, 10067, 10069, 10071, 10071, 10133, 10135, 10160, 10160, 10175, 10175, 11035, 11036, 11088, 11088, 11093, 11093, 11904, 11929, 11931, 12019, 12032, 12245, 12272, 12287, 12289, 12350, 12353, 12438, 12441, 12543, 12549, 12591, 12593, 12686, 12688, 12773, 12783, 12830, 12832, 12871, 12880, 42124, 42128, 42182, 43360, 43388, 44032, 55203, 63744, 64255, 65040, 65049, 65072, 65106, 65108, 65126, 65128, 65131, 94176, 94180, 94192, 94198, 94208, 101589, 101631, 101662, 101760, 101874, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 119552, 119638, 119648, 119670, 126980, 126980, 127183, 127183, 127374, 127374, 127377, 127386, 127488, 127490, 127504, 127547, 127552, 127560, 127568, 127569, 127584, 127589, 127744, 127776, 127789, 127797, 127799, 127868, 127870, 127891, 127904, 127946, 127951, 127955, 127968, 127984, 127988, 127988, 127992, 128062, 128064, 128064, 128066, 128252, 128255, 128317, 128331, 128334, 128336, 128359, 128378, 128378, 128405, 128406, 128420, 128420, 128507, 128591, 128640, 128709, 128716, 128716, 128720, 128722, 128725, 128728, 128732, 128735, 128747, 128748, 128756, 128764, 128992, 129003, 129008, 129008, 129292, 129338, 129340, 129349, 129351, 129535, 129648, 129660, 129664, 129674, 129678, 129734, 129736, 129736, 129741, 129756, 129759, 129770, 129775, 129784, 131072, 196605, 196608, 262141];
25057
+ }
25058
+ });
25059
+
25060
+ // node_modules/get-east-asian-width/utilities.js
25061
+ var isInRange;
25062
+ var init_utilities = __esm({
25063
+ "node_modules/get-east-asian-width/utilities.js"() {
25064
+ isInRange = (ranges, codePoint) => {
25065
+ let low = 0;
25066
+ let high = Math.floor(ranges.length / 2) - 1;
25067
+ while (low <= high) {
25068
+ const mid = Math.floor((low + high) / 2);
25069
+ const i = mid * 2;
25070
+ if (codePoint < ranges[i]) {
25071
+ high = mid - 1;
25072
+ } else if (codePoint > ranges[i + 1]) {
25073
+ low = mid + 1;
25074
+ } else {
25075
+ return true;
25076
+ }
25077
+ }
25078
+ return false;
25079
+ };
25080
+ }
25081
+ });
25082
+
25083
+ // node_modules/get-east-asian-width/lookup.js
25084
+ function findWideFastPathRange(ranges) {
25085
+ let fastPathStart = ranges[0];
25086
+ let fastPathEnd = ranges[1];
25087
+ for (let index = 0; index < ranges.length; index += 2) {
25088
+ const start = ranges[index];
25089
+ const end = ranges[index + 1];
25090
+ if (commonCjkCodePoint >= start && commonCjkCodePoint <= end) {
25091
+ return [start, end];
25092
+ }
25093
+ if (end - start > fastPathEnd - fastPathStart) {
25094
+ fastPathStart = start;
25095
+ fastPathEnd = end;
25096
+ }
25097
+ }
25098
+ return [fastPathStart, fastPathEnd];
25099
+ }
25100
+ var minimumAmbiguousCodePoint, maximumAmbiguousCodePoint, minimumFullWidthCodePoint, maximumFullWidthCodePoint, minimumHalfWidthCodePoint, maximumHalfWidthCodePoint, minimumNarrowCodePoint, maximumNarrowCodePoint, minimumWideCodePoint, maximumWideCodePoint, commonCjkCodePoint, wideFastPathStart, wideFastPathEnd, isAmbiguous, isFullWidth, isWide;
25101
+ var init_lookup = __esm({
25102
+ "node_modules/get-east-asian-width/lookup.js"() {
25103
+ init_lookup_data();
25104
+ init_utilities();
25105
+ minimumAmbiguousCodePoint = ambiguousRanges[0];
25106
+ maximumAmbiguousCodePoint = ambiguousRanges.at(-1);
25107
+ minimumFullWidthCodePoint = fullwidthRanges[0];
25108
+ maximumFullWidthCodePoint = fullwidthRanges.at(-1);
25109
+ minimumHalfWidthCodePoint = halfwidthRanges[0];
25110
+ maximumHalfWidthCodePoint = halfwidthRanges.at(-1);
25111
+ minimumNarrowCodePoint = narrowRanges[0];
25112
+ maximumNarrowCodePoint = narrowRanges.at(-1);
25113
+ minimumWideCodePoint = wideRanges[0];
25114
+ maximumWideCodePoint = wideRanges.at(-1);
25115
+ commonCjkCodePoint = 19968;
25116
+ [wideFastPathStart, wideFastPathEnd] = findWideFastPathRange(wideRanges);
25117
+ isAmbiguous = (codePoint) => {
25118
+ if (codePoint < minimumAmbiguousCodePoint || codePoint > maximumAmbiguousCodePoint) {
25119
+ return false;
25120
+ }
25121
+ return isInRange(ambiguousRanges, codePoint);
25122
+ };
25123
+ isFullWidth = (codePoint) => {
25124
+ if (codePoint < minimumFullWidthCodePoint || codePoint > maximumFullWidthCodePoint) {
25125
+ return false;
25126
+ }
25127
+ return isInRange(fullwidthRanges, codePoint);
25128
+ };
25129
+ isWide = (codePoint) => {
25130
+ if (codePoint >= wideFastPathStart && codePoint <= wideFastPathEnd) {
25131
+ return true;
25132
+ }
25133
+ if (codePoint < minimumWideCodePoint || codePoint > maximumWideCodePoint) {
25134
+ return false;
25135
+ }
25136
+ return isInRange(wideRanges, codePoint);
25137
+ };
25138
+ }
25139
+ });
25140
+
25141
+ // node_modules/get-east-asian-width/index.js
25142
+ function validate(codePoint) {
25143
+ if (!Number.isSafeInteger(codePoint)) {
25144
+ throw new TypeError(`Expected a code point, got \`${typeof codePoint}\`.`);
25145
+ }
25146
+ }
25147
+ function eastAsianWidth(codePoint, { ambiguousAsWide = false } = {}) {
25148
+ validate(codePoint);
25149
+ if (isFullWidth(codePoint) || isWide(codePoint) || ambiguousAsWide && isAmbiguous(codePoint)) {
25150
+ return 2;
25151
+ }
25152
+ return 1;
25153
+ }
25154
+ var init_get_east_asian_width = __esm({
25155
+ "node_modules/get-east-asian-width/index.js"() {
25156
+ init_lookup();
25157
+ }
25158
+ });
25159
+
25160
+ // node_modules/emoji-regex/index.js
25161
+ var require_emoji_regex = __commonJS({
25162
+ "node_modules/emoji-regex/index.js"(exports, module) {
25163
+ module.exports = () => {
25164
+ return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E-\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED8\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])))?))?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC2\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF]|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
25165
+ };
25166
+ }
25167
+ });
25168
+
25169
+ // node_modules/string-width/index.js
25170
+ function stringWidth(string, options = {}) {
25171
+ if (typeof string !== "string" || string.length === 0) {
25172
+ return 0;
25173
+ }
25174
+ const {
25175
+ ambiguousIsNarrow = true,
25176
+ countAnsiEscapeCodes = false
25177
+ } = options;
25178
+ if (!countAnsiEscapeCodes) {
25179
+ string = stripAnsi(string);
25180
+ }
25181
+ if (string.length === 0) {
25182
+ return 0;
25183
+ }
25184
+ let width = 0;
25185
+ const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
25186
+ for (const { segment: character } of segmenter.segment(string)) {
25187
+ const codePoint = character.codePointAt(0);
25188
+ if (codePoint <= 31 || codePoint >= 127 && codePoint <= 159) {
25189
+ continue;
25190
+ }
25191
+ if (codePoint >= 8203 && codePoint <= 8207 || codePoint === 65279) {
25192
+ continue;
25193
+ }
25194
+ if (codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071) {
25195
+ continue;
25196
+ }
25197
+ if (codePoint >= 55296 && codePoint <= 57343) {
25198
+ continue;
25199
+ }
25200
+ if (codePoint >= 65024 && codePoint <= 65039) {
25201
+ continue;
25202
+ }
25203
+ if (defaultIgnorableCodePointRegex.test(character)) {
25204
+ continue;
25205
+ }
25206
+ if ((0, import_emoji_regex.default)().test(character)) {
25207
+ width += 2;
25208
+ continue;
25209
+ }
25210
+ width += eastAsianWidth(codePoint, eastAsianWidthOptions);
25211
+ }
25212
+ return width;
25213
+ }
25214
+ var import_emoji_regex, segmenter, defaultIgnorableCodePointRegex;
25215
+ var init_string_width = __esm({
25216
+ "node_modules/string-width/index.js"() {
25217
+ init_strip_ansi();
25218
+ init_get_east_asian_width();
25219
+ import_emoji_regex = __toESM(require_emoji_regex(), 1);
25220
+ segmenter = new Intl.Segmenter();
25221
+ defaultIgnorableCodePointRegex = new RegExp("^\\p{Default_Ignorable_Code_Point}$", "u");
25222
+ }
25223
+ });
25224
+
24618
25225
  // src/permissions/presets.ts
24619
25226
  function normalizePermissionPreset(value, fallback = "auto") {
24620
25227
  return typeof value === "string" && isPermissionPreset(value) ? value : fallback;
@@ -24684,6 +25291,26 @@ function isCompactCommand(input2) {
24684
25291
  function isRetryCommand(input2) {
24685
25292
  return input2.trim().toLowerCase() === "/retry";
24686
25293
  }
25294
+ function isSessionCommand(input2) {
25295
+ return /^\/(?:session|sessions|conversation|conversations|conv)(?:\s|$)/i.test(input2.trim());
25296
+ }
25297
+ function parseSessionCommand(input2) {
25298
+ const raw = input2.trim();
25299
+ if (!isSessionCommand(raw)) return void 0;
25300
+ const rest = raw.replace(/^\/(?:session|sessions|conversation|conversations|conv)\b/i, "").trim();
25301
+ if (!rest) return { action: "help" };
25302
+ const tokens = shellishSplit2(rest);
25303
+ const action = tokens.shift()?.toLowerCase() ?? "help";
25304
+ if (action === "help" || action === "-h" || action === "--help") return { action: "help" };
25305
+ if (action === "list" || action === "ls") return { action: "list" };
25306
+ if (action === "current" || action === "status") return { action: "current" };
25307
+ if (action === "new" || action === "create") return { action: "new", title: tokens.join(" ").trim() || void 0 };
25308
+ if (action === "switch" || action === "select" || action === "use" || action === "open") return { action: "switch", target: tokens.join(" ").trim() || void 0 };
25309
+ if (action === "delete" || action === "remove" || action === "rm") return { action: "delete", target: tokens.join(" ").trim() || void 0 };
25310
+ if (action === "rename" || action === "name") return { action: "rename", title: tokens.join(" ").trim() || void 0 };
25311
+ if (action === "clear" || action === "reset") return { action: "clear" };
25312
+ return { action: "switch", target: [action, ...tokens].join(" ").trim() };
25313
+ }
24687
25314
  function isCoworkCommand(input2) {
24688
25315
  return /^\/cowork(?:\s|$)/i.test(input2.trim());
24689
25316
  }
@@ -25377,10 +26004,30 @@ var init_tool_summary = __esm({
25377
26004
  // src/ui/tui/store.ts
25378
26005
  var store_exports = {};
25379
26006
  __export(store_exports, {
26007
+ TUI_CONFIG_ACTION_PREFIX: () => TUI_CONFIG_ACTION_PREFIX,
26008
+ TUI_SESSION_ACTION_PREFIX: () => TUI_SESSION_ACTION_PREFIX,
25380
26009
  TuiStore: () => TuiStore,
25381
26010
  buildClaudeCodePlanExecutionPrompt: () => buildClaudeCodePlanExecutionPrompt,
26011
+ parseTuiConfigAction: () => parseTuiConfigAction,
26012
+ parseTuiSessionAction: () => parseTuiSessionAction,
25382
26013
  streamingLines: () => streamingLines
25383
26014
  });
26015
+ function parseTuiConfigAction(prompt) {
26016
+ if (!prompt.startsWith(TUI_CONFIG_ACTION_PREFIX)) return void 0;
26017
+ try {
26018
+ return JSON.parse(prompt.slice(TUI_CONFIG_ACTION_PREFIX.length));
26019
+ } catch {
26020
+ return void 0;
26021
+ }
26022
+ }
26023
+ function parseTuiSessionAction(prompt) {
26024
+ if (!prompt.startsWith(TUI_SESSION_ACTION_PREFIX)) return void 0;
26025
+ try {
26026
+ return JSON.parse(prompt.slice(TUI_SESSION_ACTION_PREFIX.length));
26027
+ } catch {
26028
+ return void 0;
26029
+ }
26030
+ }
25384
26031
  function toTuiWorkPlan(plan) {
25385
26032
  return {
25386
26033
  planId: plan.planId,
@@ -25540,6 +26187,25 @@ function cloneContextEfficiency(context) {
25540
26187
  lastCompaction: context.lastCompaction ? { ...context.lastCompaction } : void 0
25541
26188
  };
25542
26189
  }
26190
+ function configProviderFieldValue(provider, field) {
26191
+ if (field === "baseURL") return provider.baseURL ?? "";
26192
+ if (field === "apiKeyEnv") return provider.apiKeyEnv ?? "";
26193
+ return provider.auth?.header ?? "";
26194
+ }
26195
+ function defaultConfigProviderPresets() {
26196
+ return [
26197
+ { label: "OpenAI", preset: "openai", name: "openai", apiKeyEnv: "OPENAI_API_KEY", detail: "OpenAI API" },
26198
+ { label: "Anthropic", preset: "anthropic", name: "anthropic", apiKeyEnv: "ANTHROPIC_API_KEY", detail: "Anthropic API" },
26199
+ { label: "Gemini", preset: "gemini", name: "gemini", apiKeyEnv: "GEMINI_API_KEY", detail: "Google Gemini OpenAI-compatible API" },
26200
+ { label: "Groq", preset: "groq", name: "groq", apiKeyEnv: "GROQ_API_KEY", detail: "Groq Cloud" },
26201
+ { label: "Codex", preset: "codex", name: "codex", detail: "ChatGPT Codex OAuth" },
26202
+ { label: "Claude Code", preset: "claudecode", name: "claudecode", detail: "Local Claude Code runtime" },
26203
+ { label: "Ollama local", preset: "ollama-local", name: "ollama-local", detail: "http://localhost:11434" },
26204
+ { label: "LM Studio", preset: "lmstudio", name: "lmstudio", detail: "http://localhost:1234" },
26205
+ { label: "llama.cpp", preset: "llamacpp", name: "llamacpp", detail: "http://localhost:8080" },
26206
+ { label: "vLLM", preset: "vllm", name: "vllm", detail: "http://localhost:8000" }
26207
+ ];
26208
+ }
25543
26209
  function cloneToolRunDetails(details) {
25544
26210
  if (!details) return void 0;
25545
26211
  return {
@@ -25592,6 +26258,36 @@ function cloneGoalToolRun(tool) {
25592
26258
  details: cloneToolRunDetails(tool.details)
25593
26259
  };
25594
26260
  }
26261
+ function safeConversationSnapshot(snapshot) {
26262
+ if (!snapshot || typeof snapshot !== "object") return {};
26263
+ return {
26264
+ status: snapshot.status ? { ...snapshot.status } : void 0,
26265
+ blocks: Array.isArray(snapshot.blocks) ? snapshot.blocks.map((block) => ({ ...block, lines: [...block.lines ?? []], toolDetails: cloneToolRunDetails(block.toolDetails), goalWork: cloneGoalWork(block.goalWork), cowork: cloneCoworkGroup(block.cowork) })) : [],
26266
+ activity: typeof snapshot.activity === "string" ? snapshot.activity : "restored conversation",
26267
+ warnings: Array.isArray(snapshot.warnings) ? [...snapshot.warnings] : [],
26268
+ finalAnswer: snapshot.finalAnswer,
26269
+ progressNotes: Array.isArray(snapshot.progressNotes) ? snapshot.progressNotes.map((note) => ({ ...note })) : [],
26270
+ turnDiff: snapshot.turnDiff ? cloneDiffSummary(snapshot.turnDiff) : void 0,
26271
+ diffExpanded: snapshot.diffExpanded === true,
26272
+ workPlan: snapshot.workPlan ? {
26273
+ ...snapshot.workPlan,
26274
+ steps: snapshot.workPlan.steps.map((step) => ({ ...step }))
26275
+ } : void 0,
26276
+ workPlanExpanded: snapshot.workPlanExpanded === true,
26277
+ goal: snapshot.goal ? { ...snapshot.goal } : void 0,
26278
+ contextEfficiency: snapshot.contextEfficiency ? cloneContextEfficiency(snapshot.contextEfficiency) : void 0,
26279
+ canRetryLastPrompt: snapshot.canRetryLastPrompt === true,
26280
+ pendingClaudeCodePlan: snapshot.pendingClaudeCodePlan ? { ...snapshot.pendingClaudeCodePlan } : void 0
26281
+ };
26282
+ }
26283
+ function nextBlockId(blocks) {
26284
+ let max = 0;
26285
+ for (const block of blocks ?? []) {
26286
+ const match = /^block_(\d+)$/.exec(block.id);
26287
+ if (match) max = Math.max(max, Number(match[1]));
26288
+ }
26289
+ return max;
26290
+ }
25595
26291
  function formatDuration2(durationMs) {
25596
26292
  if (durationMs < 1e3) return `${durationMs}ms`;
25597
26293
  return `${Math.round(durationMs / 1e3)}s`;
@@ -25611,7 +26307,7 @@ function buildClaudeCodePlanExecutionPrompt(planText, requestText) {
25611
26307
  function streamingLines(markdown) {
25612
26308
  return renderMarkdownFallback(markdown, { width: 100 }).lines;
25613
26309
  }
25614
- var TuiStore;
26310
+ var TUI_CONFIG_ACTION_PREFIX, TUI_SESSION_ACTION_PREFIX, TuiStore;
25615
26311
  var init_store = __esm({
25616
26312
  "src/ui/tui/store.ts"() {
25617
26313
  "use strict";
@@ -25619,14 +26315,24 @@ var init_store = __esm({
25619
26315
  init_commands();
25620
26316
  init_render();
25621
26317
  init_tool_summary();
26318
+ TUI_CONFIG_ACTION_PREFIX = "demian-config:";
26319
+ TUI_SESSION_ACTION_PREFIX = "demian-session:";
25622
26320
  TuiStore = class {
25623
26321
  #state = {
25624
26322
  status: {},
25625
26323
  blocks: [],
25626
26324
  activity: "starting",
25627
26325
  inputMode: "starting",
26326
+ sessionOptions: [],
26327
+ sessionCursor: 0,
26328
+ sessionCancelBehavior: "exit",
25628
26329
  providerOptions: [],
25629
26330
  providerCursor: 0,
26331
+ configModelCursor: 0,
26332
+ configAddProviderCursor: 0,
26333
+ configModelInput: "",
26334
+ configProviderInput: "",
26335
+ configProviderPresets: defaultConfigProviderPresets(),
25630
26336
  agentOptions: [],
25631
26337
  agentCursor: 0,
25632
26338
  permissionPreset: "auto",
@@ -25646,6 +26352,7 @@ var init_store = __esm({
25646
26352
  #blockId = 0;
25647
26353
  #stopActiveTask;
25648
26354
  #promptResolve;
26355
+ #sessionResolve;
25649
26356
  #queuedPrompt;
25650
26357
  #exitRequested = false;
25651
26358
  #promptHistory = [];
@@ -25661,12 +26368,15 @@ var init_store = __esm({
25661
26368
  #pendingPermissionTimeout;
25662
26369
  #syntheticPermissionId = 0;
25663
26370
  #activeClaudeCodePlan;
26371
+ #notifyTimer;
25664
26372
  snapshot() {
25665
26373
  return {
25666
26374
  ...this.#state,
25667
26375
  status: { ...this.#state.status },
25668
26376
  selection: this.#state.selection ? { ...this.#state.selection } : void 0,
26377
+ sessionOptions: this.#state.sessionOptions.map((option) => ({ ...option })),
25669
26378
  providerOptions: this.#state.providerOptions.map((option) => ({ ...option })),
26379
+ configProviderPresets: this.#state.configProviderPresets.map((option) => ({ ...option })),
25670
26380
  agentOptions: this.#state.agentOptions.map((option) => ({ ...option })),
25671
26381
  blocks: this.#state.blocks.map((block) => ({ ...block, lines: [...block.lines], toolDetails: cloneToolRunDetails(block.toolDetails), goalWork: cloneGoalWork(block.goalWork), cowork: cloneCoworkGroup(block.cowork) })),
25672
26382
  warnings: [...this.#state.warnings],
@@ -25704,6 +26414,7 @@ var init_store = __esm({
25704
26414
  requestExit() {
25705
26415
  this.#exitRequested = true;
25706
26416
  if (this.#promptResolve) this.#resolvePrompt("");
26417
+ if (this.#sessionResolve) this.#resolveSession({ kind: "exit" });
25707
26418
  this.#stopActiveTask?.();
25708
26419
  this.#state.inputMode = "done";
25709
26420
  this.#state.done = true;
@@ -25783,6 +26494,84 @@ var init_store = __esm({
25783
26494
  this.#state.activity = `context compacted: ${summary.beforeTokenEstimate} -> ${summary.afterTokenEstimate}`;
25784
26495
  this.#notify();
25785
26496
  }
26497
+ conversationSnapshot() {
26498
+ const snapshot = this.snapshot();
26499
+ return {
26500
+ ...snapshot,
26501
+ inputMode: "prompt",
26502
+ promptInput: "",
26503
+ promptError: void 0,
26504
+ streamingText: "",
26505
+ streamingFinalized: true,
26506
+ permission: void 0,
26507
+ done: false
26508
+ };
26509
+ }
26510
+ restoreConversationSnapshot(snapshot, fallback) {
26511
+ this.#resetTransientConversationState();
26512
+ const currentSettings = {
26513
+ selection: this.#state.selection,
26514
+ providerOptions: this.#state.providerOptions,
26515
+ providerCursor: this.#state.providerCursor,
26516
+ agentOptions: this.#state.agentOptions,
26517
+ agentCursor: this.#state.agentCursor,
26518
+ selectedAgent: this.#state.selectedAgent,
26519
+ permissionPreset: this.#state.permissionPreset,
26520
+ permissionPresetCursor: this.#state.permissionPresetCursor,
26521
+ contextEfficiency: this.#state.contextEfficiency
26522
+ };
26523
+ const restoredSelection = snapshot?.selection ? { ...snapshot.selection } : currentSettings.selection;
26524
+ const restoredAgent = snapshot?.selectedAgent ?? snapshot?.status?.agent ?? currentSettings.selectedAgent;
26525
+ const restoredPermissionPreset = normalizePermissionPreset(snapshot?.permissionPreset, currentSettings.permissionPreset);
26526
+ this.#state = {
26527
+ ...this.#state,
26528
+ ...safeConversationSnapshot(snapshot),
26529
+ ...currentSettings,
26530
+ selection: restoredSelection,
26531
+ providerCursor: Math.max(0, currentSettings.providerOptions.findIndex((option) => option.name === restoredSelection?.providerName)),
26532
+ selectedAgent: restoredAgent,
26533
+ agentCursor: Math.max(0, currentSettings.agentOptions.findIndex((option) => option.name === restoredAgent)),
26534
+ permissionPreset: restoredPermissionPreset,
26535
+ permissionPresetCursor: Math.max(0, PERMISSION_PRESETS.indexOf(restoredPermissionPreset)),
26536
+ status: {
26537
+ ...snapshot?.status ?? this.#state.status,
26538
+ sessionId: fallback.sessionId,
26539
+ cwd: fallback.cwd,
26540
+ provider: restoredSelection?.providerName ?? snapshot?.status?.provider ?? this.#state.status.provider,
26541
+ model: restoredSelection?.model ?? snapshot?.status?.model ?? this.#state.status.model,
26542
+ agent: restoredAgent ?? snapshot?.status?.agent ?? this.#state.status.agent
26543
+ },
26544
+ inputMode: "prompt",
26545
+ promptInput: "",
26546
+ promptError: void 0,
26547
+ streamingText: "",
26548
+ streamingFinalized: true,
26549
+ permission: void 0,
26550
+ done: false,
26551
+ activity: `conversation: ${fallback.title}`
26552
+ };
26553
+ this.#blockId = nextBlockId(this.#state.blocks);
26554
+ this.#notify();
26555
+ }
26556
+ clearConversation(title = "New session") {
26557
+ this.#resetTransientConversationState();
26558
+ this.#state.blocks = [];
26559
+ this.#state.streamingText = "";
26560
+ this.#state.streamingFinalized = true;
26561
+ this.#state.permission = void 0;
26562
+ this.#state.warnings = [];
26563
+ this.#state.done = false;
26564
+ this.#state.finalAnswer = void 0;
26565
+ this.#state.progressNotes = [];
26566
+ this.#state.turnDiff = void 0;
26567
+ this.#state.diffExpanded = false;
26568
+ this.#state.workPlan = void 0;
26569
+ this.#state.workPlanExpanded = false;
26570
+ this.#state.goal = void 0;
26571
+ this.#state.pendingClaudeCodePlan = void 0;
26572
+ this.#state.activity = `cleared conversation: ${title}`;
26573
+ this.prepareForPrompt("waiting for next message");
26574
+ }
25786
26575
  prepareForPrompt(message = "waiting for next message") {
25787
26576
  if (this.#exitRequested) return;
25788
26577
  this.#state.inputMode = "prompt";
@@ -25809,6 +26598,10 @@ var init_store = __esm({
25809
26598
  agent: context.agent,
25810
26599
  cwd: context.cwd
25811
26600
  };
26601
+ this.#state.configPath = context.configPath;
26602
+ this.#state.configCreated = context.configCreated;
26603
+ this.#state.configDefaultProvider = context.configDefaultProvider;
26604
+ this.#state.configMessage = context.configMessage;
25812
26605
  if (context.contextEfficiency) {
25813
26606
  this.#state.contextEfficiency = {
25814
26607
  ...this.#state.contextEfficiency ?? { selectedTools: [] },
@@ -25840,7 +26633,58 @@ var init_store = __esm({
25840
26633
  currentPermissionPreset() {
25841
26634
  return this.#state.permissionPreset;
25842
26635
  }
26636
+ requestSessionSelection(options, behavior = {}) {
26637
+ if (this.#exitRequested) return Promise.resolve({ kind: "exit" });
26638
+ const normalized = options.length > 0 ? options.map((option) => ({ ...option })) : [{ kind: "new", title: "New session", status: "ready", currentWorkspace: true }];
26639
+ return new Promise((resolve) => {
26640
+ this.#sessionResolve = resolve;
26641
+ this.#state.inputMode = "session";
26642
+ this.#state.sessionOptions = normalized;
26643
+ this.#state.sessionCursor = 0;
26644
+ this.#state.sessionCancelBehavior = behavior.cancel ?? "exit";
26645
+ this.#state.promptInput = "";
26646
+ this.#state.promptError = void 0;
26647
+ this.#state.streamingText = "";
26648
+ this.#state.streamingFinalized = true;
26649
+ this.#state.done = false;
26650
+ this.#state.activity = "select session";
26651
+ this.#notify();
26652
+ });
26653
+ }
26654
+ cancelSessionSelection() {
26655
+ if (this.#state.inputMode !== "session") return false;
26656
+ if (this.#state.sessionCancelBehavior === "prompt") {
26657
+ this.#resolveSession({ kind: "cancel" });
26658
+ return false;
26659
+ }
26660
+ return true;
26661
+ }
26662
+ moveSessionCursor(delta) {
26663
+ if (this.#state.inputMode !== "session" || this.#state.sessionOptions.length === 0) return;
26664
+ const count = this.#state.sessionOptions.length;
26665
+ this.#state.sessionCursor = (this.#state.sessionCursor + delta + count) % count;
26666
+ this.#notify();
26667
+ }
26668
+ submitSessionSelection() {
26669
+ if (this.#state.inputMode !== "session") return;
26670
+ const option = this.#state.sessionOptions[this.#state.sessionCursor];
26671
+ if (!option) return;
26672
+ if (option.kind === "new") {
26673
+ this.#resolveSession({ kind: "new" });
26674
+ return;
26675
+ }
26676
+ if (option.id) this.#resolveSession({ kind: "session", id: option.id });
26677
+ }
26678
+ submitNewSessionSelection() {
26679
+ if (this.#state.inputMode !== "session") return;
26680
+ this.#resolveSession({ kind: "new" });
26681
+ }
26682
+ submitSessionSelectionShortcut() {
26683
+ if (this.#state.inputMode !== "prompt" || this.#state.promptInput.trim()) return;
26684
+ this.#resolvePrompt(`${TUI_SESSION_ACTION_PREFIX}${JSON.stringify({ type: "select" })}`);
26685
+ }
25843
26686
  requestPrompt(initialPrompt) {
26687
+ if (this.#exitRequested) return Promise.resolve("");
25844
26688
  const prompt = (initialPrompt ?? "").trim();
25845
26689
  if (prompt) {
25846
26690
  this.#rememberPrompt(prompt);
@@ -25881,12 +26725,12 @@ var init_store = __esm({
25881
26725
  this.#notify();
25882
26726
  });
25883
26727
  }
25884
- appendPromptInput(input2) {
26728
+ appendPromptInput(input2, options = {}) {
25885
26729
  if (this.#state.inputMode !== "prompt" && this.#state.inputMode !== "running") return;
25886
26730
  if (this.#state.inputMode === "prompt") this.#detachPromptHistory();
25887
26731
  this.#state.promptInput += input2;
25888
26732
  this.#state.promptError = void 0;
25889
- this.#notify();
26733
+ if (options.notify !== false) this.#notify();
25890
26734
  }
25891
26735
  navigatePromptHistory(delta) {
25892
26736
  if (this.#state.inputMode !== "prompt" || this.#promptHistory.length === 0) return;
@@ -25936,6 +26780,143 @@ var init_store = __esm({
25936
26780
  this.#state.activity = `provider ${option.name} selected`;
25937
26781
  this.#notify();
25938
26782
  }
26783
+ openConfigManager(message) {
26784
+ const canOpenFromConfig = this.#state.inputMode === "config" || this.#state.inputMode === "configModel" || this.#state.inputMode === "configModelInput" || this.#state.inputMode === "configProviderInput" || this.#state.inputMode === "configAddProvider";
26785
+ if (!canOpenFromConfig && (this.#state.inputMode !== "prompt" || this.#state.promptInput.trim())) return;
26786
+ this.#state.inputMode = "config";
26787
+ const current = this.#state.selection?.providerName ?? this.#state.status.provider;
26788
+ this.#state.providerCursor = Math.max(0, this.#state.providerOptions.findIndex((option) => option.name === current));
26789
+ this.#state.configMessage = message ?? this.#state.configMessage;
26790
+ this.#state.settingsError = void 0;
26791
+ this.#state.activity = "configure providers and models";
26792
+ this.#notify();
26793
+ }
26794
+ openConfigProviderInput(field) {
26795
+ if (this.#state.inputMode !== "config") return;
26796
+ const provider = this.#selectedConfigProvider();
26797
+ if (!provider) return;
26798
+ this.#state.inputMode = "configProviderInput";
26799
+ this.#state.configProviderInputField = field;
26800
+ this.#state.configProviderInput = configProviderFieldValue(provider, field);
26801
+ this.#state.settingsError = void 0;
26802
+ this.#state.activity = `edit ${field} for ${provider.name}`;
26803
+ this.#notify();
26804
+ }
26805
+ appendConfigProviderInput(input2) {
26806
+ if (this.#state.inputMode !== "configProviderInput") return;
26807
+ this.#state.configProviderInput += input2;
26808
+ this.#state.settingsError = void 0;
26809
+ this.#notify();
26810
+ }
26811
+ backspaceConfigProviderInput() {
26812
+ if (this.#state.inputMode !== "configProviderInput") return;
26813
+ this.#state.configProviderInput = Array.from(this.#state.configProviderInput).slice(0, -1).join("");
26814
+ this.#state.settingsError = void 0;
26815
+ this.#notify();
26816
+ }
26817
+ submitConfigProviderInput() {
26818
+ if (this.#state.inputMode !== "configProviderInput") return;
26819
+ const provider = this.#selectedConfigProvider();
26820
+ const field = this.#state.configProviderInputField;
26821
+ if (!provider || !field) return;
26822
+ this.#submitConfigAction({ type: "updateProvider", provider: provider.name, field, value: this.#state.configProviderInput });
26823
+ }
26824
+ moveConfigProviderCursor(delta) {
26825
+ if (this.#state.inputMode !== "config" || this.#state.providerOptions.length === 0) return;
26826
+ const count = this.#state.providerOptions.length;
26827
+ this.#state.providerCursor = (this.#state.providerCursor + delta + count) % count;
26828
+ this.#notify();
26829
+ }
26830
+ submitConfigDefaultProvider() {
26831
+ if (this.#state.inputMode !== "config") return;
26832
+ const option = this.#selectedConfigProvider();
26833
+ if (!option) return;
26834
+ this.#submitConfigAction({ type: "setDefaultProvider", provider: option.name });
26835
+ }
26836
+ openConfigModelSelector() {
26837
+ if (this.#state.inputMode !== "config" && this.#state.inputMode !== "configModelInput") return;
26838
+ const option = this.#selectedConfigProvider();
26839
+ if (!option) return;
26840
+ this.#state.inputMode = "configModel";
26841
+ this.#state.configModelCursor = 0;
26842
+ this.#state.settingsError = void 0;
26843
+ this.#state.activity = `choose default model for ${option.name}`;
26844
+ this.#notify();
26845
+ }
26846
+ moveConfigModelCursor(delta) {
26847
+ if (this.#state.inputMode !== "configModel") return;
26848
+ const profiles = this.#selectedConfigProvider()?.modelProfiles ?? [];
26849
+ if (profiles.length === 0) return;
26850
+ this.#state.configModelCursor = (this.#state.configModelCursor + delta + profiles.length) % profiles.length;
26851
+ this.#notify();
26852
+ }
26853
+ submitConfigDefaultModel() {
26854
+ if (this.#state.inputMode !== "configModel") return;
26855
+ const provider = this.#selectedConfigProvider();
26856
+ const profile = provider?.modelProfiles?.[this.#state.configModelCursor];
26857
+ if (!provider || !profile?.model) return;
26858
+ this.#submitConfigAction({
26859
+ type: "setDefaultModel",
26860
+ provider: provider.name,
26861
+ profileName: profile.name,
26862
+ name: profile.name,
26863
+ displayName: profile.displayName,
26864
+ model: profile.model
26865
+ });
26866
+ }
26867
+ openConfigModelInput() {
26868
+ if (this.#state.inputMode !== "configModel" && this.#state.inputMode !== "config") return;
26869
+ if (!this.#selectedConfigProvider()) return;
26870
+ this.#state.inputMode = "configModelInput";
26871
+ this.#state.configModelInput = "";
26872
+ this.#state.settingsError = void 0;
26873
+ this.#state.activity = "add a model profile";
26874
+ this.#notify();
26875
+ }
26876
+ appendConfigModelInput(input2) {
26877
+ if (this.#state.inputMode !== "configModelInput") return;
26878
+ this.#state.configModelInput += input2;
26879
+ this.#state.settingsError = void 0;
26880
+ this.#notify();
26881
+ }
26882
+ backspaceConfigModelInput() {
26883
+ if (this.#state.inputMode !== "configModelInput") return;
26884
+ this.#state.configModelInput = Array.from(this.#state.configModelInput).slice(0, -1).join("");
26885
+ this.#state.settingsError = void 0;
26886
+ this.#notify();
26887
+ }
26888
+ submitConfigModelInput() {
26889
+ if (this.#state.inputMode !== "configModelInput") return;
26890
+ const provider = this.#selectedConfigProvider();
26891
+ const model = this.#state.configModelInput.trim();
26892
+ if (!provider || !model) {
26893
+ this.#state.settingsError = "Model ID is required.";
26894
+ this.#notify();
26895
+ return;
26896
+ }
26897
+ this.#submitConfigAction({ type: "setDefaultModel", provider: provider.name, model, displayName: model });
26898
+ }
26899
+ openConfigAddProviderSelector() {
26900
+ if (this.#state.inputMode !== "config") return;
26901
+ this.#state.inputMode = "configAddProvider";
26902
+ this.#state.configAddProviderCursor = 0;
26903
+ this.#state.settingsError = void 0;
26904
+ this.#state.activity = "add provider preset";
26905
+ this.#notify();
26906
+ }
26907
+ moveConfigAddProviderCursor(delta) {
26908
+ if (this.#state.inputMode !== "configAddProvider") return;
26909
+ const count = this.#state.configProviderPresets.length;
26910
+ if (count === 0) return;
26911
+ this.#state.configAddProviderCursor = (this.#state.configAddProviderCursor + delta + count) % count;
26912
+ this.#notify();
26913
+ }
26914
+ submitConfigAddProvider() {
26915
+ if (this.#state.inputMode !== "configAddProvider") return;
26916
+ const preset = this.#state.configProviderPresets[this.#state.configAddProviderCursor];
26917
+ if (!preset) return;
26918
+ this.#submitConfigAction({ type: "addProvider", preset: preset.preset, name: preset.name, apiKeyEnv: preset.apiKeyEnv });
26919
+ }
25939
26920
  openAgentSelector() {
25940
26921
  if (this.#state.inputMode !== "prompt" || this.#state.promptInput.trim()) return;
25941
26922
  if (this.#state.agentOptions.length === 0) {
@@ -26034,26 +27015,29 @@ var init_store = __esm({
26034
27015
  this.#notify();
26035
27016
  }
26036
27017
  closeSettings() {
26037
- if (this.#state.inputMode !== "provider" && this.#state.inputMode !== "model" && this.#state.inputMode !== "agent" && this.#state.inputMode !== "permissionPreset") return;
27018
+ if (!["provider", "model", "agent", "permissionPreset", "config", "configModel", "configModelInput", "configProviderInput", "configAddProvider"].includes(this.#state.inputMode)) return;
26038
27019
  this.#state.inputMode = "prompt";
26039
27020
  this.#state.modelInput = "";
27021
+ this.#state.configModelInput = "";
27022
+ this.#state.configProviderInput = "";
27023
+ this.#state.configProviderInputField = void 0;
26040
27024
  this.#state.settingsError = void 0;
26041
27025
  this.#state.activity = "waiting for first message";
26042
27026
  this.#notify();
26043
27027
  }
26044
- backspacePromptInput() {
27028
+ backspacePromptInput(options = {}) {
26045
27029
  if (this.#state.inputMode !== "prompt" && this.#state.inputMode !== "running") return;
26046
27030
  if (this.#state.inputMode === "prompt") this.#detachPromptHistory();
26047
27031
  this.#state.promptInput = Array.from(this.#state.promptInput).slice(0, -1).join("");
26048
27032
  this.#state.promptError = void 0;
26049
- this.#notify();
27033
+ if (options.notify !== false) this.#notify();
26050
27034
  }
26051
- clearPromptInput() {
27035
+ clearPromptInput(options = {}) {
26052
27036
  if (this.#state.inputMode !== "prompt" && this.#state.inputMode !== "running") return;
26053
27037
  if (this.#state.inputMode === "prompt") this.#detachPromptHistory();
26054
27038
  this.#state.promptInput = "";
26055
27039
  this.#state.promptError = void 0;
26056
- this.#notify();
27040
+ if (options.notify !== false) this.#notify();
26057
27041
  }
26058
27042
  usePendingClaudeCodePlan() {
26059
27043
  if (this.#state.inputMode !== "prompt") return false;
@@ -26165,9 +27149,12 @@ var init_store = __esm({
26165
27149
  this.#state.streamingFinalized = false;
26166
27150
  this.#activeClaudeCodePlan = this.#isClaudeCodePlanRun(event.provider) ? { text: "", providerName: event.provider, model: event.model, createdAt: event.ts } : void 0;
26167
27151
  }
26168
- if (event.type === "model.text.delta" && !isNestedInvocationEvent(event)) {
27152
+ if (event.type === "model.text.delta") {
27153
+ if (isNestedInvocationEvent(event)) return;
26169
27154
  this.#state.streamingText += event.text;
26170
27155
  this.#state.streamingFinalized = false;
27156
+ this.#notify({ deferMs: 50 });
27157
+ return;
26171
27158
  }
26172
27159
  if (event.type === "model.text" && !isNestedInvocationEvent(event)) {
26173
27160
  this.#state.streamingText = event.text;
@@ -26881,9 +27868,38 @@ var init_store = __esm({
26881
27868
  resolve(prompt);
26882
27869
  this.#notify();
26883
27870
  }
27871
+ #selectedConfigProvider() {
27872
+ return this.#state.providerOptions[this.#state.providerCursor];
27873
+ }
27874
+ #submitConfigAction(action) {
27875
+ this.#state.settingsError = void 0;
27876
+ this.#state.configModelInput = "";
27877
+ this.#state.configProviderInput = "";
27878
+ this.#state.configProviderInputField = void 0;
27879
+ this.#resolvePrompt(`${TUI_CONFIG_ACTION_PREFIX}${JSON.stringify(action)}`);
27880
+ }
27881
+ #resolveSession(selection) {
27882
+ const resolve = this.#sessionResolve;
27883
+ if (!resolve) return;
27884
+ this.#sessionResolve = void 0;
27885
+ this.#state.sessionCancelBehavior = "exit";
27886
+ if (selection.kind === "exit") {
27887
+ this.#state.inputMode = "done";
27888
+ this.#state.done = true;
27889
+ this.#state.activity = "exiting";
27890
+ } else if (selection.kind === "cancel") {
27891
+ this.#state.inputMode = "prompt";
27892
+ this.#state.activity = "waiting for next message";
27893
+ } else {
27894
+ this.#state.inputMode = "starting";
27895
+ this.#state.activity = selection.kind === "new" ? "creating session" : "opening session";
27896
+ }
27897
+ this.#notify();
27898
+ resolve(selection);
27899
+ }
26884
27900
  #rememberPrompt(prompt) {
26885
27901
  const value = prompt.trim();
26886
- if (!value || isExitCommand(value) || isStopCommand(value) || isCompactCommand(value) || isRetryCommand(value)) return;
27902
+ if (!value || isExitCommand(value) || isStopCommand(value) || isCompactCommand(value) || isRetryCommand(value) || isSessionCommand(value)) return;
26887
27903
  if (this.#promptHistory.at(-1) === value) {
26888
27904
  this.#state.canRetryLastPrompt = true;
26889
27905
  return;
@@ -26895,6 +27911,20 @@ var init_store = __esm({
26895
27911
  #lastRetryPrompt() {
26896
27912
  return this.#promptHistory.at(-1);
26897
27913
  }
27914
+ #resetTransientConversationState() {
27915
+ this.#toolRuns.clear();
27916
+ this.#goalWorkBlocks.clear();
27917
+ this.#coworkBlocks.clear();
27918
+ this.#activeGoalWorkBlockId = void 0;
27919
+ this.#currentGoalNarrationId = void 0;
27920
+ this.#timelineProgressIds.clear();
27921
+ this.#pendingPermissionCallId = void 0;
27922
+ if (this.#pendingPermissionTimeout) clearTimeout(this.#pendingPermissionTimeout);
27923
+ this.#pendingPermissionTimeout = void 0;
27924
+ this.#activeClaudeCodePlan = void 0;
27925
+ this.#promptHistoryCursor = void 0;
27926
+ this.#promptDraft = "";
27927
+ }
26898
27928
  #detachPromptHistory() {
26899
27929
  this.#promptHistoryCursor = void 0;
26900
27930
  this.#promptDraft = "";
@@ -26971,7 +28001,23 @@ var init_store = __esm({
26971
28001
  }
26972
28002
  };
26973
28003
  }
26974
- #notify() {
28004
+ #notify(options = {}) {
28005
+ const deferMs = options.deferMs ?? 0;
28006
+ if (deferMs > 0) {
28007
+ if (this.#notifyTimer) return;
28008
+ this.#notifyTimer = setTimeout(() => {
28009
+ this.#notifyTimer = void 0;
28010
+ this.#emitNotify();
28011
+ }, deferMs);
28012
+ return;
28013
+ }
28014
+ if (this.#notifyTimer) {
28015
+ clearTimeout(this.#notifyTimer);
28016
+ this.#notifyTimer = void 0;
28017
+ }
28018
+ this.#emitNotify();
28019
+ }
28020
+ #emitNotify() {
26975
28021
  for (const listener of this.#listeners) listener();
26976
28022
  }
26977
28023
  };
@@ -26982,32 +28028,103 @@ var init_store = __esm({
26982
28028
  var app_exports = {};
26983
28029
  __export(app_exports, {
26984
28030
  TuiApp: () => TuiApp,
28031
+ commandPaletteMatches: () => commandPaletteMatches,
28032
+ inputFrameRows: () => inputFrameRows,
28033
+ isCtrlCKeypress: () => isCtrlCKeypress,
28034
+ isPromptNewlineKeypress: () => isPromptNewlineKeypress,
28035
+ sessionHeaderLine: () => sessionHeaderLine,
28036
+ sessionOptionLine: () => sessionOptionLine,
28037
+ slashCommandMatches: () => slashCommandMatches,
26985
28038
  textInputForKeypress: () => textInputForKeypress,
26986
- workPlanProgressCounts: () => workPlanProgressCounts
28039
+ userMessageBubbleWidth: () => userMessageBubbleWidth,
28040
+ workPlanProgressCounts: () => workPlanProgressCounts,
28041
+ wrapBlockLines: () => wrapBlockLines
26987
28042
  });
26988
28043
  import React, { useEffect, useState } from "react";
26989
28044
  import { Box, Text, useApp, useInput } from "ink";
28045
+ function usePromptDraftSnapshot(promptDraft) {
28046
+ const [snapshot, setSnapshot] = useState(() => promptDraft.snapshot());
28047
+ useEffect(() => promptDraft.subscribe(() => setSnapshot(promptDraft.snapshot())), [promptDraft]);
28048
+ return snapshot;
28049
+ }
26990
28050
  function TuiApp({ store, version = "dev" }) {
26991
28051
  const [state, setState] = useState(() => store.snapshot());
28052
+ const [promptDraft] = useState(() => new PromptDraftController(store.snapshot().promptInput));
26992
28053
  const [now2, setNow] = useState(() => Date.now());
26993
28054
  const [shortcutMode, setShortcutMode] = useState(false);
28055
+ const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
28056
+ const [commandPaletteQuery, setCommandPaletteQuery] = useState("");
28057
+ const [commandPaletteCursor, setCommandPaletteCursor] = useState(0);
26994
28058
  const app = useApp();
26995
28059
  useEffect(() => {
26996
- const update = () => setState(store.snapshot());
28060
+ const update = () => {
28061
+ const next = store.snapshot();
28062
+ setState(next);
28063
+ promptDraft.setValue(next.promptInput);
28064
+ };
26997
28065
  const unsubscribe = store.subscribe(update);
26998
28066
  update();
26999
28067
  return unsubscribe;
27000
- }, [store]);
28068
+ }, [store, promptDraft]);
28069
+ useEffect(() => enableEnhancedKeyboardInput(), []);
27001
28070
  useEffect(() => {
27002
28071
  if (!state.workPlan && state.inputMode !== "running" && state.inputMode !== "starting") return;
27003
- const timer = setInterval(() => setNow(Date.now()), 450);
28072
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
27004
28073
  return () => clearInterval(timer);
27005
28074
  }, [state.workPlan?.planId, state.inputMode]);
27006
28075
  useEffect(() => {
27007
28076
  setShortcutMode(false);
27008
28077
  }, [state.inputMode, state.permission !== void 0]);
28078
+ useEffect(() => {
28079
+ setCommandPaletteCursor(0);
28080
+ }, [commandPaletteQuery, state.inputMode]);
28081
+ useEffect(() => {
28082
+ if (commandPaletteOpen && !canUseCommandPalette(state)) setCommandPaletteOpen(false);
28083
+ }, [commandPaletteOpen, state.inputMode, state.permission]);
28084
+ const commandPaletteItems = commandPaletteOpen ? commandPaletteMatches(commandPaletteQuery, state.inputMode, commandPaletteAvailability(state, promptDraft.value())) : [];
28085
+ const commandPaletteItem = commandPaletteItems[Math.min(commandPaletteCursor, Math.max(0, commandPaletteItems.length - 1))];
28086
+ const openCommandPalette = () => {
28087
+ if (!canUseCommandPalette(state)) return;
28088
+ setCommandPaletteOpen((open4) => !open4);
28089
+ setCommandPaletteQuery("");
28090
+ setCommandPaletteCursor(0);
28091
+ setShortcutMode(false);
28092
+ promptDraft.closeSlashCommandsForCurrentPrompt();
28093
+ };
27009
28094
  useInput((input2, key) => {
28095
+ if (isCtrlCKeypress(input2, key)) {
28096
+ store.requestExit();
28097
+ app.exit();
28098
+ return;
28099
+ }
27010
28100
  const textInput = textInputForKeypress(input2, key);
28101
+ if (commandPaletteOpen) {
28102
+ if (key.escape) {
28103
+ closeCommandPalette(setCommandPaletteOpen, setCommandPaletteQuery, setCommandPaletteCursor);
28104
+ return;
28105
+ }
28106
+ if (key.upArrow) {
28107
+ if (commandPaletteItems.length > 0) setCommandPaletteCursor((cursor) => (cursor - 1 + commandPaletteItems.length) % commandPaletteItems.length);
28108
+ return;
28109
+ }
28110
+ if (key.downArrow) {
28111
+ if (commandPaletteItems.length > 0) setCommandPaletteCursor((cursor) => (cursor + 1) % commandPaletteItems.length);
28112
+ return;
28113
+ }
28114
+ if (key.return) {
28115
+ if (commandPaletteItem) executeCommandPaletteItem({ item: commandPaletteItem, store, app, promptDraft, setState, close: () => closeCommandPalette(setCommandPaletteOpen, setCommandPaletteQuery, setCommandPaletteCursor) });
28116
+ return;
28117
+ }
28118
+ if (key.backspace || key.delete) {
28119
+ setCommandPaletteQuery((current) => Array.from(current).slice(0, -1).join(""));
28120
+ return;
28121
+ }
28122
+ if (textInput) {
28123
+ setCommandPaletteQuery((current) => `${current}${textInput}`);
28124
+ return;
28125
+ }
28126
+ return;
28127
+ }
27011
28128
  if (shortcutMode) {
27012
28129
  setShortcutMode(false);
27013
28130
  if (key.escape) return;
@@ -27021,6 +28138,14 @@ function TuiApp({ store, version = "dev" }) {
27021
28138
  store.stopActiveTask();
27022
28139
  return;
27023
28140
  }
28141
+ if (shortcut === "k" && (state.inputMode === "prompt" || state.inputMode === "running")) {
28142
+ openCommandPalette();
28143
+ return;
28144
+ }
28145
+ if (shortcut === "s" && state.inputMode === "prompt") {
28146
+ store.submitSessionSelectionShortcut();
28147
+ return;
28148
+ }
27024
28149
  if (shortcut === "p" && state.inputMode === "prompt") {
27025
28150
  store.openProviderSelector();
27026
28151
  return;
@@ -27037,8 +28162,14 @@ function TuiApp({ store, version = "dev" }) {
27037
28162
  store.openPermissionPresetSelector();
27038
28163
  return;
27039
28164
  }
28165
+ if (shortcut === "c" && state.inputMode === "prompt") {
28166
+ store.openConfigManager();
28167
+ return;
28168
+ }
27040
28169
  if (shortcut === "u" && state.inputMode === "prompt") {
27041
- store.clearPromptInput();
28170
+ store.clearPromptInput({ notify: false });
28171
+ promptDraft.setValue("");
28172
+ clearPromptErrorPreview(setState);
27042
28173
  return;
27043
28174
  }
27044
28175
  if (shortcut === "d" && state.turnDiff) {
@@ -27070,6 +28201,38 @@ function TuiApp({ store, version = "dev" }) {
27070
28201
  if (normalized === "n" || key.return) store.answerPermission({ decision: "deny" });
27071
28202
  return;
27072
28203
  }
28204
+ if (state.inputMode === "session") {
28205
+ const normalized = input2.toLowerCase();
28206
+ if (key.escape) {
28207
+ if (store.cancelSessionSelection()) {
28208
+ store.requestExit();
28209
+ app.exit();
28210
+ }
28211
+ return;
28212
+ }
28213
+ if (normalized === "q") {
28214
+ store.requestExit();
28215
+ app.exit();
28216
+ return;
28217
+ }
28218
+ if (normalized === "n") {
28219
+ store.submitNewSessionSelection();
28220
+ return;
28221
+ }
28222
+ if (key.upArrow) {
28223
+ store.moveSessionCursor(-1);
28224
+ return;
28225
+ }
28226
+ if (key.downArrow) {
28227
+ store.moveSessionCursor(1);
28228
+ return;
28229
+ }
28230
+ if (key.return) {
28231
+ store.submitSessionSelection();
28232
+ return;
28233
+ }
28234
+ return;
28235
+ }
27073
28236
  if (state.inputMode === "provider") {
27074
28237
  if (key.escape) {
27075
28238
  store.closeSettings();
@@ -27127,6 +28290,126 @@ function TuiApp({ store, version = "dev" }) {
27127
28290
  }
27128
28291
  return;
27129
28292
  }
28293
+ if (state.inputMode === "config") {
28294
+ if (key.escape) {
28295
+ store.closeSettings();
28296
+ return;
28297
+ }
28298
+ if (key.upArrow) {
28299
+ store.moveConfigProviderCursor(-1);
28300
+ return;
28301
+ }
28302
+ if (key.downArrow) {
28303
+ store.moveConfigProviderCursor(1);
28304
+ return;
28305
+ }
28306
+ const normalized = input2.toLowerCase();
28307
+ if (key.return || normalized === "d") {
28308
+ store.submitConfigDefaultProvider();
28309
+ return;
28310
+ }
28311
+ if (normalized === "m") {
28312
+ store.openConfigModelSelector();
28313
+ return;
28314
+ }
28315
+ if (normalized === "n") {
28316
+ store.openConfigModelInput();
28317
+ return;
28318
+ }
28319
+ if (normalized === "a") {
28320
+ store.openConfigAddProviderSelector();
28321
+ return;
28322
+ }
28323
+ if (normalized === "b") {
28324
+ store.openConfigProviderInput("baseURL");
28325
+ return;
28326
+ }
28327
+ if (normalized === "e") {
28328
+ store.openConfigProviderInput("apiKeyEnv");
28329
+ return;
28330
+ }
28331
+ if (normalized === "h") {
28332
+ store.openConfigProviderInput("authHeader");
28333
+ return;
28334
+ }
28335
+ return;
28336
+ }
28337
+ if (state.inputMode === "configModel") {
28338
+ if (key.escape) {
28339
+ store.openConfigManager();
28340
+ return;
28341
+ }
28342
+ if (key.upArrow) {
28343
+ store.moveConfigModelCursor(-1);
28344
+ return;
28345
+ }
28346
+ if (key.downArrow) {
28347
+ store.moveConfigModelCursor(1);
28348
+ return;
28349
+ }
28350
+ if (input2.toLowerCase() === "n") {
28351
+ store.openConfigModelInput();
28352
+ return;
28353
+ }
28354
+ if (key.return) {
28355
+ store.submitConfigDefaultModel();
28356
+ return;
28357
+ }
28358
+ return;
28359
+ }
28360
+ if (state.inputMode === "configModelInput") {
28361
+ if (key.escape) {
28362
+ store.openConfigModelSelector();
28363
+ return;
28364
+ }
28365
+ if (key.return) {
28366
+ if (textInput) store.appendConfigModelInput(textInput);
28367
+ store.submitConfigModelInput();
28368
+ return;
28369
+ }
28370
+ if (key.backspace || key.delete) {
28371
+ store.backspaceConfigModelInput();
28372
+ return;
28373
+ }
28374
+ if (textInput) store.appendConfigModelInput(textInput);
28375
+ return;
28376
+ }
28377
+ if (state.inputMode === "configProviderInput") {
28378
+ if (key.escape) {
28379
+ store.openConfigManager();
28380
+ return;
28381
+ }
28382
+ if (key.return) {
28383
+ if (textInput) store.appendConfigProviderInput(textInput);
28384
+ store.submitConfigProviderInput();
28385
+ return;
28386
+ }
28387
+ if (key.backspace || key.delete) {
28388
+ store.backspaceConfigProviderInput();
28389
+ return;
28390
+ }
28391
+ if (textInput) store.appendConfigProviderInput(textInput);
28392
+ return;
28393
+ }
28394
+ if (state.inputMode === "configAddProvider") {
28395
+ if (key.escape) {
28396
+ store.openConfigManager();
28397
+ return;
28398
+ }
28399
+ if (key.upArrow) {
28400
+ store.moveConfigAddProviderCursor(-1);
28401
+ return;
28402
+ }
28403
+ if (key.downArrow) {
28404
+ store.moveConfigAddProviderCursor(1);
28405
+ return;
28406
+ }
28407
+ if (key.return) {
28408
+ store.submitConfigAddProvider();
28409
+ return;
28410
+ }
28411
+ return;
28412
+ }
27130
28413
  if (state.inputMode === "model") {
27131
28414
  if (key.escape) {
27132
28415
  store.closeSettings();
@@ -27145,6 +28428,27 @@ function TuiApp({ store, version = "dev" }) {
27145
28428
  return;
27146
28429
  }
27147
28430
  if (state.inputMode === "prompt" || state.inputMode === "running") {
28431
+ const promptSnapshot = promptDraft.snapshot();
28432
+ const promptInput = promptSnapshot.value;
28433
+ const slashCommands = visibleSlashCommandMatches(promptInput, state.inputMode, promptSnapshot.slashCommandClosedPrefix);
28434
+ const slashCommand = slashCommands[promptSnapshot.slashCommandCursor] ?? slashCommands[0];
28435
+ if (slashCommands.length > 0 && key.escape) {
28436
+ promptDraft.closeSlashCommandsForCurrentPrompt();
28437
+ return;
28438
+ }
28439
+ if (slashCommands.length > 0 && key.upArrow) {
28440
+ promptDraft.moveSlashCommandCursor(-1, slashCommands.length);
28441
+ return;
28442
+ }
28443
+ if (slashCommands.length > 0 && key.downArrow) {
28444
+ promptDraft.moveSlashCommandCursor(1, slashCommands.length);
28445
+ return;
28446
+ }
28447
+ if (slashCommands.length > 0 && key.tab && slashCommand) {
28448
+ applySlashCommandDraft(store, promptDraft, slashCommand);
28449
+ clearPromptErrorPreview(setState);
28450
+ return;
28451
+ }
27148
28452
  if (state.inputMode === "prompt" && key.upArrow) {
27149
28453
  store.navigatePromptHistory(-1);
27150
28454
  return;
@@ -27157,25 +28461,49 @@ function TuiApp({ store, version = "dev" }) {
27157
28461
  setShortcutMode(true);
27158
28462
  return;
27159
28463
  }
27160
- if (key.return) {
27161
- if (textInput) store.appendPromptInput(textInput);
28464
+ if (isPromptNewlineKeypress(input2, key)) {
28465
+ appendPromptDraft(store, promptDraft, `${textInput}
28466
+ `);
28467
+ clearPromptErrorPreview(setState);
28468
+ return;
28469
+ }
28470
+ if (isPromptSubmitKeypress(input2, key)) {
28471
+ if (!textInput && slashCommand && shouldSelectSlashCommand(promptInput, slashCommand)) {
28472
+ applySlashCommandDraft(store, promptDraft, slashCommand);
28473
+ clearPromptErrorPreview(setState);
28474
+ return;
28475
+ }
28476
+ if (textInput) appendPromptDraft(store, promptDraft, textInput);
27162
28477
  store.submitPromptInput();
27163
28478
  return;
27164
28479
  }
27165
28480
  if (key.backspace || key.delete) {
27166
- store.backspacePromptInput();
28481
+ store.backspacePromptInput({ notify: false });
28482
+ promptDraft.backspace();
28483
+ clearPromptErrorPreview(setState);
27167
28484
  return;
27168
28485
  }
27169
- if (textInput) store.appendPromptInput(textInput);
28486
+ if (textInput) {
28487
+ appendPromptDraft(store, promptDraft, textInput);
28488
+ clearPromptErrorPreview(setState);
28489
+ }
27170
28490
  return;
27171
28491
  }
27172
28492
  });
27173
28493
  const shellProps = { flexDirection: "column", paddingX: 1, minHeight: terminalHeight() };
28494
+ const commandPalette = commandPaletteOpen ? { items: commandPaletteItems, query: commandPaletteQuery, cursor: Math.min(commandPaletteCursor, Math.max(0, commandPaletteItems.length - 1)) } : void 0;
28495
+ if (shouldShowSessionStart(state)) {
28496
+ return React.createElement(
28497
+ Box,
28498
+ shellProps,
28499
+ React.createElement(SessionStart, { state, version })
28500
+ );
28501
+ }
27174
28502
  if (shouldShowEmptyState(state)) {
27175
28503
  return React.createElement(
27176
28504
  Box,
27177
28505
  shellProps,
27178
- React.createElement(EmptyState, { state, version, shortcutMode })
28506
+ React.createElement(EmptyState, { state, version, shortcutMode, promptDraft, commandPalette })
27179
28507
  );
27180
28508
  }
27181
28509
  return React.createElement(
@@ -27186,126 +28514,424 @@ function TuiApp({ store, version = "dev" }) {
27186
28514
  Box,
27187
28515
  { flexDirection: "column", flexGrow: 1, paddingX: 1 },
27188
28516
  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
28517
  React.createElement(Box, { flexGrow: 1 }),
27194
- React.createElement(InteractionPanel, { state, shortcutMode })
28518
+ React.createElement(BottomStatusStack, { state, now: now2 }),
28519
+ React.createElement(InteractionPanel, { state, shortcutMode, promptDraft, commandPalette })
27195
28520
  )
27196
28521
  );
27197
28522
  }
28523
+ function appendPromptDraft(store, promptDraft, value) {
28524
+ if (!value) return;
28525
+ store.appendPromptInput(value, { notify: false });
28526
+ promptDraft.append(value);
28527
+ }
28528
+ function clearPromptErrorPreview(setState) {
28529
+ setState((current) => current.promptError ? { ...current, promptError: void 0 } : current);
28530
+ }
28531
+ function applySlashCommandDraft(store, promptDraft, item) {
28532
+ const next = item.argument ? `${item.insert} ` : item.insert;
28533
+ store.clearPromptInput({ notify: false });
28534
+ store.appendPromptInput(next, { notify: false });
28535
+ promptDraft.setValue(next);
28536
+ promptDraft.closeSlashCommandsForCurrentPrompt();
28537
+ }
28538
+ function shouldSelectSlashCommand(promptInput, item) {
28539
+ const value = promptInput.trim();
28540
+ if (!value || value.includes(" ")) return false;
28541
+ if (!item.argument && value.toLowerCase() === item.insert.toLowerCase()) return false;
28542
+ return true;
28543
+ }
28544
+ function visibleSlashCommandMatches(promptInput, mode, closedPrefix) {
28545
+ if (closedPrefix && isSlashCommandClosedForPrompt(promptInput, closedPrefix)) return [];
28546
+ return slashCommandMatches(promptInput, mode).slice(0, 7);
28547
+ }
28548
+ function slashCommandPaletteForDraft(snapshot, mode) {
28549
+ const slashCommands = visibleSlashCommandMatches(snapshot.value, mode, snapshot.slashCommandClosedPrefix);
28550
+ if (slashCommands.length === 0) return void 0;
28551
+ return {
28552
+ items: slashCommands,
28553
+ cursor: Math.min(snapshot.slashCommandCursor, slashCommands.length - 1)
28554
+ };
28555
+ }
28556
+ function isSlashCommandClosedForPrompt(promptInput, closedPrefix) {
28557
+ return promptInput === closedPrefix || closedPrefix.endsWith(" ") && promptInput.startsWith(closedPrefix);
28558
+ }
28559
+ function slashCommandMatches(promptInput, mode = "prompt") {
28560
+ const query = slashCommandQuery(promptInput);
28561
+ if (query === void 0) return [];
28562
+ return SLASH_COMMANDS.filter((item) => slashCommandActiveInMode(item, mode)).map((item, index) => ({ item, index, score: slashCommandScore(item, query) })).filter((entry) => entry.score < Number.POSITIVE_INFINITY).sort((a, b2) => a.score - b2.score || a.index - b2.index).map((entry) => entry.item);
28563
+ }
28564
+ function slashCommandQuery(promptInput) {
28565
+ const value = promptInput.trimStart();
28566
+ if (!value.startsWith("/") || value.includes("\n")) return void 0;
28567
+ if (/\s/.test(value)) return void 0;
28568
+ return value.slice(1).toLowerCase();
28569
+ }
28570
+ function slashCommandActiveInMode(item, mode) {
28571
+ if (mode !== "prompt" && mode !== "running") return false;
28572
+ return item.mode === "both" || item.mode === mode || !item.mode && mode === "prompt";
28573
+ }
28574
+ function slashCommandScore(item, query) {
28575
+ if (!query) return 0;
28576
+ const command = item.command.slice(1).toLowerCase();
28577
+ const insert = item.insert.slice(1).toLowerCase();
28578
+ const aliases = item.aliases?.map((alias) => alias.toLowerCase()) ?? [];
28579
+ const aliasSearchEnabled = query.length >= 3;
28580
+ if (command.startsWith(query)) return 0;
28581
+ if (insert.startsWith(query)) return 1;
28582
+ if (aliasSearchEnabled && aliases.some((alias) => alias.startsWith(query))) return 2;
28583
+ if (command.includes(query) || insert.includes(query)) return 3;
28584
+ if (aliasSearchEnabled && aliases.some((alias) => alias.includes(query))) return 4;
28585
+ return Number.POSITIVE_INFINITY;
28586
+ }
28587
+ function canUseCommandPalette(state) {
28588
+ return !state.permission && (state.inputMode === "prompt" || state.inputMode === "running");
28589
+ }
28590
+ function commandPaletteAvailability(state, promptInput) {
28591
+ return {
28592
+ hasPromptDraft: Boolean(promptInput.trim()),
28593
+ hasSelection: Boolean(state.selection),
28594
+ hasProviders: state.providerOptions.length > 0,
28595
+ hasAgents: state.agentOptions.length > 0,
28596
+ hasDiff: Boolean(state.turnDiff),
28597
+ hasTools: state.blocks.some((block) => block.kind === "tool"),
28598
+ hasWorkPlan: Boolean(state.workPlan),
28599
+ hasPendingClaudeCodePlan: Boolean(state.pendingClaudeCodePlan),
28600
+ canRetryLastPrompt: state.canRetryLastPrompt
28601
+ };
28602
+ }
28603
+ function commandPaletteMatches(query, mode = "prompt", availability = {}) {
28604
+ const normalized = query.trim().toLowerCase();
28605
+ const items = buildCommandPaletteItems(mode, availability);
28606
+ if (!normalized) return items;
28607
+ return items.map((item, index) => ({ item, index, score: commandPaletteScore(item, normalized) })).filter((entry) => entry.score < Number.POSITIVE_INFINITY).sort((a, b2) => a.score - b2.score || a.index - b2.index).map((entry) => entry.item);
28608
+ }
28609
+ function buildCommandPaletteItems(mode, availability) {
28610
+ if (mode !== "prompt" && mode !== "running") return [];
28611
+ const items = [];
28612
+ const promptReadyForSettings = mode === "prompt" && !availability.hasPromptDraft;
28613
+ if (promptReadyForSettings && availability.hasSelection) {
28614
+ items.push({ id: "switch-model", title: "Switch model", description: "Choose the model for the current provider", group: "Suggested", shortcut: "esc m", action: "open-model", keywords: ["model", "provider model"] });
28615
+ }
28616
+ if (promptReadyForSettings && availability.hasProviders) {
28617
+ items.push({ id: "connect-provider", title: "Connect provider", description: "Change the active provider", group: "Suggested", shortcut: "esc p", action: "open-provider", keywords: ["provider", "connect"] });
28618
+ }
28619
+ if (promptReadyForSettings && availability.hasAgents) {
28620
+ items.push({ id: "switch-agent", title: "Switch agent", description: "Change the active agent profile", group: "Suggested", shortcut: "esc a", action: "open-agent", keywords: ["agent"] });
28621
+ }
28622
+ if (promptReadyForSettings) {
28623
+ 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
+ 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
+ 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"] });
28626
+ }
28627
+ for (const command of SLASH_COMMANDS) {
28628
+ if (!slashCommandActiveInMode(command, mode)) continue;
28629
+ if (command.command === "/stop" || command.command === "/exit" || command.command === "/quit" || command.command === "/retry") continue;
28630
+ items.push({
28631
+ id: `slash:${command.command}`,
28632
+ title: commandPaletteSlashTitle(command),
28633
+ description: command.description,
28634
+ group: command.command.startsWith("/session") ? "Session" : "Commands",
28635
+ shortcut: command.command,
28636
+ keywords: [command.command, command.insert, ...command.aliases ?? []],
28637
+ slashCommand: command
28638
+ });
28639
+ }
28640
+ if (mode === "running") {
28641
+ items.push({ id: "stop-task", title: "Stop running task", description: "Cancel the current task", group: "System", shortcut: "/stop", action: "stop", keywords: ["stop", "cancel"] });
28642
+ }
28643
+ if (availability.hasPromptDraft) {
28644
+ items.push({ id: "clear-prompt", title: "Clear prompt", description: "Erase the current input draft", group: "System", shortcut: "esc u", action: "clear-prompt", keywords: ["clear", "draft", "input"] });
28645
+ }
28646
+ if (mode === "prompt" && availability.canRetryLastPrompt) {
28647
+ items.push({ id: "retry-last", title: "Retry last prompt", description: "Run the previous prompt again", group: "System", shortcut: "/retry", action: "retry", keywords: ["retry", "again", "rerun"] });
28648
+ }
28649
+ if (availability.hasPendingClaudeCodePlan) {
28650
+ items.push({ id: "use-claude-code-plan", title: "Use Claude Code plan", description: "Queue the captured plan for execution", group: "System", shortcut: "esc e", action: "use-claude-plan", keywords: ["claude", "plan"] });
28651
+ }
28652
+ if (availability.hasDiff) {
28653
+ items.push({ id: "toggle-diff", title: "Toggle diff", description: "Show or hide the latest file diff", group: "System", shortcut: "esc d", action: "toggle-diff", keywords: ["diff", "changes"] });
28654
+ }
28655
+ if (availability.hasTools) {
28656
+ items.push({ id: "toggle-tools", title: "Toggle tool details", description: "Expand or collapse the latest tool block", group: "System", shortcut: "esc t", action: "toggle-tools", keywords: ["tool", "details"] });
28657
+ }
28658
+ if (availability.hasWorkPlan || mode === "running") {
28659
+ items.push({ id: "toggle-work-plan", title: "Toggle work plan", description: "Show or hide current plan details", group: "System", shortcut: "esc w", action: "toggle-work-plan", keywords: ["plan", "progress"] });
28660
+ }
28661
+ items.push({ id: "exit-app", title: "Exit the app", description: "Close Demian", group: "System", shortcut: "/exit", action: "exit", keywords: ["quit", "close"] });
28662
+ return items;
28663
+ }
28664
+ function commandPaletteSlashTitle(item) {
28665
+ switch (item.command) {
28666
+ case "/cowork":
28667
+ return "Cowork agents";
28668
+ case "/compact":
28669
+ return "Compact context";
28670
+ case "/session":
28671
+ return "Session command";
28672
+ case "/session list":
28673
+ return "List sessions";
28674
+ case "/session new":
28675
+ return "New session";
28676
+ case "/session switch":
28677
+ return "Switch session";
28678
+ case "/session rename":
28679
+ return "Rename session";
28680
+ case "/session delete":
28681
+ return "Delete session";
28682
+ case "/goal":
28683
+ return "Start goal";
28684
+ case "/goal status":
28685
+ return "Goal status";
28686
+ case "/goal clear":
28687
+ return "Clear goal state";
28688
+ case "/ralph-loop":
28689
+ return "Start Ralph loop";
28690
+ case "/cancel-ralph":
28691
+ return "Cancel Ralph loop";
28692
+ default:
28693
+ return item.command;
28694
+ }
28695
+ }
28696
+ function commandPaletteScore(item, query) {
28697
+ const title = item.title.toLowerCase();
28698
+ const shortcut = item.shortcut?.toLowerCase() ?? "";
28699
+ const description = item.description.toLowerCase();
28700
+ const keywords = item.keywords?.map((keyword) => keyword.toLowerCase()) ?? [];
28701
+ if (title.startsWith(query)) return 0;
28702
+ if (shortcut.startsWith(query)) return 1;
28703
+ if (keywords.some((keyword) => keyword.startsWith(query))) return 2;
28704
+ if (title.includes(query)) return 3;
28705
+ if (shortcut.includes(query)) return 4;
28706
+ if (keywords.some((keyword) => keyword.includes(query))) return 5;
28707
+ if (description.includes(query)) return 6;
28708
+ return Number.POSITIVE_INFINITY;
28709
+ }
28710
+ function closeCommandPalette(setCommandPaletteOpen, setCommandPaletteQuery, setCommandPaletteCursor) {
28711
+ setCommandPaletteOpen(false);
28712
+ setCommandPaletteQuery("");
28713
+ setCommandPaletteCursor(0);
28714
+ }
28715
+ function executeCommandPaletteItem({
28716
+ item,
28717
+ store,
28718
+ app,
28719
+ promptDraft,
28720
+ setState,
28721
+ close
28722
+ }) {
28723
+ close();
28724
+ if (item.slashCommand) {
28725
+ applyCommandPaletteSlashCommand(store, promptDraft, item.slashCommand);
28726
+ clearPromptErrorPreview(setState);
28727
+ return;
28728
+ }
28729
+ switch (item.action) {
28730
+ case "open-model":
28731
+ store.openModelEditor();
28732
+ return;
28733
+ case "open-provider":
28734
+ store.openProviderSelector();
28735
+ return;
28736
+ case "open-agent":
28737
+ store.openAgentSelector();
28738
+ return;
28739
+ case "open-permissions":
28740
+ store.openPermissionPresetSelector();
28741
+ return;
28742
+ case "open-config":
28743
+ store.openConfigManager();
28744
+ return;
28745
+ case "open-session":
28746
+ store.submitSessionSelectionShortcut();
28747
+ return;
28748
+ case "clear-prompt":
28749
+ store.clearPromptInput({ notify: false });
28750
+ promptDraft.setValue("");
28751
+ clearPromptErrorPreview(setState);
28752
+ return;
28753
+ case "retry":
28754
+ store.retryLastPrompt();
28755
+ return;
28756
+ case "use-claude-plan":
28757
+ store.usePendingClaudeCodePlan();
28758
+ return;
28759
+ case "toggle-diff":
28760
+ store.toggleDiffExpanded();
28761
+ return;
28762
+ case "toggle-tools":
28763
+ store.toggleLatestToolExpanded();
28764
+ return;
28765
+ case "toggle-work-plan":
28766
+ store.toggleWorkPlanExpanded();
28767
+ return;
28768
+ case "stop":
28769
+ store.stopActiveTask();
28770
+ return;
28771
+ case "exit":
28772
+ store.requestExit();
28773
+ app.exit();
28774
+ return;
28775
+ }
28776
+ }
28777
+ function applyCommandPaletteSlashCommand(store, promptDraft, item) {
28778
+ const shouldInsert = item.argument && item.command !== "/session new";
28779
+ const next = shouldInsert ? `${item.insert} ` : item.insert;
28780
+ store.clearPromptInput({ notify: false });
28781
+ store.appendPromptInput(next, { notify: false });
28782
+ promptDraft.setValue(next);
28783
+ promptDraft.closeSlashCommandsForCurrentPrompt();
28784
+ if (!shouldInsert) store.submitPromptInput();
28785
+ }
27198
28786
  function textInputForKeypress(input2, key) {
27199
28787
  if (!input2 || key.ctrl || key.tab || key.escape) return "";
28788
+ const csiText = decodeCsiUTextInput(input2);
28789
+ if (csiText !== void 0) return csiText;
28790
+ if (isKnownKeyboardSequence(input2)) return "";
27200
28791
  return Array.from(input2).filter((char) => {
27201
28792
  const codePoint = char.codePointAt(0) ?? 0;
27202
28793
  return codePoint >= 32 && codePoint !== 127;
27203
28794
  }).join("");
27204
28795
  }
28796
+ function isPromptNewlineKeypress(input2, key) {
28797
+ if (key.return && key.shift) return true;
28798
+ if (isShiftOrOptionEnterSequence(input2)) return true;
28799
+ return input2 === "\r" && !key.return;
28800
+ }
28801
+ function isCtrlCKeypress(input2, key) {
28802
+ if (input2 === "") return true;
28803
+ if (key.ctrl && input2.toLowerCase() === "c") return true;
28804
+ const csi = CSI_U_INPUT.exec(input2);
28805
+ if (!csi) return false;
28806
+ const codePoint = Number(csi[1]);
28807
+ const modifier = Number(csi[2] ?? 1);
28808
+ return (codePoint === 3 || codePoint === 99) && hasControlModifier(modifier);
28809
+ }
28810
+ function isPromptSubmitKeypress(input2, key) {
28811
+ return Boolean(key.return) || input2 === "\n";
28812
+ }
28813
+ function isShiftOrOptionEnterSequence(input2) {
28814
+ const csi = CSI_U_INPUT.exec(input2);
28815
+ if (csi) {
28816
+ const codePoint = Number(csi[1]);
28817
+ const modifier = Number(csi[2] ?? 1);
28818
+ return (codePoint === 13 || codePoint === 10) && hasShiftOrOptionModifier(modifier);
28819
+ }
28820
+ const xterm = XTERM_MODIFIED_ENTER_INPUT.exec(input2);
28821
+ return Boolean(xterm && hasShiftOrOptionModifier(Number(xterm[1])));
28822
+ }
28823
+ function decodeCsiUTextInput(input2) {
28824
+ const csi = CSI_U_INPUT.exec(input2);
28825
+ if (!csi) return void 0;
28826
+ const codePoint = Number(csi[1]);
28827
+ const modifier = Number(csi[2] ?? 1);
28828
+ if (!Number.isFinite(codePoint) || codePoint < 32 || codePoint === 127) return "";
28829
+ if (hasControlModifier(modifier) || hasOptionModifier(modifier)) return "";
28830
+ try {
28831
+ return String.fromCodePoint(codePoint);
28832
+ } catch {
28833
+ return "";
28834
+ }
28835
+ }
28836
+ function isKnownKeyboardSequence(input2) {
28837
+ return isShiftOrOptionEnterSequence(input2) || XTERM_MODIFIED_ENTER_INPUT.test(input2);
28838
+ }
28839
+ function hasShiftOrOptionModifier(modifier) {
28840
+ return hasShiftModifier(modifier) || hasOptionModifier(modifier);
28841
+ }
28842
+ function hasShiftModifier(modifier) {
28843
+ return (modifier - 1 & 1) !== 0;
28844
+ }
28845
+ function hasOptionModifier(modifier) {
28846
+ return (modifier - 1 & 2) !== 0;
28847
+ }
28848
+ function hasControlModifier(modifier) {
28849
+ return (modifier - 1 & 4) !== 0;
28850
+ }
27205
28851
  function shouldShowLoading(state) {
27206
28852
  return state.inputMode === "starting" || state.inputMode === "running" && state.blocks.length === 0 && !state.streamingText;
27207
28853
  }
28854
+ function enableEnhancedKeyboardInput() {
28855
+ if (!process.stdout.isTTY) return () => void 0;
28856
+ process.stdout.write("\x1B[>1u");
28857
+ process.stdout.write("\x1B[>4;2m");
28858
+ return () => {
28859
+ process.stdout.write("\x1B[<u");
28860
+ process.stdout.write("\x1B[>4;0m");
28861
+ };
28862
+ }
27208
28863
  function shouldShowEmptyState(state) {
27209
28864
  return state.inputMode === "prompt" && !state.permission && state.blocks.length === 0 && !state.streamingText && !state.turnDiff && !state.workPlan && !state.goal;
27210
28865
  }
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)));
28866
+ function shouldShowSessionStart(state) {
28867
+ return state.inputMode === "session";
28868
+ }
28869
+ function SessionStart({ state, version }) {
28870
+ const topGap = Math.max(1, Math.min(5, Math.floor(terminalHeight() * 0.12)));
28871
+ const width = sessionPanelWidth();
27215
28872
  return React.createElement(
27216
28873
  Box,
27217
28874
  { flexDirection: "column", flexGrow: 1 },
27218
28875
  React.createElement(Box, { height: topGap }),
27219
28876
  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 }),
28877
+ React.createElement(Box, { height: 1 }),
28878
+ React.createElement(SessionSelector, { state }),
27227
28879
  React.createElement(
27228
28880
  Box,
27229
- { alignSelf: "center", width: promptPanelWidth(), justifyContent: "flex-end", marginTop: 1 },
27230
- React.createElement(Text, { color: shortcutMode ? "cyan" : "gray" }, hint)
28881
+ { alignSelf: "center", width, justifyContent: "space-between", marginTop: 1 },
28882
+ React.createElement(Text, { color: "white" }, "enter open \xB7 n new \xB7 q quit"),
28883
+ React.createElement(Text, { color: "white" }, `v${version}`)
27231
28884
  ),
27232
- state.promptError ? React.createElement(Box, { alignSelf: "center", width: promptPanelWidth(), marginTop: 1 }, React.createElement(Text, { color: "yellow" }, state.promptError)) : null,
27233
28885
  React.createElement(Box, { flexGrow: 1 })
27234
28886
  );
27235
28887
  }
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
- ];
28888
+ function EmptyState({
28889
+ state,
28890
+ version,
28891
+ shortcutMode,
28892
+ promptDraft,
28893
+ commandPalette
28894
+ }) {
28895
+ const promptSnapshot = usePromptDraftSnapshot(promptDraft);
28896
+ const slashCommandPalette = commandPalette ? void 0 : slashCommandPaletteForDraft(promptSnapshot, state.inputMode);
28897
+ const placeholder = 'Ask anything... "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uAD6C\uC870\uB97C \uC54C\uB824\uC918"';
28898
+ const hint = shortcutMode ? shortcutHelp(state) : "enter send \xB7 shift/option+enter newline \xB7 esc k commands \xB7 esc s sessions";
28899
+ const hasOverlay = Boolean(slashCommandPalette || commandPalette);
28900
+ const topGap = hasOverlay ? 0 : Math.max(1, Math.min(6, Math.floor(terminalHeight() * 0.12)));
27271
28901
  return React.createElement(
27272
28902
  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)
28903
+ { flexDirection: "column", flexGrow: 1 },
28904
+ React.createElement(Box, { height: topGap }),
28905
+ React.createElement(DemianWordmark),
28906
+ React.createElement(Box, { height: hasOverlay ? 1 : 2 }),
28907
+ commandPalette ? React.createElement(CommandPalettePanel, { palette: commandPalette }) : null,
28908
+ slashCommandPalette ? React.createElement(SlashCommandPalette, { palette: slashCommandPalette }) : null,
28909
+ React.createElement(InputFrame, {
28910
+ borderColor: shortcutMode ? "cyan" : "blue",
28911
+ cursorColor: "cyan",
28912
+ value: promptSnapshot.value,
28913
+ placeholder,
28914
+ marginTop: 0
28915
+ }),
28916
+ React.createElement(CommandGuide, { hint, color: shortcutMode ? "cyan" : "gray", error: state.promptError }),
28917
+ hasOverlay ? null : React.createElement(SessionMetaBox, { state, version, marginTop: 2 }),
28918
+ React.createElement(
28919
+ Box,
28920
+ { flexGrow: 1 }
27297
28921
  )
27298
28922
  );
27299
28923
  }
27300
28924
  function LoadingView({ state }) {
27301
28925
  const title = state.inputMode === "starting" ? "Demian runtime" : "Preparing session";
27302
28926
  const message = state.activity || (state.inputMode === "starting" ? "Loading configuration" : "Starting session");
28927
+ const width = statusSurfaceWidth();
28928
+ const contentWidth = Math.max(24, width - 4);
27303
28929
  return React.createElement(
27304
28930
  Box,
27305
- { flexDirection: "column", marginTop: 1, borderStyle: "double", borderColor: "cyan", paddingX: 1, paddingY: 1 },
27306
- React.createElement(Text, { color: "cyan", bold: true }, title),
27307
- React.createElement(Text, { color: "white" }, message),
27308
- React.createElement(Text, { color: "gray" }, "Loading providers, tools, workspace state, and permissions.")
28931
+ { alignSelf: "center", width, flexDirection: "column", marginTop: 1, borderStyle: "double", borderColor: "cyan", paddingX: 1, paddingY: 1 },
28932
+ React.createElement(SurfaceLine, { text: title, width: contentWidth, backgroundColor: SURFACE.status, color: "cyan", bold: true }),
28933
+ React.createElement(SurfaceLine, { text: message, width: contentWidth, backgroundColor: SURFACE.status, color: "white" }),
28934
+ React.createElement(SurfaceLine, { text: "Loading providers, tools, workspace state, and permissions.", width: contentWidth, backgroundColor: SURFACE.status, color: "gray" })
27309
28935
  );
27310
28936
  }
27311
28937
  function contextEfficiencySummary(state) {
@@ -27361,59 +28987,72 @@ function tuiMode(state) {
27361
28987
  if (state.done) return { label: "done", color: "green" };
27362
28988
  if (state.permission) return { label: "approval", color: "yellow" };
27363
28989
  if (state.inputMode === "starting") return { label: "booting", color: "cyan" };
28990
+ if (state.inputMode === "session") return { label: "sessions", color: "magenta" };
27364
28991
  if (state.inputMode === "running") return { label: "running", color: "cyan" };
27365
28992
  if (state.inputMode === "provider" || state.inputMode === "agent" || state.inputMode === "model" || state.inputMode === "permissionPreset") return { label: "settings", color: "magenta" };
28993
+ if (state.inputMode === "config" || state.inputMode === "configModel" || state.inputMode === "configModelInput" || state.inputMode === "configProviderInput" || state.inputMode === "configAddProvider") return { label: "config", color: "magenta" };
27366
28994
  if (state.inputMode === "prompt") return { label: "ready", color: "green" };
27367
28995
  return { label: state.inputMode, color: "gray" };
27368
28996
  }
27369
- function TranscriptView({ state }) {
27370
- const blocks = state.blocks.slice(-12);
27371
- const streaming = !state.streamingFinalized && state.streamingText ? { kind: "assistant", title: "Assistant streaming", lines: streamingLines(state.streamingText) } : void 0;
27372
- return React.createElement(
27373
- Box,
27374
- { flexDirection: "column", marginTop: 1, paddingX: 1 },
27375
- ...blocks.map((block) => React.createElement(BlockView, { key: block.id, block })),
27376
- streaming ? React.createElement(BlockView, { key: "streaming", block: streaming }) : null
27377
- );
28997
+ function sameBlockViewProps(previous, next) {
28998
+ return previous.width === next.width && blockRenderSignature(previous.block) === blockRenderSignature(next.block);
28999
+ }
29000
+ function blockRenderSignature(block) {
29001
+ return JSON.stringify({
29002
+ id: "id" in block ? block.id : void 0,
29003
+ kind: block.kind,
29004
+ title: block.title,
29005
+ lines: block.lines,
29006
+ meta: block.meta,
29007
+ toolStatus: block.toolStatus,
29008
+ expanded: block.expanded,
29009
+ toolDetails: block.expanded ? block.toolDetails : void 0,
29010
+ goalWork: block.kind === "goal-work" ? block.goalWork : void 0,
29011
+ cowork: block.kind === "cowork" ? block.cowork : void 0
29012
+ });
27378
29013
  }
27379
- function BlockView({ block }) {
27380
- if (block.kind === "tool") return React.createElement(ToolBlockView, { block });
27381
- if (block.kind === "goal-work") return React.createElement(GoalWorkBlockView, { block });
27382
- if (block.kind === "cowork") return React.createElement(CoworkBlockView, { block });
27383
- const color = block.kind === "user" ? "green" : block.kind === "assistant" ? "white" : block.kind === "warning" ? "yellow" : "magenta";
29014
+ function UserBlockView({ block, width }) {
29015
+ const lines = normalizedBlockLines(block.lines).slice(0, 80);
29016
+ const bubbleWidth = userMessageBubbleWidth(lines, width);
29017
+ const contentWidth = Math.max(8, bubbleWidth - 4);
29018
+ const wrappedLines = wrapBlockLines(lines, contentWidth, 80);
27384
29019
  return React.createElement(
27385
29020
  Box,
27386
- { flexDirection: "column", marginBottom: 1 },
27387
- React.createElement(Text, { color, bold: true }, block.title),
27388
- ...block.lines.slice(0, 80).map((line, index) => React.createElement(Text, { key: index }, line || " "))
29021
+ { alignSelf: "flex-end", width: bubbleWidth, flexDirection: "column", marginBottom: 1, marginLeft: 2 },
29022
+ React.createElement(
29023
+ Box,
29024
+ { flexDirection: "column", borderStyle: "round", borderColor: "blue", paddingX: 1 },
29025
+ React.createElement(
29026
+ Box,
29027
+ { justifyContent: "flex-end" },
29028
+ React.createElement(Text, { color: "blue", bold: true }, "You")
29029
+ ),
29030
+ ...wrappedLines.map((line, index) => React.createElement(Text, { key: index, color: "white" }, line || " "))
29031
+ )
27389
29032
  );
27390
29033
  }
27391
- function CoworkBlockView({ block }) {
29034
+ function CoworkBlockView({ block, width }) {
27392
29035
  const group = block.cowork;
27393
29036
  const warning = group?.status && group.status !== "running" && group.status !== "completed";
27394
29037
  const color = warning ? "yellow" : group?.status === "running" ? "cyan" : "gray";
29038
+ const contentWidth = Math.max(16, width - 4);
27395
29039
  return React.createElement(
27396
29040
  Box,
27397
- { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: color, paddingX: 1 },
27398
- React.createElement(
27399
- Box,
27400
- null,
27401
- React.createElement(Text, { color, bold: true }, "[cowork] "),
27402
- React.createElement(Text, { color: "white", bold: true }, block.title),
27403
- block.meta ? React.createElement(Text, { color: "gray" }, ` \xB7 ${block.meta}`) : null
27404
- ),
27405
- React.createElement(CoworkDetailsView, { block })
29041
+ { width, flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: color, paddingX: 1 },
29042
+ React.createElement(SurfaceLine, { text: `[cowork] ${block.title}${block.meta ? ` \xB7 ${block.meta}` : ""}`, width: contentWidth, backgroundColor: SURFACE.status, color, bold: true }),
29043
+ React.createElement(CoworkDetailsView, { block, width: contentWidth })
27406
29044
  );
27407
29045
  }
27408
- function CoworkDetailsView({ block }) {
29046
+ function CoworkDetailsView({ block, width }) {
27409
29047
  const group = block.cowork;
27410
- if (!group) return React.createElement(GoalFallbackLines, { lines: block.lines });
29048
+ if (!group) return React.createElement(GoalFallbackLines, { lines: block.lines, width });
27411
29049
  const expanded = block.expanded !== false;
27412
29050
  const lines = expanded ? block.lines : block.lines.filter((line) => line.startsWith("Status:") || line.startsWith("Members:") || line.startsWith("Summary:")).slice(0, 4);
29051
+ const wrappedLines = wrapBlockLines(lines, width, 100);
27413
29052
  return React.createElement(
27414
29053
  Box,
27415
29054
  { flexDirection: "column", marginTop: 1 },
27416
- ...lines.slice(0, 100).map((line, index) => React.createElement(Text, { key: index, color: coworkLineColor(line) }, line || " "))
29055
+ ...wrappedLines.map((line, index) => React.createElement(Text, { key: index, color: coworkLineColor(line) }, line || " "))
27417
29056
  );
27418
29057
  }
27419
29058
  function coworkLineColor(line) {
@@ -27421,22 +29060,17 @@ function coworkLineColor(line) {
27421
29060
  if (line.startsWith(" ")) return "gray";
27422
29061
  return void 0;
27423
29062
  }
27424
- function GoalWorkBlockView({ block }) {
29063
+ function GoalWorkBlockView({ block, width }) {
27425
29064
  const work = block.goalWork;
29065
+ const contentWidth = Math.max(16, width - 4);
27426
29066
  return React.createElement(
27427
29067
  Box,
27428
- { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 },
27429
- React.createElement(
27430
- Box,
27431
- null,
27432
- React.createElement(Text, { color: "cyan", bold: true }, "[goal] "),
27433
- React.createElement(Text, { color: "white", bold: true }, block.title),
27434
- block.meta ? React.createElement(Text, { color: "gray" }, ` \xB7 ${block.meta}`) : null
27435
- ),
27436
- work ? React.createElement(GoalWorkDetailsView, { work }) : React.createElement(GoalFallbackLines, { lines: block.lines })
29068
+ { width, flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 },
29069
+ React.createElement(SurfaceLine, { text: `[goal] ${block.title}${block.meta ? ` \xB7 ${block.meta}` : ""}`, width: contentWidth, backgroundColor: SURFACE.status, color: "cyan", bold: true }),
29070
+ work ? React.createElement(GoalWorkDetailsView, { work, width: contentWidth }) : React.createElement(GoalFallbackLines, { lines: block.lines, width: contentWidth })
27437
29071
  );
27438
29072
  }
27439
- function GoalWorkDetailsView({ work }) {
29073
+ function GoalWorkDetailsView({ work, width }) {
27440
29074
  const status = work.completedAt ? "completed" : "running";
27441
29075
  const diffLines = work.diff && work.diff.files.length > 0 ? [
27442
29076
  `Changes: ${work.diff.files.length} ${work.diff.files.length === 1 ? "file" : "files"} +${work.diff.addedLines} -${work.diff.removedLines}`,
@@ -27445,33 +29079,34 @@ function GoalWorkDetailsView({ work }) {
27445
29079
  return React.createElement(
27446
29080
  Box,
27447
29081
  { flexDirection: "column", marginTop: 1 },
27448
- React.createElement(Text, { color: "gray" }, `Goal: ${work.goalId}`),
27449
- React.createElement(Text, { color: "gray" }, `Status: ${status}`),
27450
- ...diffLines.map((line, index) => React.createElement(Text, { key: `diff-${index}`, color: line.startsWith(" ") ? "gray" : "white" }, line)),
29082
+ ...wrapBlockLines([`Goal: ${work.goalId}`, `Status: ${status}`], width, 4).map((line, index) => React.createElement(Text, { key: `meta-${index}`, color: "gray" }, line)),
29083
+ ...wrapBlockLines(diffLines, width, 20).map((line, index) => React.createElement(Text, { key: `diff-${index}`, color: line.startsWith(" ") ? "gray" : "white" }, line)),
27451
29084
  ...work.narrations.flatMap((narration, index) => [
27452
29085
  React.createElement(
27453
29086
  Box,
27454
29087
  { key: `narration-${narration.narrationId}`, flexDirection: "column", marginTop: index === 0 && diffLines.length === 0 ? 1 : 1 },
27455
- ...narration.lines.slice(0, 40).map((line, lineIndex) => React.createElement(Text, { key: lineIndex, color: "white" }, line || " ")),
27456
- narration.tools.length > 0 ? React.createElement(GoalToolGroupView, { key: `${narration.narrationId}-tools`, tools: narration.tools }) : null
29088
+ ...wrapBlockLines(narration.lines, width, 40).map((line, lineIndex) => React.createElement(Text, { key: lineIndex, color: "white" }, line || " ")),
29089
+ narration.tools.length > 0 ? React.createElement(GoalToolGroupView, { key: `${narration.narrationId}-tools`, tools: narration.tools, width }) : null
27457
29090
  )
27458
29091
  ]),
27459
- work.orphanTools.length > 0 ? React.createElement(GoalToolGroupView, { tools: work.orphanTools, title: "Tools" }) : null
29092
+ work.orphanTools.length > 0 ? React.createElement(GoalToolGroupView, { tools: work.orphanTools, title: "Tools", width }) : null
27460
29093
  );
27461
29094
  }
27462
- function GoalToolGroupView({ tools, title }) {
29095
+ function GoalToolGroupView({ tools, title, width }) {
29096
+ const nestedWidth = Math.max(24, width - 2);
27463
29097
  return React.createElement(
27464
29098
  Box,
27465
29099
  { flexDirection: "column", marginTop: 1, marginLeft: 2 },
27466
29100
  title ? React.createElement(Text, { color: "gray" }, title) : null,
27467
- ...tools.map((tool) => React.createElement(ToolBlockView, { key: tool.callId, block: goalToolAsBlock(tool) }))
29101
+ ...tools.map((tool) => React.createElement(ToolBlockView, { key: tool.callId, block: goalToolAsBlock(tool), width: nestedWidth }))
27468
29102
  );
27469
29103
  }
27470
- function GoalFallbackLines({ lines }) {
29104
+ function GoalFallbackLines({ lines, width }) {
29105
+ const wrappedLines = wrapBlockLines(lines, width, 120);
27471
29106
  return React.createElement(
27472
29107
  Box,
27473
29108
  { flexDirection: "column", marginTop: 1 },
27474
- ...lines.slice(0, 120).map((line, index) => React.createElement(Text, { key: index, color: line.startsWith(" ") || line.startsWith(" ") ? "gray" : void 0 }, line || " "))
29109
+ ...wrappedLines.map((line, index) => React.createElement(Text, { key: index, color: line.startsWith(" ") || line.startsWith(" ") ? "gray" : void 0 }, line || " "))
27475
29110
  );
27476
29111
  }
27477
29112
  function goalToolAsBlock(tool) {
@@ -27485,38 +29120,33 @@ function goalToolAsBlock(tool) {
27485
29120
  expanded: false
27486
29121
  };
27487
29122
  }
27488
- function ToolBlockView({ block }) {
29123
+ function ToolBlockView({ block, width }) {
27489
29124
  const status = block.toolStatus ?? "requested";
27490
29125
  const color = status === "completed" ? "green" : status === "failed" || status === "denied" ? "red" : status === "permission" ? "yellow" : status === "running" ? "cyan" : "magenta";
27491
29126
  const badge = status === "completed" ? "ok" : status === "failed" || status === "denied" ? "!" : status === "permission" ? "?" : status === "running" ? "..." : ">";
29127
+ const contentWidth = Math.max(16, width - 4);
29128
+ const header = `[${badge}] ${block.expanded ? "[-]" : "[+]"} ${block.title}${block.meta ? ` \xB7 ${block.meta}` : ""}`;
27492
29129
  return React.createElement(
27493
29130
  Box,
27494
- { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: color, paddingX: 1 },
27495
- React.createElement(
27496
- Box,
27497
- null,
27498
- React.createElement(Text, { color, bold: true }, `[${badge}] `),
27499
- React.createElement(Text, { color: "gray" }, block.expanded ? "[-] " : "[+] "),
27500
- React.createElement(Text, { color: "white", bold: true }, block.title),
27501
- block.meta ? React.createElement(Text, { color: "gray" }, ` \xB7 ${block.meta}`) : null
27502
- ),
27503
- block.expanded ? React.createElement(ToolDetailsView, { details: block.toolDetails, fallbackLines: block.lines }) : null
29131
+ { width, flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: color, paddingX: 1 },
29132
+ React.createElement(SurfaceLine, { text: header, width: contentWidth, backgroundColor: SURFACE.status, color, bold: true }),
29133
+ block.expanded ? React.createElement(ToolDetailsView, { details: block.toolDetails, fallbackLines: block.lines, width: contentWidth }) : null
27504
29134
  );
27505
29135
  }
27506
- function ToolDetailsView({ details, fallbackLines }) {
29136
+ function ToolDetailsView({ details, fallbackLines, width }) {
27507
29137
  if (!details) {
27508
29138
  return React.createElement(
27509
29139
  Box,
27510
29140
  { flexDirection: "column", marginTop: 1 },
27511
- ...fallbackLines.slice(0, 80).map((line, index) => React.createElement(Text, { key: index, color: index === 0 ? "gray" : void 0 }, line || " "))
29141
+ ...wrapBlockLines(fallbackLines, width, 80).map((line, index) => React.createElement(Text, { key: index, color: index === 0 ? "gray" : void 0 }, line || " "))
27512
29142
  );
27513
29143
  }
27514
29144
  return React.createElement(
27515
29145
  Box,
27516
29146
  { flexDirection: "column", marginTop: 1 },
27517
- React.createElement(Text, { color: "white" }, details.summary),
29147
+ ...wrapBlockLines([details.summary], width, 4).map((line, index) => React.createElement(Text, { key: `summary-${index}`, color: "white" }, line || " ")),
27518
29148
  details.chips.length > 0 ? React.createElement(ToolChipLine, { chips: details.chips }) : null,
27519
- ...details.sections.slice(0, 8).map((section, index) => React.createElement(ToolSectionView, { key: index, section }))
29149
+ ...details.sections.slice(0, 8).map((section, index) => React.createElement(ToolSectionView, { key: index, section, width }))
27520
29150
  );
27521
29151
  }
27522
29152
  function ToolChipLine({ chips }) {
@@ -27529,26 +29159,36 @@ function ToolChipLine({ chips }) {
27529
29159
  ].filter(Boolean))
27530
29160
  );
27531
29161
  }
27532
- function ToolSectionView({ section }) {
29162
+ function ToolSectionView({ section, width }) {
27533
29163
  const rows = section.rows ?? [];
27534
29164
  const lines = section.lines ?? [];
29165
+ const bodyWidth = Math.max(8, width - 2);
27535
29166
  const body = rows.length > 0 || lines.length > 0 ? [
27536
- ...rows.map((row, index) => React.createElement(ToolRowView, { key: `row-${index}`, row })),
27537
- ...lines.slice(0, 40).map((line, index) => React.createElement(Text, { key: `line-${index}`, color: section.code ? "gray" : "white" }, ` ${line || " "}`))
27538
- ] : section.emptyText ? [React.createElement(Text, { key: "empty", color: "gray" }, ` ${section.emptyText}`)] : [];
29167
+ ...rows.map((row, index) => React.createElement(ToolRowView, { key: `row-${index}`, row, width: bodyWidth })),
29168
+ ...wrapBlockLines(lines.map((line) => ` ${line || " "}`), bodyWidth, 40).map((line, index) => React.createElement(Text, { key: `line-${index}`, color: section.code ? "gray" : "white" }, line))
29169
+ ] : section.emptyText ? wrapBlockLines([` ${section.emptyText}`], bodyWidth, 2).map((line, index) => React.createElement(Text, { key: `empty-${index}`, color: "gray" }, line)) : [];
27539
29170
  return React.createElement(
27540
29171
  Box,
27541
29172
  { flexDirection: "column", marginTop: 1 },
27542
- React.createElement(Text, { color: toneColor(section.tone) ?? "cyan", bold: true }, section.title),
29173
+ React.createElement(Text, { color: toneColor(section.tone) ?? "cyan", bold: true }, fitToWidth(section.title, width)),
27543
29174
  ...body
27544
29175
  );
27545
29176
  }
27546
- function ToolRowView({ row }) {
29177
+ function ToolRowView({ row, width }) {
29178
+ const label = ` ${padLabel(row.label)} `;
29179
+ const valueWidth = Math.max(8, width - stringWidth(label));
29180
+ const valueLines = wrapBlockLines([row.value], valueWidth, 6);
27547
29181
  return React.createElement(
27548
29182
  Box,
27549
- null,
27550
- React.createElement(Text, { color: "gray" }, ` ${padLabel(row.label)} `),
27551
- React.createElement(Text, { color: toneColor(row.tone) ?? (row.code ? "cyan" : "white") }, row.value)
29183
+ { flexDirection: "column" },
29184
+ ...valueLines.map(
29185
+ (line, index) => React.createElement(
29186
+ Box,
29187
+ { key: index },
29188
+ React.createElement(Text, { color: "gray" }, index === 0 ? label : padToWidth("", stringWidth(label))),
29189
+ React.createElement(Text, { color: toneColor(row.tone) ?? (row.code ? "cyan" : "white") }, line || " ")
29190
+ )
29191
+ )
27552
29192
  );
27553
29193
  }
27554
29194
  function padLabel(value) {
@@ -27562,22 +29202,33 @@ function toneColor(tone) {
27562
29202
  if (tone === "neutral") return "gray";
27563
29203
  return void 0;
27564
29204
  }
29205
+ function BottomStatusStack({ state, now: now2 }) {
29206
+ if (!shouldShowBottomStatusStack(state)) return null;
29207
+ return React.createElement(
29208
+ Box,
29209
+ { alignSelf: "center", width: statusSurfaceWidth(), flexDirection: "column", marginBottom: 1 },
29210
+ React.createElement(GoalIsland, { state }),
29211
+ React.createElement(DiffAccordion, { state }),
29212
+ React.createElement(WorkPlanPanel, { state, now: now2 }),
29213
+ React.createElement(ActivityBar, { state })
29214
+ );
29215
+ }
29216
+ function shouldShowBottomStatusStack(state) {
29217
+ return Boolean(state.goal || state.turnDiff && state.turnDiff.files.length > 0 || state.workPlan || state.inputMode === "running" || shouldShowActivity(state));
29218
+ }
27565
29219
  function ActivityBar({ state }) {
27566
29220
  if (!shouldShowActivity(state)) return null;
27567
29221
  const warning = state.warnings.at(-1);
27568
29222
  const context = contextEfficiencySummary(state);
27569
29223
  const color = state.done ? "green" : state.inputMode === "running" ? "cyan" : "blue";
29224
+ const width = statusSurfaceWidth();
29225
+ const contentWidth = Math.max(24, width - 4);
29226
+ const summary = `Activity ${state.activity}${context ? ` | ${context}` : ""}`;
27570
29227
  return React.createElement(
27571
29228
  Box,
27572
- { borderStyle: "round", borderColor: color, paddingX: 1, flexDirection: "column" },
27573
- React.createElement(
27574
- Box,
27575
- null,
27576
- React.createElement(Text, { color, bold: true }, "Activity "),
27577
- React.createElement(Text, { color: state.done ? "green" : "white" }, state.activity),
27578
- context ? React.createElement(Text, { color: "gray" }, ` | ${context}`) : null
27579
- ),
27580
- warning ? React.createElement(Text, { color: "yellow" }, ` \xB7 ${warning}`) : null
29229
+ { alignSelf: "center", width, borderStyle: "round", borderColor: color, paddingX: 1, flexDirection: "column" },
29230
+ React.createElement(SurfaceLine, { text: summary, width: contentWidth, backgroundColor: SURFACE.status, color: state.done ? "green" : color, bold: true }),
29231
+ warning ? React.createElement(SurfaceLine, { text: `! ${warning}`, width: contentWidth, backgroundColor: SURFACE.status, color: "yellow" }) : null
27581
29232
  );
27582
29233
  }
27583
29234
  function shouldShowActivity(state) {
@@ -27589,18 +29240,15 @@ function GoalIsland({ state }) {
27589
29240
  const color = goalIslandColor(goal.status);
27590
29241
  const progress = goal.maxIterations > 0 ? `${goal.iteration}/${goal.maxIterations}` : "active";
27591
29242
  const decision = goal.decision ? ` | ${goalDecisionLabel(goal.decision)}` : "";
27592
- const reason = goal.reason ? compactText(goal.reason, 88) : void 0;
29243
+ const width = statusSurfaceWidth();
29244
+ const contentWidth = Math.max(24, width - 6);
29245
+ const reason = goal.reason ? compactText(goal.reason, contentWidth) : void 0;
29246
+ const titleLine = `Goal ${goal.title ?? goal.objective} | ${goalStatusLabel(goal.status)} | iter ${progress}${decision}`;
27593
29247
  return React.createElement(
27594
29248
  Box,
27595
- { alignSelf: "center", borderStyle: "round", borderColor: color, paddingX: 2, marginTop: 1, flexDirection: "column" },
27596
- React.createElement(
27597
- Box,
27598
- null,
27599
- React.createElement(Text, { color, bold: true }, "Goal "),
27600
- React.createElement(Text, { color: "white", bold: true }, compactText(goal.title ?? goal.objective, 60)),
27601
- React.createElement(Text, { color: "gray" }, ` | ${goalStatusLabel(goal.status)} | iter ${progress}${decision}`)
27602
- ),
27603
- goal.title ? React.createElement(Text, { color: "gray" }, compactText(goal.objective, 88)) : null,
29249
+ { alignSelf: "center", width, borderStyle: "round", borderColor: color, paddingX: 2, marginTop: 1, flexDirection: "column" },
29250
+ React.createElement(SurfaceLine, { text: titleLine, width: contentWidth, backgroundColor: SURFACE.status, color, bold: true }),
29251
+ goal.title ? React.createElement(Text, { color: "gray" }, fitToWidth(goal.objective, contentWidth)) : null,
27604
29252
  reason ? React.createElement(Text, { color: "gray" }, reason) : null
27605
29253
  );
27606
29254
  }
@@ -27630,57 +29278,43 @@ function DiffAccordion({ state }) {
27630
29278
  const indicator = state.diffExpanded ? "[-]" : "[+]";
27631
29279
  const visibleFiles = diff.files.slice(0, 8);
27632
29280
  const remaining = diff.files.length - visibleFiles.length;
29281
+ const width = statusSurfaceWidth();
29282
+ const contentWidth = Math.max(24, width - 4);
27633
29283
  return React.createElement(
27634
29284
  Box,
27635
- { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1 },
27636
- React.createElement(
27637
- Box,
27638
- null,
27639
- React.createElement(Text, { color: "cyan", bold: true }, `${indicator} Diff `),
27640
- React.createElement(Text, { color: "white" }, summary),
27641
- React.createElement(Text, { color: "gray" }, " | esc then d toggle")
27642
- ),
29285
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1 },
29286
+ React.createElement(SurfaceLine, { text: `${indicator} Diff ${summary} | esc then d toggle`, width: contentWidth, backgroundColor: SURFACE.status, color: "cyan", bold: true }),
27643
29287
  state.diffExpanded ? React.createElement(
27644
29288
  Box,
27645
29289
  { flexDirection: "column", marginTop: 1 },
27646
- ...visibleFiles.map((file) => React.createElement(DiffFileCard, { key: file.path, file })),
29290
+ ...visibleFiles.map((file) => React.createElement(DiffFileCard, { key: file.path, file, width: contentWidth })),
27647
29291
  remaining > 0 ? React.createElement(Text, { key: "remaining", color: "gray" }, `... ${remaining} more files`) : null
27648
29292
  ) : null
27649
29293
  );
27650
29294
  }
27651
- function DiffFileCard({ file }) {
29295
+ function DiffFileCard({ file, width }) {
27652
29296
  const kind = file.kind === "add" ? "add" : file.kind === "delete" ? "delete" : "modified";
27653
29297
  const kindColor = file.kind === "add" ? "green" : file.kind === "delete" ? "red" : "yellow";
29298
+ const line = `[file] ${file.path} \xB7 ${kind} +${file.addedLines} -${file.removedLines}`;
27654
29299
  return React.createElement(
27655
29300
  Box,
27656
- { borderStyle: "single", borderColor: "gray", paddingX: 1, marginBottom: 1 },
27657
- React.createElement(Text, { color: "cyan", bold: true }, "[file] "),
27658
- React.createElement(Text, { color: "white", bold: true }, file.path),
27659
- React.createElement(Text, { color: "gray" }, " \xB7 "),
27660
- React.createElement(Text, { color: kindColor }, kind),
27661
- React.createElement(Text, { color: "gray" }, " "),
27662
- React.createElement(Text, { color: "green" }, `+${file.addedLines}`),
27663
- React.createElement(Text, { color: "gray" }, " "),
27664
- React.createElement(Text, { color: "red" }, `-${file.removedLines}`)
29301
+ { width, borderStyle: "single", borderColor: "gray", paddingX: 1, marginBottom: 1 },
29302
+ React.createElement(Text, { color: kindColor, bold: true }, fitToWidth(line, Math.max(8, width - 4)))
27665
29303
  );
27666
29304
  }
27667
29305
  function WorkPlanPanel({ state, now: now2 }) {
27668
29306
  const plan = state.workPlan;
27669
29307
  const steps = plan?.steps ?? [];
27670
29308
  const expanded = state.workPlanExpanded || steps.some((step) => step.status === "blocked" || step.status === "failed");
29309
+ const width = statusSurfaceWidth();
29310
+ const contentWidth = Math.max(24, width - 4);
27671
29311
  if (steps.length === 0) {
27672
29312
  if (state.inputMode !== "running") return null;
27673
29313
  return React.createElement(
27674
29314
  Box,
27675
- { flexDirection: "column", borderStyle: "single", borderColor: "cyan", paddingX: 1 },
27676
- React.createElement(
27677
- Box,
27678
- null,
27679
- React.createElement(Text, { color: "cyan", bold: true }, `${expanded ? "[-]" : "[+]"} Plan `),
27680
- React.createElement(Text, { color: "white" }, "Thinking"),
27681
- React.createElement(Text, { color: "gray" }, " | esc then w toggle")
27682
- ),
27683
- expanded ? React.createElement(Text, { color: "gray" }, "Demian is inspecting context and preparing a step-by-step plan.") : null
29315
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "cyan", paddingX: 1 },
29316
+ React.createElement(SurfaceLine, { text: `${expanded ? "[-]" : "[+]"} Plan Thinking | esc then w toggle`, width: contentWidth, backgroundColor: SURFACE.status, color: "cyan", bold: true }),
29317
+ expanded ? React.createElement(Text, { color: "gray" }, fitToWidth("Demian is inspecting context and preparing a step-by-step plan.", contentWidth)) : null
27684
29318
  );
27685
29319
  }
27686
29320
  const phase = workPlanPhase(state);
@@ -27693,57 +29327,63 @@ function WorkPlanPanel({ state, now: now2 }) {
27693
29327
  const elapsed = plan ? planElapsed(plan, now2) : void 0;
27694
29328
  return React.createElement(
27695
29329
  Box,
27696
- { flexDirection: "column", borderStyle: "single", borderColor: phase.color, paddingX: 1 },
27697
- React.createElement(
27698
- Box,
27699
- null,
27700
- React.createElement(Text, { color: phase.color, bold: true }, `${expanded ? "[-]" : "[+]"} ${phase.label} `),
27701
- React.createElement(Text, { color: "white", bold: true }, plan?.title ?? "Main goal"),
27702
- elapsed ? React.createElement(Text, { color: "gray" }, ` (${elapsed})`) : null,
27703
- React.createElement(Text, { color: "gray" }, ` | ${planProgressGraph(progress.resolved, progress.total)} ${progress.resolved}/${progress.total}`),
27704
- blocked ? React.createElement(Text, { color: "red" }, ` | ${blocked} blocked`) : null,
27705
- React.createElement(Text, { color: "gray" }, " | esc then w toggle")
27706
- ),
29330
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: phase.color, paddingX: 1 },
29331
+ React.createElement(SurfaceLine, {
29332
+ text: `${expanded ? "[-]" : "[+]"} ${phase.label} ${plan?.title ?? "Main goal"}${elapsed ? ` (${elapsed})` : ""} | ${planProgressGraph(progress.resolved, progress.total)} ${progress.resolved}/${progress.total}${blocked ? ` | ${blocked} blocked` : ""} | esc then w toggle`,
29333
+ width: contentWidth,
29334
+ backgroundColor: SURFACE.status,
29335
+ color: phase.color,
29336
+ bold: true
29337
+ }),
27707
29338
  expanded ? React.createElement(
27708
29339
  Box,
27709
29340
  { flexDirection: "column", marginTop: 1 },
27710
- plan?.summary ? React.createElement(WorkPlanInfoLine, { label: "Plan", value: plan.summary }) : null,
29341
+ plan?.summary ? React.createElement(WorkPlanInfoLine, { label: "Plan", value: plan.summary, width: contentWidth }) : null,
27711
29342
  active ? React.createElement(WorkPlanInfoLine, {
27712
29343
  label: "Now",
27713
29344
  value: `${activeIndex + 1}/${steps.length} ${active.title}`,
27714
29345
  meta: stepElapsed(active, now2),
27715
- color: "yellow"
29346
+ color: "yellow",
29347
+ width: contentWidth
27716
29348
  }) : null
27717
29349
  ) : null,
27718
29350
  expanded ? React.createElement(
27719
29351
  Box,
27720
29352
  { flexDirection: "column", marginTop: 1 },
27721
- ...visibleSteps.map((step) => React.createElement(WorkPlanStepLine, { key: step.id, step, active: step.id === active?.id, now: now2 }))
29353
+ ...visibleSteps.map((step) => React.createElement(WorkPlanStepLine, { key: step.id, step, active: step.id === active?.id, now: now2, width: contentWidth }))
27722
29354
  ) : null,
27723
- ...expanded ? notes.map((note) => React.createElement(WorkPlanInfoLine, { key: note.id, label: "Update", value: note.message, color: noteColor(note.level) })) : []
29355
+ ...expanded ? notes.map((note) => React.createElement(WorkPlanInfoLine, { key: note.id, label: "Update", value: note.message, color: noteColor(note.level), width: contentWidth })) : []
27724
29356
  );
27725
29357
  }
27726
- function WorkPlanInfoLine({ label, value, meta, color = "gray" }) {
29358
+ function WorkPlanInfoLine({ label, value, meta, color = "gray", width }) {
29359
+ const prefix = `[${label}] `;
29360
+ const valueWidth = Math.max(8, width - stringWidth(prefix) - (meta ? stringWidth(` (${meta})`) : 0));
29361
+ const valueLines = wrapBlockLines([value], valueWidth, 4);
27727
29362
  return React.createElement(
27728
29363
  Box,
27729
- null,
27730
- React.createElement(Text, { color: "gray" }, `[${label}] `),
27731
- React.createElement(Text, { color }, value),
27732
- meta ? React.createElement(Text, { color: "gray" }, ` (${meta})`) : null
29364
+ { flexDirection: "column" },
29365
+ ...valueLines.map(
29366
+ (line, index) => React.createElement(
29367
+ Box,
29368
+ { key: index },
29369
+ React.createElement(Text, { color: "gray" }, index === 0 ? prefix : padToWidth("", stringWidth(prefix))),
29370
+ React.createElement(Text, { color }, line),
29371
+ index === 0 && meta ? React.createElement(Text, { color: "gray" }, ` (${meta})`) : null
29372
+ )
29373
+ )
27733
29374
  );
27734
29375
  }
27735
- function WorkPlanStepLine({ step, active, now: now2 }) {
29376
+ function WorkPlanStepLine({ step, active, now: now2, width }) {
27736
29377
  const detail = [step.detail, step.agent ? `agent ${step.agent}` : void 0].filter(Boolean).join(" | ");
27737
29378
  const elapsed = stepElapsed(step, now2);
29379
+ const title = `${stepGlyph(step.status)} ${step.title}${elapsed ? ` (${elapsed})` : ""}`;
27738
29380
  return React.createElement(
27739
29381
  Box,
27740
29382
  { flexDirection: "column" },
27741
- React.createElement(
27742
- Text,
27743
- { color: stepColor(step.status), bold: active || step.status === "in_progress" },
27744
- `${stepGlyph(step.status)} ${step.title}${elapsed ? ` (${elapsed})` : ""}`
29383
+ ...wrapBlockLines([title], width, 3).map(
29384
+ (line, index) => React.createElement(Text, { key: `title-${index}`, color: stepColor(step.status), bold: active || step.status === "in_progress" }, line)
27745
29385
  ),
27746
- detail ? React.createElement(Text, { color: "gray" }, ` ${detail}`) : null
29386
+ detail ? wrapBlockLines([` ${detail}`], width, 4).map((line, index) => React.createElement(Text, { key: `detail-${index}`, color: "gray" }, line)) : null
27747
29387
  );
27748
29388
  }
27749
29389
  function workPlanPhase(state) {
@@ -27823,29 +29463,45 @@ function formatElapsed2(ms2) {
27823
29463
  if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
27824
29464
  return `${seconds}s`;
27825
29465
  }
27826
- function InteractionPanel({ state, shortcutMode }) {
29466
+ function formatSessionAge(updatedAt) {
29467
+ if (!updatedAt || !Number.isFinite(updatedAt)) return "-";
29468
+ const seconds = Math.max(0, Math.floor((Date.now() - updatedAt) / 1e3));
29469
+ if (seconds < 60) return `${seconds}s ago`;
29470
+ const minutes = Math.floor(seconds / 60);
29471
+ if (minutes < 60) return `${minutes}m ago`;
29472
+ const hours = Math.floor(minutes / 60);
29473
+ if (hours < 48) return `${hours}h ago`;
29474
+ return `${Math.floor(hours / 24)}d ago`;
29475
+ }
29476
+ function InteractionPanel({ state, shortcutMode, promptDraft, commandPalette }) {
27827
29477
  if (state.inputMode === "starting") return React.createElement(LoadingBar);
29478
+ if (state.inputMode === "session") return React.createElement(SessionSelector, { state });
27828
29479
  if (state.permission) return React.createElement(PermissionBar, { state });
27829
29480
  if (state.inputMode === "provider") return React.createElement(ProviderSelector, { state });
27830
29481
  if (state.inputMode === "agent") return React.createElement(AgentSelector, { state });
27831
29482
  if (state.inputMode === "permissionPreset") return React.createElement(PermissionPresetSelector, { state });
29483
+ if (state.inputMode === "config") return React.createElement(ConfigManager, { state });
29484
+ if (state.inputMode === "configModel") return React.createElement(ConfigModelSelector, { state });
29485
+ if (state.inputMode === "configModelInput") return React.createElement(ConfigModelInput, { state });
29486
+ if (state.inputMode === "configProviderInput") return React.createElement(ConfigProviderInput, { state });
29487
+ if (state.inputMode === "configAddProvider") return React.createElement(ConfigAddProviderSelector, { state });
27832
29488
  if (state.inputMode === "model") return React.createElement(ModelEditor, { state });
27833
- if (state.inputMode === "running") return React.createElement(CommandBar, { state, shortcutMode });
27834
- if (state.inputMode === "prompt") return React.createElement(PromptBar, { state, shortcutMode });
29489
+ if (state.inputMode === "running") return React.createElement(CommandBar, { state, shortcutMode, promptDraft, commandPalette });
29490
+ if (state.inputMode === "prompt") return React.createElement(PromptBar, { state, shortcutMode, promptDraft, commandPalette });
27835
29491
  return React.createElement(PermissionBar, { state });
27836
29492
  }
27837
29493
  function LoadingBar() {
27838
29494
  return React.createElement(
27839
29495
  Box,
27840
29496
  { flexDirection: "column", borderStyle: "double", paddingX: 1 },
27841
- React.createElement(Text, { color: "cyan", bold: true }, "loading"),
27842
- React.createElement(Text, { color: "gray" }, "demian is preparing the session | type /exit after loading to quit")
29497
+ React.createElement(SurfaceLine, { text: "loading", width: promptPanelWidth() - 4, backgroundColor: SURFACE.status, color: "cyan", bold: true }),
29498
+ React.createElement(SurfaceLine, { text: "demian is preparing the session | type /exit after loading to quit", width: promptPanelWidth() - 4, backgroundColor: SURFACE.status, color: "gray" })
27843
29499
  );
27844
29500
  }
27845
29501
  function shortcutHelp(state) {
27846
29502
  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");
29503
+ if (state.inputMode === "prompt") parts.push("k commands", "s sessions", "p provider", "a agent", "m model", "o permissions", "c config", "u clear");
29504
+ if (state.inputMode === "running") parts.push("k commands", "s stop");
27849
29505
  if (state.turnDiff) parts.push("d diff");
27850
29506
  if (state.blocks.some((block) => block.kind === "tool")) parts.push("t tools");
27851
29507
  if (state.workPlan || state.inputMode === "running") parts.push("w plan");
@@ -27854,46 +29510,250 @@ function shortcutHelp(state) {
27854
29510
  parts.push("q quit");
27855
29511
  return `shortcut: ${parts.join(" | ")}`;
27856
29512
  }
27857
- function PromptBar({ state, shortcutMode }) {
27858
- const value = state.promptInput || "Type first message and press Enter";
27859
- const hint = shortcutMode ? shortcutHelp(state) : "enter send | esc shortcuts | up/down history | /cowork | /compact | /retry | /exit";
29513
+ function PromptBar({ state, shortcutMode, promptDraft, commandPalette }) {
29514
+ const promptSnapshot = usePromptDraftSnapshot(promptDraft);
29515
+ const slashCommandPalette = commandPalette ? void 0 : slashCommandPaletteForDraft(promptSnapshot, state.inputMode);
29516
+ const hint = shortcutMode ? shortcutHelp(state) : "enter send | shift/option+enter newline | esc k commands | esc s sessions | esc shortcuts | up/down history | /compact | /retry | /exit";
27860
29517
  return React.createElement(
27861
29518
  Box,
27862
29519
  { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", marginTop: 1 },
27863
- React.createElement(SessionMetaBox, { state, marginTop: 0 }),
27864
- React.createElement(
27865
- Box,
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
- )
29520
+ commandPalette ? React.createElement(CommandPalettePanel, { palette: commandPalette }) : null,
29521
+ commandPalette ? null : React.createElement(SessionMetaBox, { state, marginTop: 0 }),
29522
+ slashCommandPalette ? React.createElement(SlashCommandPalette, { palette: slashCommandPalette }) : null,
29523
+ React.createElement(InputFrame, {
29524
+ borderColor: shortcutMode ? "cyan" : "blue",
29525
+ cursorColor: "cyan",
29526
+ value: promptSnapshot.value,
29527
+ placeholder: "Type first message and press Enter",
29528
+ marginTop: 1
29529
+ }),
29530
+ React.createElement(CommandGuide, { hint, color: shortcutMode ? "cyan" : "gray", error: state.promptError })
27875
29531
  );
27876
29532
  }
27877
- function CommandBar({ state, shortcutMode }) {
27878
- const value = state.promptInput || "Type /stop to stop or /exit to quit";
27879
- const hint = shortcutMode ? shortcutHelp(state) : "running | /stop stop | /exit quit | esc shortcuts";
29533
+ function CommandBar({ state, shortcutMode, promptDraft, commandPalette }) {
29534
+ const promptSnapshot = usePromptDraftSnapshot(promptDraft);
29535
+ const slashCommandPalette = commandPalette ? void 0 : slashCommandPaletteForDraft(promptSnapshot, state.inputMode);
29536
+ const hint = shortcutMode ? shortcutHelp(state) : "running | shift/option+enter newline | esc k commands | /stop stop | /exit quit | esc shortcuts";
27880
29537
  return React.createElement(
27881
29538
  Box,
27882
29539
  { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", marginTop: 1 },
27883
- React.createElement(SessionMetaBox, { state, marginTop: 0 }),
27884
- React.createElement(
29540
+ commandPalette ? React.createElement(CommandPalettePanel, { palette: commandPalette }) : null,
29541
+ commandPalette ? null : React.createElement(SessionMetaBox, { state, marginTop: 0 }),
29542
+ slashCommandPalette ? React.createElement(SlashCommandPalette, { palette: slashCommandPalette }) : null,
29543
+ React.createElement(InputFrame, {
29544
+ borderColor: shortcutMode ? "cyan" : "yellow",
29545
+ cursorColor: "yellow",
29546
+ value: promptSnapshot.value,
29547
+ placeholder: "Type /stop to stop or /exit to quit",
29548
+ marginTop: 1
29549
+ }),
29550
+ React.createElement(CommandGuide, { hint, color: shortcutMode ? "cyan" : "gray", error: state.promptError })
29551
+ );
29552
+ }
29553
+ function CommandPalettePanel({ palette }) {
29554
+ const width = commandPalettePanelWidth();
29555
+ const contentWidth = Math.max(32, width - 4);
29556
+ const maxRows = commandPaletteMaxRows();
29557
+ const selectedCursor = Math.min(palette.cursor, Math.max(0, palette.items.length - 1));
29558
+ const start = Math.max(0, Math.min(selectedCursor - maxRows + 1, Math.max(0, palette.items.length - maxRows)));
29559
+ const visibleItems = palette.items.slice(start, start + maxRows);
29560
+ const visibleCursor = selectedCursor - start;
29561
+ return React.createElement(
29562
+ Box,
29563
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "#374151", paddingX: 1, paddingY: 1, marginTop: 1 },
29564
+ React.createElement(CommandPaletteHeader, { width: contentWidth }),
29565
+ React.createElement(CommandPaletteSearchLine, { query: palette.query, width: contentWidth }),
29566
+ React.createElement(SurfaceLine, { text: "", width: contentWidth, backgroundColor: SURFACE.dialog }),
29567
+ start > 0 ? React.createElement(SurfaceLine, { text: `+ ${start} above`, width: contentWidth, backgroundColor: SURFACE.dialog, color: "gray" }) : null,
29568
+ ...commandPaletteRenderedRows(visibleItems, visibleCursor, contentWidth),
29569
+ palette.items.length === 0 ? React.createElement(SurfaceLine, { text: "No commands", width: contentWidth, backgroundColor: SURFACE.dialog, color: "gray" }) : null,
29570
+ palette.items.length > start + visibleItems.length ? React.createElement(SurfaceLine, { text: `+ ${palette.items.length - start - visibleItems.length} more`, width: contentWidth, backgroundColor: SURFACE.dialog, color: "gray" }) : null
29571
+ );
29572
+ }
29573
+ function CommandPaletteHeader({ width }) {
29574
+ const label = "Commands";
29575
+ const esc = "esc";
29576
+ const gap = Math.max(1, width - stringWidth(label) - stringWidth(esc));
29577
+ return React.createElement(
29578
+ Text,
29579
+ { backgroundColor: SURFACE.dialog, color: "white", bold: true },
29580
+ `${label}${" ".repeat(gap)}${esc}`
29581
+ );
29582
+ }
29583
+ function CommandPaletteSearchLine({ query, width }) {
29584
+ const text = query || "Search";
29585
+ const textColor = query ? "white" : "gray";
29586
+ const textWidth = Math.max(0, width - 1);
29587
+ return React.createElement(
29588
+ Box,
29589
+ null,
29590
+ React.createElement(CursorCell, { cursorColor: "#f4a261" }),
29591
+ React.createElement(Text, { color: textColor, backgroundColor: SURFACE.dialog }, padToWidth(fitToWidth(text, textWidth), textWidth))
29592
+ );
29593
+ }
29594
+ function commandPaletteRenderedRows(items, cursor, width) {
29595
+ const rows = [];
29596
+ let lastGroup;
29597
+ items.forEach((item, index) => {
29598
+ if (item.group !== lastGroup) {
29599
+ if (rows.length > 0) rows.push(React.createElement(SurfaceLine, { key: `gap-${item.group}-${index}`, text: "", width, backgroundColor: SURFACE.dialog }));
29600
+ rows.push(React.createElement(SurfaceLine, { key: `group-${item.group}-${index}`, text: item.group, width, backgroundColor: SURFACE.dialog, color: "magenta", bold: true }));
29601
+ lastGroup = item.group;
29602
+ }
29603
+ rows.push(React.createElement(CommandPaletteRow, { key: item.id, item, selected: index === cursor, width }));
29604
+ });
29605
+ return rows;
29606
+ }
29607
+ function CommandPaletteRow({ item, selected, width }) {
29608
+ const shortcutWidth = Math.min(18, Math.max(8, Math.floor(width * 0.22)));
29609
+ const titleWidth = Math.min(26, Math.max(16, Math.floor(width * 0.34)));
29610
+ const descriptionWidth = Math.max(0, width - titleWidth - shortcutWidth - 2);
29611
+ const line = [
29612
+ padCell(item.title, titleWidth),
29613
+ padCell(item.description, descriptionWidth),
29614
+ padCell(item.shortcut ?? "", shortcutWidth, "start")
29615
+ ].join(" ");
29616
+ return React.createElement(
29617
+ Text,
29618
+ {
29619
+ color: selected ? "black" : "white",
29620
+ backgroundColor: selected ? "#f4a261" : SURFACE.dialog,
29621
+ bold: selected
29622
+ },
29623
+ padToWidth(line, width)
29624
+ );
29625
+ }
29626
+ function SlashCommandPalette({ palette }) {
29627
+ const width = promptPanelWidth();
29628
+ const contentWidth = Math.max(24, width - 4);
29629
+ return React.createElement(
29630
+ Box,
29631
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "blue", paddingX: 1, marginTop: 1 },
29632
+ ...palette.items.map((item, index) => {
29633
+ const selected = index === palette.cursor;
29634
+ return React.createElement(
29635
+ Text,
29636
+ {
29637
+ key: item.command,
29638
+ color: selected ? "black" : "white",
29639
+ backgroundColor: selected ? "#f4a261" : SURFACE.dialog,
29640
+ bold: selected
29641
+ },
29642
+ slashCommandLine(item, contentWidth)
29643
+ );
29644
+ })
29645
+ );
29646
+ }
29647
+ function slashCommandLine(item, width) {
29648
+ const commandWidth = Math.max(12, Math.min(22, Math.floor(width * 0.34)));
29649
+ const descriptionWidth = Math.max(0, width - commandWidth - 1);
29650
+ return `${padCell(item.command, commandWidth)} ${padCell(item.description, descriptionWidth)}`;
29651
+ }
29652
+ function InputFrame({
29653
+ borderColor,
29654
+ cursorColor,
29655
+ value,
29656
+ placeholder,
29657
+ marginTop = 1
29658
+ }) {
29659
+ const width = promptPanelWidth();
29660
+ const contentWidth = Math.max(18, width - 2);
29661
+ const rows = inputFrameRows(value, placeholder, contentWidth);
29662
+ return React.createElement(
29663
+ Box,
29664
+ {
29665
+ alignSelf: "center",
29666
+ width,
29667
+ flexDirection: "column",
29668
+ borderStyle: "double",
29669
+ borderTopColor: borderColor,
29670
+ borderLeftColor: borderColor,
29671
+ borderRightColor: "gray",
29672
+ borderBottomColor: "gray",
29673
+ marginTop
29674
+ },
29675
+ ...rows.map((row, index) => React.createElement(InputFrameLine, { key: index, row, width: contentWidth, cursorColor }))
29676
+ );
29677
+ }
29678
+ function InputFrameLine({ row, width, cursorColor }) {
29679
+ if (row.cursorBeforeText) {
29680
+ const cursor2 = row.cursor ? React.createElement(CursorCell, { cursorColor }) : React.createElement(Text, { backgroundColor: SURFACE.input }, " ");
29681
+ const textWidth2 = Math.max(0, width - 1);
29682
+ return React.createElement(
27885
29683
  Box,
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
- )
29684
+ null,
29685
+ cursor2,
29686
+ React.createElement(Text, { color: row.placeholder ? "gray" : "white", backgroundColor: SURFACE.input }, padToWidth(row.text, textWidth2))
29687
+ );
29688
+ }
29689
+ const cursor = row.cursor ? React.createElement(CursorCell, { cursorColor }) : null;
29690
+ const textWidth = row.cursor ? Math.max(0, width - 1) : width;
29691
+ const text = fitToWidth(row.text, textWidth);
29692
+ const fillWidth = Math.max(0, textWidth - stringWidth(text));
29693
+ return React.createElement(
29694
+ Box,
29695
+ null,
29696
+ React.createElement(Text, { color: row.placeholder ? "gray" : "white", backgroundColor: SURFACE.input }, text),
29697
+ cursor,
29698
+ React.createElement(Text, { backgroundColor: SURFACE.input }, padToWidth("", fillWidth))
29699
+ );
29700
+ }
29701
+ function CursorCell({ cursorColor }) {
29702
+ return React.createElement(Text, { color: "black", backgroundColor: cursorColor, bold: true }, "\x1B[5m \x1B[25m");
29703
+ }
29704
+ function SessionSelector({ state }) {
29705
+ const items = state.sessionOptions;
29706
+ const width = sessionPanelWidth();
29707
+ const contentWidth = Math.max(36, width - 6);
29708
+ return React.createElement(
29709
+ DialogFrame,
29710
+ { title: "Start Session", accent: "magenta", width },
29711
+ React.createElement(Text, { color: "gray" }, sessionHeaderLine(contentWidth)),
29712
+ ...items.map((item, index) => {
29713
+ const selected = index === state.sessionCursor;
29714
+ const color = selected ? "cyan" : item.kind === "new" ? "green" : item.currentWorkspace ? "white" : "gray";
29715
+ return React.createElement(
29716
+ Text,
29717
+ { key: item.kind === "new" ? "new-session" : item.id, color, bold: selected },
29718
+ sessionOptionLine(item, index, selected, contentWidth)
29719
+ );
29720
+ }),
29721
+ items.length === 0 ? React.createElement(Text, { color: "gray" }, "No saved sessions. Press n to create one.") : null
27895
29722
  );
27896
29723
  }
29724
+ function sessionSelectorColumns(width) {
29725
+ const contentWidth = Math.max(24, width);
29726
+ const compact = contentWidth < 52;
29727
+ const stateWidth = compact ? 6 : 8;
29728
+ const updatedWidth = compact ? 8 : 10;
29729
+ const available = Math.max(0, contentWidth - stateWidth - updatedWidth - 5);
29730
+ const titleMin = Math.min(compact ? 8 : 14, available);
29731
+ const titleMax = compact ? 20 : 42;
29732
+ const titleWidth = Math.min(available, Math.max(titleMin, Math.min(titleMax, Math.floor(available * (compact ? 0.52 : 0.52)))));
29733
+ const workspaceWidth = Math.max(0, available - titleWidth);
29734
+ return { title: titleWidth, state: stateWidth, updated: updatedWidth, workspace: workspaceWidth };
29735
+ }
29736
+ function sessionHeaderLine(width) {
29737
+ const columns = sessionSelectorColumns(width);
29738
+ return sessionSelectorLine(" ", "Session", "State", "Updated", "Workspace", columns);
29739
+ }
29740
+ function sessionOptionLine(item, index, selected, width) {
29741
+ const pointer = selected ? ">" : " ";
29742
+ const columns = sessionSelectorColumns(width);
29743
+ if (item.kind === "new") {
29744
+ return sessionSelectorLine(pointer, "+ New session", "ready", "-", item.cwd ?? "current workspace", columns);
29745
+ }
29746
+ const workspace = item.currentWorkspace ? "current workspace" : item.cwd ?? "-";
29747
+ return sessionSelectorLine(pointer, `${index + 1}. ${item.title}`, item.status, formatSessionAge(item.updatedAt), workspace, columns);
29748
+ }
29749
+ function sessionSelectorLine(pointer, title, state, updated, workspace, columns) {
29750
+ return [
29751
+ `${pointer} ${padCell(title, columns.title)}`,
29752
+ padCell(state, columns.state),
29753
+ padCell(updated, columns.updated),
29754
+ padCell(workspace, columns.workspace, "start")
29755
+ ].join(" ");
29756
+ }
27897
29757
  function ProviderSelector({ state }) {
27898
29758
  const items = state.providerOptions;
27899
29759
  return React.createElement(
@@ -27960,6 +29820,95 @@ function ModelEditor({ state }) {
27960
29820
  state.settingsError ? React.createElement(Text, { color: "yellow" }, state.settingsError) : React.createElement(Text, { color: "gray" }, "enter apply | backspace edit | esc cancel")
27961
29821
  );
27962
29822
  }
29823
+ function ConfigManager({ state }) {
29824
+ const items = state.providerOptions;
29825
+ const selected = items[state.providerCursor];
29826
+ const defaultProvider = state.configDefaultProvider ?? state.status.provider;
29827
+ const contentWidth = Math.max(40, promptPanelWidth() - 8);
29828
+ return React.createElement(
29829
+ DialogFrame,
29830
+ { title: "Config", accent: "magenta" },
29831
+ React.createElement(Text, { color: state.configCreated ? "green" : "gray" }, state.configPath ? `config: ${shorten(state.configPath, contentWidth)}` : "config: ~/.demian/config.json"),
29832
+ state.configMessage ? React.createElement(Text, { color: "cyan" }, state.configMessage) : null,
29833
+ React.createElement(Text, { color: "gray" }, `${"Provider".padEnd(20)} ${"Default model".padEnd(28)} Status`),
29834
+ ...items.map((item, index) => {
29835
+ const isDefault = item.name === defaultProvider;
29836
+ const pointer = index === state.providerCursor ? ">" : " ";
29837
+ const marker = isDefault ? "*" : " ";
29838
+ return React.createElement(
29839
+ Text,
29840
+ { key: item.name, color: index === state.providerCursor ? "cyan" : item.available === false ? "gray" : "white", bold: index === state.providerCursor },
29841
+ `${pointer}${marker} ${shorten(item.label ?? item.name, 18).padEnd(18)} ${shorten((item.modelLabel ?? item.model) || "-", 28).padEnd(28)} ${providerStatusLabel(item)}`
29842
+ );
29843
+ }),
29844
+ selected ? React.createElement(Text, { color: "gray" }, `${selected.type ?? "provider"}${selected.baseURL ? ` | base ${shorten(selected.baseURL, 42)}` : " | base -"}${selected.apiKeyEnv ? ` | env ${selected.apiKeyEnv}` : " | env -"}${selected.auth?.header ? ` | auth ${selected.auth.header}` : ""}`) : null,
29845
+ React.createElement(Text, { color: "gray" }, "enter/d default | m models | b baseURL | e env | h auth | n new model | a add provider | esc close")
29846
+ );
29847
+ }
29848
+ function ConfigModelSelector({ state }) {
29849
+ const provider = state.providerOptions[state.providerCursor];
29850
+ const profiles = provider?.modelProfiles ?? [];
29851
+ return React.createElement(
29852
+ DialogFrame,
29853
+ { title: provider ? `Models: ${provider.name}` : "Models", accent: "magenta" },
29854
+ profiles.length ? React.createElement(Text, { color: "gray" }, `${"Model".padEnd(32)} Source`) : React.createElement(Text, { color: "yellow" }, "No configured or discoverable models. Press n to add one."),
29855
+ ...profiles.slice(0, 12).map(
29856
+ (item, index) => React.createElement(
29857
+ Text,
29858
+ { key: `${item.name}-${item.model}`, color: index === state.configModelCursor ? "cyan" : item.available === false ? "gray" : "white", bold: index === state.configModelCursor },
29859
+ `${index === state.configModelCursor ? ">" : " "} ${shorten(item.label ?? item.displayName ?? item.model, 30).padEnd(30)} ${item.source ?? "config"}`
29860
+ )
29861
+ ),
29862
+ React.createElement(Text, { color: "gray" }, "enter set default model | n add custom model | esc back")
29863
+ );
29864
+ }
29865
+ function ConfigModelInput({ state }) {
29866
+ const provider = state.providerOptions[state.providerCursor];
29867
+ const value = state.configModelInput || "Model ID";
29868
+ return React.createElement(
29869
+ DialogFrame,
29870
+ { title: provider ? `Add model: ${provider.name}` : "Add model", accent: "magenta" },
29871
+ React.createElement(
29872
+ Box,
29873
+ null,
29874
+ React.createElement(Text, { color: "cyan", bold: true }, "> "),
29875
+ React.createElement(Text, { color: state.configModelInput ? "white" : "gray" }, value)
29876
+ ),
29877
+ state.settingsError ? React.createElement(Text, { color: "yellow" }, state.settingsError) : React.createElement(Text, { color: "gray" }, "enter add and make default | backspace edit | esc back")
29878
+ );
29879
+ }
29880
+ function ConfigProviderInput({ state }) {
29881
+ const provider = state.providerOptions[state.providerCursor];
29882
+ const field = state.configProviderInputField ?? "baseURL";
29883
+ const label = field === "baseURL" ? "Base URL" : field === "apiKeyEnv" ? "API key env" : "Auth header";
29884
+ const placeholder = field === "baseURL" ? "empty removes provider baseURL" : field === "apiKeyEnv" ? "empty removes provider apiKeyEnv" : "empty removes custom auth header";
29885
+ const value = state.configProviderInput || placeholder;
29886
+ return React.createElement(
29887
+ DialogFrame,
29888
+ { title: provider ? `Edit ${label}: ${provider.name}` : `Edit ${label}`, accent: "magenta" },
29889
+ React.createElement(
29890
+ Box,
29891
+ null,
29892
+ React.createElement(Text, { color: "cyan", bold: true }, "> "),
29893
+ React.createElement(Text, { color: state.configProviderInput ? "white" : "gray" }, value)
29894
+ ),
29895
+ state.settingsError ? React.createElement(Text, { color: "yellow" }, state.settingsError) : React.createElement(Text, { color: "gray" }, "enter save | backspace edit/clear | esc back")
29896
+ );
29897
+ }
29898
+ function ConfigAddProviderSelector({ state }) {
29899
+ return React.createElement(
29900
+ DialogFrame,
29901
+ { title: "Add Provider", accent: "magenta" },
29902
+ ...state.configProviderPresets.map(
29903
+ (item, index) => React.createElement(
29904
+ Text,
29905
+ { key: item.preset, color: index === state.configAddProviderCursor ? "cyan" : "white", bold: index === state.configAddProviderCursor },
29906
+ `${index === state.configAddProviderCursor ? ">" : " "} ${shorten(item.label, 18).padEnd(18)} ${item.detail ?? item.preset}`
29907
+ )
29908
+ ),
29909
+ React.createElement(Text, { color: "gray" }, "enter add preset | esc back")
29910
+ );
29911
+ }
27963
29912
  function PermissionBar({ state }) {
27964
29913
  const pending = state.permission;
27965
29914
  if (!pending) {
@@ -27974,11 +29923,11 @@ function PermissionBar({ state }) {
27974
29923
  ...permissionShortcutLines().map((line, index) => React.createElement(Text, { key: `shortcut-${index}`, color: "cyan", bold: true }, line))
27975
29924
  );
27976
29925
  }
27977
- function DialogFrame({ title, accent, children }) {
29926
+ function DialogFrame({ title, accent, children, width = dialogPanelWidth() }) {
27978
29927
  return React.createElement(
27979
29928
  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),
29929
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "round", borderColor: accent, paddingX: 2, paddingY: 1, marginTop: 1 },
29930
+ React.createElement(SurfaceLine, { text: title, width: width - 6, backgroundColor: SURFACE.dialog, color: accent, bold: true }),
27982
29931
  React.createElement(Box, { height: 1 }),
27983
29932
  children
27984
29933
  );
@@ -27989,25 +29938,188 @@ function providerStatusLabel(item) {
27989
29938
  if (item.catalog?.status === "unavailable") return "offline";
27990
29939
  return "n/a";
27991
29940
  }
29941
+ function SurfaceLine({ text, width, backgroundColor, color = "white", bold = false }) {
29942
+ return React.createElement(Text, { color, backgroundColor, bold, wrap: "truncate-end" }, padToWidth(text, width));
29943
+ }
29944
+ function inputFrameRows(value, placeholder, width) {
29945
+ const contentWidth = Math.max(8, width);
29946
+ const textWidthWithCursor = Math.max(1, contentWidth - 1);
29947
+ if (!value) {
29948
+ const line = fitToWidth(` ${placeholder}`, textWidthWithCursor);
29949
+ return [
29950
+ { text: line, placeholder: true, cursor: true, cursorBeforeText: true },
29951
+ ...Array.from({ length: 2 }, () => ({ text: "", placeholder: false, cursor: false, cursorBeforeText: false }))
29952
+ ];
29953
+ }
29954
+ const rawLines = value.split(/\n/);
29955
+ const wrapped = rawLines.flatMap((line) => wrapInputLine(line, textWidthWithCursor));
29956
+ const rows = wrapped.map((line) => ({ text: line, placeholder: false, cursor: false, cursorBeforeText: false }));
29957
+ const last = rows.at(-1);
29958
+ if (last) last.cursor = true;
29959
+ else rows.push({ text: "", placeholder: false, cursor: true, cursorBeforeText: false });
29960
+ while (rows.length < 3) rows.push({ text: "", placeholder: false, cursor: false, cursorBeforeText: false });
29961
+ return rows.slice(-6);
29962
+ }
29963
+ function wrapInputLine(value, width) {
29964
+ const target = Math.max(1, width);
29965
+ if (value === "") return [""];
29966
+ const lines = [];
29967
+ let current = "";
29968
+ for (const char of Array.from(value)) {
29969
+ if (stringWidth(current) + stringWidth(char) > target && current) {
29970
+ lines.push(current);
29971
+ current = char;
29972
+ } else {
29973
+ current += char;
29974
+ }
29975
+ }
29976
+ lines.push(current);
29977
+ return lines;
29978
+ }
29979
+ function wrapBlockLines(lines, width, maxLines = Number.POSITIVE_INFINITY) {
29980
+ const target = Math.max(8, width);
29981
+ const wrapped = [];
29982
+ for (const line of normalizedBlockLines(lines)) {
29983
+ if (wrapped.length >= maxLines) break;
29984
+ const chunks = wrapDisplayLine(line, target);
29985
+ for (const chunk of chunks) {
29986
+ if (wrapped.length >= maxLines) break;
29987
+ wrapped.push(chunk);
29988
+ }
29989
+ }
29990
+ return wrapped.length > 0 ? wrapped : [""];
29991
+ }
29992
+ function wrapDisplayLine(value, width) {
29993
+ const target = Math.max(1, width);
29994
+ if (value === "") return [""];
29995
+ if (shouldTruncateDisplayLine(value)) return [fitToWidth(value, target)];
29996
+ const chars = Array.from(value);
29997
+ const lines = [];
29998
+ let start = 0;
29999
+ while (start < chars.length) {
30000
+ let current = "";
30001
+ let end = start;
30002
+ let breakAt = -1;
30003
+ for (let index = start; index < chars.length; index += 1) {
30004
+ const next = chars[index] ?? "";
30005
+ if (stringWidth(current) + stringWidth(next) > target && current) break;
30006
+ current += next;
30007
+ end = index + 1;
30008
+ if (isSoftWrapBreak(next)) breakAt = end;
30009
+ }
30010
+ if (end >= chars.length) {
30011
+ lines.push(current);
30012
+ break;
30013
+ }
30014
+ if (breakAt > start && stringWidth(chars.slice(start, breakAt).join("").trimEnd()) >= Math.floor(target * 0.45)) {
30015
+ const line = chars.slice(start, breakAt).join("").trimEnd();
30016
+ lines.push(line || current);
30017
+ start = breakAt;
30018
+ while (chars[start] === " ") start += 1;
30019
+ } else {
30020
+ lines.push(current);
30021
+ start = end;
30022
+ }
30023
+ }
30024
+ return lines;
30025
+ }
30026
+ function shouldTruncateDisplayLine(value) {
30027
+ return /^[╭╰╮╯│]/u.test(value.trimStart());
30028
+ }
30029
+ function isSoftWrapBreak(value) {
30030
+ return /\s/u.test(value) || value === "/" || value === "-";
30031
+ }
30032
+ function padToWidth(value, width) {
30033
+ const target = Math.max(0, width);
30034
+ const current = stringWidth(value);
30035
+ if (current >= target) return value;
30036
+ return `${value}${" ".repeat(target - current)}`;
30037
+ }
30038
+ function padCell(value, width, truncate2 = "end") {
30039
+ const text = truncate2 === "start" ? truncateStartToWidth(value, width) : truncateEndToWidth(value, width);
30040
+ return padToWidth(text, width);
30041
+ }
30042
+ function fitToWidth(value, width) {
30043
+ const target = Math.max(0, width);
30044
+ if (stringWidth(value) <= target) return value;
30045
+ let current = "";
30046
+ for (const char of Array.from(value)) {
30047
+ if (stringWidth(current) + stringWidth(char) > target) break;
30048
+ current += char;
30049
+ }
30050
+ return current;
30051
+ }
30052
+ function truncateEndToWidth(value, width) {
30053
+ const target = Math.max(0, width);
30054
+ if (stringWidth(value) <= target) return value;
30055
+ const marker = "\u2026";
30056
+ const markerWidth = stringWidth(marker);
30057
+ if (target <= markerWidth) return fitToWidth(marker, target);
30058
+ return `${fitToWidth(value, target - markerWidth)}${marker}`;
30059
+ }
30060
+ function truncateStartToWidth(value, width) {
30061
+ const target = Math.max(0, width);
30062
+ if (stringWidth(value) <= target) return value;
30063
+ const marker = "\u2026";
30064
+ const markerWidth = stringWidth(marker);
30065
+ if (target <= markerWidth) return fitToWidth(marker, target);
30066
+ let current = "";
30067
+ for (const char of Array.from(value).reverse()) {
30068
+ if (stringWidth(current) + stringWidth(char) > target - markerWidth) break;
30069
+ current = `${char}${current}`;
30070
+ }
30071
+ return `${marker}${current}`;
30072
+ }
27992
30073
  function shorten(value, max) {
27993
30074
  if (value.length <= max) return value;
27994
30075
  return `\u2026${value.slice(value.length - max + 1)}`;
27995
30076
  }
27996
30077
  function terminalHeight() {
27997
30078
  const rows = process.stdout.rows;
27998
- return typeof rows === "number" && Number.isFinite(rows) && rows > 0 ? Math.max(18, rows - 1) : 24;
30079
+ return typeof rows === "number" && Number.isFinite(rows) && rows > 0 ? Math.max(12, rows - 3) : 24;
27999
30080
  }
28000
30081
  function terminalWidth() {
28001
30082
  const columns = process.stdout.columns;
28002
30083
  return typeof columns === "number" && Number.isFinite(columns) && columns > 0 ? Math.max(48, columns) : 100;
28003
30084
  }
28004
30085
  function promptPanelWidth() {
28005
- const columns = terminalWidth();
28006
- return Math.max(40, Math.min(96, columns - 8, Math.floor(columns * 0.86)));
30086
+ return responsivePanelWidth({ min: 40, max: 96, margin: 8, ratio: 0.86 });
30087
+ }
30088
+ function statusSurfaceWidth() {
30089
+ return responsivePanelWidth({ min: 36, max: 112, margin: 8, ratio: 0.95 });
30090
+ }
30091
+ function transcriptPanelWidth() {
30092
+ return responsivePanelWidth({ min: 48, max: 118, margin: 8, ratio: 0.82 });
30093
+ }
30094
+ function userMessageBubbleWidth(lines, columns = terminalWidth()) {
30095
+ const available = Math.max(18, columns - 4);
30096
+ const maxWidth = Math.max(18, Math.min(84, available, Math.floor(columns * 0.68)));
30097
+ const minWidth = Math.min(maxWidth, 18);
30098
+ const contentWidth = Math.max(0, ...normalizedBlockLines(lines).map((line) => stringWidth(line)));
30099
+ return Math.max(minWidth, Math.min(maxWidth, contentWidth + 4));
30100
+ }
30101
+ function normalizedBlockLines(lines) {
30102
+ const normalized = lines.flatMap((line) => String(line ?? "").split(/\r?\n/));
30103
+ return normalized.length > 0 ? normalized : [""];
28007
30104
  }
28008
30105
  function dialogPanelWidth() {
30106
+ return responsivePanelWidth({ min: 52, max: 98, margin: 10, ratio: 0.82 });
30107
+ }
30108
+ function sessionPanelWidth() {
30109
+ return responsivePanelWidth({ min: 72, max: 118, margin: 6, ratio: 0.92 });
30110
+ }
30111
+ function commandPalettePanelWidth() {
30112
+ return responsivePanelWidth({ min: 56, max: 88, margin: 10, ratio: 0.74 });
30113
+ }
30114
+ function commandPaletteMaxRows() {
30115
+ return Math.max(6, Math.min(16, terminalHeight() - 12));
30116
+ }
30117
+ function responsivePanelWidth({ min, max, margin, ratio }) {
28009
30118
  const columns = terminalWidth();
28010
- return Math.max(48, Math.min(92, columns - 10, Math.floor(columns * 0.78)));
30119
+ const available = Math.max(24, columns - margin);
30120
+ const preferred = Math.min(max, Math.max(min, Math.floor(columns * ratio)));
30121
+ const minimumWithinTerminal = Math.min(min, available);
30122
+ return Math.max(minimumWithinTerminal, Math.min(preferred, available));
28011
30123
  }
28012
30124
  function formatTokens(value) {
28013
30125
  const tokens = typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0;
@@ -28022,13 +30134,120 @@ function clamp01(value) {
28022
30134
  if (!Number.isFinite(value)) return 0;
28023
30135
  return Math.max(0, Math.min(1, value));
28024
30136
  }
28025
- var DEMIAN_WORDMARK_LINES;
30137
+ var SURFACE, CSI_U_INPUT, XTERM_MODIFIED_ENTER_INPUT, SLASH_COMMANDS, PromptDraftController, SessionMetaBox, DEMIAN_WORDMARK_LINES, DemianWordmark, TranscriptView, BlockView, CommandGuide;
28026
30138
  var init_app = __esm({
28027
30139
  "src/ui/tui/app.ts"() {
28028
30140
  "use strict";
30141
+ init_string_width();
28029
30142
  init_store();
28030
30143
  init_presets();
28031
30144
  init_tool_summary();
30145
+ SURFACE = {
30146
+ input: "#050505",
30147
+ status: "#101820",
30148
+ dialog: "#15121e"
30149
+ };
30150
+ CSI_U_INPUT = /^\x1b?\[(\d+)(?:;(\d+))?u$/;
30151
+ XTERM_MODIFIED_ENTER_INPUT = /^\x1b?\[27;([2-4]);13~$/;
30152
+ SLASH_COMMANDS = [
30153
+ { command: "/cowork", insert: "/cowork", argument: true, mode: "prompt", description: "Run cowork sub agents on a task", aliases: ["agent", "parallel", "review"] },
30154
+ { command: "/compact", insert: "/compact", mode: "prompt", description: "Compact current conversation context", aliases: ["context", "compress"] },
30155
+ { command: "/retry", insert: "/retry", mode: "prompt", description: "Retry the last task", aliases: ["again", "rerun"] },
30156
+ { command: "/session", insert: "/session", argument: true, mode: "prompt", description: "Manage saved conversations", aliases: ["sessions", "conversation", "conv"] },
30157
+ { command: "/session list", insert: "/session list", mode: "prompt", description: "List saved conversations", aliases: ["sessions", "ls"] },
30158
+ { command: "/session new", insert: "/session new", argument: true, mode: "prompt", description: "Create a new conversation", aliases: ["new", "create"] },
30159
+ { command: "/session switch", insert: "/session switch", argument: true, mode: "prompt", description: "Switch to a saved conversation", aliases: ["open", "select", "use"] },
30160
+ { command: "/session rename", insert: "/session rename", argument: true, mode: "prompt", description: "Rename the current conversation", aliases: ["name"] },
30161
+ { command: "/session delete", insert: "/session delete", argument: true, mode: "prompt", description: "Delete a saved conversation", aliases: ["remove", "rm"] },
30162
+ { command: "/goal", insert: "/goal", argument: true, mode: "prompt", description: "Start or inspect a goal loop", aliases: ["task", "objective"] },
30163
+ { command: "/goal status", insert: "/goal status", mode: "prompt", description: "Show active goal status", aliases: ["status"] },
30164
+ { command: "/goal clear", insert: "/goal clear", mode: "prompt", description: "Clear visible goal state", aliases: ["reset"] },
30165
+ { command: "/ralph-loop", insert: "/ralph-loop", argument: true, mode: "prompt", description: "Start a goal loop with Ralph mode", aliases: ["ralph"] },
30166
+ { command: "/cancel-ralph", insert: "/cancel-ralph", mode: "prompt", description: "Pause the active Ralph loop", aliases: ["cancel", "pause"] },
30167
+ { command: "/stop", insert: "/stop", mode: "running", description: "Stop the running task", aliases: ["cancel"] },
30168
+ { command: "/exit", insert: "/exit", mode: "both", description: "Exit Demian", aliases: ["quit", "close"] },
30169
+ { command: "/quit", insert: "/quit", mode: "both", description: "Exit Demian", aliases: ["exit", "close"] }
30170
+ ];
30171
+ PromptDraftController = class {
30172
+ #snapshot;
30173
+ #listeners = /* @__PURE__ */ new Set();
30174
+ constructor(initialValue) {
30175
+ this.#snapshot = {
30176
+ value: initialValue,
30177
+ slashCommandCursor: 0
30178
+ };
30179
+ }
30180
+ snapshot() {
30181
+ return this.#snapshot;
30182
+ }
30183
+ value() {
30184
+ return this.#snapshot.value;
30185
+ }
30186
+ subscribe(listener) {
30187
+ this.#listeners.add(listener);
30188
+ return () => {
30189
+ this.#listeners.delete(listener);
30190
+ };
30191
+ }
30192
+ setValue(value) {
30193
+ const sameValue = value === this.#snapshot.value;
30194
+ const slashCommandClosedPrefix = this.#snapshot.slashCommandClosedPrefix && isSlashCommandClosedForPrompt(value, this.#snapshot.slashCommandClosedPrefix) ? this.#snapshot.slashCommandClosedPrefix : void 0;
30195
+ this.#set({
30196
+ value,
30197
+ slashCommandCursor: sameValue ? this.#snapshot.slashCommandCursor : 0,
30198
+ slashCommandClosedPrefix
30199
+ });
30200
+ }
30201
+ append(value) {
30202
+ if (!value) return;
30203
+ this.setValue(`${this.#snapshot.value}${value}`);
30204
+ }
30205
+ backspace() {
30206
+ this.setValue(Array.from(this.#snapshot.value).slice(0, -1).join(""));
30207
+ }
30208
+ closeSlashCommandsForCurrentPrompt() {
30209
+ this.#set({
30210
+ ...this.#snapshot,
30211
+ slashCommandCursor: 0,
30212
+ slashCommandClosedPrefix: this.#snapshot.value
30213
+ });
30214
+ }
30215
+ moveSlashCommandCursor(delta, count) {
30216
+ if (count <= 0) return;
30217
+ this.#set({
30218
+ ...this.#snapshot,
30219
+ slashCommandCursor: (this.#snapshot.slashCommandCursor + delta + count) % count
30220
+ });
30221
+ }
30222
+ #set(next) {
30223
+ if (next.value === this.#snapshot.value && next.slashCommandCursor === this.#snapshot.slashCommandCursor && next.slashCommandClosedPrefix === this.#snapshot.slashCommandClosedPrefix) {
30224
+ return;
30225
+ }
30226
+ this.#snapshot = next;
30227
+ for (const listener of this.#listeners) listener();
30228
+ }
30229
+ };
30230
+ SessionMetaBox = React.memo(function SessionMetaBox2({ state, version, marginTop = 1 }) {
30231
+ const width = promptPanelWidth();
30232
+ const compact = width < 68;
30233
+ const agent = shorten(state.selectedAgent ?? state.status.agent ?? "general", compact ? 14 : 18);
30234
+ const model = shorten(state.selection?.model ?? state.status.model ?? "model", compact ? Math.max(18, width - 14) : 30);
30235
+ const workspace = shorten(state.status.cwd ?? process.cwd(), compact ? Math.max(18, width - 18) : 46);
30236
+ const rows = compact ? [
30237
+ `agent ${agent} \xB7 perm ${state.permissionPreset}`,
30238
+ `model ${model}`,
30239
+ `workspace ${workspace}`,
30240
+ version ? `version v${version}` : void 0
30241
+ ].filter(Boolean) : [
30242
+ `agent ${agent} \xB7 model ${model} \xB7 perm ${state.permissionPreset}`,
30243
+ `workspace ${workspace}${version ? ` \xB7 version v${version}` : ""}`
30244
+ ];
30245
+ return React.createElement(
30246
+ Box,
30247
+ { alignSelf: "center", width, flexDirection: "column", borderStyle: "single", borderColor: "#334155", paddingX: 1, marginTop },
30248
+ ...rows.map((line, index) => React.createElement(SurfaceLine, { key: index, text: line ?? "", width: width - 4, backgroundColor: SURFACE.status, color: "white" }))
30249
+ );
30250
+ });
28032
30251
  DEMIAN_WORDMARK_LINES = [
28033
30252
  " _ _ ",
28034
30253
  " __| | ___ _ __ ___ (_) __ _ _ __ ",
@@ -28036,6 +30255,56 @@ var init_app = __esm({
28036
30255
  "| (_| | __/ | | | | | | (_| | | | |",
28037
30256
  " \\__,_|\\___|_| |_| |_|_|\\__,_|_| |_|"
28038
30257
  ];
30258
+ DemianWordmark = React.memo(function DemianWordmark2() {
30259
+ if (terminalWidth() < 58) {
30260
+ return React.createElement(Box, { alignSelf: "center" }, React.createElement(Text, { color: "gray", bold: true }, "DEMIAN"));
30261
+ }
30262
+ return React.createElement(
30263
+ Box,
30264
+ { alignSelf: "center", flexDirection: "column" },
30265
+ ...DEMIAN_WORDMARK_LINES.map(
30266
+ (line, index) => React.createElement(Text, { key: index, color: index < 2 ? "gray" : "white", bold: true }, line)
30267
+ )
30268
+ );
30269
+ });
30270
+ TranscriptView = React.memo(function TranscriptView2({ state }) {
30271
+ const blocks = state.blocks.slice(-12);
30272
+ const streaming = !state.streamingFinalized && state.streamingText ? { kind: "assistant", title: "Assistant streaming", lines: streamingLines(state.streamingText) } : void 0;
30273
+ const width = transcriptPanelWidth();
30274
+ const contentWidth = Math.max(24, width - 2);
30275
+ return React.createElement(
30276
+ Box,
30277
+ { alignSelf: "center", width, flexDirection: "column", marginTop: 1 },
30278
+ ...blocks.map((block) => React.createElement(BlockView, { key: block.id, block, width: contentWidth })),
30279
+ streaming ? React.createElement(BlockView, { key: "streaming", block: streaming, width: contentWidth }) : null
30280
+ );
30281
+ });
30282
+ BlockView = React.memo(function BlockView2({ block, width }) {
30283
+ if (block.kind === "tool") return React.createElement(ToolBlockView, { block, width });
30284
+ if (block.kind === "goal-work") return React.createElement(GoalWorkBlockView, { block, width });
30285
+ if (block.kind === "cowork") return React.createElement(CoworkBlockView, { block, width });
30286
+ if (block.kind === "user") return React.createElement(UserBlockView, { block, width });
30287
+ const color = block.kind === "user" ? "green" : block.kind === "assistant" ? "white" : block.kind === "warning" ? "yellow" : "magenta";
30288
+ const lines = wrapBlockLines(block.lines, width, 80);
30289
+ return React.createElement(
30290
+ Box,
30291
+ { flexDirection: "column", marginBottom: 1 },
30292
+ React.createElement(Text, { color, bold: true }, fitToWidth(block.title, width)),
30293
+ ...lines.map((line, index) => React.createElement(Text, { key: index }, line || " "))
30294
+ );
30295
+ }, sameBlockViewProps);
30296
+ CommandGuide = React.memo(function CommandGuide2({ hint, color, error }) {
30297
+ return React.createElement(
30298
+ Box,
30299
+ { alignSelf: "center", width: promptPanelWidth(), flexDirection: "column", marginTop: 1 },
30300
+ error ? React.createElement(Text, { color: "yellow" }, error) : null,
30301
+ React.createElement(
30302
+ Box,
30303
+ { justifyContent: "flex-end" },
30304
+ React.createElement(Text, { color }, hint)
30305
+ )
30306
+ );
30307
+ });
28039
30308
  }
28040
30309
  });
28041
30310
 
@@ -32056,8 +34325,8 @@ async function summarizeGoalTitleWithProvider(options) {
32056
34325
  }
32057
34326
  function normalizeGoalTitle(value, objective) {
32058
34327
  const raw = typeof value === "string" ? value : "";
32059
- const firstLine = raw.split(/\r?\n/).find((line) => line.trim()) ?? "";
32060
- const cleaned = stripDecorations(firstLine);
34328
+ const firstLine2 = raw.split(/\r?\n/).find((line) => line.trim()) ?? "";
34329
+ const cleaned = stripDecorations(firstLine2);
32061
34330
  return truncateGoalTitle(cleaned || fallbackGoalTitle(objective));
32062
34331
  }
32063
34332
  function fallbackGoalTitle(objective) {
@@ -32763,17 +35032,17 @@ function findDependencyCycle(group) {
32763
35032
  const byId = new Map(group.map((member) => [member.memberId, member]));
32764
35033
  const visiting = /* @__PURE__ */ new Set();
32765
35034
  const visited = /* @__PURE__ */ new Set();
32766
- const path36 = [];
35035
+ const path37 = [];
32767
35036
  const visit = (id) => {
32768
- if (visiting.has(id)) return [...path36.slice(path36.indexOf(id)), id];
35037
+ if (visiting.has(id)) return [...path37.slice(path37.indexOf(id)), id];
32769
35038
  if (visited.has(id)) return void 0;
32770
35039
  visiting.add(id);
32771
- path36.push(id);
35040
+ path37.push(id);
32772
35041
  for (const dep of byId.get(id)?.dependsOn ?? []) {
32773
35042
  const cycle = visit(dep);
32774
35043
  if (cycle) return cycle;
32775
35044
  }
32776
- path36.pop();
35045
+ path37.pop();
32777
35046
  visiting.delete(id);
32778
35047
  visited.add(id);
32779
35048
  return void 0;
@@ -34073,6 +36342,9 @@ function providerOptions(config, catalogs = {}) {
34073
36342
  permissionMode: provider.type === "claudecode" ? provider.permissionMode : void 0,
34074
36343
  ga: provider.type === "claudecode" ? claudeCodeGaEnabled(provider) : void 0,
34075
36344
  available,
36345
+ baseURL: typeof provider.baseURL === "string" ? provider.baseURL : void 0,
36346
+ apiKeyEnv: typeof provider.apiKeyEnv === "string" ? provider.apiKeyEnv : void 0,
36347
+ auth: sanitizeProviderAuth(provider.auth),
34076
36348
  catalog: catalog ? { status: catalog.status, source: catalog.source, message: catalog.message } : void 0
34077
36349
  };
34078
36350
  }).sort((left, right) => {
@@ -34143,6 +36415,13 @@ function providerCatalogAvailable(catalog) {
34143
36415
  function unavailableLabel(value) {
34144
36416
  return `(n/a) ${value}`;
34145
36417
  }
36418
+ function sanitizeProviderAuth(auth) {
36419
+ if (!auth || typeof auth !== "object" || Array.isArray(auth)) return void 0;
36420
+ const record = auth;
36421
+ const type = typeof record.type === "string" ? record.type : void 0;
36422
+ const header = typeof record.header === "string" ? record.header : void 0;
36423
+ return type || header ? { type, header } : void 0;
36424
+ }
34146
36425
  function errorMessage4(error) {
34147
36426
  return error instanceof Error ? error.message : String(error);
34148
36427
  }
@@ -34157,18 +36436,230 @@ var init_settings = __esm({
34157
36436
  }
34158
36437
  });
34159
36438
 
36439
+ // src/ui/conversations.ts
36440
+ import { mkdir as mkdir15, readFile as readFile18, readdir as readdir4, rm as rm4, writeFile as writeFile15 } from "node:fs/promises";
36441
+ import path35 from "node:path";
36442
+ function createConversationRecord(input2) {
36443
+ const timestamp = input2.now ?? Date.now();
36444
+ return {
36445
+ id: sanitizeConversationId(input2.id),
36446
+ title: input2.title?.trim() || "New session",
36447
+ createdAt: timestamp,
36448
+ updatedAt: timestamp,
36449
+ cwd: path35.resolve(input2.cwd),
36450
+ modelHistory: cleanModelHistory(input2.history ?? []),
36451
+ snapshot: input2.snapshot
36452
+ };
36453
+ }
36454
+ async function loadConversationIndex(storageDir = defaultDemianStorageDir()) {
36455
+ const index = await readJson(path35.join(storageDir, CONVERSATION_INDEX_FILE));
36456
+ const conversations = Array.isArray(index?.conversations) ? index.conversations.map((item) => {
36457
+ if (!item || typeof item !== "object") return void 0;
36458
+ const object2 = item;
36459
+ const id = typeof object2.id === "string" ? sanitizeConversationId(object2.id) : "";
36460
+ if (!id) return void 0;
36461
+ return {
36462
+ id,
36463
+ title: typeof object2.title === "string" && object2.title.trim() ? object2.title.trim() : "New session",
36464
+ updatedAt: numberOrNow(object2.updatedAt),
36465
+ cwd: typeof object2.cwd === "string" ? object2.cwd : void 0
36466
+ };
36467
+ }).filter((item) => Boolean(item)) : [];
36468
+ return {
36469
+ selectedSessionId: typeof index?.selectedSessionId === "string" ? sanitizeConversationId(index.selectedSessionId) : void 0,
36470
+ conversations
36471
+ };
36472
+ }
36473
+ async function loadConversationRecords(storageDir = defaultDemianStorageDir()) {
36474
+ const index = await loadConversationIndex(storageDir);
36475
+ const ids = new Set(index.conversations.map((item) => item.id));
36476
+ for (const id of await listConversationRecordIds(storageDir)) ids.add(id);
36477
+ const records = (await Promise.all(
36478
+ [...ids].map(async (id) => {
36479
+ const item = await readJson(conversationRecordPath(storageDir, id));
36480
+ return normalizeConversationRecord(item, id);
36481
+ })
36482
+ )).filter((item) => Boolean(item));
36483
+ records.sort((a, b2) => b2.updatedAt - a.updatedAt);
36484
+ return { selectedSessionId: index.selectedSessionId, records };
36485
+ }
36486
+ async function saveConversationRecords(storageDir, records, selectedSessionId) {
36487
+ const root = storageDir ?? defaultDemianStorageDir();
36488
+ const kept = [...records].sort((a, b2) => b2.updatedAt - a.updatedAt).slice(0, MAX_STORED_CONVERSATIONS).map((record) => ({ ...record, id: sanitizeConversationId(record.id), modelHistory: cleanModelHistory(record.modelHistory) }));
36489
+ await mkdir15(path35.join(root, CONVERSATIONS_DIR), { recursive: true });
36490
+ for (const record of kept) {
36491
+ await writeJson(conversationRecordPath(root, record.id), {
36492
+ version: 1,
36493
+ id: record.id,
36494
+ title: record.title,
36495
+ createdAt: record.createdAt,
36496
+ updatedAt: record.updatedAt,
36497
+ cwd: record.cwd,
36498
+ modelHistory: record.modelHistory,
36499
+ snapshot: record.snapshot
36500
+ });
36501
+ }
36502
+ await writeJson(path35.join(root, CONVERSATION_INDEX_FILE), {
36503
+ version: 1,
36504
+ selectedSessionId,
36505
+ conversations: kept.map((record) => ({
36506
+ id: record.id,
36507
+ title: record.title,
36508
+ updatedAt: record.updatedAt,
36509
+ cwd: record.cwd,
36510
+ path: conversationDir(root, record.id)
36511
+ }))
36512
+ });
36513
+ }
36514
+ async function deleteConversationRecord(storageDir, id) {
36515
+ const root = storageDir ?? defaultDemianStorageDir();
36516
+ await rm4(conversationDir(root, id), { recursive: true, force: true });
36517
+ }
36518
+ function findConversation(records, target) {
36519
+ const value = target?.trim();
36520
+ if (!value) return void 0;
36521
+ const index = Number(value);
36522
+ if (Number.isInteger(index) && index >= 1 && index <= records.length) return records[index - 1];
36523
+ const normalized = value.toLowerCase();
36524
+ return records.find((record) => record.id === value || record.id.startsWith(value)) ?? records.find((record) => record.title.toLowerCase() === normalized) ?? records.find((record) => record.title.toLowerCase().includes(normalized));
36525
+ }
36526
+ function sortConversationRecords(records, cwd) {
36527
+ const resolvedCwd = path35.resolve(cwd);
36528
+ return [...records].sort((a, b2) => {
36529
+ const aCurrent = isConversationForCwd(a, resolvedCwd) ? 0 : 1;
36530
+ const bCurrent = isConversationForCwd(b2, resolvedCwd) ? 0 : 1;
36531
+ if (aCurrent !== bCurrent) return aCurrent - bCurrent;
36532
+ const statusDelta = conversationStatusRank(conversationRuntimeStatus(a, resolvedCwd)) - conversationStatusRank(conversationRuntimeStatus(b2, resolvedCwd));
36533
+ if (statusDelta !== 0) return statusDelta;
36534
+ return b2.updatedAt - a.updatedAt;
36535
+ });
36536
+ }
36537
+ function conversationRuntimeStatus(record, cwd) {
36538
+ if (record.snapshot?.inputMode === "running") return "running";
36539
+ return isConversationForCwd(record, cwd) ? "ready" : "stored";
36540
+ }
36541
+ function isConversationForCwd(record, cwd) {
36542
+ return path35.resolve(record.cwd) === path35.resolve(cwd);
36543
+ }
36544
+ function conversationSummaryLine(record, index, activeId) {
36545
+ const active = record.id === activeId ? "*" : " ";
36546
+ const age = formatRelativeAge(Date.now() - record.updatedAt);
36547
+ return `${active} ${index + 1}. ${record.title} ${record.id} ${age} ${record.cwd}`;
36548
+ }
36549
+ function conversationStatusRank(status) {
36550
+ if (status === "running") return 0;
36551
+ if (status === "ready") return 1;
36552
+ return 2;
36553
+ }
36554
+ function titleFromHistory(history, fallback = "New session") {
36555
+ const firstUser = history.find((message) => message.role === "user" && typeof message.content === "string");
36556
+ return firstLine(firstUser?.content ?? fallback);
36557
+ }
36558
+ function cleanModelHistory(history) {
36559
+ return history.filter((message) => !isInternalModelHistoryMessage(message)).slice(-MAX_MODEL_HISTORY);
36560
+ }
36561
+ function conversationDir(storageDir, id) {
36562
+ return path35.join(storageDir, CONVERSATIONS_DIR, sanitizeConversationId(id));
36563
+ }
36564
+ function conversationRecordPath(storageDir, id) {
36565
+ return path35.join(conversationDir(storageDir, id), CONVERSATION_FILE);
36566
+ }
36567
+ async function listConversationRecordIds(storageDir) {
36568
+ try {
36569
+ const entries = await readdir4(path35.join(storageDir, CONVERSATIONS_DIR), { withFileTypes: true });
36570
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => sanitizeConversationId(entry.name)).filter(Boolean);
36571
+ } catch {
36572
+ return [];
36573
+ }
36574
+ }
36575
+ async function readJson(filePath) {
36576
+ try {
36577
+ return JSON.parse(await readFile18(filePath, "utf8"));
36578
+ } catch {
36579
+ return void 0;
36580
+ }
36581
+ }
36582
+ async function writeJson(filePath, value) {
36583
+ await mkdir15(path35.dirname(filePath), { recursive: true });
36584
+ await writeFile15(filePath, `${JSON.stringify(value, null, 2)}
36585
+ `, "utf8");
36586
+ }
36587
+ function normalizeConversationRecord(value, fallbackId) {
36588
+ if (!value || typeof value !== "object") return void 0;
36589
+ const object2 = value;
36590
+ const id = sanitizeConversationId(typeof object2.id === "string" ? object2.id : fallbackId);
36591
+ if (!id) return void 0;
36592
+ const history = Array.isArray(object2.modelHistory) ? object2.modelHistory.filter(isMessage) : [];
36593
+ const snapshot = object2.snapshot && typeof object2.snapshot === "object" && !Array.isArray(object2.snapshot) ? object2.snapshot : void 0;
36594
+ return {
36595
+ id,
36596
+ title: typeof object2.title === "string" && object2.title.trim() ? object2.title.trim() : titleFromHistory(history),
36597
+ createdAt: numberOrNow(object2.createdAt),
36598
+ updatedAt: numberOrNow(object2.updatedAt),
36599
+ cwd: typeof object2.cwd === "string" && object2.cwd.trim() ? object2.cwd : process.cwd(),
36600
+ modelHistory: cleanModelHistory(history),
36601
+ snapshot
36602
+ };
36603
+ }
36604
+ function isMessage(value) {
36605
+ if (!value || typeof value !== "object") return false;
36606
+ const message = value;
36607
+ if (message.role === "user") return typeof message.content === "string" || Array.isArray(message.content);
36608
+ if (message.role === "assistant") return message.content === void 0 || message.content === null || typeof message.content === "string" || Array.isArray(message.toolCalls);
36609
+ if (message.role === "tool") return typeof message.toolCallId === "string" && typeof message.name === "string" && typeof message.content === "string";
36610
+ if (message.role === "system") return typeof message.content === "string";
36611
+ return false;
36612
+ }
36613
+ function isInternalModelHistoryMessage(message) {
36614
+ return message.role === "user" && typeof message.content === "string" && /^You are running as sub agent:/i.test(message.content.trim());
36615
+ }
36616
+ function sanitizeConversationId(id) {
36617
+ return String(id || "conversation").replace(/[^a-zA-Z0-9._-]+/g, "_") || "conversation";
36618
+ }
36619
+ function numberOrNow(value) {
36620
+ return typeof value === "number" && Number.isFinite(value) ? value : Date.now();
36621
+ }
36622
+ function firstLine(value) {
36623
+ const line = value.replace(/\s+/g, " ").trim();
36624
+ if (!line) return "New session";
36625
+ return line.length > 60 ? `${line.slice(0, 57)}...` : line;
36626
+ }
36627
+ function formatRelativeAge(ms2) {
36628
+ const seconds = Math.max(0, Math.floor(ms2 / 1e3));
36629
+ if (seconds < 60) return `${seconds}s ago`;
36630
+ const minutes = Math.floor(seconds / 60);
36631
+ if (minutes < 60) return `${minutes}m ago`;
36632
+ const hours = Math.floor(minutes / 60);
36633
+ if (hours < 48) return `${hours}h ago`;
36634
+ return `${Math.floor(hours / 24)}d ago`;
36635
+ }
36636
+ var CONVERSATIONS_DIR, CONVERSATION_INDEX_FILE, CONVERSATION_FILE, MAX_STORED_CONVERSATIONS, MAX_MODEL_HISTORY;
36637
+ var init_conversations = __esm({
36638
+ "src/ui/conversations.ts"() {
36639
+ "use strict";
36640
+ init_transcript();
36641
+ CONVERSATIONS_DIR = "conversations";
36642
+ CONVERSATION_INDEX_FILE = "conversations.json";
36643
+ CONVERSATION_FILE = "conversation.json";
36644
+ MAX_STORED_CONVERSATIONS = 40;
36645
+ MAX_MODEL_HISTORY = 80;
36646
+ }
36647
+ });
36648
+
34160
36649
  // src/ui/tui/controller.ts
34161
36650
  var controller_exports = {};
34162
36651
  __export(controller_exports, {
34163
36652
  runTuiSession: () => runTuiSession
34164
36653
  });
34165
- import path35 from "node:path";
36654
+ import path36 from "node:path";
36655
+ import { existsSync as existsSync5 } from "node:fs";
34166
36656
  async function runTuiSession(flags, store) {
34167
- const cwd = path35.resolve(flags.cwd ?? process.cwd());
36657
+ const cwd = path36.resolve(flags.cwd ?? process.cwd());
34168
36658
  const eventBus = new EventBus();
34169
36659
  eventBus.subscribe((event) => store.handleEvent(event));
34170
- const loadedConfig = await loadConfig({ cwd, configPath: flags.configPath });
34171
- const config = flags.context ? mergeConfig(loadedConfig, {
36660
+ const configInit = await ensureTuiConfigExists(flags);
36661
+ let loadedConfig = await loadConfig({ cwd, configPath: flags.configPath });
36662
+ let config = flags.context ? mergeConfig(loadedConfig, {
34172
36663
  context: {
34173
36664
  main: flags.context
34174
36665
  }
@@ -34188,6 +36679,10 @@ async function runTuiSession(flags, store) {
34188
36679
  store.configureSettings(initialSelection, await providerOptionsWithCatalog(config, { timeoutMs: 1500 }), {
34189
36680
  agent: agentName,
34190
36681
  cwd,
36682
+ configPath: configMutationPath(flags),
36683
+ configCreated: configInit.created,
36684
+ configDefaultProvider: config.defaultProvider,
36685
+ configMessage: configInit.created ? `Created config at ${configInit.path}` : void 0,
34191
36686
  agentOptions,
34192
36687
  contextEfficiency: {
34193
36688
  maxContextTokens: config.context.main.maxContextTokens,
@@ -34196,10 +36691,21 @@ async function runTuiSession(flags, store) {
34196
36691
  }
34197
36692
  });
34198
36693
  let nextPrompt = flags.prompt;
34199
- let history = flags.initialHistory ? [...flags.initialHistory] : [];
36694
+ let openConfigOnFirstPrompt = configInit.created && !flags.prompt.trim();
36695
+ const conversationsEnabled = flags.conversationManagement === true;
36696
+ const conversationState = conversationsEnabled ? await initializeConversationState(cwd, flags) : void 0;
36697
+ let currentConversation = conversationState?.current;
36698
+ const conversationRecords = conversationState?.records ?? /* @__PURE__ */ new Map();
36699
+ const shouldShowStartupSessionSelector = conversationsEnabled && !flags.prompt.trim() && !flags.sessionId;
36700
+ let history = flags.initialHistory ? [...flags.initialHistory] : currentConversation?.modelHistory ? [...currentConversation.modelHistory] : [];
34200
36701
  let lastExternalSessionKey;
34201
- const externalSessions = new ClaudeCodeSessionMap();
34202
- const rootSessionId = flags.sessionId ?? createRootSessionId();
36702
+ let externalSessions = new ClaudeCodeSessionMap();
36703
+ const runtimeByConversation = /* @__PURE__ */ new Map();
36704
+ let rootSessionId = currentConversation?.id ?? flags.sessionId ?? createRootSessionId();
36705
+ if (currentConversation && !shouldShowStartupSessionSelector) {
36706
+ store.restoreConversationSnapshot(currentConversation.snapshot, { sessionId: currentConversation.id, title: currentConversation.title, cwd: currentConversation.cwd });
36707
+ runtimeByConversation.set(currentConversation.id, { externalSessions, lastExternalSessionKey });
36708
+ }
34203
36709
  const goalTitleGenerator = async (input2) => {
34204
36710
  const selection = store.currentSelection();
34205
36711
  const runtime = resolveProviderRuntimeConfig(config, selection);
@@ -34217,19 +36723,48 @@ async function runTuiSession(flags, store) {
34217
36723
  if (resolved.kind === "external-agent") return input2.objective.trim().split(/\s+/).slice(0, 8).join(" ");
34218
36724
  return summarizeGoalTitleWithProvider({ ...input2, provider: resolved.provider, model: resolved.model });
34219
36725
  };
36726
+ if (shouldShowStartupSessionSelector) {
36727
+ const selected = await store.requestSessionSelection(startupSessionOptions());
36728
+ if (selected.kind === "exit" || store.exitRequested()) {
36729
+ await saveCurrentSelection();
36730
+ return 0;
36731
+ }
36732
+ await applyStartupSessionSelection(selected);
36733
+ }
34220
36734
  for (; ; ) {
34221
- const prompt = await store.requestPrompt(nextPrompt);
36735
+ const promptPromise = store.requestPrompt(nextPrompt);
36736
+ if (openConfigOnFirstPrompt) {
36737
+ openConfigOnFirstPrompt = false;
36738
+ store.openConfigManager(`Created config at ${configInit.path}`);
36739
+ }
36740
+ const prompt = await promptPromise;
34222
36741
  nextPrompt = void 0;
34223
36742
  if (store.exitRequested()) {
34224
36743
  await saveCurrentSelection();
34225
36744
  return 0;
34226
36745
  }
34227
36746
  if (!prompt) return 0;
36747
+ const configAction = parseTuiConfigAction(prompt);
36748
+ if (configAction) {
36749
+ await handleConfigAction(configAction);
36750
+ continue;
36751
+ }
36752
+ const sessionAction = parseTuiSessionAction(prompt);
36753
+ if (sessionAction) {
36754
+ await handleSessionSelectionShortcut();
36755
+ continue;
36756
+ }
36757
+ const sessionCommand = parseSessionCommand(prompt);
36758
+ if (sessionCommand) {
36759
+ await handleSessionCommand(sessionCommand);
36760
+ continue;
36761
+ }
34228
36762
  if (isCompactCommand(prompt)) {
34229
36763
  const result = compactInteractiveHistory(history, config.context.main);
34230
36764
  history = result.messages;
34231
36765
  store.prepareForPrompt("waiting for next message");
34232
36766
  store.markHistoryCompacted(result);
36767
+ await persistCurrentConversation();
34233
36768
  continue;
34234
36769
  }
34235
36770
  const mode = resolveAgentMode(config, flags.mode);
@@ -34381,6 +36916,7 @@ async function runTuiSession(flags, store) {
34381
36916
  history = interactiveHistoryFromRunMessages(result.messages);
34382
36917
  lastExternalSessionKey = externalSessionKey ?? lastExternalSessionKey;
34383
36918
  }
36919
+ await persistCurrentConversation();
34384
36920
  } catch (error) {
34385
36921
  store.setStopTask(void 0);
34386
36922
  if (activeAbort?.signal.aborted) {
@@ -34393,6 +36929,7 @@ async function runTuiSession(flags, store) {
34393
36929
  store.setStopTask(void 0);
34394
36930
  }
34395
36931
  if (store.exitRequested()) {
36932
+ await persistCurrentConversation();
34396
36933
  await saveCurrentSelection();
34397
36934
  return 0;
34398
36935
  }
@@ -34401,6 +36938,184 @@ async function runTuiSession(flags, store) {
34401
36938
  async function saveCurrentSelection() {
34402
36939
  await saveSelection(store.currentSelection());
34403
36940
  }
36941
+ async function handleSessionCommand(command) {
36942
+ if (!conversationsEnabled) {
36943
+ store.showSystemMessage("Sessions", "Session management is available in demian-cli TUI, but this embedded runtime lets the host manage sessions.");
36944
+ store.prepareForPrompt("waiting for next message");
36945
+ return;
36946
+ }
36947
+ if (!currentConversation) {
36948
+ currentConversation = createConversationRecord({ id: flags.sessionId ?? createRootSessionId(), cwd });
36949
+ conversationRecords.set(currentConversation.id, currentConversation);
36950
+ }
36951
+ if (command.action === "help") {
36952
+ store.showSystemMessage("Sessions", sessionUsage());
36953
+ store.prepareForPrompt("waiting for next message");
36954
+ return;
36955
+ }
36956
+ if (command.action === "list") {
36957
+ store.showSystemMessage("Sessions", sessionListMessage());
36958
+ store.prepareForPrompt("waiting for next message");
36959
+ return;
36960
+ }
36961
+ if (command.action === "current") {
36962
+ store.showSystemMessage("Current Session", conversationSummaryLine(currentConversation, sortedConversations().findIndex((item) => item.id === currentConversation?.id), currentConversation.id));
36963
+ store.prepareForPrompt("waiting for next message");
36964
+ return;
36965
+ }
36966
+ if (command.action === "new") {
36967
+ await persistCurrentConversation();
36968
+ const next = createConversationRecord({ id: createRootSessionId(), cwd, title: command.title });
36969
+ conversationRecords.set(next.id, next);
36970
+ await switchConversation(next);
36971
+ store.prepareForPrompt(`new session: ${next.title}`);
36972
+ await persistCurrentConversation();
36973
+ return;
36974
+ }
36975
+ if (command.action === "switch") {
36976
+ const target = findConversation(sortedConversations(), command.target);
36977
+ if (!target) {
36978
+ store.showSystemMessage("Sessions", `No session matched "${command.target ?? ""}".
36979
+
36980
+ ${sessionListMessage()}`);
36981
+ store.prepareForPrompt("waiting for next message");
36982
+ return;
36983
+ }
36984
+ await persistCurrentConversation();
36985
+ await switchConversation(target);
36986
+ store.prepareForPrompt(`switched session: ${target.title}`);
36987
+ await persistCurrentConversation();
36988
+ return;
36989
+ }
36990
+ if (command.action === "delete") {
36991
+ const target = findConversation(sortedConversations(), command.target);
36992
+ if (!target) {
36993
+ store.showSystemMessage("Sessions", `No session matched "${command.target ?? ""}".`);
36994
+ store.prepareForPrompt("waiting for next message");
36995
+ return;
36996
+ }
36997
+ conversationRecords.delete(target.id);
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();
37007
+ return;
37008
+ }
37009
+ if (command.action === "rename") {
37010
+ const title = command.title?.trim();
37011
+ if (!title) {
37012
+ store.showSystemMessage("Sessions", "Usage: /session rename <title>");
37013
+ store.prepareForPrompt("waiting for next message");
37014
+ return;
37015
+ }
37016
+ currentConversation.title = title;
37017
+ currentConversation.updatedAt = Date.now();
37018
+ store.prepareForPrompt(`renamed session: ${title}`);
37019
+ await persistCurrentConversation();
37020
+ return;
37021
+ }
37022
+ if (command.action === "clear") {
37023
+ history = [];
37024
+ currentConversation.modelHistory = [];
37025
+ currentConversation.snapshot = void 0;
37026
+ currentConversation.title = "New session";
37027
+ currentConversation.updatedAt = Date.now();
37028
+ store.clearConversation(currentConversation.title);
37029
+ store.prepareForPrompt("cleared current conversation");
37030
+ await persistCurrentConversation();
37031
+ }
37032
+ }
37033
+ async function handleSessionSelectionShortcut() {
37034
+ if (!conversationsEnabled) {
37035
+ store.showSystemMessage("Sessions", "Session management is available in demian-cli TUI, but this embedded runtime lets the host manage sessions.");
37036
+ store.prepareForPrompt("waiting for next message");
37037
+ return;
37038
+ }
37039
+ if (!currentConversation) {
37040
+ currentConversation = createConversationRecord({ id: flags.sessionId ?? createRootSessionId(), cwd });
37041
+ conversationRecords.set(currentConversation.id, currentConversation);
37042
+ }
37043
+ await persistCurrentConversation();
37044
+ const selection = await store.requestSessionSelection(startupSessionOptions(), { cancel: "prompt" });
37045
+ if (selection.kind === "cancel") {
37046
+ store.prepareForPrompt("waiting for next message");
37047
+ return;
37048
+ }
37049
+ if (selection.kind === "exit" || store.exitRequested()) return;
37050
+ await applyStartupSessionSelection(selection);
37051
+ }
37052
+ function startupSessionOptions() {
37053
+ const records = sortedConversations();
37054
+ const sessionOptions = records.map((record) => ({
37055
+ kind: "session",
37056
+ id: record.id,
37057
+ title: record.title,
37058
+ cwd: record.cwd,
37059
+ status: conversationRuntimeStatus(record, cwd),
37060
+ updatedAt: record.updatedAt,
37061
+ currentWorkspace: path36.resolve(record.cwd) === cwd
37062
+ }));
37063
+ return [...sessionOptions, { kind: "new", title: "New session", cwd, status: "ready", currentWorkspace: true }];
37064
+ }
37065
+ async function applyStartupSessionSelection(selection) {
37066
+ if (selection.kind === "cancel") {
37067
+ store.prepareForPrompt("waiting for next message");
37068
+ return;
37069
+ }
37070
+ if (selection.kind === "new") {
37071
+ const next = createConversationRecord({ id: createRootSessionId(), cwd });
37072
+ conversationRecords.set(next.id, next);
37073
+ await switchConversation(next);
37074
+ store.prepareForPrompt("new session ready");
37075
+ await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], next.id);
37076
+ return;
37077
+ }
37078
+ const target = conversationRecords.get(selection.id) ?? findConversation(sortedConversations(), selection.id);
37079
+ if (!target) {
37080
+ const next = createConversationRecord({ id: createRootSessionId(), cwd });
37081
+ conversationRecords.set(next.id, next);
37082
+ await switchConversation(next);
37083
+ store.prepareForPrompt("new session ready");
37084
+ await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], next.id);
37085
+ return;
37086
+ }
37087
+ await switchConversation(target);
37088
+ store.prepareForPrompt(`session ready: ${target.title}`);
37089
+ await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], target.id);
37090
+ }
37091
+ async function switchConversation(record) {
37092
+ currentConversation = record;
37093
+ rootSessionId = record.id;
37094
+ history = [...record.modelHistory];
37095
+ const runtime = runtimeByConversation.get(record.id) ?? { externalSessions: new ClaudeCodeSessionMap(), lastExternalSessionKey: void 0 };
37096
+ externalSessions = runtime.externalSessions;
37097
+ lastExternalSessionKey = runtime.lastExternalSessionKey;
37098
+ runtimeByConversation.set(record.id, runtime);
37099
+ store.restoreConversationSnapshot(record.snapshot, { sessionId: record.id, title: record.title, cwd: record.cwd || cwd });
37100
+ }
37101
+ async function persistCurrentConversation() {
37102
+ if (!conversationsEnabled || !currentConversation) return;
37103
+ currentConversation.modelHistory = cleanModelHistory(history);
37104
+ if (currentConversation.title === "New session" && currentConversation.modelHistory.length > 0) currentConversation.title = titleFromHistory(currentConversation.modelHistory);
37105
+ currentConversation.snapshot = store.conversationSnapshot();
37106
+ currentConversation.updatedAt = Date.now();
37107
+ conversationRecords.set(currentConversation.id, currentConversation);
37108
+ runtimeByConversation.set(currentConversation.id, { externalSessions, lastExternalSessionKey });
37109
+ await saveConversationRecords(flags.conversationStorageDir, [...conversationRecords.values()], currentConversation.id);
37110
+ }
37111
+ function sortedConversations() {
37112
+ return sortConversationRecords([...conversationRecords.values()], cwd);
37113
+ }
37114
+ function sessionListMessage() {
37115
+ const records = sortedConversations();
37116
+ if (!records.length) return "No saved sessions.";
37117
+ return ["Saved sessions:", ...records.map((record, index) => conversationSummaryLine(record, index, currentConversation?.id)), "", "Use /session switch <number|id|title> to open one."].join("\n");
37118
+ }
34404
37119
  async function saveSelection(selection) {
34405
37120
  if (!shouldPersistSelection(selection)) return;
34406
37121
  const key = preferenceKey(selection);
@@ -34408,16 +37123,126 @@ async function runTuiSession(flags, store) {
34408
37123
  await preferenceStore.save(selection);
34409
37124
  savedSelectionKey = key;
34410
37125
  }
37126
+ async function handleConfigAction(action) {
37127
+ const path37 = configMutationPath(flags);
37128
+ try {
37129
+ let message = "Config updated.";
37130
+ let nextSelection = {};
37131
+ if (action.type === "setDefaultProvider") {
37132
+ await updateConfigDefaults({ path: path37, defaultProvider: action.provider });
37133
+ message = `Default provider saved: ${action.provider}`;
37134
+ nextSelection = { provider: action.provider };
37135
+ } else if (action.type === "setDefaultModel") {
37136
+ await setDefaultModelProfile({
37137
+ path: path37,
37138
+ provider: action.provider,
37139
+ profileName: action.profileName,
37140
+ name: action.name,
37141
+ displayName: action.displayName,
37142
+ model: action.model
37143
+ });
37144
+ message = `Default model saved for ${action.provider}: ${action.displayName ?? action.model}`;
37145
+ nextSelection = { provider: action.provider, model: action.displayName ?? action.model };
37146
+ } else if (action.type === "addProvider") {
37147
+ await addProvider({ path: path37, name: action.name, preset: action.preset, apiKeyEnv: action.apiKeyEnv });
37148
+ message = `Provider added: ${action.name}`;
37149
+ nextSelection = { provider: action.name };
37150
+ } else if (action.type === "updateProvider") {
37151
+ await updateProviderSettings({
37152
+ path: path37,
37153
+ provider: action.provider,
37154
+ ...action.field === "baseURL" ? { baseURL: action.value } : {},
37155
+ ...action.field === "apiKeyEnv" ? { apiKeyEnv: action.value } : {},
37156
+ ...action.field === "authHeader" ? { authHeader: action.value } : {}
37157
+ });
37158
+ message = `Provider ${action.field} saved: ${action.provider}`;
37159
+ nextSelection = { provider: action.provider };
37160
+ }
37161
+ await reloadConfigSettings(message, nextSelection);
37162
+ } catch (error) {
37163
+ store.prepareForPrompt("config action failed");
37164
+ store.showSystemMessage("Config", errorMessage(error));
37165
+ store.openConfigManager(errorMessage(error));
37166
+ }
37167
+ }
37168
+ async function reloadConfigSettings(message, selectionFlags = {}) {
37169
+ loadedConfig = await loadConfig({ cwd, configPath: flags.configPath });
37170
+ config = flags.context ? mergeConfig(loadedConfig, {
37171
+ context: {
37172
+ main: flags.context
37173
+ }
37174
+ }) : loadedConfig;
37175
+ const currentSelection = store.currentSelection();
37176
+ const selection = createInteractiveModelSelection(
37177
+ config,
37178
+ selectionFlags.provider || selectionFlags.model ? selectionFlags : { provider: currentSelection.providerName, model: currentSelection.model }
37179
+ );
37180
+ store.configureSettings(selection, await providerOptionsWithCatalog(config, { timeoutMs: 1500 }), {
37181
+ agent: store.currentAgentName(),
37182
+ cwd,
37183
+ configPath: configMutationPath(flags),
37184
+ configDefaultProvider: config.defaultProvider,
37185
+ configMessage: message,
37186
+ agentOptions,
37187
+ contextEfficiency: {
37188
+ maxContextTokens: config.context.main.maxContextTokens,
37189
+ compactAtRatio: config.context.main.compactAtRatio ?? config.context.main.compressAtRatio ?? 0.5,
37190
+ compactAtTokens: config.context.main.maxContextTokens ? Math.floor(config.context.main.maxContextTokens * (config.context.main.compactAtRatio ?? config.context.main.compressAtRatio ?? 0.5)) : void 0
37191
+ }
37192
+ });
37193
+ store.openConfigManager(message);
37194
+ }
37195
+ }
37196
+ async function ensureTuiConfigExists(flags) {
37197
+ const target = configMutationPath(flags);
37198
+ if (!flags.configPath) {
37199
+ const jsondPath = target.replace(/\.json$/i, ".jsond");
37200
+ if (existsSync5(target) || existsSync5(jsondPath)) return { path: target, created: false };
37201
+ }
37202
+ const result = await createUserConfig({ path: target });
37203
+ return { path: result.path, created: result.created };
37204
+ }
37205
+ function configMutationPath(flags) {
37206
+ return flags.configPath ? path36.resolve(flags.configPath) : defaultUserConfigPath();
34411
37207
  }
34412
37208
  function isGoalStateClearingAction(action) {
34413
37209
  return action === "status" || action === "pause" || action === "resume";
34414
37210
  }
37211
+ async function initializeConversationState(cwd, flags) {
37212
+ const loaded = await loadConversationRecords(flags.conversationStorageDir);
37213
+ const records = new Map(loaded.records.map((record) => [record.id, record]));
37214
+ const shouldResumeSelected = !flags.prompt.trim();
37215
+ const orderedRecords = sortConversationRecords([...records.values()], cwd);
37216
+ const preferred = (flags.sessionId ? findConversation(orderedRecords, flags.sessionId) : void 0) ?? (shouldResumeSelected && loaded.selectedSessionId ? findConversation(orderedRecords, loaded.selectedSessionId) : void 0) ?? (shouldResumeSelected ? orderedRecords[0] : void 0);
37217
+ const current = preferred ?? (flags.prompt.trim() || flags.sessionId || flags.initialHistory?.length ? createConversationRecord({
37218
+ id: flags.sessionId ?? createRootSessionId(),
37219
+ cwd,
37220
+ history: flags.initialHistory
37221
+ }) : void 0);
37222
+ if (current && flags.initialHistory?.length) current.modelHistory = cleanModelHistory(flags.initialHistory);
37223
+ if (current) records.set(current.id, current);
37224
+ return { records, current };
37225
+ }
37226
+ function sessionUsage() {
37227
+ return [
37228
+ "Usage:",
37229
+ " /session list",
37230
+ " /session new [title]",
37231
+ " /session switch <number|id|title>",
37232
+ " /session rename <title>",
37233
+ " /session delete <number|id|title>",
37234
+ " /session clear",
37235
+ "",
37236
+ "Aliases: /sessions, /conversation, /conversations, /conv"
37237
+ ].join("\n");
37238
+ }
34415
37239
  var init_controller = __esm({
34416
37240
  "src/ui/tui/controller.ts"() {
34417
37241
  "use strict";
34418
37242
  init_registry();
34419
37243
  init_types();
34420
37244
  init_config();
37245
+ init_config_scaffold();
34421
37246
  init_execution();
34422
37247
  init_events();
34423
37248
  init_runtime();
@@ -34432,6 +37257,8 @@ var init_controller = __esm({
34432
37257
  init_history();
34433
37258
  init_preferences();
34434
37259
  init_settings();
37260
+ init_store();
37261
+ init_conversations();
34435
37262
  }
34436
37263
  });
34437
37264
 
@@ -34664,283 +37491,16 @@ Flags:
34664
37491
  }
34665
37492
 
34666
37493
  // src/config-command.ts
37494
+ init_config_scaffold();
37495
+ init_config();
37496
+ init_catalog();
37497
+ init_claudecode_auth_preflight();
37498
+ init_claudecode_paths();
34667
37499
  import { spawn as spawn4 } from "node:child_process";
34668
37500
  import { stat as stat4 } from "node:fs/promises";
34669
37501
  import os11 from "node:os";
34670
37502
  import path17 from "node:path";
34671
37503
  import readline3 from "node:readline/promises";
34672
-
34673
- // src/config-scaffold.ts
34674
- init_path_expansion();
34675
- import { chmod, mkdir, readFile as readFile2, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
34676
- import os3 from "node:os";
34677
- import path3 from "node:path";
34678
- function defaultUserConfigPath() {
34679
- return path3.join(os3.homedir(), ".demian", "config.json");
34680
- }
34681
- function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
34682
- return {
34683
- version: 2,
34684
- defaultProvider,
34685
- providers: {
34686
- openai: {
34687
- type: "openai-compatible",
34688
- baseURL: "https://api.openai.com/v1",
34689
- apiKey: "",
34690
- apiKeyEnv: "OPENAI_API_KEY",
34691
- catalog: { type: "openai-models", endpoint: "https://api.openai.com/v1/models" }
34692
- },
34693
- anthropic: {
34694
- type: "anthropic",
34695
- baseURL: "https://api.anthropic.com/v1",
34696
- apiKey: "",
34697
- apiKeyEnv: "ANTHROPIC_API_KEY",
34698
- catalog: { type: "anthropic-models", endpoint: "https://api.anthropic.com/v1/models" }
34699
- },
34700
- gemini: {
34701
- type: "openai-compatible",
34702
- baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
34703
- apiKey: "",
34704
- apiKeyEnv: "GEMINI_API_KEY",
34705
- apiKeyEnvAliases: ["GOOGLE_API_KEY"],
34706
- catalog: { type: "gemini-openai-models", endpoint: "https://generativelanguage.googleapis.com/v1beta/openai/models" }
34707
- },
34708
- groq: {
34709
- type: "openai-compatible",
34710
- baseURL: "https://api.groq.com/openai/v1",
34711
- apiKey: "",
34712
- apiKeyEnv: "GROQ_API_KEY",
34713
- catalog: { type: "groq-models", endpoint: "https://api.groq.com/openai/v1/models" }
34714
- },
34715
- azure: {
34716
- type: "openai-compatible",
34717
- auth: { type: "api-key", header: "api-key" },
34718
- modelProfiles: [
34719
- {
34720
- name: "azure-example",
34721
- displayName: "Azure example",
34722
- model: "azure-deployment-name",
34723
- baseURL: "https://example.openai.azure.com/openai/v1",
34724
- apiKey: "",
34725
- apiKeyEnv: "AZURE_OPENAI_API_KEY"
34726
- }
34727
- ]
34728
- },
34729
- lmstudio: {
34730
- type: "openai-compatible",
34731
- baseURL: "http://localhost:1234/v1",
34732
- apiKey: "lm-studio",
34733
- modelProfiles: [],
34734
- catalog: { type: "openai-compatible-models", endpoint: "http://localhost:1234/v1/models" }
34735
- },
34736
- "ollama-local": {
34737
- type: "openai-compatible",
34738
- baseURL: "http://localhost:11434/v1",
34739
- apiKey: "ollama",
34740
- modelProfiles: [],
34741
- quirks: { omitTemperature: true },
34742
- catalog: { type: "openai-compatible-models", endpoint: "http://localhost:11434/v1/models" }
34743
- },
34744
- "ollama-cloud": {
34745
- type: "ollama",
34746
- baseURL: "https://ollama.com/api",
34747
- apiKey: "",
34748
- apiKeyEnv: "OLLAMA_API_KEY",
34749
- modelProfiles: [],
34750
- catalog: { type: "ollama-tags", endpoint: "https://ollama.com/api/tags" }
34751
- },
34752
- llamacpp: {
34753
- type: "openai-compatible",
34754
- baseURL: "http://localhost:8080/v1",
34755
- apiKey: "llama.cpp",
34756
- modelProfiles: [],
34757
- catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8080/v1/models" }
34758
- },
34759
- vllm: {
34760
- type: "openai-compatible",
34761
- baseURL: "http://localhost:8000/v1",
34762
- apiKey: "vllm",
34763
- modelProfiles: [],
34764
- catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8000/v1/models" }
34765
- },
34766
- codex: {
34767
- type: "codex",
34768
- baseURL: "https://chatgpt.com/backend-api/codex",
34769
- authStore: "auto",
34770
- allowApiKeyFallback: false,
34771
- promptCacheKey: "root-session",
34772
- catalog: { type: "codex-oauth-models" }
34773
- },
34774
- claudecode: {
34775
- type: "claudecode",
34776
- runtime: "agent-sdk",
34777
- cliPath: "~/.local/bin/claude",
34778
- cwdMode: "session",
34779
- historyPolicy: "passthrough-resume",
34780
- onInvalidResume: "fresh",
34781
- attachmentFallback: "block",
34782
- allowSubagents: false,
34783
- sanitizeApiKeyEnv: true,
34784
- authPreflight: true,
34785
- useBareMode: false,
34786
- usageLedgerScope: "process",
34787
- sessionLock: true,
34788
- abortPolicy: "record-only",
34789
- catalog: { type: "claudecode-supported-models" }
34790
- }
34791
- }
34792
- };
34793
- }
34794
- async function createUserConfig(options = {}) {
34795
- const filePath = expandHome(options.path ?? defaultUserConfigPath());
34796
- const content = `${JSON.stringify(defaultUserConfig(options.defaultProvider), null, 2)}
34797
- `;
34798
- if (options.print) return { path: filePath, created: false, content };
34799
- const existed = await exists(filePath);
34800
- if (existed && !options.force) return { path: filePath, created: false, content: await readFile2(filePath, "utf8"), existed: true };
34801
- await writeJsonAtomic(filePath, content);
34802
- return { path: filePath, created: true, content, existed };
34803
- }
34804
- async function addProvider(options) {
34805
- const filePath = expandHome(options.path ?? defaultUserConfigPath());
34806
- const config = await readConfigObject(filePath);
34807
- const providers = objectValue(config.providers);
34808
- const name = options.name;
34809
- if (providers[name] && !options.force) throw new Error(`Provider ${name} already exists. Use --force to overwrite it.`);
34810
- providers[name] = providerPreset(options);
34811
- config.providers = providers;
34812
- const content = `${JSON.stringify(config, null, 2)}
34813
- `;
34814
- await writeJsonAtomic(filePath, content);
34815
- return { path: filePath, created: true, content };
34816
- }
34817
- async function addModelProfile(options) {
34818
- const filePath = expandHome(options.path ?? defaultUserConfigPath());
34819
- const config = await readConfigObject(filePath);
34820
- const providers = objectValue(config.providers);
34821
- const provider = objectValue(providers[options.provider]);
34822
- const profiles = Array.isArray(provider.modelProfiles) ? [...provider.modelProfiles] : [];
34823
- const displayName = options.displayName ?? options.name;
34824
- const existingByName = profiles.findIndex((entry) => entry.name === options.name);
34825
- const existingByDisplay = profiles.findIndex((entry) => entry.displayName === displayName);
34826
- if (existingByName >= 0 && !options.force) throw new Error(`Profile name "${options.name}" already exists on provider ${options.provider}. Use --force to overwrite it.`);
34827
- if (existingByDisplay >= 0 && existingByDisplay !== existingByName && !options.force) {
34828
- throw new Error(`Profile displayName "${displayName}" already exists on provider ${options.provider} (profile name: ${profiles[existingByDisplay].name}). Use --force or pick a different --display-name.`);
34829
- }
34830
- const next = {
34831
- name: options.name,
34832
- displayName,
34833
- model: options.model,
34834
- ...options.baseURL ? { baseURL: options.baseURL } : {},
34835
- ...options.apiKey !== void 0 || options.apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
34836
- ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {}
34837
- };
34838
- if (existingByName >= 0) profiles[existingByName] = next;
34839
- else profiles.push(next);
34840
- provider.modelProfiles = profiles;
34841
- providers[options.provider] = provider;
34842
- config.providers = providers;
34843
- const content = `${JSON.stringify(config, null, 2)}
34844
- `;
34845
- await writeJsonAtomic(filePath, content);
34846
- return { path: filePath, created: true, content };
34847
- }
34848
- async function readConfigObject(filePath) {
34849
- if (!await exists(filePath)) await createUserConfig({ path: filePath });
34850
- const raw = JSON.parse(await readFile2(filePath, "utf8"));
34851
- raw.version ??= 2;
34852
- raw.providers = objectValue(raw.providers);
34853
- return raw;
34854
- }
34855
- function providerPreset(options) {
34856
- const preset = options.preset ?? options.name;
34857
- const auth = apiKeyAuthFields(options);
34858
- const openAIAuth = options.authHeader ? { auth: { type: "api-key", header: options.authHeader } } : {};
34859
- if (preset === "openai") return { type: "openai-compatible", baseURL: options.baseURL ?? "https://api.openai.com/v1", ...apiKeyAuthFields(options, "OPENAI_API_KEY"), ...openAIAuth, catalog: { type: "openai-models", endpoint: `${(options.baseURL ?? "https://api.openai.com/v1").replace(/\/+$/, "")}/models` } };
34860
- if (preset === "anthropic") return { type: "anthropic", baseURL: options.baseURL ?? "https://api.anthropic.com/v1", ...apiKeyAuthFields(options, "ANTHROPIC_API_KEY"), catalog: { type: "anthropic-models", endpoint: `${(options.baseURL ?? "https://api.anthropic.com/v1").replace(/\/+$/, "")}/models` } };
34861
- if (preset === "gemini") {
34862
- const geminiBase = options.baseURL ?? "https://generativelanguage.googleapis.com/v1beta/openai/";
34863
- return { type: "openai-compatible", baseURL: geminiBase, ...apiKeyAuthFields(options, "GEMINI_API_KEY"), apiKeyEnvAliases: ["GOOGLE_API_KEY"], ...openAIAuth, catalog: { type: "gemini-openai-models", endpoint: `${geminiBase.replace(/\/+$/, "")}/models` } };
34864
- }
34865
- if (preset === "groq") return { type: "openai-compatible", baseURL: options.baseURL ?? "https://api.groq.com/openai/v1", ...apiKeyAuthFields(options, "GROQ_API_KEY"), ...openAIAuth, catalog: { type: "groq-models", endpoint: `${(options.baseURL ?? "https://api.groq.com/openai/v1").replace(/\/+$/, "")}/models` } };
34866
- if (preset === "azure") {
34867
- return {
34868
- type: "openai-compatible",
34869
- auth: { type: "api-key", header: options.authHeader ?? "api-key" },
34870
- ...auth,
34871
- modelProfiles: [
34872
- {
34873
- name: "azure-example",
34874
- displayName: "Azure example",
34875
- model: "azure-deployment-name",
34876
- baseURL: options.baseURL ?? "https://example.openai.azure.com/openai/v1",
34877
- ...options.apiKey ? {} : { apiKey: "" },
34878
- apiKeyEnv: options.apiKeyEnv ?? "AZURE_OPENAI_API_KEY"
34879
- }
34880
- ]
34881
- };
34882
- }
34883
- if (preset === "lmstudio") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:1234/v1", apiKey: options.apiKey ?? "lm-studio", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:1234/v1").replace(/\/+$/, "")}/models` } };
34884
- if (preset === "ollama-local" || preset === "ollama") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:11434/v1", apiKey: options.apiKey ?? "ollama", modelProfiles: [], quirks: { omitTemperature: true }, catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:11434/v1").replace(/\/+$/, "")}/models` } };
34885
- if (preset === "ollama-cloud") return { type: "ollama", baseURL: options.baseURL ?? "https://ollama.com/api", ...apiKeyAuthFields(options, "OLLAMA_API_KEY"), modelProfiles: [], catalog: { type: "ollama-tags", endpoint: `${(options.baseURL ?? "https://ollama.com/api").replace(/\/+$/, "")}/tags` } };
34886
- if (preset === "llamacpp") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:8080/v1", apiKey: options.apiKey ?? "llama.cpp", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:8080/v1").replace(/\/+$/, "")}/models` } };
34887
- if (preset === "vllm") return { type: "openai-compatible", baseURL: options.baseURL ?? "http://localhost:8000/v1", apiKey: options.apiKey ?? "vllm", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options.baseURL ?? "http://localhost:8000/v1").replace(/\/+$/, "")}/models` } };
34888
- if (preset === "codex") return { type: "codex", baseURL: options.baseURL ?? "https://chatgpt.com/backend-api/codex", authStore: "auto", allowApiKeyFallback: false, promptCacheKey: "root-session", catalog: { type: "codex-oauth-models" } };
34889
- if (preset === "claudecode") return { type: "claudecode", runtime: "agent-sdk", cliPath: "~/.local/bin/claude", cwdMode: "session", historyPolicy: "passthrough-resume", onInvalidResume: "fresh", attachmentFallback: "block", allowSubagents: false, sanitizeApiKeyEnv: true, authPreflight: true, useBareMode: false, usageLedgerScope: "process", sessionLock: true, abortPolicy: "record-only", catalog: { type: "claudecode-supported-models" } };
34890
- if ((options.type ?? preset) === "openai-compatible") {
34891
- const baseURL = options.baseURL;
34892
- if (!baseURL) throw new Error("openai-compatible provider requires --base-url.");
34893
- return {
34894
- type: "openai-compatible",
34895
- baseURL,
34896
- ...auth,
34897
- ...openAIAuth,
34898
- catalog: { type: "openai-compatible-models", endpoint: `${baseURL.replace(/\/+$/, "")}/models` }
34899
- };
34900
- }
34901
- throw new Error(`Unknown provider preset: ${preset}`);
34902
- }
34903
- function apiKeyAuthFields(options, defaultApiKeyEnv) {
34904
- const apiKeyEnv = options.apiKeyEnv ?? defaultApiKeyEnv;
34905
- return {
34906
- ...options.apiKey !== void 0 || apiKeyEnv ? { apiKey: options.apiKey ?? "" } : {},
34907
- ...apiKeyEnv ? { apiKeyEnv } : {}
34908
- };
34909
- }
34910
- async function writeJsonAtomic(filePath, content) {
34911
- await mkdir(path3.dirname(filePath), { recursive: true, mode: 448 });
34912
- const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
34913
- await writeFile2(temp, content, { mode: 384 });
34914
- await rename(temp, filePath);
34915
- await chmod(filePath, 384).catch(() => void 0);
34916
- }
34917
- function detectDefaultProvider() {
34918
- if (process.env.OPENAI_API_KEY) return "openai";
34919
- if (process.env.ANTHROPIC_API_KEY) return "anthropic";
34920
- if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) return "gemini";
34921
- if (process.env.GROQ_API_KEY) return "groq";
34922
- return "openai";
34923
- }
34924
- function objectValue(value) {
34925
- return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
34926
- }
34927
- async function exists(filePath) {
34928
- try {
34929
- await stat(filePath);
34930
- return true;
34931
- } catch {
34932
- return false;
34933
- }
34934
- }
34935
- function expandHome(value) {
34936
- return resolveExpandedPath(value);
34937
- }
34938
-
34939
- // src/config-command.ts
34940
- init_config();
34941
- init_catalog();
34942
- init_claudecode_auth_preflight();
34943
- init_claudecode_paths();
34944
37504
  async function maybeRunConfigCommand(argv) {
34945
37505
  if (argv[0] === "config") return runConfigCommand(argv.slice(1));
34946
37506
  if (argv[0] === "auth") return runAuthCommand(argv.slice(1));
@@ -35054,6 +37614,41 @@ async function runConfigCommand(argv) {
35054
37614
  force
35055
37615
  });
35056
37616
  process.stdout.write(`Updated config: ${result.path}
37617
+ `);
37618
+ return 0;
37619
+ }
37620
+ if (command === "set-default-model") {
37621
+ const [provider, ...tail2] = rest;
37622
+ if (!provider) throw new Error("config set-default-model requires a provider name.");
37623
+ const flags = parseFlags(tail2);
37624
+ const result = await setDefaultModelProfile({
37625
+ path: stringFlag(flags, "path"),
37626
+ provider,
37627
+ profileName: stringFlag(flags, "profile"),
37628
+ name: stringFlag(flags, "name"),
37629
+ displayName: stringFlag(flags, "display-name"),
37630
+ model: stringFlag(flags, "model"),
37631
+ baseURL: stringFlag(flags, "base-url"),
37632
+ apiKey: await apiKeyFromFlags(flags),
37633
+ apiKeyEnv: stringFlag(flags, "api-key-env")
37634
+ });
37635
+ process.stdout.write(`Updated config: ${result.path}
37636
+ `);
37637
+ return 0;
37638
+ }
37639
+ if (command === "update-provider") {
37640
+ const [provider, ...tail2] = rest;
37641
+ if (!provider) throw new Error("config update-provider requires a provider name.");
37642
+ const flags = parseFlags(tail2);
37643
+ const result = await updateProviderSettings({
37644
+ path: stringFlag(flags, "path"),
37645
+ provider,
37646
+ baseURL: stringFlagOrEmpty(flags, "base-url"),
37647
+ apiKey: await apiKeyFromFlags(flags),
37648
+ apiKeyEnv: stringFlagOrEmpty(flags, "api-key-env"),
37649
+ authHeader: stringFlagOrEmpty(flags, "auth-header")
37650
+ });
37651
+ process.stdout.write(`Updated config: ${result.path}
35057
37652
  `);
35058
37653
  return 0;
35059
37654
  }
@@ -35151,6 +37746,10 @@ function stringFlag(flags, name) {
35151
37746
  const value = flags.get(name);
35152
37747
  return typeof value === "string" && value.trim() ? value : void 0;
35153
37748
  }
37749
+ function stringFlagOrEmpty(flags, name) {
37750
+ const value = flags.get(name);
37751
+ return typeof value === "string" ? value : void 0;
37752
+ }
35154
37753
  function requiredStringFlag(flags, name) {
35155
37754
  const value = stringFlag(flags, name);
35156
37755
  if (!value) throw new Error(`--${name} is required.`);
@@ -35193,6 +37792,8 @@ Usage:
35193
37792
  demian config setup interactive multi-step provider wizard
35194
37793
  demian config add-provider <preset|openai-compatible> [--name <name>] [--base-url <url>] [--api-key-env <env>] [--api-key-stdin] [--auth-header <header>] [--force]
35195
37794
  demian config add-model <provider> --name <name> --model <model> [--display-name <label>] [--base-url <url>] [--api-key-env <env>] [--api-key-stdin] [--force]
37795
+ demian config set-default-model <provider> [--profile <name> | --model <model>] [--display-name <label>]
37796
+ demian config update-provider <provider> [--base-url <url>] [--api-key-env <env>] [--auth-header <header>]
35196
37797
  demian config models <provider> [--refresh] [--config <path>]
35197
37798
  demian config list [--config <path>]
35198
37799
  demian config path
@@ -35247,8 +37848,8 @@ async function fileExists2(filePath) {
35247
37848
  }
35248
37849
  async function readJsonSafe(filePath) {
35249
37850
  try {
35250
- const { readFile: readFile18 } = await import("node:fs/promises");
35251
- const text = await readFile18(filePath, "utf8");
37851
+ const { readFile: readFile19 } = await import("node:fs/promises");
37852
+ const text = await readFile19(filePath, "utf8");
35252
37853
  return JSON.parse(text);
35253
37854
  } catch {
35254
37855
  return void 0;
@@ -35376,7 +37977,6 @@ async function main(argv = process.argv.slice(2)) {
35376
37977
  process.stderr.write("demian TUI requires an interactive terminal. Use demian-plain for pipes or non-interactive use.\n");
35377
37978
  return 1;
35378
37979
  }
35379
- if (!flags.noWizard) await runFirstRunWizard().catch(() => void 0);
35380
37980
  try {
35381
37981
  const [{ render }, ReactModule, { TuiApp: TuiApp2 }, { TuiStore: TuiStore2 }, { runTuiSession: runTuiSession2 }] = await Promise.all([
35382
37982
  dynamicImport4("ink"),
@@ -35389,7 +37989,7 @@ async function main(argv = process.argv.slice(2)) {
35389
37989
  const store = new TuiStore2();
35390
37990
  const instance = render(React2.createElement(TuiApp2, { store, version: packageVersion() }));
35391
37991
  try {
35392
- const code = await runTuiSession2(flags, store);
37992
+ const code = await runTuiSession2({ ...flags, conversationManagement: true }, store);
35393
37993
  setTimeout(() => instance.unmount(), 100);
35394
37994
  await instance.waitUntilExit();
35395
37995
  return code;
@@ -35537,12 +38137,16 @@ Default:
35537
38137
  Press Esc then p to select provider, Esc then m to edit model, then Enter to send.
35538
38138
  Press Esc then a to select the main agent before sending a message.
35539
38139
  Press Esc then o to select the default permission preset: deny, ask, auto, or full.
38140
+ Press Esc then c to configure provider defaults and model profiles.
38141
+ Press Esc then k to open the command palette, or Esc then s to select a saved session.
35540
38142
  Press up/down in the message composer to recall previous prompts.
35541
38143
  Provider/model selections are remembered in ~/.demian/preferences.json.
35542
38144
  After each answer, the TUI returns to standby for the next message.
35543
38145
  Use /cowork <task> to explicitly ask Demian to coordinate cowork sub agents in multi-agent mode.
35544
38146
  Use /compact to compact current history, /stop to stop active work, and /exit or /quit to close the TUI.
35545
- Press Esc then q to quit, Esc then s to stop a running task, and Esc then u to clear the composer.
38147
+ Use /session list, /session new, /session switch <number|id|title>, /session rename <title>,
38148
+ and /session delete <number|id|title> to manage saved CLI conversations.
38149
+ Press Esc then q to quit, Esc then s while running to stop the task, and Esc then u to clear the composer.
35546
38150
  Tool approvals show the requested command/path in a permission dialog.
35547
38151
  Press y to allow once, a to always allow the grant scope, or n/Enter to deny.
35548
38152
  Prompt arguments are still accepted as a compatibility shortcut.