← back to kcisoul__remotecode

Function bodies 137 total

All specs Real LLM only Function bodies
removePid function · typescript · L54-L61 (8 LOC)
src/daemon.ts
function removePid(): void {
  try {
    const content = fs.readFileSync(pidFilePath(), "utf-8").trim();
    if (content === String(process.pid)) {
      fs.unlinkSync(pidFilePath());
    }
  } catch (err) { logger.debug("daemon", `removePid: ${errorMessage(err)}`); }
}
killOrphanDaemons function · typescript · L64-L79 (16 LOC)
src/daemon.ts
export function killOrphanDaemons(): void {
  try {
    const out = execSync("ps -eo pid,args", { stdio: "pipe" }).toString();
    const myPid = process.pid;
    for (const line of out.split("\n")) {
      if (!line.includes("--daemon")) continue;
      if (!line.includes("remotecode") && !line.includes("index.js") && !line.includes("index.ts")) continue;
      const pid = parseInt(line.trim(), 10);
      if (isNaN(pid) || pid === myPid) continue;
      try {
        process.kill(pid, "SIGTERM");
        console.log(`  Killed orphan daemon (pid ${pid})`);
      } catch (err) { logger.debug("daemon", `kill orphan ${pid}: ${errorMessage(err)}`); }
    }
  } catch (err) { logger.debug("daemon", `killOrphanDaemons: ${errorMessage(err)}`); }
}
setupLogging function · typescript · L84-L122 (39 LOC)
src/daemon.ts
function setupLogging(): void {
  const logPath = logFilePath();
  let logStream = fs.createWriteStream(logPath, { flags: "a" });
  let byteCount = 0;
  try {
    byteCount = fs.statSync(logPath).size;
  } catch { /* file may not exist yet */ }

  function rotateIfNeeded(bytes: number): void {
    byteCount += bytes;
    if (byteCount >= LOG_MAX_BYTES) {
      logStream.end();
      try {
        fs.renameSync(logPath, logPath + ".old");
      } catch (err) { logger.debug("daemon", `log rotate: ${errorMessage(err)}`); }
      logStream = fs.createWriteStream(logPath, { flags: "a" });
      byteCount = 0;
      patchWrite();
    }
  }

  function patchWrite(): void {
    process.stdout.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
      const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
      logStream.write(buf);
      rotateIfNeeded(buf.length);
      return true;
    };

    process.stderr.write = (chunk: string | Uint8Array, ...ar
rotateIfNeeded function · typescript · L92-L103 (12 LOC)
src/daemon.ts
  function rotateIfNeeded(bytes: number): void {
    byteCount += bytes;
    if (byteCount >= LOG_MAX_BYTES) {
      logStream.end();
      try {
        fs.renameSync(logPath, logPath + ".old");
      } catch (err) { logger.debug("daemon", `log rotate: ${errorMessage(err)}`); }
      logStream = fs.createWriteStream(logPath, { flags: "a" });
      byteCount = 0;
      patchWrite();
    }
  }
patchWrite function · typescript · L105-L119 (15 LOC)
src/daemon.ts
  function patchWrite(): void {
    process.stdout.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
      const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
      logStream.write(buf);
      rotateIfNeeded(buf.length);
      return true;
    };

    process.stderr.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
      const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
      logStream.write(buf);
      rotateIfNeeded(buf.length);
      return true;
    };
  }
registerCommands function · typescript · L125-L138 (14 LOC)
src/daemon.ts
async function registerCommands(config: TelegramConfig): Promise<void> {
  const commands = [
    { command: "start", description: "Welcome message and quick actions" },
    { command: "help", description: "Show help and commands" },
    { command: "sessions", description: "List local Claude Code sessions" },
    { command: "projects", description: "Browse sessions by project" },
    { command: "new", description: "New Claude session" },
    { command: "history", description: "Show last 10 turns of current session" },
    { command: "cancel", description: "Cancel the current task" },
    { command: "model", description: "Switch Claude model" },
    { command: "sync", description: "Toggle auto-sync notifications" },
  ];
  await setMyCommands(config, commands);
}
saveChatId function · typescript · L140-L145 (6 LOC)
src/daemon.ts
function saveChatId(sessionsFile: string, chatId: number): void {
  let lines = readEnvLines(sessionsFile);
  lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_CHAT_ID="));
  lines.push(`REMOTECODE_CHAT_ID=${chatId}`);
  writeEnvLines(sessionsFile, lines);
}
If a scraper extracted this row, it came from Repobility (https://repobility.com)
processUpdate function · typescript · L147-L178 (32 LOC)
src/daemon.ts
async function processUpdate(update: Update, ctx: HandlerContext): Promise<void> {
  try {
    const chatId = update.message?.chat.id || update.callback_query?.message?.chat.id;
    if (chatId) saveChatId(ctx.sessionsFile, chatId);

    const from = update.message?.from || update.callback_query?.from;
    const msg = update.message;
    const content = msg?.text
      ? `text="${msg.text}"`
      : msg?.voice ? `voice ${msg.voice.duration || 0}s`
      : msg?.audio ? `audio "${msg.audio.file_name || "unknown"}"`
      : msg?.photo ? `photo${msg.caption ? ` "${msg.caption}"` : ""}`
      : msg?.document ? `document "${msg.document.file_name || "unknown"}"`
      : update.callback_query ? `callback "${update.callback_query.data || ""}"`
      : "unknown";
    logger.info("poll", `Update ${update.update_id} from=${from?.username || from?.id || "unknown"} ${content}`);
    if (update.callback_query) {
      await handleCallbackQuery(update.callback_query, ctx);
    } else if (update.messag
pollLoop function · typescript · L180-L252 (73 LOC)
src/daemon.ts
async function pollLoop(telegramConfig: TelegramConfig, ctx: HandlerContext): Promise<void> {
  let offset = 0;
  let conflictCount = 0;
  const MAX_CONFLICT_RETRIES = 3;

  // Flush pending updates from before daemon start to avoid processing stale messages
  try {
    const pending = await getUpdates(telegramConfig, -1, 0);
    if (pending.length > 0) {
      offset = pending[pending.length - 1].update_id + 1;
      const texts = pending
        .map((u) => u.message?.text || u.callback_query?.data || "")
        .filter(Boolean);
      logger.info("poll", `Flushed ${pending.length} pending update(s)`);
      const chatId = pending[0].message?.chat.id || pending[0].callback_query?.message?.chat.id;
      if (chatId) {
        const dropped = texts.length > 0 ? `\n\nDropped: ${texts.join(", ")}` : "";
        await sendMessage(telegramConfig, chatId, `RemoteCode restarted.${dropped}`);
      }
    }
  } catch (err) {
    logger.debug("poll", `flush pending: ${errorMessage(err)}`);
  }
daemonMain function · typescript · L259-L315 (57 LOC)
src/daemon.ts
export async function daemonMain(): Promise<void> {
  ensureConfigDir();
  setupLogging();
  writePid();

  const cfgPath = loadConfig();
  if (cfgPath) logger.info("config", `Loaded: ${cfgPath}`);
  const config = getConfig();

  if (config.yolo && isPrivileged()) {
    logger.error("daemon", "YOLO mode cannot run with root/sudo privileges (Claude Code restriction).");
    logger.error("daemon", "Either switch to a non-root user or set REMOTECODE_YOLO=false in config.");
    removePid();
    process.exit(1);
  }
  const telegramConfig: TelegramConfig = { botToken: config.botToken };

  logger.info("daemon", "RemoteCode daemon starting...");
  if (config.verbose) logger.info("daemon", "Verbose logging enabled");

  try {
    await deleteWebhook(telegramConfig);
    logger.debug("daemon", "Webhook cleared for long polling");
  } catch (err) {
    logger.warn("daemon", `Failed to clear webhook: ${errorMessage(err)}`);
  }

  try {
    await registerCommands(telegramConfig);
    logger.de
spawnDaemon function · typescript · L318-L348 (31 LOC)
src/daemon.ts
export async function spawnDaemon(): Promise<void> {
  const logPath = logFilePath();
  const logFd = fs.openSync(logPath, "a");

  const env = { ...process.env };
  delete env.CLAUDECODE; // Allow SDK to spawn Claude Code subprocesses

  const child = spawn(process.execPath, [...process.execArgv, process.argv[1], "--daemon"], {
    detached: true,
    stdio: ["ignore", logFd, logFd],
    env,
  });
  child.unref();
  fs.closeSync(logFd);

  const childPid = child.pid;
  if (!childPid) {
    console.error("Failed to spawn daemon.");
    process.exit(1);
  }

  // Wait a moment and verify the daemon is still running
  await new Promise((resolve) => setTimeout(resolve, 1000));
  if (isRunning(childPid)) {
    console.log(`  Daemon running in background (pid ${childPid})\n`);
  } else {
    console.error("  Daemon exited immediately. Check logs:");
    console.error(`  ${logPath}\n`);
    process.exit(1);
  }
}
convertTables function · typescript · L6-L48 (43 LOC)
src/format.ts
function convertTables(text: string): string {
  const lines = text.split("\n");
  const result: string[] = [];
  let i = 0;

  while (i < lines.length) {
    // Detect table: header row with |, separator row with |---
    if (
      i + 1 < lines.length &&
      /^\|(.+\|)+\s*$/.test(lines[i]) &&
      /^\|[\s:]*-+/.test(lines[i + 1])
    ) {
      const headers = lines[i].split("|").map(c => c.trim()).filter(Boolean);
      const rows: string[][] = [];
      i += 2; // skip header + separator

      while (i < lines.length && /^\|(.+\|)+\s*$/.test(lines[i])) {
        rows.push(lines[i].split("|").map(c => c.trim()).filter(Boolean));
        i++;
      }

      const bulletLines: string[] = [];
      for (const row of rows) {
        bulletLines.push(`• ${headers[0]}: ${row[0] || ""}`);
        for (let c = 1; c < headers.length; c++) {
          bulletLines.push(`  ${headers[c]}: ${row[c] || ""}`);
        }
        bulletLines.push("");
      }
      // Remove trailing empty line
 
formatToolDetail function · typescript · L116-L141 (26 LOC)
src/format.ts
function formatToolDetail(toolName: string, input: Record<string, unknown>): string {
  const e = escapeHtml;
  switch (toolName) {
    case "Bash":
      return `${e(String(input.command || "").slice(0, 200))}`;
    case "Edit":
    case "Write":
    case "Read":
      return `${e(String(input.file_path || ""))}`;
    case "Glob":
    case "Grep":
      return `${e(String(input.pattern || ""))}`;
    case "Task":
      return `${e(String(input.description || ""))}`;
    case "WebSearch":
      return `${e(String(input.query || "").slice(0, 200))}`;
    case "WebFetch":
      return `${e(String(input.url || "").slice(0, 200))}`;
    case "NotebookEdit":
      return `${e(String(input.notebook_path || ""))}`;
    case "Skill":
      return `${e(String(input.skill || ""))}`;
    default:
      return "";
  }
}
detectBashLang function · typescript · L143-L153 (11 LOC)
src/format.ts
export function detectBashLang(command: string): string {
  const cmd = command.trimStart();
  if (/^python[23]?\s/.test(cmd)) return "python";
  if (/^node\s/.test(cmd)) return "javascript";
  if (/^ruby\s/.test(cmd)) return "ruby";
  if (/^go\s/.test(cmd)) return "go";
  if (/^cargo\s|^rustc\s/.test(cmd)) return "rust";
  if (/^swift\s|^swiftc\s/.test(cmd)) return "swift";
  if (/^java\s|^javac\s|^gradle\s|^mvn\s/.test(cmd)) return "java";
  return "bash";
}
toolLanguage function · typescript · L155-L168 (14 LOC)
src/format.ts
function toolLanguage(toolName: string, input: Record<string, unknown>): string {
  switch (toolName) {
    case "Bash": return detectBashLang(String(input.command || ""));
    case "Edit":
    case "Write":
    case "Read":
    case "Glob":
    case "Grep": return "bash";
    case "WebSearch":
    case "WebFetch": return "bash";
    case "Task": return "bash";
    default: return "bash";
  }
}
Open data scored by Repobility · https://repobility.com
formatToolDescription function · typescript · L170-L177 (8 LOC)
src/format.ts
export function formatToolDescription(toolName: string, input: Record<string, unknown>): string {
  const lang = toolLanguage(toolName, input);
  const detail = formatToolDetail(toolName, input);
  if (detail) {
    return `<pre><code class="language-${lang}">${escapeHtml(toolName)}: ${detail}</code></pre>`;
  }
  return `<pre><code class="language-${lang}">${escapeHtml(toolName)}</code></pre>`;
}
mimeFromExt function · typescript · L52-L58 (7 LOC)
src/handler.ts
function mimeFromExt(filePath: string): string {
  const ext = path.extname(filePath).toLowerCase();
  if (ext === ".png") return "image/png";
  if (ext === ".gif") return "image/gif";
  if (ext === ".webp") return "image/webp";
  return "image/jpeg";
}
formatPrompt function · typescript · L60-L72 (13 LOC)
src/handler.ts
function formatPrompt(prompt: string, imagePaths?: string[]): MessageContent {
  if (!imagePaths || imagePaths.length === 0) return prompt;
  const blocks: Array<
    | { type: "text"; text: string }
    | { type: "image"; source: { type: "base64"; media_type: string; data: string } }
  > = [];
  if (prompt) blocks.push({ type: "text", text: prompt });
  for (const p of imagePaths) {
    const data = fs.readFileSync(p).toString("base64");
    blocks.push({ type: "image", source: { type: "base64", media_type: mimeFromExt(p), data } });
  }
  return blocks;
}
downloadAndSaveImage function · typescript · L81-L89 (9 LOC)
src/handler.ts
async function downloadAndSaveImage(config: TelegramConfig, fileId: string): Promise<string> {
  const { data, filePath } = await downloadFile(config, fileId);
  const ext = path.extname(filePath) || ".jpg";
  const tempDir = ensureTempDir();
  const filename = `image_${uuidv4().replace(/-/g, "")}${ext}`;
  const savePath = path.join(tempDir, filename);
  fs.writeFileSync(savePath, data);
  return savePath;
}
pickBestPhotoId function · typescript · L91-L100 (10 LOC)
src/handler.ts
function pickBestPhotoId(photos: Array<{ file_id: string; file_size?: number; width?: number; height?: number }>): string | null {
  if (photos.length === 0) return null;
  let best = photos[0];
  for (const photo of photos) {
    const score = (p: typeof photo) =>
      p.file_size != null && p.file_size > 0 ? p.file_size : (p.width ?? 0) * (p.height ?? 0);
    if (score(photo) > score(best)) best = photo;
  }
  return best.file_id;
}
startTyping function · typescript · L114-L131 (18 LOC)
src/handler.ts
function startTyping(config: TelegramConfig, chatId: number): TypingHandle {
  let interval: ReturnType<typeof setInterval> | null = null;

  const start = () => {
    if (interval) return;
    silentCatch("typing", "sendChatAction", sendChatAction(config, chatId));
    interval = setInterval(() => {
      silentCatch("typing", "sendChatAction", sendChatAction(config, chatId));
    }, 4000);
  };

  const stop = () => {
    if (interval) { clearInterval(interval); interval = null; }
  };

  start();
  return { stop, pause: stop, resume: start };
}
buildCanUseTool function · typescript · L157-L284 (128 LOC)
src/handler.ts
function buildCanUseTool(ctx: HandlerContext, chatId: number, messageId: number, sessionId: string, flushRef?: FlushRef, toolMsgRef?: ToolMsgRef): CanUseToolFn {
  // Serialize permission dialogs so only one is shown at a time
  let permGate: Promise<void> = Promise.resolve();

  return async (toolName, input, { decisionReason, toolUseID }) => {
    // Helper: reveal tool block and return allow
    const allowWithReveal = async () => {
      if (toolMsgRef) await toolMsgRef.revealBlock(toolUseID);
      return { behavior: "allow" as const, updatedInput: input };
    };

    // Guard: if session was suppressed (switched away), auto-allow without UI
    if (isSessionSuppressed(sessionId)) {
      return { behavior: "allow" as const, updatedInput: input };
    }

    // 1) AskUserQuestion → inline keyboard with options
    if (toolName === "AskUserQuestion") {
      // Flush accumulated text so the user sees context before the question
      if (flushRef) await flushRef.flush();

      co
handleMessage function · typescript · L287-L305 (19 LOC)
src/handler.ts
export async function handleMessage(msg: Message, ctx: HandlerContext): Promise<void> {
  const user = msg.from;
  if (!isUserAllowed(user?.id, user?.username, ctx.allowedIds, ctx.allowedNames)) {
    const userKey = String(user?.id || user?.username || msg.chat.id);
    if (warnedUsers.has(userKey)) {
      logger.debug("handler", `Blocked repeat unauthorized message from ${user?.username || user?.id || "unknown"}`);
      return;
    }
    warnedUsers.add(userKey);
    logger.warn("handler", `Unauthorized message from ${user?.username || user?.id || "unknown"}`);
    await sendMessage(ctx.telegram, msg.chat.id, "Not authorized.", { replyToMessageId: msg.message_id });
    return;
  }

  if (msg.voice || msg.audio) return handleVoiceMessage(msg, ctx);
  if (msg.photo && msg.photo.length > 0) return handleImageMessage(msg, ctx, pickBestPhotoId(msg.photo), "photo");
  if (msg.document && isImageDocument(msg.document)) return handleImageMessage(msg, ctx, msg.document.file_id, "document")
All rows above produced by Repobility · https://repobility.com
handleTextMessage function · typescript · L307-L332 (26 LOC)
src/handler.ts
async function handleTextMessage(msg: Message, ctx: HandlerContext): Promise<void> {
  const chatId = msg.chat.id;
  const messageId = msg.message_id;
  const text = (msg.text || "").trim();
  if (!text) return;

  try {
    logger.debug("text", `chat_id=${chatId} message_id=${messageId} text=${text}`);

    // Check pending input (e.g. new project name)
    const pendingType = consumePendingInput(chatId);
    if (pendingType && !text.startsWith("/")) {
      if (pendingType === "new_project") {
        await handleNewProject(text, chatId, messageId, ctx);
        return;
      }
    }

    // Commands always handled immediately (regardless of busy state)
    if (await handleCommand(text, chatId, messageId, ctx)) return;
    await handlePrompt(text, chatId, messageId, ctx);
  } catch (err) {
    logger.error("handler", `Error in handleTextMessage: ${errorMessage(err)}`, err);
    await sendMessage(ctx.telegram, chatId, `Error: ${escapeHtml(rewriteSdkError(errorMessage(err)))}`, { reply
handleImageMessage function · typescript · L334-L355 (22 LOC)
src/handler.ts
async function handleImageMessage(msg: Message, ctx: HandlerContext, fileId: string | null | undefined, tag: string): Promise<void> {
  const chatId = msg.chat.id;
  const messageId = msg.message_id;
  const caption = (msg.caption || "").trim();
  const prompt = caption || "User sent an image.";

  if (!fileId) {
    logger.warn(tag, `No ${tag} data found chat_id=${chatId}`);
    await sendMessage(ctx.telegram, chatId, `Error: No ${tag} data found.`, { replyToMessageId: messageId });
    return;
  }

  try {
    await sendMessage(ctx.telegram, chatId, "Processing your image...", { replyToMessageId: messageId });
    logger.debug(tag, `chat_id=${chatId} message_id=${messageId} caption=${caption}`);
    const imagePath = await downloadAndSaveImage(ctx.telegram, fileId);
    await handlePrompt(prompt, chatId, messageId, ctx, [imagePath], false, true);
  } catch (err) {
    logger.error("handler", `Error in handleImageMessage: ${errorMessage(err)}`, err);
    await sendMessage(ctx.telegram
handleVoiceMessage function · typescript · L357-L412 (56 LOC)
src/handler.ts
async function handleVoiceMessage(msg: Message, ctx: HandlerContext): Promise<void> {
  const chatId = msg.chat.id;
  const messageId = msg.message_id;
  const fileId = msg.voice?.file_id || msg.audio?.file_id;
  if (!fileId) {
    logger.warn("voice", `No audio data found chat_id=${chatId}`);
    await sendMessage(ctx.telegram, chatId, "Error: No audio data found.", { replyToMessageId: messageId });
    return;
  }

  try {
    logger.debug("voice", `chat_id=${chatId} message_id=${messageId}`);

    if (!isSttReady()) {
      logger.warn("whisper", "Speech-to-text not set up");
      const notReadyText = isMacOS()
        ? "Speech-to-text is not set up.\nRun: remotecode setup-stt"
        : "Speech-to-text is currently not supported on Linux.";
      await sendMessage(ctx.telegram, chatId, notReadyText, { replyToMessageId: messageId });
      return;
    }

    await sendMessage(ctx.telegram, chatId, "Transcribing audio...", { replyToMessageId: messageId });

    const { data, filePa
handleNewProject function · typescript · L415-L467 (53 LOC)
src/handler.ts
async function handleNewProject(
  input: string,
  chatId: number,
  messageId: number,
  ctx: HandlerContext,
): Promise<void> {
  // Validate input
  if (!input || input.includes("..") || path.isAbsolute(input)) {
    await sendMessage(ctx.telegram, chatId, "Invalid path.", { replyToMessageId: messageId });
    return;
  }

  const sanitized = input.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
  if (!sanitized) {
    await sendMessage(ctx.telegram, chatId, "Invalid path.", { replyToMessageId: messageId });
    return;
  }

  const fullPath = path.join(os.homedir(), sanitized);

  // Check if final directory already exists
  if (fs.existsSync(fullPath)) {
    await sendMessage(ctx.telegram, chatId, `Already exists: <code>~/${escapeHtml(sanitized)}</code>`, {
      replyToMessageId: messageId,
      parseMode: "HTML",
    });
    return;
  }

  // Create parent directories if needed, then final directory
  try {
    const parentDir = path.dirname(fullPath);
    fs
handlePrompt function · typescript · L470-L507 (38 LOC)
src/handler.ts
export async function handlePrompt(
  prompt: string,
  chatId: number,
  messageId: number,
  ctx: HandlerContext,
  imagePaths?: string[],
  voiceMode?: boolean,
  quiet?: boolean,
): Promise<void> {
  const sessionId = getOrCreateSessionId(ctx.sessionsFile);
  const sessionCwd = loadSessionCwd(ctx.sessionsFile);
  const cwd = sessionCwd || defaultCwd();
  if (!fs.existsSync(cwd)) {
    await sendMessage(ctx.telegram, chatId, `Working directory not found: ${cwd}\nSwitch session or set a valid project path.`, {
      replyToMessageId: messageId,
    });
    return;
  }
  const formattedPrompt = formatPrompt(prompt, imagePaths);

  // If this session is busy, queue the message
  if (isSessionBusy(sessionId)) {
    // Pending AskUserQuestion → use text as the answer (don't queue as new prompt)
    if (hasPendingAsks()) {
      resolveAsksWithText(typeof formattedPrompt === "string" ? formattedPrompt : prompt);
      updateReplyTarget(sessionId, messageId);
      return;
    }
    enqueu
streamResponse function · typescript · L515-L706 (192 LOC)
src/handler.ts
async function streamResponse(
  sessionId: string,
  prompt: MessageContent,
  chatId: number,
  messageId: number,
  ctx: HandlerContext,
  options: { cwd: string; model?: string; typingHandle?: TypingHandle; quiet?: boolean },
): Promise<StreamResult> {
  const textParts: string[] = [];
  let gotResult = false;
  // Mutable reply target: updated when user answers AskUserQuestion via text
  const getReplyId = () => getReplyTarget(sessionId) ?? messageId;

  // Track the last tool description message so sequential tool_use
  // blocks are edited into the same Telegram message instead of spamming.
  let toolMsgId: number | null = null;
  let toolBlocks: ToolBlock[] = [];

  function resetToolMsg(): void {
    toolMsgId = null;
    toolBlocks = [];
  }

  function renderToolBlocks(): string {
    return toolBlocks
      .filter(b => b.visible)
      .map(b => b.status ? `${b.desc}\n${b.status}` : b.desc)
      .join("\n");
  }

  // Mutable flush ref: canUseTool calls this to send accum
renderToolBlocks function · typescript · L538-L543 (6 LOC)
src/handler.ts
  function renderToolBlocks(): string {
    return toolBlocks
      .filter(b => b.visible)
      .map(b => b.status ? `${b.desc}\n${b.status}` : b.desc)
      .join("\n");
  }
sendFinalResponse function · typescript · L709-L737 (29 LOC)
src/handler.ts
async function sendFinalResponse(
  textParts: string[],
  prompt: string,
  chatId: number,
  messageId: number,
  ctx: HandlerContext,
  voiceMode?: boolean,
): Promise<void> {
  const fullText = stripThinking(textParts.join("\n"));
  if (!fullText) return;

  if (voiceMode) {
    const userHtml = mdToTelegramHtml(prompt);
    const botHtml = mdToTelegramHtml(truncateMessage(fullText, 3200));
    const formatted = `<blockquote><b><code>You:</code></b>\n${userHtml}\n\n<b><code>Bot:</code></b>\n${botHtml}</blockquote>`;
    await sendMessage(ctx.telegram, chatId, formatted, {
      replyToMessageId: messageId,
      replyMarkup: sessionsReplyKeyboard(ctx.sessionsFile),
      parseMode: "HTML",
    });
  } else {
    const formatted = tryMdToHtml(fullText);
    await sendMessage(ctx.telegram, chatId, formatted.text, {
      replyToMessageId: messageId,
      replyMarkup: sessionsReplyKeyboard(ctx.sessionsFile),
      parseMode: formatted.parseMode,
    });
  }
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
executePrompt function · typescript · L740-L817 (78 LOC)
src/handler.ts
async function executePrompt(
  sessionId: string,
  prompt: MessageContent,
  chatId: number,
  messageId: number,
  ctx: HandlerContext,
  voiceMode?: boolean,
  quiet?: boolean,
): Promise<void> {
  const cwd = loadSessionCwd(ctx.sessionsFile) || defaultCwd();
  const model = loadModel(ctx.sessionsFile);

  // Cancel any pending cleanup timeout — we're starting a new query for this session
  clearCleanupTimeout(sessionId);

  clearPermDenied(sessionId);
  markProcessing(sessionId);
  activeQueries.add(sessionId);
  const typingHandle = startTyping(ctx.telegram, chatId);

  try {
    const { textParts, gotResult } = await streamResponse(
      sessionId, prompt, chatId, messageId, ctx, { cwd, model, typingHandle, quiet },
    );

    if (!gotResult) return;

    // Don't send final response if session was switched away
    if (!isSessionSuppressed(sessionId)) {
      const replyId = getReplyTarget(sessionId) ?? messageId;
      const promptText = typeof prompt === "string" ? prompt :
main function · typescript · L12-L118 (107 LOC)
src/index.ts
async function main(): Promise<void> {
  const args = process.argv.slice(2);

  // Internal flag: run as daemon process
  if (args.includes("--daemon")) {
    await daemonMain();
    return;
  }

  // Verbose flag
  if (args.includes("-v") || args.includes("--verbose")) {
    process.env.REMOTECODE_VERBOSE = "1";
  }

  const command = args.find((a) => !a.startsWith("-")) || "";

  switch (command) {
    case "start":
      await cmdStart();
      break;

    case "stop":
      await cmdStop();
      break;

    case "restart":
      await cmdRestart();
      break;

    case "status":
      cmdStatus();
      break;

    case "logs": {
      const levelIdx = args.indexOf("--level");
      const tagIdx = args.indexOf("--tag");
      const nIdx = args.indexOf("-n");
      let lines = 50;
      let follow = true;
      if (nIdx !== -1 && args[nIdx + 1]) {
        const n = parseInt(args[nIdx + 1], 10);
        if (!isNaN(n) && n > 0) lines = n;
        follow = false; // -n implies stati
parseJsonlLines function · typescript · L4-L13 (10 LOC)
src/jsonl.ts
export function* parseJsonlLines(content: string, tag: string = "jsonl"): Generator<Record<string, unknown>> {
  for (const line of content.split("\n")) {
    if (!line.trim()) continue;
    try {
      yield JSON.parse(line) as Record<string, unknown>;
    } catch {
      logger.debug(tag, `skipped invalid JSONL line: ${line.slice(0, 80)}`);
    }
  }
}
extractMessageContent function · typescript · L16-L27 (12 LOC)
src/jsonl.ts
export function extractMessageContent(content: unknown): string {
  if (typeof content === "string") return content;
  if (Array.isArray(content)) {
    for (const block of content) {
      if (block && typeof block === "object" && (block as Record<string, unknown>).type === "text") {
        const text = (block as Record<string, unknown>).text;
        if (typeof text === "string") return text;
      }
    }
  }
  return "";
}
cleanUserMessage function · typescript · L30-L36 (7 LOC)
src/jsonl.ts
export function cleanUserMessage(text: string): string {
  let msg = text;
  if (msg.startsWith("User said:\n")) msg = msg.slice("User said:\n".length);
  msg = msg.replace(/\n+Reply concisely\.?\s*$/, "");
  msg = msg.replace(/\n+Image file path\(s\):.*/s, "");
  return msg.trim();
}
cleanFirstMessage function · typescript · L39-L46 (8 LOC)
src/jsonl.ts
export function cleanFirstMessage(text: string): string | null {
  const msg = cleanUserMessage(text);
  if (!msg || msg.startsWith("<") || msg.startsWith("#")) return null;
  const firstLine = msg.split("\n", 1)[0].trim();
  if (!firstLine) return null;
  if (firstLine.length > 60) return firstLine.slice(0, 57) + "...";
  return firstLine;
}
setSessionToolAllow function · typescript · L17-L24 (8 LOC)
src/session-state.ts
export function setSessionToolAllow(sessionId: string, toolName: string): void {
  let tools = sessionAutoAllowTools.get(sessionId);
  if (!tools) {
    tools = new Set();
    sessionAutoAllowTools.set(sessionId, tools);
  }
  tools.add(toolName);
}
drainNext function · typescript · L99-L105 (7 LOC)
src/session-state.ts
export function drainNext(sessionId: string): QueuedMessage | null {
  const queue = messageQueue.get(sessionId);
  if (!queue || queue.length === 0) return null;
  const next = queue.shift()!;
  if (queue.length === 0) messageQueue.delete(sessionId);
  return next;
}
If a scraper extracted this row, it came from Repobility (https://repobility.com)
decodeProjectPath function · typescript · L45-L64 (20 LOC)
src/sessions.ts
export function decodeProjectPath(encodedDir: string): string {
  // Claude Code encodes: / -> -, _ -> -, leading . stripped (-- means /.)
  // Since both / and _ map to -, we resolve ambiguity by checking the filesystem.

  // Split on -- first (hidden dir boundary: /. ), then split each part on -
  const raw = encodedDir.replace(/^-/, "");
  const hiddenParts = raw.split("--");
  const segments: string[] = [];

  for (let h = 0; h < hiddenParts.length; h++) {
    const segs = hiddenParts[h].split("-").filter(Boolean);
    if (h > 0 && segs.length > 0) {
      // -- means /. so prepend . to the first segment after --
      segs[0] = "." + segs[0];
    }
    segments.push(...segs);
  }

  return resolvePathSegments("/", segments);
}
resolvePathSegments function · typescript · L66-L85 (20 LOC)
src/sessions.ts
function resolvePathSegments(base: string, segments: string[]): string {
  if (segments.length === 0) return base;

  // Try greedily joining segments with _ and check filesystem
  for (let take = segments.length; take >= 1; take--) {
    const candidate = segments.slice(0, take).join("_");
    const full = path.join(base, candidate);
    if (fs.existsSync(full)) {
      if (take === segments.length) return full;
      return resolvePathSegments(full, segments.slice(take));
    }
  }

  // Fallback: treat first segment as a directory (original / encoding)
  const first = segments[0];
  const rest = segments.slice(1);
  const fallback = path.join(base, first);
  if (rest.length === 0) return fallback;
  return resolvePathSegments(fallback, rest);
}
projectDisplayName function · typescript · L87-L92 (6 LOC)
src/sessions.ts
function projectDisplayName(encodedDir: string): string {
  const decoded = decodeProjectPath(encodedDir);
  const home = os.homedir();
  if (decoded.replace(/\/$/, "") === home.replace(/\/$/, "")) return "~/";
  return decoded.replace(/\/$/, "").split("/").pop() || decoded;
}
parseSessionFile function · typescript · L102-L125 (24 LOC)
src/sessions.ts
function parseSessionFile(filePath: string): ParsedSession | null {
  try {
    const content = fs.readFileSync(filePath, "utf-8");
    let slug: string | null = null;
    let firstMessage: string | null = null;
    let lastMessage: string | null = null;

    for (const entry of parseJsonlLines(content, "sessions")) {
      if (!slug && entry.slug) slug = entry.slug as string;
      if (entry.type === "user" && !entry.isMeta) {
        const msgObj = entry.message as Record<string, unknown> | undefined;
        const msgContent = extractMessageContent(msgObj?.content);
        if (msgContent.trim() && !msgContent.startsWith("<")) {
          const cleaned = cleanFirstMessage(msgContent.trim());
          if (!firstMessage) firstMessage = cleaned;
          lastMessage = cleaned;
        }
      }
    }
    return { slug, firstMessage, lastMessage };
  } catch {
    return null;
  }
}
saveActiveSessionId function · typescript · L132-L137 (6 LOC)
src/sessions.ts
export function saveActiveSessionId(sessionsFile: string, sessionId: string | null): void {
  let lines = readEnvLines(sessionsFile);
  lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_SESSION_CLAUDE="));
  if (sessionId) lines.push(`REMOTECODE_SESSION_CLAUDE=${sessionId}`);
  writeEnvLines(sessionsFile, lines);
}
getOrCreateSessionId function · typescript · L139-L145 (7 LOC)
src/sessions.ts
export function getOrCreateSessionId(sessionsFile: string): string {
  const existing = loadActiveSessionId(sessionsFile);
  if (existing) return existing;
  const newId = uuidv4();
  saveActiveSessionId(sessionsFile, newId);
  return newId;
}
createNewSession function · typescript · L147-L152 (6 LOC)
src/sessions.ts
export function createNewSession(sessionsFile: string, cwd?: string): string {
  const newId = uuidv4();
  saveActiveSessionId(sessionsFile, newId);
  saveSessionCwd(sessionsFile, cwd || "");
  return newId;
}
saveSessionCwd function · typescript · L158-L163 (6 LOC)
src/sessions.ts
export function saveSessionCwd(sessionsFile: string, cwd: string): void {
  let lines = readEnvLines(sessionsFile);
  lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_SESSION_CLAUDE_CWD="));
  if (cwd) lines.push(`REMOTECODE_SESSION_CLAUDE_CWD=${cwd}`);
  writeEnvLines(sessionsFile, lines);
}
Open data scored by Repobility · https://repobility.com
findSessionFilePath function · typescript · L170-L182 (13 LOC)
src/sessions.ts
export function findSessionFilePath(sessionId: string): string | null {
  const pDir = projectsDir();
  if (!fs.existsSync(pDir)) return null;
  try {
    for (const dir of fs.readdirSync(pDir)) {
      const dirPath = path.join(pDir, dir);
      if (!fs.statSync(dirPath).isDirectory()) continue;
      const candidate = path.join(dirPath, `${sessionId}.jsonl`);
      if (fs.existsSync(candidate)) return candidate;
    }
  } catch (err) { logger.debug("sessions", `findSessionFilePath scan: ${errorMessage(err)}`); }
  return null;
}
candidateToSessionInfo function · typescript · L198-L214 (17 LOC)
src/sessions.ts
function candidateToSessionInfo(
  mtime: number,
  filePath: string,
  encodedDir: string
): SessionInfo | null {
  const parsed = parseSessionFile(filePath);
  if (!parsed) return null;
  return {
    sessionId: path.basename(filePath, ".jsonl"),
    slug: parsed.slug,
    project: decodeProjectPath(encodedDir),
    projectName: projectDisplayName(encodedDir),
    lastModified: mtime,
    firstMessage: parsed.firstMessage,
    lastMessage: parsed.lastMessage,
  };
}
discoverSessions function · typescript · L217-L240 (24 LOC)
src/sessions.ts
export function discoverSessions(limit: number = 10): SessionInfo[] {
  const pDir = projectsDir();
  if (!fs.existsSync(pDir)) return [];

  const all: Array<{ mtime: number; filePath: string; encodedDir: string }> = [];
  try {
    for (const dir of fs.readdirSync(pDir)) {
      const dirPath = path.join(pDir, dir);
      if (!fs.statSync(dirPath).isDirectory()) continue;
      for (const c of listSessionCandidates(dirPath)) {
        all.push({ ...c, encodedDir: dir });
      }
    }
  } catch { return []; }

  all.sort((a, b) => b.mtime - a.mtime);

  const results: SessionInfo[] = [];
  for (const { mtime, filePath, encodedDir } of all.slice(0, limit)) {
    const info = candidateToSessionInfo(mtime, filePath, encodedDir);
    if (info) results.push(info);
  }
  return results;
}
‹ prevpage 2 / 3next ›