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/README.md +46 -3
- package/dist/cli.mjs +146 -3
- package/dist/tui.mjs +3192 -588
- package/dist/vscode-worker.mjs +1600 -439
- package/docs/ko/README.md +44 -3
- package/package.json +1 -1
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
|
|
22794
|
-
if (
|
|
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 (
|
|
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"
|
|
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
|
-
|
|
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 = () =>
|
|
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()),
|
|
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
|
|
27161
|
-
|
|
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)
|
|
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(
|
|
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
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
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:
|
|
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
|
|
27230
|
-
React.createElement(Text, { color:
|
|
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
|
|
27237
|
-
|
|
27238
|
-
|
|
27239
|
-
|
|
27240
|
-
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
|
|
27246
|
-
|
|
27247
|
-
|
|
27248
|
-
|
|
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
|
-
{
|
|
27274
|
-
|
|
27275
|
-
|
|
27276
|
-
}
|
|
27277
|
-
|
|
27278
|
-
|
|
27279
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
27284
|
-
|
|
27285
|
-
|
|
27286
|
-
|
|
27287
|
-
}
|
|
27288
|
-
|
|
27289
|
-
|
|
27290
|
-
|
|
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(
|
|
27307
|
-
React.createElement(
|
|
27308
|
-
React.createElement(
|
|
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
|
|
27370
|
-
|
|
27371
|
-
|
|
27372
|
-
|
|
27373
|
-
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
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
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
const
|
|
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(
|
|
27388
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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" },
|
|
27449
|
-
React.createElement(Text, { color: "gray" },
|
|
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
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
|
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" },
|
|
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.
|
|
27538
|
-
] : section.emptyText ? [React.createElement(Text, { key:
|
|
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
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
27699
|
-
|
|
27700
|
-
|
|
27701
|
-
|
|
27702
|
-
|
|
27703
|
-
|
|
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
|
-
|
|
27730
|
-
|
|
27731
|
-
|
|
27732
|
-
|
|
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
|
-
|
|
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" },
|
|
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
|
|
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(
|
|
27842
|
-
React.createElement(
|
|
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
|
|
27859
|
-
const
|
|
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(
|
|
27864
|
-
React.createElement(
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
|
|
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
|
|
27879
|
-
const
|
|
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(
|
|
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
|
-
|
|
27887
|
-
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
|
|
27891
|
-
|
|
27892
|
-
|
|
27893
|
-
|
|
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
|
|
27981
|
-
React.createElement(
|
|
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(
|
|
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
|
-
|
|
28006
|
-
|
|
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
|
-
|
|
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
|
|
32060
|
-
const cleaned = stripDecorations(
|
|
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
|
|
35035
|
+
const path37 = [];
|
|
32767
35036
|
const visit = (id) => {
|
|
32768
|
-
if (visiting.has(id)) return [...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
36654
|
+
import path36 from "node:path";
|
|
36655
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
34166
36656
|
async function runTuiSession(flags, store) {
|
|
34167
|
-
const 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
|
|
34171
|
-
|
|
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
|
|
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
|
-
|
|
34202
|
-
const
|
|
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
|
|
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:
|
|
35251
|
-
const text = await
|
|
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
|
-
|
|
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.
|