refactor: replace Bun APIs with Node.js equivalents

Replace Bun.argv with process.argv, Bun.file() with fs/promises,
Bun.stringWidth with string-width package, and Bun.wrapAnsi with
wrap-ansi package for better compatibility.
This commit is contained in:
mitchell 2026-05-25 23:13:16 -04:00
parent b212fee200
commit 0c71e2cd54
7 changed files with 39 additions and 18 deletions

View file

@ -1,9 +1,10 @@
import fs from "node:fs/promises";
import os from "node:os";
import { autocomplete, intro, isCancel, outro, text } from "@clack/prompts";
import chalk from "chalk";
const home = os.homedir();
const configFile = Bun.file(`${home}/.lmc_config.json`);
const configPath = `${home}/.lmc_config.json`;
export interface AppConfig {
baseURL: string;
@ -36,7 +37,7 @@ export async function initConfig(old: AppConfig | null): Promise<AppConfig> {
const config = { baseURL, model } satisfies AppConfig;
await configFile.write(JSON.stringify(config, null, " "));
await fs.writeFile(configPath, JSON.stringify(config, null, " "));
outro(chalk.green("Configuration saved!"));
@ -63,10 +64,11 @@ export async function loadConfig(
): Promise<AppConfig | null> {
let config: AppConfig;
try {
config = await configFile.json();
const text = await fs.readFile(configPath);
config = JSON.parse(text.toString("utf8"));
} catch {
if (configFlag) return null;
console.error(`Config not found at ${configFile.name}`);
console.error(`Config not found at ${configPath}`);
return null;
}

View file

@ -1,8 +1,9 @@
import fs from "node:fs/promises";
import os from "node:os";
import type { ModelMessage } from "ai";
const home = os.homedir();
const convoFile = Bun.file(`${home}/.lmc_convo.json`);
const convoPath = `${home}/.lmc_convo.json`;
export interface IConversation {
model: string;
@ -16,7 +17,8 @@ export class Conversation implements IConversation {
async load() {
try {
const convo: IConversation = await convoFile.json();
const text = await fs.readFile(convoPath);
const convo: IConversation = JSON.parse(text.toString("utf8"));
this.model = convo.model;
this.messages = convo.messages;
} catch {
@ -25,7 +27,8 @@ export class Conversation implements IConversation {
}
async save() {
await convoFile.write(
await fs.writeFile(
convoPath,
JSON.stringify({
model: this.model,
messages: this.messages,

View file

@ -8,6 +8,7 @@ import type {
} from "ai";
import styles from "ansi-styles";
import { chalkStderr, supportsColorStderr } from "chalk";
import stringWidth from "string-width";
import { renderMarkdown } from "./markdown";
export default class Renderer {
@ -40,12 +41,11 @@ export default class Renderer {
private checkContainment(text: string, pipe: WriteStream = process.stdout) {
const [columns, rows] = pipe.getWindowSize();
this.charsSinceNL += Bun.stringWidth(text);
this.charsSinceNL += stringWidth(text);
if (text.includes("\n")) {
this.lineCount += text.match(/\n/g)?.length ?? 1;
this.charsSinceNL =
Bun.stringWidth(text) -
Bun.stringWidth(text.slice(0, text.lastIndexOf("\n")));
stringWidth(text) - stringWidth(text.slice(0, text.lastIndexOf("\n")));
} else if (this.charsSinceNL > columns) {
this.lineCount++;
this.charsSinceNL = this.charsSinceNL - columns;

View file

@ -7,7 +7,9 @@ import {
createHighlighter,
type HighlighterGeneric,
} from "shiki";
import stringWidth from "string-width";
import terminalLink from "terminal-link";
import wrapAnsi from "wrap-ansi";
import { codeToANSI } from "./codeToANSI";
let highlighter: HighlighterGeneric<BundledLanguage, BundledTheme> | undefined;
@ -15,7 +17,7 @@ let highligherLangs = "";
class SWCell extends Cell {
public override get length() {
return Bun.stringWidth(this.toString(), { ambiguousIsNarrow: true });
return stringWidth(this.toString(), { ambiguousIsNarrow: true });
}
}
@ -23,7 +25,7 @@ class LmcRenderer extends Renderer {
private columns: number;
constructor(columns: number) {
super();
super({ gfm: true });
this.columns = columns;
}
@ -38,7 +40,7 @@ class LmcRenderer extends Renderer {
];
const color = colors[depth - 1] ?? chalk.white;
const text = this.parser.parseInline(tokens);
return Bun.wrapAnsi(
return wrapAnsi(
color.bold(`${"#".repeat(depth)} ${text}\n\n`),
this.columns,
{ trim: false },
@ -47,12 +49,12 @@ class LmcRenderer extends Renderer {
override paragraph({ tokens }: Tokens.Paragraph) {
const text = this.parser.parseInline(tokens);
return Bun.wrapAnsi(`${text}\n\n`, this.columns, { trim: false });
return wrapAnsi(`${text}\n\n`, this.columns, { trim: false });
}
override blockquote({ tokens }: Tokens.Blockquote) {
const text = this.parser.parse(tokens).trim();
const lines = Bun.wrapAnsi(text, this.columns - 2, { trim: false })
const lines = wrapAnsi(text, this.columns - 2, { trim: false })
.replace(/\n+/g, "\n")
.concat("\n")
.split("\n");
@ -96,7 +98,7 @@ class LmcRenderer extends Renderer {
}
const cols = this.columns - 2;
return Bun.wrapAnsi(`${marker} ${text}`, cols, { trim: false })
return wrapAnsi(`${marker} ${text}`, cols, { trim: false })
.split("\n")
.map((line) => (line.length > 0 ? ` ${line}` : ""))
.join("\n")