cc-claw 0.20.4 → 0.20.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +384 -62
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.20.
|
|
36
|
+
VERSION = true ? "0.20.6" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -6085,6 +6085,7 @@ async function healthCheck(serverName) {
|
|
|
6085
6085
|
return [];
|
|
6086
6086
|
}
|
|
6087
6087
|
const results = [];
|
|
6088
|
+
let needsCatalogRefresh = false;
|
|
6088
6089
|
for (const server of servers) {
|
|
6089
6090
|
const baseUrl = getBaseUrl(server);
|
|
6090
6091
|
const ok = await ping(baseUrl, {
|
|
@@ -6097,6 +6098,35 @@ async function healthCheck(serverName) {
|
|
|
6097
6098
|
if (newStatus !== server.status) {
|
|
6098
6099
|
log(`[ollama] Server ${server.name}: ${server.status} \u2192 ${newStatus}`);
|
|
6099
6100
|
}
|
|
6101
|
+
if (ok) {
|
|
6102
|
+
try {
|
|
6103
|
+
const tags = await listModels(baseUrl, { apiKey: server.apiKey });
|
|
6104
|
+
const remoteNames = new Set(tags.models.map((m) => m.name));
|
|
6105
|
+
let hasNew = false;
|
|
6106
|
+
for (const name of remoteNames) {
|
|
6107
|
+
if (!getModelByName(name, server.id)) {
|
|
6108
|
+
hasNew = true;
|
|
6109
|
+
break;
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
if (hasNew) {
|
|
6113
|
+
await discoverModels(server.name);
|
|
6114
|
+
needsCatalogRefresh = true;
|
|
6115
|
+
log(`[ollama] New models detected on ${server.name} \u2014 refreshed`);
|
|
6116
|
+
}
|
|
6117
|
+
} catch {
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
}
|
|
6121
|
+
if (needsCatalogRefresh) {
|
|
6122
|
+
try {
|
|
6123
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
6124
|
+
const adapter = getAdapter4("ollama");
|
|
6125
|
+
if ("refreshModelCatalog" in adapter) {
|
|
6126
|
+
adapter.refreshModelCatalog();
|
|
6127
|
+
}
|
|
6128
|
+
} catch {
|
|
6129
|
+
}
|
|
6100
6130
|
}
|
|
6101
6131
|
return results;
|
|
6102
6132
|
}
|
|
@@ -10593,6 +10623,7 @@ var init_chat = __esm({
|
|
|
10593
10623
|
const { getMode: getMode3, getCwd: getCwd3, getModel: getModel3, addUsage: addUsage3, getBackend: getBackend3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
10594
10624
|
const { getAdapterForChat: getAdapterForChat2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
10595
10625
|
const chatId = body.chatId;
|
|
10626
|
+
const backend2 = body.backend;
|
|
10596
10627
|
const PERM_LEVEL = { plan: 0, safe: 1, yolo: 2 };
|
|
10597
10628
|
const storedMode = getMode3(chatId);
|
|
10598
10629
|
const requestedMode = body.mode ?? storedMode;
|
|
@@ -10630,7 +10661,8 @@ data: ${JSON.stringify(data)}
|
|
|
10630
10661
|
sendSSE("text", partial);
|
|
10631
10662
|
},
|
|
10632
10663
|
model: model2,
|
|
10633
|
-
permMode: mode
|
|
10664
|
+
permMode: mode,
|
|
10665
|
+
backend: backend2
|
|
10634
10666
|
});
|
|
10635
10667
|
if (response.usage) addUsage3(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2 ?? "unknown", void 0, response.usage.contextSize);
|
|
10636
10668
|
sendSSE("done", JSON.stringify({ text: response.text, usage: response.usage }));
|
|
@@ -10640,7 +10672,7 @@ data: ${JSON.stringify(data)}
|
|
|
10640
10672
|
if (!res.writableEnded) res.end();
|
|
10641
10673
|
}
|
|
10642
10674
|
} else {
|
|
10643
|
-
const response = await askAgent3(chatId, body.message, { cwd, model: model2, permMode: mode });
|
|
10675
|
+
const response = await askAgent3(chatId, body.message, { cwd, model: model2, permMode: mode, backend: backend2 });
|
|
10644
10676
|
if (response.usage) addUsage3(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2 ?? "unknown", void 0, response.usage.contextSize);
|
|
10645
10677
|
jsonResponse(res, { text: response.text, usage: response.usage, sessionId: response.sessionId });
|
|
10646
10678
|
}
|
|
@@ -15621,6 +15653,14 @@ async function handleAdd(chatId, channel, name, host, port) {
|
|
|
15621
15653
|
return;
|
|
15622
15654
|
}
|
|
15623
15655
|
const models = await OllamaService.discoverModels(name);
|
|
15656
|
+
try {
|
|
15657
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
15658
|
+
const adapter = getAdapter4("ollama");
|
|
15659
|
+
if ("refreshModelCatalog" in adapter) {
|
|
15660
|
+
adapter.refreshModelCatalog();
|
|
15661
|
+
}
|
|
15662
|
+
} catch {
|
|
15663
|
+
}
|
|
15624
15664
|
const lines = [
|
|
15625
15665
|
`\u2705 Added "${name}" (${host}:${actualPort})`,
|
|
15626
15666
|
"",
|
|
@@ -15667,6 +15707,14 @@ async function sendDiscover(chatId, channel, serverName) {
|
|
|
15667
15707
|
try {
|
|
15668
15708
|
const { OllamaService } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15669
15709
|
const models = await OllamaService.discoverModels(serverName);
|
|
15710
|
+
try {
|
|
15711
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
15712
|
+
const adapter = getAdapter4("ollama");
|
|
15713
|
+
if ("refreshModelCatalog" in adapter) {
|
|
15714
|
+
adapter.refreshModelCatalog();
|
|
15715
|
+
}
|
|
15716
|
+
} catch {
|
|
15717
|
+
}
|
|
15670
15718
|
if (models.length === 0) {
|
|
15671
15719
|
await channel.sendText(chatId, "No models found. Check server connectivity.", { parseMode: "plain" });
|
|
15672
15720
|
return;
|
|
@@ -17193,12 +17241,28 @@ async function handleVoice(msg, channel) {
|
|
|
17193
17241
|
await channel.sendText(chatId, vLimitMsg, { parseMode: "plain" });
|
|
17194
17242
|
return;
|
|
17195
17243
|
}
|
|
17196
|
-
await channel.sendText(chatId, "Thinking...", { parseMode: "plain" });
|
|
17197
17244
|
const mode = getMode(chatId);
|
|
17198
17245
|
const vModel = resolveModel(chatId);
|
|
17199
17246
|
const vVerbose = getVerboseLevel(chatId);
|
|
17200
|
-
const
|
|
17247
|
+
const adapter = getAdapterForChat(chatId);
|
|
17248
|
+
const modelLabel = formatModelShort(vModel ?? adapter.defaultModel);
|
|
17249
|
+
const showThinking = getShowThinkingUi(chatId);
|
|
17250
|
+
const needsLiveStatus = vVerbose !== "off" || showThinking;
|
|
17251
|
+
let vToolCb;
|
|
17252
|
+
let vLiveStatus = null;
|
|
17253
|
+
if (needsLiveStatus) {
|
|
17254
|
+
const effectiveVerbose = vVerbose === "off" ? "normal" : vVerbose;
|
|
17255
|
+
const ls = makeLiveStatus(chatId, channel, modelLabel, effectiveVerbose, showThinking);
|
|
17256
|
+
vLiveStatus = ls.liveStatus;
|
|
17257
|
+
vToolCb = vVerbose !== "off" ? ls.toolCb : void 0;
|
|
17258
|
+
await vLiveStatus.init();
|
|
17259
|
+
if (showThinking && adapter.id !== "claude") {
|
|
17260
|
+
vLiveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${adapter.displayName}`);
|
|
17261
|
+
}
|
|
17262
|
+
}
|
|
17263
|
+
const sigT0 = Date.now();
|
|
17201
17264
|
const response = await askAgent(chatId, transcript, { cwd: getCwd(chatId), model: vModel, permMode: mode, onToolAction: vToolCb });
|
|
17265
|
+
if (vLiveStatus) await vLiveStatus.finalize(Date.now() - sigT0);
|
|
17202
17266
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, vModel, void 0, response.usage.contextSize);
|
|
17203
17267
|
if (await handleResponseExhaustion(response.text, chatId, msg, channel)) return;
|
|
17204
17268
|
const voiceResponse = ensureReaction(response.text, transcript);
|
|
@@ -17263,8 +17327,25 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
|
|
|
17263
17327
|
const vidModel = resolveModel(chatId);
|
|
17264
17328
|
const vMode = getMode(chatId);
|
|
17265
17329
|
const vidVerbose = getVerboseLevel(chatId);
|
|
17266
|
-
const
|
|
17330
|
+
const vidAdapter = getAdapterForChat(chatId);
|
|
17331
|
+
const vidModelLabel = formatModelShort(vidModel ?? vidAdapter.defaultModel);
|
|
17332
|
+
const vidShowThinking = getShowThinkingUi(chatId);
|
|
17333
|
+
const vidNeedsLiveStatus = vidVerbose !== "off" || vidShowThinking;
|
|
17334
|
+
let vidToolCb;
|
|
17335
|
+
let vidLiveStatus = null;
|
|
17336
|
+
if (vidNeedsLiveStatus) {
|
|
17337
|
+
const effectiveVerbose = vidVerbose === "off" ? "normal" : vidVerbose;
|
|
17338
|
+
const ls = makeLiveStatus(chatId, channel, vidModelLabel, effectiveVerbose, vidShowThinking);
|
|
17339
|
+
vidLiveStatus = ls.liveStatus;
|
|
17340
|
+
vidToolCb = vidVerbose !== "off" ? ls.toolCb : void 0;
|
|
17341
|
+
await vidLiveStatus.init();
|
|
17342
|
+
if (vidShowThinking && vidAdapter.id !== "claude") {
|
|
17343
|
+
vidLiveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${vidAdapter.displayName}`);
|
|
17344
|
+
}
|
|
17345
|
+
}
|
|
17346
|
+
const vidT0 = Date.now();
|
|
17267
17347
|
const response2 = await askAgent(chatId, prompt2, { cwd: getCwd(chatId), model: vidModel, permMode: vMode, onToolAction: vidToolCb });
|
|
17348
|
+
if (vidLiveStatus) await vidLiveStatus.finalize(Date.now() - vidT0);
|
|
17268
17349
|
if (response2.usage) addUsage(chatId, response2.usage.input, response2.usage.output, response2.usage.cacheRead, vidModel, void 0, response2.usage.contextSize);
|
|
17269
17350
|
if (await handleResponseExhaustion(response2.text, chatId, msg, channel)) return;
|
|
17270
17351
|
const vidResponse = ensureReaction(response2.text, caption || "video");
|
|
@@ -17320,8 +17401,25 @@ ${content}
|
|
|
17320
17401
|
const mediaModel = resolveModel(chatId);
|
|
17321
17402
|
const mMode = getMode(chatId);
|
|
17322
17403
|
const mVerbose = getVerboseLevel(chatId);
|
|
17323
|
-
const
|
|
17404
|
+
const mAdapter = getAdapterForChat(chatId);
|
|
17405
|
+
const mModelLabel = formatModelShort(mediaModel ?? mAdapter.defaultModel);
|
|
17406
|
+
const mShowThinking = getShowThinkingUi(chatId);
|
|
17407
|
+
const mNeedsLiveStatus = mVerbose !== "off" || mShowThinking;
|
|
17408
|
+
let mToolCb;
|
|
17409
|
+
let mLiveStatus = null;
|
|
17410
|
+
if (mNeedsLiveStatus) {
|
|
17411
|
+
const effectiveVerbose = mVerbose === "off" ? "normal" : mVerbose;
|
|
17412
|
+
const ls = makeLiveStatus(chatId, channel, mModelLabel, effectiveVerbose, mShowThinking);
|
|
17413
|
+
mLiveStatus = ls.liveStatus;
|
|
17414
|
+
mToolCb = mVerbose !== "off" ? ls.toolCb : void 0;
|
|
17415
|
+
await mLiveStatus.init();
|
|
17416
|
+
if (mShowThinking && mAdapter.id !== "claude") {
|
|
17417
|
+
mLiveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${mAdapter.displayName}`);
|
|
17418
|
+
}
|
|
17419
|
+
}
|
|
17420
|
+
const mT0 = Date.now();
|
|
17324
17421
|
const response = await askAgent(chatId, prompt, { cwd: getCwd(chatId), model: mediaModel, permMode: mMode, onToolAction: mToolCb });
|
|
17422
|
+
if (mLiveStatus) await mLiveStatus.finalize(Date.now() - mT0);
|
|
17325
17423
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, mediaModel, void 0, response.usage.contextSize);
|
|
17326
17424
|
if (await handleResponseExhaustion(response.text, chatId, msg, channel)) return;
|
|
17327
17425
|
const mediaResponse = ensureReaction(response.text, caption || "file");
|
|
@@ -17341,8 +17439,11 @@ var init_media = __esm({
|
|
|
17341
17439
|
init_stt();
|
|
17342
17440
|
init_video();
|
|
17343
17441
|
init_store5();
|
|
17442
|
+
init_chat_settings();
|
|
17443
|
+
init_backends();
|
|
17344
17444
|
init_helpers();
|
|
17345
17445
|
init_response();
|
|
17446
|
+
init_live_status();
|
|
17346
17447
|
MEDIA_INCOMING_PATH = join17(MEDIA_PATH, "incoming");
|
|
17347
17448
|
}
|
|
17348
17449
|
});
|
|
@@ -20747,7 +20848,7 @@ function buildSelectKeyboard(chatId) {
|
|
|
20747
20848
|
const checkmark = isSelected ? "\u2713 " : " ";
|
|
20748
20849
|
const row = [{
|
|
20749
20850
|
label: `${checkmark}${modelInfo.label}`,
|
|
20750
|
-
data: `council:toggle:${adapter.id}:${modelId}
|
|
20851
|
+
data: `council:toggle:${adapter.id}:${modelId}`,
|
|
20751
20852
|
...isSelected ? { style: "success" } : {}
|
|
20752
20853
|
}];
|
|
20753
20854
|
buttons.push(row);
|
|
@@ -21311,7 +21412,7 @@ async function handleStatusCommand(chatId, commandArgs, msg, channel) {
|
|
|
21311
21412
|
`\u{1F507} Voice: ${voice2 ? "on" : "off"} \xB7 Sig: ${modelSig}`,
|
|
21312
21413
|
``,
|
|
21313
21414
|
buildSectionHeader("Session"),
|
|
21314
|
-
`\u{1F4CB} ${sessionId
|
|
21415
|
+
`\u{1F4CB} ${sessionId ?? "no active session"}`,
|
|
21315
21416
|
`\u{1F4C1} ${cwd ?? "default workspace"}`,
|
|
21316
21417
|
`\u{1F4D0} Context: ${ctxBar} ${usedK}K/${maxK}K (${contextPct.toFixed(1)}%)`,
|
|
21317
21418
|
...sqCount > 0 ? [`\u{1F5FA} Side quests: ${sqCount} active`] : [],
|
|
@@ -21605,7 +21706,7 @@ async function handleInfoCommand(chatId, commandArgs, msg, channel) {
|
|
|
21605
21706
|
const alias = aliases.find((a) => a.chatId === chatId);
|
|
21606
21707
|
if (alias) lines.push(`Alias: ${alias.alias}`);
|
|
21607
21708
|
const sessionId = getSessionId(chatId);
|
|
21608
|
-
if (sessionId) lines.push(`Session: ${sessionId
|
|
21709
|
+
if (sessionId) lines.push(`Session: ${sessionId}`);
|
|
21609
21710
|
const backendId = getBackend(chatId) ?? "claude";
|
|
21610
21711
|
const currentModel = getModel(chatId);
|
|
21611
21712
|
lines.push(`Backend: ${backendId}`);
|
|
@@ -23441,7 +23542,9 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
23441
23542
|
if (action === "toggle") {
|
|
23442
23543
|
const backend2 = parts[2];
|
|
23443
23544
|
const model2 = parts[3];
|
|
23444
|
-
const
|
|
23545
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
23546
|
+
const toggleAdapter = getAdapter4(backend2);
|
|
23547
|
+
const label2 = toggleAdapter.availableModels[model2]?.label ?? model2;
|
|
23445
23548
|
const { toggleParticipant: toggleParticipant2, buildSelectKeyboard: buildSelectKeyboard2, hasPendingCouncil: hasPendingCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
|
|
23446
23549
|
if (!hasPendingCouncil2(chatId)) {
|
|
23447
23550
|
await channel.sendText(chatId, "No council wizard active. Use /council to start.", { parseMode: "plain" });
|
|
@@ -26251,7 +26354,10 @@ var init_telegram2 = __esm({
|
|
|
26251
26354
|
{ command: "reflect", description: "Trigger reflection analysis" },
|
|
26252
26355
|
{ command: "optimize", description: "Audit identity files and skills" },
|
|
26253
26356
|
// Ollama
|
|
26254
|
-
{ command: "ollama", description: "Manage Ollama local LLM servers" }
|
|
26357
|
+
{ command: "ollama", description: "Manage Ollama local LLM servers" },
|
|
26358
|
+
// Context & info
|
|
26359
|
+
{ command: "info", description: "Current chat context (ID, topic, sender, settings)" },
|
|
26360
|
+
{ command: "council", description: "Multi-model debate (select models, anonymous rounds)" }
|
|
26255
26361
|
]);
|
|
26256
26362
|
this.bot.on("message", async (ctx) => {
|
|
26257
26363
|
const chatId = ctx.chat.id.toString();
|
|
@@ -26261,8 +26367,12 @@ var init_telegram2 = __esm({
|
|
|
26261
26367
|
if (!authorized) return;
|
|
26262
26368
|
const msg = this.buildIncomingMessage(ctx);
|
|
26263
26369
|
if (!msg) {
|
|
26264
|
-
|
|
26265
|
-
|
|
26370
|
+
const m = ctx.message;
|
|
26371
|
+
const hasUserContent = m && (m.sticker || m.animation || m.contact || m.location || m.poll || m.dice || m.video_note || m.audio);
|
|
26372
|
+
if (hasUserContent) {
|
|
26373
|
+
this.sendText(chatId, "I can handle text, voice, photos, documents, and videos. This message type isn't supported yet.", { parseMode: "plain" }).catch(() => {
|
|
26374
|
+
});
|
|
26375
|
+
}
|
|
26266
26376
|
return;
|
|
26267
26377
|
}
|
|
26268
26378
|
log(`[telegram] Processing ${msg.type}: "${msg.text?.slice(0, 50) || msg.command || "(media)"}"`);
|
|
@@ -27243,6 +27353,8 @@ You are running inside CC-Claw. Your current chat ID, group name, and forum topi
|
|
|
27243
27353
|
- \`/skills\` \u2014 List skills
|
|
27244
27354
|
- \`/skill-install <url>\` \u2014 Install skill from GitHub
|
|
27245
27355
|
- \`/evolve\` \u2014 Self-learning interactive keyboard (includes quick analyze)
|
|
27356
|
+
- \`/info\` \u2014 Current chat context (chatId, topic thread, sender, session, settings, copyable cron target)
|
|
27357
|
+
- \`/council\` \u2014 Multi-model debate wizard (select backends/models, anonymous rounds)
|
|
27246
27358
|
- \`/gemini_accounts\` \u2014 Gemini credential rotation management
|
|
27247
27359
|
- \`/setup-profile\` \u2014 User profile setup wizard
|
|
27248
27360
|
|
|
@@ -27946,7 +28058,7 @@ async function apiGet(path) {
|
|
|
27946
28058
|
req.end();
|
|
27947
28059
|
});
|
|
27948
28060
|
}
|
|
27949
|
-
async function apiPost(path, body) {
|
|
28061
|
+
async function apiPost(path, body, opts) {
|
|
27950
28062
|
const token = getToken();
|
|
27951
28063
|
const payload = JSON.stringify(body);
|
|
27952
28064
|
return new Promise((resolve, reject) => {
|
|
@@ -27958,7 +28070,7 @@ async function apiPost(path, body) {
|
|
|
27958
28070
|
"Content-Length": Buffer.byteLength(payload).toString(),
|
|
27959
28071
|
...token ? { "Authorization": `Bearer ${token}` } : {}
|
|
27960
28072
|
},
|
|
27961
|
-
timeout: 3e4,
|
|
28073
|
+
timeout: opts?.timeout ?? 3e4,
|
|
27962
28074
|
agent: keepAliveAgent
|
|
27963
28075
|
}, (res) => {
|
|
27964
28076
|
const chunks = [];
|
|
@@ -32010,6 +32122,7 @@ async function chatSend(globalOpts, message, cmdOpts) {
|
|
|
32010
32122
|
chatId,
|
|
32011
32123
|
message,
|
|
32012
32124
|
stream: !!cmdOpts.stream,
|
|
32125
|
+
backend: cmdOpts.backend,
|
|
32013
32126
|
model: cmdOpts.model,
|
|
32014
32127
|
cwd: cmdOpts.cwd,
|
|
32015
32128
|
mode: void 0
|
|
@@ -32125,37 +32238,151 @@ var init_chat2 = __esm({
|
|
|
32125
32238
|
}
|
|
32126
32239
|
});
|
|
32127
32240
|
|
|
32241
|
+
// src/cli/theme.ts
|
|
32242
|
+
import pc2 from "picocolors";
|
|
32243
|
+
function setTheme(mode) {
|
|
32244
|
+
currentTheme = mode;
|
|
32245
|
+
}
|
|
32246
|
+
function getCurrentThemeMode() {
|
|
32247
|
+
return currentTheme;
|
|
32248
|
+
}
|
|
32249
|
+
function detectTerminalBackground() {
|
|
32250
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
32251
|
+
if (colorfgbg) {
|
|
32252
|
+
const parts = colorfgbg.split(";");
|
|
32253
|
+
const bg = parseInt(parts[parts.length - 1], 10);
|
|
32254
|
+
if (!isNaN(bg) && bg >= 7) return "light";
|
|
32255
|
+
return "dark";
|
|
32256
|
+
}
|
|
32257
|
+
if (process.env.TERM_PROGRAM === "Apple_Terminal") return "light";
|
|
32258
|
+
return "dark";
|
|
32259
|
+
}
|
|
32260
|
+
function getTheme() {
|
|
32261
|
+
const mode = currentTheme === "auto" ? detectTerminalBackground() : currentTheme;
|
|
32262
|
+
if (mode === "light") {
|
|
32263
|
+
return {
|
|
32264
|
+
prompt: pc2.blue,
|
|
32265
|
+
border: pc2.black,
|
|
32266
|
+
success: (s) => pc2.green(pc2.bold(s)),
|
|
32267
|
+
text: pc2.black,
|
|
32268
|
+
dim: pc2.gray,
|
|
32269
|
+
error: (s) => pc2.red(pc2.bold(s)),
|
|
32270
|
+
warn: pc2.yellow
|
|
32271
|
+
};
|
|
32272
|
+
}
|
|
32273
|
+
return {
|
|
32274
|
+
prompt: pc2.cyan,
|
|
32275
|
+
border: pc2.gray,
|
|
32276
|
+
success: (s) => pc2.green(pc2.bold(s)),
|
|
32277
|
+
text: pc2.white,
|
|
32278
|
+
dim: pc2.dim,
|
|
32279
|
+
error: (s) => pc2.red(pc2.bold(s)),
|
|
32280
|
+
warn: pc2.yellow
|
|
32281
|
+
};
|
|
32282
|
+
}
|
|
32283
|
+
var currentTheme;
|
|
32284
|
+
var init_theme = __esm({
|
|
32285
|
+
"src/cli/theme.ts"() {
|
|
32286
|
+
"use strict";
|
|
32287
|
+
currentTheme = "auto";
|
|
32288
|
+
}
|
|
32289
|
+
});
|
|
32290
|
+
|
|
32291
|
+
// src/cli/tui-completer.ts
|
|
32292
|
+
function tuiCompleter(line) {
|
|
32293
|
+
for (const [cmd, args] of Object.entries(COMMAND_ARGS)) {
|
|
32294
|
+
const prefix = cmd + " ";
|
|
32295
|
+
if (line.startsWith(prefix)) {
|
|
32296
|
+
const partial = line.slice(prefix.length).toLowerCase();
|
|
32297
|
+
const hits = args.filter((a) => a.toLowerCase().startsWith(partial));
|
|
32298
|
+
const completions = hits.map((a) => `${cmd} ${a}`);
|
|
32299
|
+
return [completions.length ? completions : args.map((a) => `${cmd} ${a}`), line];
|
|
32300
|
+
}
|
|
32301
|
+
}
|
|
32302
|
+
if (line.startsWith("/")) {
|
|
32303
|
+
const hits = BASE_COMMANDS.filter((c) => c.startsWith(line));
|
|
32304
|
+
return [hits.length ? hits : [], line];
|
|
32305
|
+
}
|
|
32306
|
+
return [[], line];
|
|
32307
|
+
}
|
|
32308
|
+
var BASE_COMMANDS, COMMAND_ARGS;
|
|
32309
|
+
var init_tui_completer = __esm({
|
|
32310
|
+
"src/cli/tui-completer.ts"() {
|
|
32311
|
+
"use strict";
|
|
32312
|
+
BASE_COMMANDS = [
|
|
32313
|
+
"/quit",
|
|
32314
|
+
"/exit",
|
|
32315
|
+
"/new",
|
|
32316
|
+
"/status",
|
|
32317
|
+
"/stop",
|
|
32318
|
+
"/backend",
|
|
32319
|
+
"/model",
|
|
32320
|
+
"/theme",
|
|
32321
|
+
"/help",
|
|
32322
|
+
"/cost",
|
|
32323
|
+
"/usage",
|
|
32324
|
+
"/memory",
|
|
32325
|
+
"/permissions",
|
|
32326
|
+
"/thinking",
|
|
32327
|
+
"/cwd",
|
|
32328
|
+
"/debug",
|
|
32329
|
+
"/evolve"
|
|
32330
|
+
];
|
|
32331
|
+
COMMAND_ARGS = {
|
|
32332
|
+
"/backend": ["Claude", "Gemini", "Codex", "Cursor"],
|
|
32333
|
+
"/theme": ["dark", "light", "auto"],
|
|
32334
|
+
"/permissions": ["yolo", "safe", "readonly", "plan"],
|
|
32335
|
+
"/thinking": ["off", "low", "medium", "high"]
|
|
32336
|
+
};
|
|
32337
|
+
}
|
|
32338
|
+
});
|
|
32339
|
+
|
|
32128
32340
|
// src/cli/commands/tui.ts
|
|
32129
32341
|
var tui_exports = {};
|
|
32130
32342
|
__export(tui_exports, {
|
|
32131
32343
|
tuiCommand: () => tuiCommand
|
|
32132
32344
|
});
|
|
32133
32345
|
import { createInterface as createInterface10 } from "readline";
|
|
32134
|
-
import
|
|
32346
|
+
import pc3 from "picocolors";
|
|
32347
|
+
import { marked as marked2 } from "marked";
|
|
32348
|
+
import TerminalRenderer from "marked-terminal";
|
|
32135
32349
|
async function tuiCommand(globalOpts, cmdOpts) {
|
|
32136
|
-
const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
32350
|
+
const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
32137
32351
|
if (!await isDaemonRunning2()) {
|
|
32138
32352
|
outputError("DAEMON_OFFLINE", "CC-Claw daemon is not running.\n\n Start it with: cc-claw service start");
|
|
32139
32353
|
process.exit(1);
|
|
32140
32354
|
}
|
|
32141
32355
|
const chatId = resolveChatId2(globalOpts);
|
|
32142
|
-
|
|
32356
|
+
let theme = getTheme();
|
|
32143
32357
|
const rl2 = createInterface10({
|
|
32144
32358
|
input: process.stdin,
|
|
32145
32359
|
output: process.stdout,
|
|
32146
|
-
|
|
32360
|
+
completer: tuiCompleter,
|
|
32147
32361
|
terminal: true
|
|
32148
32362
|
});
|
|
32363
|
+
function updatePrompt() {
|
|
32364
|
+
rl2.setPrompt(theme.prompt("you > "));
|
|
32365
|
+
}
|
|
32366
|
+
console.log("");
|
|
32367
|
+
console.log(theme.text(pc3.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")));
|
|
32368
|
+
console.log(theme.text(pc3.bold(" \u2551 CC-Claw Terminal Chat \u2551")));
|
|
32369
|
+
console.log(theme.text(pc3.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")));
|
|
32370
|
+
try {
|
|
32371
|
+
const health = await apiGet2("/api/health");
|
|
32372
|
+
if (health.ok) {
|
|
32373
|
+
const parts = [];
|
|
32374
|
+
if (health.data.backend) parts.push(`Backend: ${pc3.bold(health.data.backend)}`);
|
|
32375
|
+
if (health.data.model) parts.push(`Model: ${pc3.bold(health.data.model)}`);
|
|
32376
|
+
parts.push(`Uptime: ${Math.floor(health.data.uptime / 60)}m`);
|
|
32377
|
+
console.log(theme.dim(` ${parts.join(" \u2022 ")}`));
|
|
32378
|
+
}
|
|
32379
|
+
} catch {
|
|
32380
|
+
}
|
|
32381
|
+
console.log(theme.dim(` Chat: ${chatId} \u2022 Theme: ${getCurrentThemeMode()}`));
|
|
32149
32382
|
console.log("");
|
|
32150
|
-
console.log(
|
|
32151
|
-
console.log(pc2.dim(" Type a message and press Enter. Commands:"));
|
|
32152
|
-
console.log(pc2.dim(" /quit or /exit \u2014 Exit the TUI"));
|
|
32153
|
-
console.log(pc2.dim(" /new \u2014 Start a new session"));
|
|
32154
|
-
console.log(pc2.dim(" /status \u2014 Show current status"));
|
|
32155
|
-
console.log(pc2.dim(" /stop \u2014 Cancel running task"));
|
|
32156
|
-
console.log(pc2.dim(" /backend <name> \u2014 Switch backend"));
|
|
32157
|
-
console.log(pc2.dim(" /model <name> \u2014 Switch model"));
|
|
32383
|
+
console.log(theme.dim(" Commands: /help for list \u2022 Tab to autocomplete \u2022 Ctrl+C to cancel"));
|
|
32158
32384
|
console.log("");
|
|
32385
|
+
updatePrompt();
|
|
32159
32386
|
rl2.prompt();
|
|
32160
32387
|
rl2.on("line", async (line) => {
|
|
32161
32388
|
const input = line.trim();
|
|
@@ -32164,30 +32391,47 @@ async function tuiCommand(globalOpts, cmdOpts) {
|
|
|
32164
32391
|
return;
|
|
32165
32392
|
}
|
|
32166
32393
|
if (input === "/quit" || input === "/exit") {
|
|
32167
|
-
console.log(
|
|
32394
|
+
console.log(theme.dim("\n Goodbye.\n"));
|
|
32168
32395
|
rl2.close();
|
|
32169
32396
|
process.exit(0);
|
|
32170
32397
|
}
|
|
32398
|
+
if (input === "/help") {
|
|
32399
|
+
console.log("");
|
|
32400
|
+
console.log(theme.text(pc3.bold(" Available Commands:")));
|
|
32401
|
+
console.log(theme.dim(" /quit, /exit \u2014 Exit the TUI"));
|
|
32402
|
+
console.log(theme.dim(" /new \u2014 Start a new session"));
|
|
32403
|
+
console.log(theme.dim(" /status \u2014 Show daemon status"));
|
|
32404
|
+
console.log(theme.dim(" /stop \u2014 Cancel running task"));
|
|
32405
|
+
console.log(theme.dim(" /backend <name> \u2014 Switch backend (Claude/Gemini/Codex/Cursor)"));
|
|
32406
|
+
console.log(theme.dim(" /model <name> \u2014 Switch model"));
|
|
32407
|
+
console.log(theme.dim(" /theme <mode> \u2014 Switch theme (dark/light/auto)"));
|
|
32408
|
+
console.log("");
|
|
32409
|
+
console.log(theme.dim(" Other slash commands (forwarded to daemon):"));
|
|
32410
|
+
console.log(theme.dim(" /cost, /usage, /memory, /permissions, /thinking, /cwd,"));
|
|
32411
|
+
console.log(theme.dim(" /debug, /evolve, /cron, /agents, and more..."));
|
|
32412
|
+
console.log("");
|
|
32413
|
+
rl2.prompt();
|
|
32414
|
+
return;
|
|
32415
|
+
}
|
|
32171
32416
|
if (input === "/new") {
|
|
32172
32417
|
try {
|
|
32173
32418
|
await apiPost2("/api/session/new", { chatId });
|
|
32174
|
-
console.log(
|
|
32419
|
+
console.log(theme.success(" \u2713 Session cleared.\n"));
|
|
32175
32420
|
} catch {
|
|
32176
|
-
console.log(
|
|
32421
|
+
console.log(theme.error(" \u2717 Failed to clear session.\n"));
|
|
32177
32422
|
}
|
|
32178
32423
|
rl2.prompt();
|
|
32179
32424
|
return;
|
|
32180
32425
|
}
|
|
32181
32426
|
if (input === "/status") {
|
|
32182
32427
|
try {
|
|
32183
|
-
const { apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
32184
32428
|
const res = await apiGet2("/api/health");
|
|
32185
32429
|
if (res.ok) {
|
|
32186
|
-
console.log(
|
|
32430
|
+
console.log(theme.success(` \u2713 Daemon: running (uptime ${Math.floor(res.data.uptime / 60)}m)
|
|
32187
32431
|
`));
|
|
32188
32432
|
}
|
|
32189
32433
|
} catch {
|
|
32190
|
-
console.log(
|
|
32434
|
+
console.log(theme.error(" \u2717 Could not reach daemon.\n"));
|
|
32191
32435
|
}
|
|
32192
32436
|
rl2.prompt();
|
|
32193
32437
|
return;
|
|
@@ -32195,21 +32439,26 @@ async function tuiCommand(globalOpts, cmdOpts) {
|
|
|
32195
32439
|
if (input === "/stop") {
|
|
32196
32440
|
try {
|
|
32197
32441
|
await apiPost2("/api/chat/stop", { chatId });
|
|
32198
|
-
console.log(
|
|
32442
|
+
console.log(theme.warn(" \u23F9 Stopping current task...\n"));
|
|
32199
32443
|
} catch {
|
|
32200
|
-
console.log(
|
|
32444
|
+
console.log(theme.dim(" Nothing running.\n"));
|
|
32201
32445
|
}
|
|
32202
32446
|
rl2.prompt();
|
|
32203
32447
|
return;
|
|
32204
32448
|
}
|
|
32205
32449
|
if (input.startsWith("/backend ")) {
|
|
32206
32450
|
const name = input.slice(9).trim();
|
|
32451
|
+
if (!name) {
|
|
32452
|
+
console.log(theme.error(" Usage: /backend <name>\n"));
|
|
32453
|
+
rl2.prompt();
|
|
32454
|
+
return;
|
|
32455
|
+
}
|
|
32207
32456
|
try {
|
|
32208
32457
|
await apiPost2("/api/backend/set", { chatId, backend: name });
|
|
32209
|
-
console.log(
|
|
32458
|
+
console.log(theme.success(` \u2713 Backend switched to ${name}.
|
|
32210
32459
|
`));
|
|
32211
32460
|
} catch {
|
|
32212
|
-
console.log(
|
|
32461
|
+
console.log(theme.error(` \u2717 Failed to switch to ${name}.
|
|
32213
32462
|
`));
|
|
32214
32463
|
}
|
|
32215
32464
|
rl2.prompt();
|
|
@@ -32217,53 +32466,124 @@ async function tuiCommand(globalOpts, cmdOpts) {
|
|
|
32217
32466
|
}
|
|
32218
32467
|
if (input.startsWith("/model ")) {
|
|
32219
32468
|
const name = input.slice(7).trim();
|
|
32469
|
+
if (!name) {
|
|
32470
|
+
console.log(theme.error(" Usage: /model <name>\n"));
|
|
32471
|
+
rl2.prompt();
|
|
32472
|
+
return;
|
|
32473
|
+
}
|
|
32220
32474
|
try {
|
|
32221
32475
|
await apiPost2("/api/model/set", { chatId, model: name });
|
|
32222
|
-
console.log(
|
|
32476
|
+
console.log(theme.success(` \u2713 Model switched to ${name}.
|
|
32223
32477
|
`));
|
|
32224
32478
|
} catch {
|
|
32225
|
-
console.log(
|
|
32479
|
+
console.log(theme.error(` \u2717 Failed to switch to ${name}.
|
|
32226
32480
|
`));
|
|
32227
32481
|
}
|
|
32228
32482
|
rl2.prompt();
|
|
32229
32483
|
return;
|
|
32230
32484
|
}
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
|
|
32234
|
-
|
|
32235
|
-
|
|
32236
|
-
|
|
32237
|
-
|
|
32238
|
-
model: cmdOpts.model,
|
|
32239
|
-
cwd: cmdOpts.cwd
|
|
32240
|
-
});
|
|
32241
|
-
if (response.ok) {
|
|
32242
|
-
const data = response.data;
|
|
32243
|
-
console.log(pc2.bold("cc-claw > ") + (data.text ?? "No response."));
|
|
32244
|
-
if (data.usage) {
|
|
32245
|
-
console.log(pc2.dim(` [${data.usage.input?.toLocaleString()} in / ${data.usage.output?.toLocaleString()} out tokens]`));
|
|
32246
|
-
}
|
|
32247
|
-
console.log("");
|
|
32248
|
-
} else {
|
|
32249
|
-
console.log(pc2.red(` Error: ${JSON.stringify(response.data)}
|
|
32485
|
+
if (input.startsWith("/theme ")) {
|
|
32486
|
+
const name = input.slice(7).trim();
|
|
32487
|
+
if (["dark", "light", "auto"].includes(name)) {
|
|
32488
|
+
setTheme(name);
|
|
32489
|
+
theme = getTheme();
|
|
32490
|
+
updatePrompt();
|
|
32491
|
+
console.log(theme.success(` \u2713 Theme switched to ${name}.
|
|
32250
32492
|
`));
|
|
32493
|
+
} else {
|
|
32494
|
+
console.log(theme.error(" Invalid theme. Use dark, light, or auto.\n"));
|
|
32251
32495
|
}
|
|
32252
|
-
|
|
32253
|
-
|
|
32496
|
+
rl2.prompt();
|
|
32497
|
+
return;
|
|
32498
|
+
}
|
|
32499
|
+
const cmdMatch = input.match(/^\/(\S+)/);
|
|
32500
|
+
if (cmdMatch && !LOCAL_COMMANDS.has("/" + cmdMatch[1])) {
|
|
32501
|
+
console.log(theme.dim(` \u24D8 /${cmdMatch[1]} \u2014 forwarding to AI (interactive menus not yet supported in TUI)
|
|
32254
32502
|
`));
|
|
32503
|
+
await sendToDaemon(rl2, theme, chatId, input, cmdOpts, apiPost2);
|
|
32504
|
+
return;
|
|
32255
32505
|
}
|
|
32256
|
-
rl2
|
|
32506
|
+
await sendToDaemon(rl2, theme, chatId, input, cmdOpts, apiPost2);
|
|
32257
32507
|
});
|
|
32258
32508
|
rl2.on("close", () => process.exit(0));
|
|
32259
32509
|
await new Promise(() => {
|
|
32260
32510
|
});
|
|
32261
32511
|
}
|
|
32512
|
+
async function sendToDaemon(rl2, theme, chatId, input, cmdOpts, apiPost2) {
|
|
32513
|
+
console.log(theme.dim(" Thinking...\n"));
|
|
32514
|
+
rl2.pause();
|
|
32515
|
+
let cancelled = false;
|
|
32516
|
+
const sigintHandler = () => {
|
|
32517
|
+
cancelled = true;
|
|
32518
|
+
apiPost2("/api/chat/stop", { chatId }).catch(() => {
|
|
32519
|
+
});
|
|
32520
|
+
console.log(theme.warn("\n \u23F9 Cancelled.\n"));
|
|
32521
|
+
rl2.resume();
|
|
32522
|
+
rl2.prompt();
|
|
32523
|
+
};
|
|
32524
|
+
const existingListeners = process.listeners("SIGINT");
|
|
32525
|
+
process.removeAllListeners("SIGINT");
|
|
32526
|
+
process.once("SIGINT", sigintHandler);
|
|
32527
|
+
try {
|
|
32528
|
+
const response = await apiPost2("/api/chat", {
|
|
32529
|
+
chatId,
|
|
32530
|
+
message: input,
|
|
32531
|
+
stream: false,
|
|
32532
|
+
model: cmdOpts.model,
|
|
32533
|
+
cwd: cmdOpts.cwd
|
|
32534
|
+
}, { timeout: 6e5 });
|
|
32535
|
+
if (cancelled) return;
|
|
32536
|
+
if (response.ok) {
|
|
32537
|
+
const data = response.data;
|
|
32538
|
+
const textToFormat = data.text ?? "No response.";
|
|
32539
|
+
const markdownOutput = marked2.parse(textToFormat);
|
|
32540
|
+
console.log(theme.text(pc3.bold("cc-claw > ")));
|
|
32541
|
+
console.log(markdownOutput.trimEnd());
|
|
32542
|
+
if (data.usage) {
|
|
32543
|
+
console.log(theme.dim(`
|
|
32544
|
+
[${data.usage.input?.toLocaleString()} in / ${data.usage.output?.toLocaleString()} out tokens]`));
|
|
32545
|
+
}
|
|
32546
|
+
console.log("");
|
|
32547
|
+
} else {
|
|
32548
|
+
console.log(theme.error(` \u2717 Error: ${JSON.stringify(response.data)}
|
|
32549
|
+
`));
|
|
32550
|
+
}
|
|
32551
|
+
} catch (err) {
|
|
32552
|
+
if (cancelled) return;
|
|
32553
|
+
const msg = err.message;
|
|
32554
|
+
if (msg.includes("timed out")) {
|
|
32555
|
+
console.log(theme.error(" \u2717 Request timed out (10 minutes). The agent may still be running.\n"));
|
|
32556
|
+
} else {
|
|
32557
|
+
console.log(theme.error(` \u2717 Error: ${msg}
|
|
32558
|
+
`));
|
|
32559
|
+
}
|
|
32560
|
+
} finally {
|
|
32561
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
32562
|
+
for (const listener of existingListeners) {
|
|
32563
|
+
process.on("SIGINT", listener);
|
|
32564
|
+
}
|
|
32565
|
+
if (!cancelled) {
|
|
32566
|
+
rl2.resume();
|
|
32567
|
+
rl2.prompt();
|
|
32568
|
+
}
|
|
32569
|
+
}
|
|
32570
|
+
}
|
|
32571
|
+
var LOCAL_COMMANDS;
|
|
32262
32572
|
var init_tui = __esm({
|
|
32263
32573
|
"src/cli/commands/tui.ts"() {
|
|
32264
32574
|
"use strict";
|
|
32265
32575
|
init_format2();
|
|
32266
32576
|
init_resolve_chat();
|
|
32577
|
+
init_theme();
|
|
32578
|
+
init_tui_completer();
|
|
32579
|
+
marked2.setOptions({
|
|
32580
|
+
renderer: new TerminalRenderer({
|
|
32581
|
+
showSectionPrefix: false,
|
|
32582
|
+
reflowText: true,
|
|
32583
|
+
width: Math.min(process.stdout.columns || 100, 120)
|
|
32584
|
+
})
|
|
32585
|
+
});
|
|
32586
|
+
LOCAL_COMMANDS = /* @__PURE__ */ new Set(["/quit", "/exit", "/new", "/status", "/stop", "/backend", "/model", "/theme", "/help"]);
|
|
32267
32587
|
}
|
|
32268
32588
|
});
|
|
32269
32589
|
|
|
@@ -32409,6 +32729,8 @@ var init_completion = __esm({
|
|
|
32409
32729
|
"db",
|
|
32410
32730
|
"gemini",
|
|
32411
32731
|
"evolve",
|
|
32732
|
+
"info",
|
|
32733
|
+
"council",
|
|
32412
32734
|
"setup",
|
|
32413
32735
|
"version",
|
|
32414
32736
|
"help",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-claw",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.6",
|
|
4
4
|
"description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex, Cursor), sub-agent orchestration, MCP management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -45,12 +45,14 @@
|
|
|
45
45
|
"dotenv": "^16.4.7",
|
|
46
46
|
"grammy": "^1.35.0",
|
|
47
47
|
"marked": "^9.1.6",
|
|
48
|
+
"marked-terminal": "^7.3.0",
|
|
48
49
|
"picocolors": "^1.1.1",
|
|
49
50
|
"zod": "^4.3.6"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/better-sqlite3": "^7.6.13",
|
|
53
54
|
"@types/bun": "^1.3.10",
|
|
55
|
+
"@types/marked-terminal": "^6.1.1",
|
|
54
56
|
"@types/node": "^25.3.5",
|
|
55
57
|
"@vitest/coverage-v8": "^4.0.18",
|
|
56
58
|
"tsup": "^8.5.1",
|