Skip to content

Commit 4e3e429

Browse files
authored
Merge pull request #15 from IMvision12/logs
Fix logs permission issue and tui
2 parents 823e17b + 1ead3df commit 4e3e429

14 files changed

Lines changed: 290 additions & 45 deletions

File tree

src/cli/commands/interactive.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import chalk from "chalk";
2-
import { logger } from "../../shared/logger";
32
import { showMainMenu, showGoodbyeScreen, pressAnyKey } from "../tui";
43
import { authCommand, loadConfig } from "./auth";
54
import { configCommand } from "./config";
@@ -11,9 +10,6 @@ import { startCommand } from "./start";
1110
export async function interactiveMode(): Promise<void> {
1211
let running = true;
1312

14-
// Initialize logger early so logs dir is created on first run
15-
logger.debug("TxtCode interactive mode started");
16-
1713
while (running) {
1814
console.clear();
1915

src/cli/commands/logs.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ export function logsCommand(session: string | undefined, options: LogsOptions) {
126126
console.log(` ${chalk.white(`[${i + 1}]`)} ${ts} ${chalk.gray(`(${sizeStr})`)}${label}`);
127127
}
128128

129-
console.log("");
130-
console.log(chalk.gray(" txtcode logs <number> View a session"));
131-
console.log(chalk.gray(" txtcode logs -f Follow the latest session"));
132-
console.log(chalk.gray(" txtcode logs --clear Delete all logs"));
133129
console.log("");
134130
return;
135131
}

src/cli/commands/start.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,31 +148,55 @@ export async function startCommand(_options: { daemon?: boolean }) {
148148
process.on("SIGINT", shutdownHandler);
149149
process.on("SIGTERM", shutdownHandler);
150150

151+
// Set up Enter key listener to stop the agent
152+
const waitForEnter = new Promise<void>((resolve) => {
153+
process.stdin.setRawMode?.(false);
154+
process.stdin.resume();
155+
process.stdin.once("data", () => resolve());
156+
});
157+
151158
try {
159+
let bot: { start(): Promise<void> };
160+
152161
if (config.platform === "whatsapp") {
153-
const bot = new WhatsAppBot(agent);
154-
await bot.start();
162+
bot = new WhatsAppBot(agent);
155163
} else if (config.platform === "telegram") {
156-
const bot = new TelegramBot(agent);
157-
await bot.start();
164+
bot = new TelegramBot(agent);
158165
} else if (config.platform === "discord") {
159-
const bot = new DiscordBot(agent);
160-
await bot.start();
166+
bot = new DiscordBot(agent);
161167
} else if (config.platform === "slack") {
162-
const bot = new SlackBot(agent);
163-
await bot.start();
168+
bot = new SlackBot(agent);
164169
} else if (config.platform === "teams") {
165-
const bot = new TeamsBot(agent);
166-
await bot.start();
170+
bot = new TeamsBot(agent);
167171
} else if (config.platform === "signal") {
168-
const bot = new SignalBot(agent);
169-
await bot.start();
172+
bot = new SignalBot(agent);
170173
} else {
171174
logger.error("Invalid platform specified");
172175
process.exit(1);
173176
}
177+
178+
// Start bot without blocking — race with Enter key
179+
bot.start().catch((error) => {
180+
logger.error("Failed to start agent", error);
181+
process.exit(1);
182+
});
183+
184+
// Show message after a short delay to let the bot print its startup logs
185+
setTimeout(() => {
186+
console.log(chalk.gray("\nPress Enter to stop the agent...\n"));
187+
}, 2000);
188+
189+
// Wait for user to press Enter
190+
await waitForEnter;
191+
192+
logger.debug("User requested stop via Enter key");
193+
process.stdin.pause();
194+
await agent.shutdown();
195+
// Return to main menu instead of exiting
196+
return;
174197
} catch (error) {
175198
logger.error("Failed to start agent", error);
176-
process.exit(1);
199+
// Return to main menu instead of exiting
200+
return;
177201
}
178202
}

src/core/router.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,25 @@ export class Router {
249249
return "[WARN] AI model not configured. Run: txtcode config";
250250
}
251251

252+
logger.debug(`[Router] Chat → provider=${this.provider}, model=${this.model}`);
253+
const startTime = Date.now();
254+
255+
try {
256+
const result = await this._routeToProvider(instruction);
257+
logger.debug(
258+
`[Router] Chat complete → provider=${this.provider}, time=${Date.now() - startTime}ms, response=${result.length} chars`,
259+
);
260+
return result;
261+
} catch (error) {
262+
logger.error(
263+
`[Router] Chat failed → provider=${this.provider}, time=${Date.now() - startTime}ms`,
264+
error,
265+
);
266+
throw error;
267+
}
268+
}
269+
270+
private async _routeToProvider(instruction: string): Promise<string> {
252271
switch (this.provider) {
253272
case "anthropic":
254273
return await processWithAnthropic(instruction, this.apiKey, this.model, this.toolRegistry);
@@ -287,6 +306,9 @@ export class Router {
287306
this.currentAbortController = new AbortController();
288307
const signal = this.currentAbortController.signal;
289308

309+
logger.debug(`[Router] Code → adapter=${this.currentAdapterName}`);
310+
const startTime = Date.now();
311+
290312
try {
291313
this.contextManager.addEntry("user", instruction);
292314

@@ -307,6 +329,10 @@ export class Router {
307329

308330
this.contextManager.addEntry("assistant", result);
309331

332+
logger.debug(
333+
`[Router] Code complete → adapter=${this.currentAdapterName}, time=${Date.now() - startTime}ms, response=${result.length} chars`,
334+
);
335+
310336
return result;
311337
} finally {
312338
this.currentAbortController = null;

src/providers/anthropic.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
ToolUnion,
1010
ToolUseBlock,
1111
} from "@anthropic-ai/sdk/resources/messages/messages";
12+
import { logger } from "../shared/logger";
1213
import { ToolRegistry } from "../tools/registry";
1314

1415
const MAX_ITERATIONS = 10;
@@ -28,6 +29,9 @@ export async function processWithAnthropic(
2829
model: string,
2930
toolRegistry?: ToolRegistry,
3031
): Promise<string> {
32+
const startTime = Date.now();
33+
logger.debug(`[Anthropic] Request → model=${model}, prompt=${instruction.length} chars`);
34+
3135
try {
3236
const anthropic = new Anthropic({ apiKey });
3337

@@ -38,6 +42,7 @@ export async function processWithAnthropic(
3842
const messages: MessageParam[] = [{ role: "user", content: instruction }];
3943

4044
for (let i = 0; i < MAX_ITERATIONS; i++) {
45+
const iterStart = Date.now();
4146
const response = await anthropic.messages.create({
4247
model,
4348
max_tokens: 4096,
@@ -46,6 +51,12 @@ export async function processWithAnthropic(
4651
...(tools ? { tools } : {}),
4752
});
4853

54+
logger.debug(
55+
`[Anthropic] Response ← iteration=${i + 1}, stop=${response.stop_reason}, ` +
56+
`tokens=${response.usage.input_tokens}in/${response.usage.output_tokens}out, ` +
57+
`time=${Date.now() - iterStart}ms`,
58+
);
59+
4960
const textParts = response.content
5061
.filter((block: ContentBlock): block is TextBlock => block.type === "text")
5162
.map((block: TextBlock) => block.text);
@@ -55,9 +66,12 @@ export async function processWithAnthropic(
5566
);
5667

5768
if (toolCalls.length === 0 || !toolRegistry) {
69+
logger.debug(`[Anthropic] Done in ${Date.now() - startTime}ms (${i + 1} iteration(s))`);
5870
return textParts.join("\n") || "No response from Claude";
5971
}
6072

73+
logger.debug(`[Anthropic] Tool calls: ${toolCalls.map((t) => t.name).join(", ")}`);
74+
6175
messages.push({ role: "assistant", content: response.content });
6276

6377
const toolResults: ToolResultBlockParam[] = [];
@@ -76,8 +90,10 @@ export async function processWithAnthropic(
7690
messages.push({ role: "user", content: toolResults });
7791
}
7892

93+
logger.warn(`[Anthropic] Reached max ${MAX_ITERATIONS} iterations`);
7994
return "Reached maximum tool iterations.";
8095
} catch (error: unknown) {
96+
logger.error(`[Anthropic] API error after ${Date.now() - startTime}ms`, error);
8197
throw new Error(
8298
`Anthropic API error: ${error instanceof Error ? error.message : "Unknown error"}`,
8399
{ cause: error },

src/providers/gemini.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type Tool as GeminiTool,
66
GoogleGenerativeAI,
77
} from "@google/generative-ai";
8+
import { logger } from "../shared/logger";
89
import { ToolRegistry } from "../tools/registry";
910

1011
const MAX_ITERATIONS = 10;
@@ -24,6 +25,9 @@ export async function processWithGemini(
2425
model: string,
2526
toolRegistry?: ToolRegistry,
2627
): Promise<string> {
28+
const startTime = Date.now();
29+
logger.debug(`[Gemini] Request → model=${model}, prompt=${instruction.length} chars`);
30+
2731
try {
2832
const genAI = new GoogleGenerativeAI(apiKey);
2933

@@ -38,16 +42,26 @@ export async function processWithGemini(
3842
});
3943

4044
const chat = genModel.startChat();
45+
let iterStart = Date.now();
4146
let result = await chat.sendMessage(instruction);
4247

4348
for (let i = 0; i < MAX_ITERATIONS; i++) {
4449
const response = result.response;
4550
const calls = response.functionCalls();
4651

52+
logger.debug(
53+
`[Gemini] Response ← iteration=${i + 1}, ` +
54+
`toolCalls=${calls?.length ?? 0}, ` +
55+
`time=${Date.now() - iterStart}ms`,
56+
);
57+
4758
if (!calls || calls.length === 0 || !toolRegistry) {
59+
logger.debug(`[Gemini] Done in ${Date.now() - startTime}ms (${i + 1} iteration(s))`);
4860
return response.text();
4961
}
5062

63+
logger.debug(`[Gemini] Tool calls: ${calls.map((c) => c.name).join(", ")}`);
64+
5165
const toolResults: FunctionResponsePart[] = [];
5266
for (const call of calls) {
5367
const execResult = await toolRegistry.execute(
@@ -62,11 +76,14 @@ export async function processWithGemini(
6276
});
6377
}
6478

79+
iterStart = Date.now();
6580
result = await chat.sendMessage(toolResults);
6681
}
6782

83+
logger.warn(`[Gemini] Reached max ${MAX_ITERATIONS} iterations`);
6884
return "Reached maximum tool iterations.";
6985
} catch (error: unknown) {
86+
logger.error(`[Gemini] API error after ${Date.now() - startTime}ms`, error);
7087
throw new Error(
7188
`Gemini API error: ${error instanceof Error ? error.message : "Unknown error"}`,
7289
{ cause: error },

src/providers/huggingface.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
ChatCompletionMessageParam,
66
ChatCompletionTool,
77
} from "openai/resources/chat/completions/completions";
8+
import { logger } from "../shared/logger";
89
import { ToolRegistry } from "../tools/registry";
910

1011
const MAX_ITERATIONS = 10;
@@ -25,6 +26,9 @@ export async function processWithHuggingFace(
2526
model: string,
2627
toolRegistry?: ToolRegistry,
2728
): Promise<string> {
29+
const startTime = Date.now();
30+
logger.debug(`[HuggingFace] Request → model=${model}, prompt=${instruction.length} chars`);
31+
2832
try {
2933
// HuggingFace Inference Providers use OpenAI-compatible API
3034
const client = new OpenAI({
@@ -42,6 +46,7 @@ export async function processWithHuggingFace(
4246
];
4347

4448
for (let i = 0; i < MAX_ITERATIONS; i++) {
49+
const iterStart = Date.now();
4550
const completion = await client.chat.completions.create({
4651
model,
4752
messages,
@@ -52,10 +57,21 @@ export async function processWithHuggingFace(
5257
const choice = completion.choices[0];
5358
const assistantMsg = choice.message;
5459

60+
logger.debug(
61+
`[HuggingFace] Response ← iteration=${i + 1}, finish=${choice.finish_reason}, ` +
62+
`tokens=${completion.usage?.prompt_tokens ?? "?"}in/${completion.usage?.completion_tokens ?? "?"}out, ` +
63+
`time=${Date.now() - iterStart}ms`,
64+
);
65+
5566
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0 || !toolRegistry) {
67+
logger.debug(`[HuggingFace] Done in ${Date.now() - startTime}ms (${i + 1} iteration(s))`);
5668
return assistantMsg.content || "No response from HuggingFace";
5769
}
5870

71+
logger.debug(
72+
`[HuggingFace] Tool calls: ${assistantMsg.tool_calls.map((t) => ("function" in t ? t.function.name : t.type)).join(", ")}`,
73+
);
74+
5975
messages.push(assistantMsg);
6076

6177
for (const toolCall of assistantMsg.tool_calls) {
@@ -72,8 +88,10 @@ export async function processWithHuggingFace(
7288
}
7389
}
7490

91+
logger.warn(`[HuggingFace] Reached max ${MAX_ITERATIONS} iterations`);
7592
return "Reached maximum tool iterations.";
7693
} catch (error: unknown) {
94+
logger.error(`[HuggingFace] API error after ${Date.now() - startTime}ms`, error);
7795
throw new Error(
7896
`HuggingFace API error: ${error instanceof Error ? error.message : "Unknown error"}`,
7997
{ cause: error },

src/providers/minimax.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
ToolUnion,
1010
ToolUseBlock,
1111
} from "@anthropic-ai/sdk/resources/messages/messages";
12+
import { logger } from "../shared/logger";
1213
import { ToolRegistry } from "../tools/registry";
1314

1415
const MAX_ITERATIONS = 10;
@@ -29,6 +30,9 @@ export async function processWithMiniMax(
2930
model: string,
3031
toolRegistry?: ToolRegistry,
3132
): Promise<string> {
33+
const startTime = Date.now();
34+
logger.debug(`[MiniMax] Request → model=${model}, prompt=${instruction.length} chars`);
35+
3236
try {
3337
// MiniMax uses Anthropic-compatible API
3438
const client = new Anthropic({
@@ -43,6 +47,7 @@ export async function processWithMiniMax(
4347
const messages: MessageParam[] = [{ role: "user", content: instruction }];
4448

4549
for (let i = 0; i < MAX_ITERATIONS; i++) {
50+
const iterStart = Date.now();
4651
const response = await client.messages.create({
4752
model,
4853
max_tokens: 4096,
@@ -51,6 +56,12 @@ export async function processWithMiniMax(
5156
...(tools ? { tools } : {}),
5257
});
5358

59+
logger.debug(
60+
`[MiniMax] Response ← iteration=${i + 1}, stop=${response.stop_reason}, ` +
61+
`tokens=${response.usage.input_tokens}in/${response.usage.output_tokens}out, ` +
62+
`time=${Date.now() - iterStart}ms`,
63+
);
64+
5465
const textParts = response.content
5566
.filter((block: ContentBlock): block is TextBlock => block.type === "text")
5667
.map((block: TextBlock) => block.text);
@@ -60,9 +71,12 @@ export async function processWithMiniMax(
6071
);
6172

6273
if (toolCalls.length === 0 || !toolRegistry) {
74+
logger.debug(`[MiniMax] Done in ${Date.now() - startTime}ms (${i + 1} iteration(s))`);
6375
return textParts.join("\n") || "No response from MiniMax";
6476
}
6577

78+
logger.debug(`[MiniMax] Tool calls: ${toolCalls.map((t) => t.name).join(", ")}`);
79+
6680
messages.push({ role: "assistant", content: response.content });
6781

6882
const toolResults: ToolResultBlockParam[] = [];
@@ -81,8 +95,10 @@ export async function processWithMiniMax(
8195
messages.push({ role: "user", content: toolResults });
8296
}
8397

98+
logger.warn(`[MiniMax] Reached max ${MAX_ITERATIONS} iterations`);
8499
return "Reached maximum tool iterations.";
85100
} catch (error: unknown) {
101+
logger.error(`[MiniMax] API error after ${Date.now() - startTime}ms`, error);
86102
throw new Error(
87103
`MiniMax API error: ${error instanceof Error ? error.message : "Unknown error"}`,
88104
{ cause: error },

0 commit comments

Comments
 (0)