Compare commits
No commits in common. "main" and "feat/tts-input" have entirely different histories.
main
...
feat/tts-i
14 changed files with 245 additions and 325 deletions
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
@jsr:registry=https://npm.jsr.io
|
||||||
26
AGENTS.md
26
AGENTS.md
|
|
@ -1,36 +1,26 @@
|
||||||
# lmc
|
# lmc
|
||||||
|
|
||||||
TypeScript CLI tool for streaming LLM chat with inline markdown rendering.
|
Bun CLI tool for streaming LLM chat with inline markdown rendering.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```
|
```
|
||||||
pnpm run dev # dev run (rolldown bundle + node)
|
bun run dev # dev run
|
||||||
pnpm run check # biome lint + format + organize imports + typecheck
|
bun run prod # run bundled output
|
||||||
pnpm run build # check + bundle index.ts and tts.ts (minified ESM)
|
bun run check # biome lint + format + organize imports
|
||||||
pnpm run build:bin # check + bundle with esbuild (CJS) + build SEA binaries
|
|
||||||
pnpm run clean # remove dist/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- `src/index.ts` — LMC CLI entry point, orchestration
|
- `index.ts` — CLI entry point, orchestration
|
||||||
- `src/tts.ts` — TTSC CLI entry point (text-to-speech, Kokoro pipeline via `uv run`)
|
|
||||||
- `src/config.ts` — `initConfig()`, `loadConfig()`: manages `~/.lmc_config.json`
|
|
||||||
- `src/conversation.ts` — `Conversation` class: load/save to `~/.lmc_convo.json`
|
|
||||||
- `src/errors.ts` — `handleTopLevel()`: TTY-aware error wrapping
|
|
||||||
- `src/prompts.ts` — `commitDiff()`, `reviewFile()`, `reviewDiff()`, `file()`: prompt builders
|
|
||||||
- `src/render/index.ts` — `Renderer` class: streams LLM response, buffers markdown, renders to stdout/stderr
|
- `src/render/index.ts` — `Renderer` class: streams LLM response, buffers markdown, renders to stdout/stderr
|
||||||
- `src/render/markdown.ts` — `renderMarkdown()`: markdown via `marked` + Shiki + `@cliffy/table`
|
- `src/render/markdown.ts` — `renderMarkdown()`: syntax-highlighted markdown via `@comark/ansi` + Shiki
|
||||||
- `src/render/codeToANSI.ts` — Shiki token output to ANSI escape sequences
|
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
- **Node.js** — Use modern Node APIs (`node:fs/promises`, `node:stream/consumers`, etc.).
|
- **Bun only** — not Node. Use `Bun.file()`, `Bun.argv`, `Bun.sleep()`.
|
||||||
- **No tests** — none exist.
|
- **No tests** — none exist.
|
||||||
- **Biome config**: double quotes, space indent, auto-import-organize on. `pnpm run check` also runs `tsc --noEmit`.
|
- **Biome config**: double quotes, space indent, auto-import-organize on. Run `bun run check` before committing.
|
||||||
- **Build tools**: rolldown for ESM bundles, esbuild for CJS binaries, `node --build-sea` for SEA single-executable apps.
|
|
||||||
- **TTS**: requires Python with `uv` — Kokoro pipeline lives in `tts/`.
|
|
||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
||||||
|
|
|
||||||
84
index.ts
Normal file
84
index.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import util from "node:util";
|
||||||
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||||
|
import { streamText } from "ai";
|
||||||
|
|
||||||
|
import { initConfig, loadConfig } from "./src/config";
|
||||||
|
import { Conversation } from "./src/conversation";
|
||||||
|
import Renderer from "./src/render";
|
||||||
|
|
||||||
|
const { values: args, positionals } = util.parseArgs({
|
||||||
|
args: process.argv.slice(2),
|
||||||
|
options: {
|
||||||
|
model: { type: "string" },
|
||||||
|
m: { type: "string" },
|
||||||
|
new: { type: "boolean" },
|
||||||
|
n: { type: "boolean" },
|
||||||
|
config: { type: "boolean" },
|
||||||
|
c: { type: "boolean" },
|
||||||
|
plain: { type: "boolean" },
|
||||||
|
plainOut: { type: "boolean" },
|
||||||
|
plainErr: { type: "boolean" },
|
||||||
|
v: { type: "boolean" },
|
||||||
|
},
|
||||||
|
strict: true,
|
||||||
|
allowPositionals: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const newConvo = args.new ?? args.n;
|
||||||
|
const plainOut = args.plainOut ?? args.plain ?? !process.stdout.isTTY;
|
||||||
|
const plainErr = args.plainErr ?? args.plain ?? !process.stderr.isTTY;
|
||||||
|
const modelFlag = args.model ?? args.m;
|
||||||
|
const configFlag = args.config ?? args.c;
|
||||||
|
|
||||||
|
if (args.v) {
|
||||||
|
console.log("0.0.0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await loadConfig(configFlag);
|
||||||
|
|
||||||
|
if (configFlag || !config) {
|
||||||
|
await initConfig(config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = modelFlag ?? config.model;
|
||||||
|
|
||||||
|
const renderer = new Renderer(plainOut, plainErr);
|
||||||
|
|
||||||
|
const provider = createOpenAICompatible({
|
||||||
|
name: "llama.cpp",
|
||||||
|
apiKey: "local",
|
||||||
|
baseURL: config.baseURL,
|
||||||
|
includeUsage: true, // Include usage information in streaming responses
|
||||||
|
});
|
||||||
|
|
||||||
|
const convo = new Conversation(model);
|
||||||
|
|
||||||
|
if (!newConvo) {
|
||||||
|
await convo.load();
|
||||||
|
if (modelFlag) convo.model = modelFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = positionals.join(" ") || "Introduce yourself.";
|
||||||
|
convo.messages.push({ role: "user", content: prompt });
|
||||||
|
|
||||||
|
const { fullStream, text, totalUsage } = streamText({
|
||||||
|
model: provider(convo.model),
|
||||||
|
messages: convo.messages,
|
||||||
|
});
|
||||||
|
|
||||||
|
await renderer.renderStream(fullStream);
|
||||||
|
|
||||||
|
convo.messages.push({ role: "assistant", content: await text });
|
||||||
|
await convo.save();
|
||||||
|
|
||||||
|
await renderer.renderStats(totalUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
25
package.json
25
package.json
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lmc",
|
"name": "lmc",
|
||||||
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -7,35 +8,35 @@
|
||||||
"ttsc": "dist/tts.js"
|
"ttsc": "dist/tts.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "pnpm run bundle src/index.ts >/dev/null && node dist/index.js",
|
"dev": "pnpm run bundle index.ts &>/dev/null && node dist/index.js",
|
||||||
"check": "biome check --write && pnpm run tc",
|
"check": "biome check --write && pnpm run tc",
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"build": "pnpm run check && pnpm run bundle --minify src/index.ts && pnpm run bundle --minify src/tts.ts",
|
"build": "pnpm run check && pnpm run bundle --minify index.ts && pnpm run bundle --minify tts.ts",
|
||||||
"build:bin": "pnpm run check && pnpm run bundle:bin --minify src/index.ts && pnpm run bundle:bin --minify src/tts.ts && pnpm run bin",
|
"build:bin": "pnpm run check && pnpm run bundle:bin --minify index.ts && pnpm run bundle:bin --minify tts.ts && pnpm run bin",
|
||||||
"bundle": "rolldown --dir=dist --platform=node --transform.target=node22 --format=esm",
|
"bundle": "rolldown --dir=dist --platform=node --transform.target=node22 --format=esm",
|
||||||
"bundle:bin": "esbuild --bundle --outdir=dist --platform=node --target=node26 --format=cjs",
|
"bundle:bin": "esbuild --bundle --outdir=dist --platform=node --target=node26 --format=cjs",
|
||||||
"bin": "node --build-sea sea-config.json && node --build-sea sea-config-tts.json",
|
"bin": "node --build-sea sea-config.json && node --build-sea sea-config-tts.json",
|
||||||
"tc": "tsc --noEmit --incremental"
|
"tc": "tsc --noEmit --incremental"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ai-sdk/openai-compatible": "^2.0.50",
|
"@ai-sdk/openai-compatible": "^2.0.41",
|
||||||
"@biomejs/biome": "2.4.12",
|
"@biomejs/biome": "2.4.12",
|
||||||
"@clack/prompts": "^1.5.1",
|
"@clack/prompts": "^1.3.0",
|
||||||
"@cliffy/table": "jsr:^1.2.1",
|
"@cliffy/table": "npm:@jsr/cliffy__table",
|
||||||
"@mozilla/readability": "^0.6.0",
|
"@mozilla/readability": "^0.6.0",
|
||||||
"@types/node": "^25.9.3",
|
"@types/node": "^25.9.2",
|
||||||
"ai": "^6.0.206",
|
"ai": "^6.0.161",
|
||||||
"ansi-styles": "^6.2.3",
|
"ansi-styles": "^6.2.3",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"esbuild": "^0.28.1",
|
"esbuild": "^0.28.1",
|
||||||
"execa": "^9.6.1",
|
"execa": "^9.6.1",
|
||||||
"happy-dom": "^20.10.3",
|
"happy-dom": "^20.10.2",
|
||||||
"marked": "^18.0.5",
|
"marked": "^18.0.4",
|
||||||
"rolldown": "^1.1.1",
|
"rolldown": "^1.1.1",
|
||||||
"shiki": "^4.2.0",
|
"shiki": "^4.0.2",
|
||||||
"string-width": "^8.2.1",
|
"string-width": "^8.2.1",
|
||||||
"terminal-link": "^5.0.0",
|
"terminal-link": "^5.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5",
|
||||||
"wrap-ansi": "^10.0.0"
|
"wrap-ansi": "^10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
199
pnpm-lock.yaml
generated
199
pnpm-lock.yaml
generated
|
|
@ -9,26 +9,26 @@ importers:
|
||||||
.:
|
.:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@ai-sdk/openai-compatible':
|
'@ai-sdk/openai-compatible':
|
||||||
specifier: ^2.0.50
|
specifier: ^2.0.41
|
||||||
version: 2.0.50(zod@4.4.3)
|
version: 2.0.48(zod@4.4.3)
|
||||||
'@biomejs/biome':
|
'@biomejs/biome':
|
||||||
specifier: 2.4.12
|
specifier: 2.4.12
|
||||||
version: 2.4.12
|
version: 2.4.12
|
||||||
'@clack/prompts':
|
'@clack/prompts':
|
||||||
specifier: ^1.5.1
|
specifier: ^1.3.0
|
||||||
version: 1.5.1
|
version: 1.4.0
|
||||||
'@cliffy/table':
|
'@cliffy/table':
|
||||||
specifier: jsr:^1.2.1
|
specifier: npm:@jsr/cliffy__table
|
||||||
version: '@jsr/cliffy__table@1.2.1'
|
version: '@jsr/cliffy__table@1.2.1'
|
||||||
'@mozilla/readability':
|
'@mozilla/readability':
|
||||||
specifier: ^0.6.0
|
specifier: ^0.6.0
|
||||||
version: 0.6.0
|
version: 0.6.0
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^25.9.3
|
specifier: ^25.9.2
|
||||||
version: 25.9.3
|
version: 25.9.2
|
||||||
ai:
|
ai:
|
||||||
specifier: ^6.0.206
|
specifier: ^6.0.161
|
||||||
version: 6.0.206(zod@4.4.3)
|
version: 6.0.191(zod@4.4.3)
|
||||||
ansi-styles:
|
ansi-styles:
|
||||||
specifier: ^6.2.3
|
specifier: ^6.2.3
|
||||||
version: 6.2.3
|
version: 6.2.3
|
||||||
|
|
@ -42,17 +42,17 @@ importers:
|
||||||
specifier: ^9.6.1
|
specifier: ^9.6.1
|
||||||
version: 9.6.1
|
version: 9.6.1
|
||||||
happy-dom:
|
happy-dom:
|
||||||
specifier: ^20.10.3
|
specifier: ^20.10.2
|
||||||
version: 20.10.3
|
version: 20.10.2
|
||||||
marked:
|
marked:
|
||||||
specifier: ^18.0.5
|
specifier: ^18.0.4
|
||||||
version: 18.0.5
|
version: 18.0.4
|
||||||
rolldown:
|
rolldown:
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
shiki:
|
shiki:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.0.2
|
||||||
version: 4.2.0
|
version: 4.1.0
|
||||||
string-width:
|
string-width:
|
||||||
specifier: ^8.2.1
|
specifier: ^8.2.1
|
||||||
version: 8.2.1
|
version: 8.2.1
|
||||||
|
|
@ -60,7 +60,7 @@ importers:
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.3
|
specifier: ^5
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
wrap-ansi:
|
wrap-ansi:
|
||||||
specifier: ^10.0.0
|
specifier: ^10.0.0
|
||||||
|
|
@ -68,20 +68,20 @@ importers:
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@ai-sdk/gateway@3.0.132':
|
'@ai-sdk/gateway@3.0.120':
|
||||||
resolution: {integrity: sha512-sFZFGk6aVSNKmgDrb14efaAbvM71AB3UUvaTsU+bvhWP9jrkRrwix/jFX8IoOAJk0/X7Xf7p3m0J2O2G6ljZbA==}
|
resolution: {integrity: sha512-MYKAeD2q7/sa1ZdqtL2tw0Me0B8Tok6Q/fhkJDhJl39dG8u+VBlWO9yk9lcdm784bM418o1EKObo4aOxs6+18Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4.1.8
|
zod: ^3.25.76 || ^4.1.8
|
||||||
|
|
||||||
'@ai-sdk/openai-compatible@2.0.50':
|
'@ai-sdk/openai-compatible@2.0.48':
|
||||||
resolution: {integrity: sha512-HyuxddF2Yv5G8qxK/0uksAINjQ4h6TpwOqHuqzsCM0u78/JWAW2OXcIplQeB44PIAORgPjbMzrw9DhnPYHMskA==}
|
resolution: {integrity: sha512-z9MC6M4Oh/yUY/F/eszOtO8wc2nMz99XmZQKd2gWTtyIfe716xTfrKe3aYZKg20NZDtyjqPPKPSR+wqz7q1T7Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4.1.8
|
zod: ^3.25.76 || ^4.1.8
|
||||||
|
|
||||||
'@ai-sdk/provider-utils@4.0.29':
|
'@ai-sdk/provider-utils@4.0.27':
|
||||||
resolution: {integrity: sha512-uhukHaCBvqkwBHkT8C2PrnqKTCoLn3pdHXqtcR9I8ErH+flbzgW4o7VHSNIup9LRu+WBvZIZDQLsx6rwl2tiOA==}
|
resolution: {integrity: sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4.1.8
|
zod: ^3.25.76 || ^4.1.8
|
||||||
|
|
@ -147,12 +147,12 @@ packages:
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@clack/core@1.4.1':
|
'@clack/core@1.3.1':
|
||||||
resolution: {integrity: sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==}
|
resolution: {integrity: sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==}
|
||||||
engines: {node: '>= 20.12.0'}
|
engines: {node: '>= 20.12.0'}
|
||||||
|
|
||||||
'@clack/prompts@1.5.1':
|
'@clack/prompts@1.4.0':
|
||||||
resolution: {integrity: sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==}
|
resolution: {integrity: sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==}
|
||||||
engines: {node: '>= 20.12.0'}
|
engines: {node: '>= 20.12.0'}
|
||||||
|
|
||||||
'@emnapi/core@1.11.0':
|
'@emnapi/core@1.11.0':
|
||||||
|
|
@ -444,32 +444,32 @@ packages:
|
||||||
'@sec-ant/readable-stream@0.4.1':
|
'@sec-ant/readable-stream@0.4.1':
|
||||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||||
|
|
||||||
'@shikijs/core@4.2.0':
|
'@shikijs/core@4.1.0':
|
||||||
resolution: {integrity: sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==}
|
resolution: {integrity: sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/engine-javascript@4.2.0':
|
'@shikijs/engine-javascript@4.1.0':
|
||||||
resolution: {integrity: sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==}
|
resolution: {integrity: sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/engine-oniguruma@4.2.0':
|
'@shikijs/engine-oniguruma@4.1.0':
|
||||||
resolution: {integrity: sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==}
|
resolution: {integrity: sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/langs@4.2.0':
|
'@shikijs/langs@4.1.0':
|
||||||
resolution: {integrity: sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==}
|
resolution: {integrity: sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/primitive@4.2.0':
|
'@shikijs/primitive@4.1.0':
|
||||||
resolution: {integrity: sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==}
|
resolution: {integrity: sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/themes@4.2.0':
|
'@shikijs/themes@4.1.0':
|
||||||
resolution: {integrity: sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==}
|
resolution: {integrity: sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/types@4.2.0':
|
'@shikijs/types@4.1.0':
|
||||||
resolution: {integrity: sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==}
|
resolution: {integrity: sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@shikijs/vscode-textmate@10.0.2':
|
'@shikijs/vscode-textmate@10.0.2':
|
||||||
|
|
@ -491,8 +491,11 @@ packages:
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||||
|
|
||||||
'@types/node@25.9.3':
|
'@types/node@25.9.1':
|
||||||
resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==}
|
resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
|
||||||
|
|
||||||
|
'@types/node@25.9.2':
|
||||||
|
resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==}
|
||||||
|
|
||||||
'@types/unist@3.0.3':
|
'@types/unist@3.0.3':
|
||||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||||
|
|
@ -510,8 +513,8 @@ packages:
|
||||||
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
|
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
|
||||||
engines: {node: '>= 20'}
|
engines: {node: '>= 20'}
|
||||||
|
|
||||||
ai@6.0.206:
|
ai@6.0.191:
|
||||||
resolution: {integrity: sha512-hVdB5PozMMxmkPBfbCxkFOn0eVeCMi/imkfPvk65cxL4O8oIrvjUxDUX8dGkdkWG1No+R7e7D9ti2DHO61Z4YQ==}
|
resolution: {integrity: sha512-zAxvjKebQE7YkSyyNIl0OM7i6/zygnKeF+yNUjD4nWOelYrG+LpDd6RnH6mjySI4zUpZ7o4wbnmAy8jc6u98vQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4.1.8
|
zod: ^3.25.76 || ^4.1.8
|
||||||
|
|
@ -572,8 +575,8 @@ packages:
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
eventsource-parser@3.1.0:
|
eventsource-parser@3.0.8:
|
||||||
resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==}
|
resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
execa@9.6.1:
|
execa@9.6.1:
|
||||||
|
|
@ -601,8 +604,8 @@ packages:
|
||||||
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
|
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
happy-dom@20.10.3:
|
happy-dom@20.10.2:
|
||||||
resolution: {integrity: sha512-Hjdiy8RziuCcn5z04QI/rlsNuQoG8P0xxjgvsSMpi89cvIXIOcucQtiHS1yHSShxoBcSCeYqAskINmTiy/mlfw==}
|
resolution: {integrity: sha512-5p9Sxis3eowDJKqx90QCsgbNA02XXqJ59NOHvD4V6cxp+rP4d/xOyVx7uY3hS8hiUbY1VeiFH8lbJ81AyuDVLQ==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
has-flag@5.0.1:
|
has-flag@5.0.1:
|
||||||
|
|
@ -640,8 +643,8 @@ packages:
|
||||||
json-schema@0.4.0:
|
json-schema@0.4.0:
|
||||||
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
|
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
|
||||||
|
|
||||||
marked@18.0.5:
|
marked@18.0.4:
|
||||||
resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==}
|
resolution: {integrity: sha512-c/BTaKzg0G6ezQx97DAkYU7k0HM6ys0FqYeKBL6hlBByZwy+ycA1+f0vDdjMHKKeEjdgkx0GOv9Il6D+85cOqA==}
|
||||||
engines: {node: '>= 20'}
|
engines: {node: '>= 20'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
|
@ -689,8 +692,8 @@ packages:
|
||||||
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
|
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
property-information@7.2.0:
|
property-information@7.1.0:
|
||||||
resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
|
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
|
||||||
|
|
||||||
regex-recursion@6.0.2:
|
regex-recursion@6.0.2:
|
||||||
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
|
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
|
||||||
|
|
@ -714,8 +717,8 @@ packages:
|
||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
shiki@4.2.0:
|
shiki@4.1.0:
|
||||||
resolution: {integrity: sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==}
|
resolution: {integrity: sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
signal-exit@4.1.0:
|
signal-exit@4.1.0:
|
||||||
|
|
@ -831,24 +834,24 @@ packages:
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@ai-sdk/gateway@3.0.132(zod@4.4.3)':
|
'@ai-sdk/gateway@3.0.120(zod@4.4.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ai-sdk/provider': 3.0.10
|
'@ai-sdk/provider': 3.0.10
|
||||||
'@ai-sdk/provider-utils': 4.0.29(zod@4.4.3)
|
'@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
|
||||||
'@vercel/oidc': 3.2.0
|
'@vercel/oidc': 3.2.0
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
|
|
||||||
'@ai-sdk/openai-compatible@2.0.50(zod@4.4.3)':
|
'@ai-sdk/openai-compatible@2.0.48(zod@4.4.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ai-sdk/provider': 3.0.10
|
'@ai-sdk/provider': 3.0.10
|
||||||
'@ai-sdk/provider-utils': 4.0.29(zod@4.4.3)
|
'@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
|
|
||||||
'@ai-sdk/provider-utils@4.0.29(zod@4.4.3)':
|
'@ai-sdk/provider-utils@4.0.27(zod@4.4.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ai-sdk/provider': 3.0.10
|
'@ai-sdk/provider': 3.0.10
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
eventsource-parser: 3.1.0
|
eventsource-parser: 3.0.8
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
|
|
||||||
'@ai-sdk/provider@3.0.10':
|
'@ai-sdk/provider@3.0.10':
|
||||||
|
|
@ -890,14 +893,14 @@ snapshots:
|
||||||
'@biomejs/cli-win32-x64@2.4.12':
|
'@biomejs/cli-win32-x64@2.4.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@clack/core@1.4.1':
|
'@clack/core@1.3.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-wrap-ansi: 0.2.2
|
fast-wrap-ansi: 0.2.2
|
||||||
sisteransi: 1.0.5
|
sisteransi: 1.0.5
|
||||||
|
|
||||||
'@clack/prompts@1.5.1':
|
'@clack/prompts@1.4.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@clack/core': 1.4.1
|
'@clack/core': 1.3.1
|
||||||
fast-string-width: 3.0.2
|
fast-string-width: 3.0.2
|
||||||
fast-wrap-ansi: 0.2.2
|
fast-wrap-ansi: 0.2.2
|
||||||
sisteransi: 1.0.5
|
sisteransi: 1.0.5
|
||||||
|
|
@ -1068,40 +1071,40 @@ snapshots:
|
||||||
|
|
||||||
'@sec-ant/readable-stream@0.4.1': {}
|
'@sec-ant/readable-stream@0.4.1': {}
|
||||||
|
|
||||||
'@shikijs/core@4.2.0':
|
'@shikijs/core@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/primitive': 4.2.0
|
'@shikijs/primitive': 4.1.0
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
hast-util-to-html: 9.0.5
|
hast-util-to-html: 9.0.5
|
||||||
|
|
||||||
'@shikijs/engine-javascript@4.2.0':
|
'@shikijs/engine-javascript@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
oniguruma-to-es: 4.3.6
|
oniguruma-to-es: 4.3.6
|
||||||
|
|
||||||
'@shikijs/engine-oniguruma@4.2.0':
|
'@shikijs/engine-oniguruma@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
'@shikijs/langs@4.2.0':
|
'@shikijs/langs@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
|
|
||||||
'@shikijs/primitive@4.2.0':
|
'@shikijs/primitive@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
'@shikijs/themes@4.2.0':
|
'@shikijs/themes@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
|
|
||||||
'@shikijs/types@4.2.0':
|
'@shikijs/types@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|
@ -1125,7 +1128,11 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
'@types/node@25.9.3':
|
'@types/node@25.9.1':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.24.6
|
||||||
|
|
||||||
|
'@types/node@25.9.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.24.6
|
undici-types: 7.24.6
|
||||||
|
|
||||||
|
|
@ -1135,17 +1142,17 @@ snapshots:
|
||||||
|
|
||||||
'@types/ws@8.18.1':
|
'@types/ws@8.18.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 25.9.1
|
||||||
|
|
||||||
'@ungap/structured-clone@1.3.1': {}
|
'@ungap/structured-clone@1.3.1': {}
|
||||||
|
|
||||||
'@vercel/oidc@3.2.0': {}
|
'@vercel/oidc@3.2.0': {}
|
||||||
|
|
||||||
ai@6.0.206(zod@4.4.3):
|
ai@6.0.191(zod@4.4.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ai-sdk/gateway': 3.0.132(zod@4.4.3)
|
'@ai-sdk/gateway': 3.0.120(zod@4.4.3)
|
||||||
'@ai-sdk/provider': 3.0.10
|
'@ai-sdk/provider': 3.0.10
|
||||||
'@ai-sdk/provider-utils': 4.0.29(zod@4.4.3)
|
'@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
|
||||||
'@opentelemetry/api': 1.9.1
|
'@opentelemetry/api': 1.9.1
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
|
|
||||||
|
|
@ -1159,7 +1166,7 @@ snapshots:
|
||||||
|
|
||||||
buffer-image-size@0.6.4:
|
buffer-image-size@0.6.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 25.9.1
|
||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
||||||
|
|
@ -1216,7 +1223,7 @@ snapshots:
|
||||||
'@esbuild/win32-ia32': 0.28.1
|
'@esbuild/win32-ia32': 0.28.1
|
||||||
'@esbuild/win32-x64': 0.28.1
|
'@esbuild/win32-x64': 0.28.1
|
||||||
|
|
||||||
eventsource-parser@3.1.0: {}
|
eventsource-parser@3.0.8: {}
|
||||||
|
|
||||||
execa@9.6.1:
|
execa@9.6.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1254,9 +1261,9 @@ snapshots:
|
||||||
'@sec-ant/readable-stream': 0.4.1
|
'@sec-ant/readable-stream': 0.4.1
|
||||||
is-stream: 4.0.1
|
is-stream: 4.0.1
|
||||||
|
|
||||||
happy-dom@20.10.3:
|
happy-dom@20.10.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 25.9.1
|
||||||
'@types/whatwg-mimetype': 3.0.2
|
'@types/whatwg-mimetype': 3.0.2
|
||||||
'@types/ws': 8.18.1
|
'@types/ws': 8.18.1
|
||||||
buffer-image-size: 0.6.4
|
buffer-image-size: 0.6.4
|
||||||
|
|
@ -1278,7 +1285,7 @@ snapshots:
|
||||||
hast-util-whitespace: 3.0.0
|
hast-util-whitespace: 3.0.0
|
||||||
html-void-elements: 3.0.0
|
html-void-elements: 3.0.0
|
||||||
mdast-util-to-hast: 13.2.1
|
mdast-util-to-hast: 13.2.1
|
||||||
property-information: 7.2.0
|
property-information: 7.1.0
|
||||||
space-separated-tokens: 2.0.2
|
space-separated-tokens: 2.0.2
|
||||||
stringify-entities: 4.0.4
|
stringify-entities: 4.0.4
|
||||||
zwitch: 2.0.4
|
zwitch: 2.0.4
|
||||||
|
|
@ -1301,7 +1308,7 @@ snapshots:
|
||||||
|
|
||||||
json-schema@0.4.0: {}
|
json-schema@0.4.0: {}
|
||||||
|
|
||||||
marked@18.0.5: {}
|
marked@18.0.4: {}
|
||||||
|
|
||||||
mdast-util-to-hast@13.2.1:
|
mdast-util-to-hast@13.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1355,7 +1362,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
parse-ms: 4.0.0
|
parse-ms: 4.0.0
|
||||||
|
|
||||||
property-information@7.2.0: {}
|
property-information@7.1.0: {}
|
||||||
|
|
||||||
regex-recursion@6.0.2:
|
regex-recursion@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1394,14 +1401,14 @@ snapshots:
|
||||||
|
|
||||||
shebang-regex@3.0.0: {}
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
shiki@4.2.0:
|
shiki@4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/core': 4.2.0
|
'@shikijs/core': 4.1.0
|
||||||
'@shikijs/engine-javascript': 4.2.0
|
'@shikijs/engine-javascript': 4.1.0
|
||||||
'@shikijs/engine-oniguruma': 4.2.0
|
'@shikijs/engine-oniguruma': 4.1.0
|
||||||
'@shikijs/langs': 4.2.0
|
'@shikijs/langs': 4.1.0
|
||||||
'@shikijs/themes': 4.2.0
|
'@shikijs/themes': 4.1.0
|
||||||
'@shikijs/types': 4.2.0
|
'@shikijs/types': 4.1.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
minimumReleaseAge: 4320
|
|
||||||
allowBuilds:
|
allowBuilds:
|
||||||
esbuild: true
|
esbuild: true
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { chalkStderr } from "chalk";
|
|
||||||
import wrapAnsi from "wrap-ansi";
|
|
||||||
|
|
||||||
export interface ErrorLike {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isErrorLike = (err: unknown): err is ErrorLike =>
|
|
||||||
typeof err === "object" &&
|
|
||||||
err !== null &&
|
|
||||||
"message" in err &&
|
|
||||||
typeof err.message === "string";
|
|
||||||
|
|
||||||
export function handleTopLevel(err: unknown) {
|
|
||||||
if (isErrorLike(err)) {
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
const [columns] = process.stderr.getWindowSize();
|
|
||||||
console.error(wrapAnsi(chalkStderr.red(err.message), columns));
|
|
||||||
} else {
|
|
||||||
console.error(err.message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error(String(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
107
src/index.ts
107
src/index.ts
|
|
@ -1,107 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
import fs from "node:fs/promises";
|
|
||||||
import consumers from "node:stream/consumers";
|
|
||||||
import util from "node:util";
|
|
||||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
||||||
import { streamText } from "ai";
|
|
||||||
|
|
||||||
import { initConfig, loadConfig } from "./config";
|
|
||||||
import { Conversation } from "./conversation";
|
|
||||||
import { handleTopLevel } from "./errors";
|
|
||||||
import {
|
|
||||||
base,
|
|
||||||
commitDiff,
|
|
||||||
contextFence,
|
|
||||||
diffFence,
|
|
||||||
fileFence,
|
|
||||||
review,
|
|
||||||
} from "./prompts";
|
|
||||||
import Renderer from "./render";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const { values: args, positionals } = util.parseArgs({
|
|
||||||
args: process.argv.slice(2),
|
|
||||||
options: {
|
|
||||||
model: { type: "string", short: "m" },
|
|
||||||
new: { type: "boolean", short: "n" },
|
|
||||||
config: { type: "boolean", short: "c" },
|
|
||||||
plain: { type: "boolean" },
|
|
||||||
"plain-out": { type: "boolean" },
|
|
||||||
"plain-err": { type: "boolean" },
|
|
||||||
version: { type: "boolean", short: "v" },
|
|
||||||
"git-message": { type: "boolean", short: "g" },
|
|
||||||
review: { type: "boolean", short: "r" },
|
|
||||||
file: { type: "string", short: "f", multiple: true },
|
|
||||||
stdin: { type: "boolean", short: "i" },
|
|
||||||
},
|
|
||||||
strict: true,
|
|
||||||
allowPositionals: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (args.version) {
|
|
||||||
console.log("0.0.0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = await loadConfig(args.config);
|
|
||||||
|
|
||||||
if (args.config || !config) {
|
|
||||||
await initConfig(config);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const model = args.model ?? config.model;
|
|
||||||
|
|
||||||
const plainOut = args["plain-out"] ?? args.plain ?? !process.stdout.isTTY;
|
|
||||||
const plainErr = args["plain-err"] ?? args.plain ?? !process.stderr.isTTY;
|
|
||||||
const renderer = new Renderer(plainOut, plainErr);
|
|
||||||
|
|
||||||
const provider = createOpenAICompatible({
|
|
||||||
name: "llama.cpp",
|
|
||||||
apiKey: "local",
|
|
||||||
baseURL: config.baseURL,
|
|
||||||
includeUsage: true, // Include usage information in streaming responses
|
|
||||||
});
|
|
||||||
|
|
||||||
const convo = new Conversation(model);
|
|
||||||
|
|
||||||
if (!args.new) {
|
|
||||||
await convo.load();
|
|
||||||
if (args.model) convo.model = args.model;
|
|
||||||
}
|
|
||||||
|
|
||||||
const input = positionals.join(" ").trim();
|
|
||||||
let stdinText = "";
|
|
||||||
if (args.stdin) stdinText = await consumers.text(process.stdin);
|
|
||||||
const files = [];
|
|
||||||
for (const file of args.file ?? [])
|
|
||||||
files.push(fileFence(file, await fs.readFile(file, "utf8")));
|
|
||||||
|
|
||||||
let prompt = base(input, [
|
|
||||||
...files,
|
|
||||||
stdinText ? contextFence(stdinText) : "",
|
|
||||||
]);
|
|
||||||
if (args["git-message"]) {
|
|
||||||
prompt = commitDiff(stdinText, [...files]);
|
|
||||||
} else if (args.review) {
|
|
||||||
prompt = review([...files, stdinText ? diffFence(stdinText) : "", input]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prompt) throw new Error("No prompt given.");
|
|
||||||
|
|
||||||
convo.messages.push({ role: "user", content: prompt });
|
|
||||||
|
|
||||||
const { fullStream, text, totalUsage } = streamText({
|
|
||||||
model: provider(convo.model),
|
|
||||||
messages: convo.messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderer.renderStream(fullStream);
|
|
||||||
|
|
||||||
convo.messages.push({ role: "assistant", content: await text });
|
|
||||||
await convo.save();
|
|
||||||
|
|
||||||
await renderer.renderStats(totalUsage);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch(handleTopLevel);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
const blocks = (input?: string[]) =>
|
|
||||||
input?.map((block) => (block ? `${block}\n` : "")).join("") ?? "";
|
|
||||||
|
|
||||||
export const base = (input: string, extra?: string[]) =>
|
|
||||||
`${blocks(extra)}${input}`;
|
|
||||||
|
|
||||||
export const fileFence = (name: string, input: string) => `<file name="${name}">
|
|
||||||
${input}
|
|
||||||
</file>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const diffFence = (input: string) => `<diff>
|
|
||||||
${input}
|
|
||||||
</diff>`;
|
|
||||||
|
|
||||||
export const contextFence = (input: string) => `<context>
|
|
||||||
${input}
|
|
||||||
</context>`;
|
|
||||||
|
|
||||||
export const commitDiff = (
|
|
||||||
input: string,
|
|
||||||
extra?: string[],
|
|
||||||
) => `${blocks(extra)}${diffFence(input)}
|
|
||||||
Generate a conventional commit message for the included context. If you are unable to generate a reasonable option, ask for more context.`;
|
|
||||||
|
|
||||||
export const review = (input: string[]) => `${blocks(input)}
|
|
||||||
Review the included context for any potential bugs, best practices, performance issues, or security flaws.
|
|
||||||
|
|
||||||
Today's date is ${new Date().toISOString()}, your version info is likely out-of-date.
|
|
||||||
|
|
||||||
Let me know if you need any more context to complete your review.`;
|
|
||||||
|
|
@ -51,7 +51,7 @@ export default class Renderer {
|
||||||
this.charsSinceNL = this.charsSinceNL - columns;
|
this.charsSinceNL = this.charsSinceNL - columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lineCount >= Math.max(rows - 4, 1)) {
|
if (this.lineCount >= Math.max(rows - 10, 1)) {
|
||||||
this.moveClearReset(-this.lineCount, pipe);
|
this.moveClearReset(-this.lineCount, pipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,13 +84,7 @@ export default class Renderer {
|
||||||
async renderStream(stream: AsyncIterableStream<TextStreamPart<ToolSet>>) {
|
async renderStream(stream: AsyncIterableStream<TextStreamPart<ToolSet>>) {
|
||||||
this.resetCounts();
|
this.resetCounts();
|
||||||
|
|
||||||
const spin = spinner({
|
const spin = spinner({ withGuide: false, indicator: "timer" });
|
||||||
withGuide: false,
|
|
||||||
indicator: "timer",
|
|
||||||
onCancel: () => {
|
|
||||||
process.exit(130);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!this.isPlain) {
|
if (!this.isPlain) {
|
||||||
spin.start("Waiting for response");
|
spin.start("Waiting for response");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ class LmcRenderer extends Renderer {
|
||||||
this.listitem(item, ordered ? num + index : undefined),
|
this.listitem(item, ordered ? num + index : undefined),
|
||||||
)
|
)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
return `\n${renderedItems}\n`;
|
return `\n${renderedItems}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,8 +91,7 @@ class LmcRenderer extends Renderer {
|
||||||
const item = `${marker} ${text}`;
|
const item = `${marker} ${text}`;
|
||||||
return item
|
return item
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter((line) => line.length > 0)
|
.map((line) => (line.length > 0 ? ` ${line}` : ""))
|
||||||
.map((line) => ` ${line}`)
|
|
||||||
.join("\n");
|
.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import events from "node:events";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
import consumers from "node:stream/consumers";
|
|
||||||
import util from "node:util";
|
import util from "node:util";
|
||||||
import { Readability } from "@mozilla/readability";
|
import { Readability } from "@mozilla/readability";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { ExecaError, execa } from "execa";
|
import { execa } from "execa";
|
||||||
import { Window } from "happy-dom";
|
import { Window } from "happy-dom";
|
||||||
import wrapAnsi from "wrap-ansi";
|
import wrapAnsi from "wrap-ansi";
|
||||||
import { handleTopLevel } from "./errors";
|
|
||||||
|
class InputError extends Error {
|
||||||
|
override name = "InputError";
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
@ -21,8 +24,6 @@ async function main() {
|
||||||
options: {
|
options: {
|
||||||
wait: { type: "boolean" },
|
wait: { type: "boolean" },
|
||||||
w: { type: "boolean" },
|
w: { type: "boolean" },
|
||||||
pause: { type: "boolean" },
|
|
||||||
p: { type: "boolean" },
|
|
||||||
url: { type: "boolean" },
|
url: { type: "boolean" },
|
||||||
u: { type: "boolean" },
|
u: { type: "boolean" },
|
||||||
edit: { type: "boolean" },
|
edit: { type: "boolean" },
|
||||||
|
|
@ -32,20 +33,27 @@ async function main() {
|
||||||
allowPositionals: true,
|
allowPositionals: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const waitAndCombine = args.wait ?? args.w;
|
const wait = args.wait ?? args.w;
|
||||||
const pausePlayer = args.pause ?? args.p;
|
const url = args.url ?? args.u;
|
||||||
const readURL = args.url ?? args.u;
|
const edit = args.edit ?? args.e;
|
||||||
const editContent = args.edit ?? args.e;
|
|
||||||
|
|
||||||
let input = positionals.join(" ").trim();
|
let input = positionals.join(" ").trim();
|
||||||
|
|
||||||
if (!input) input = (await consumers.text(process.stdin)).trim();
|
|
||||||
|
|
||||||
if (!input) {
|
if (!input) {
|
||||||
throw new Error("No input was given. Use positional arguments or stdin.");
|
process.stdin.setEncoding("utf8");
|
||||||
|
process.stdin.on("data", (data) => {
|
||||||
|
input += data;
|
||||||
|
});
|
||||||
|
await events.once(process.stdin, "end");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readURL) {
|
if (!input) {
|
||||||
|
throw new InputError(
|
||||||
|
"No input was given. Use positional arguments or stdin.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url) {
|
||||||
const result = await fetch(input);
|
const result = await fetch(input);
|
||||||
const window = new Window({ url: input });
|
const window = new Window({ url: input });
|
||||||
const doc = window.document;
|
const doc = window.document;
|
||||||
|
|
@ -53,7 +61,7 @@ async function main() {
|
||||||
await window.happyDOM.waitUntilComplete();
|
await window.happyDOM.waitUntilComplete();
|
||||||
const readable = new Readability(doc).parse();
|
const readable = new Readability(doc).parse();
|
||||||
if (!readable?.textContent) {
|
if (!readable?.textContent) {
|
||||||
throw new Error(
|
throw new InputError(
|
||||||
"Unable to read webpage. This usually means it's blocking scrapers.",
|
"Unable to read webpage. This usually means it's blocking scrapers.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +71,7 @@ async function main() {
|
||||||
const home = os.homedir();
|
const home = os.homedir();
|
||||||
const cwd = path.join(home, ".ttsc");
|
const cwd = path.join(home, ".ttsc");
|
||||||
|
|
||||||
if (editContent) {
|
if (edit) {
|
||||||
const file = path.join(cwd, "input.txt");
|
const file = path.join(cwd, "input.txt");
|
||||||
await fs.writeFile(file, input, "utf8");
|
await fs.writeFile(file, input, "utf8");
|
||||||
const editor = process.env.EDITOR || "nano";
|
const editor = process.env.EDITOR || "nano";
|
||||||
|
|
@ -93,7 +101,7 @@ async function main() {
|
||||||
stderr: "inherit",
|
stderr: "inherit",
|
||||||
})`uv run -- python -u main.py`;
|
})`uv run -- python -u main.py`;
|
||||||
|
|
||||||
if (!waitAndCombine) {
|
if (!wait) {
|
||||||
koko.catch(() => {});
|
koko.catch(() => {});
|
||||||
koko.then(() => {
|
koko.then(() => {
|
||||||
const stop = Date.now();
|
const stop = Date.now();
|
||||||
|
|
@ -127,7 +135,7 @@ async function main() {
|
||||||
|
|
||||||
const file = path.join(cwd, `${info.file}.wav`);
|
const file = path.join(cwd, `${info.file}.wav`);
|
||||||
|
|
||||||
if (waitAndCombine) {
|
if (wait) {
|
||||||
files.push(file);
|
files.push(file);
|
||||||
process.stdout.write(".");
|
process.stdout.write(".");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -139,12 +147,12 @@ async function main() {
|
||||||
stdin: "ignore",
|
stdin: "ignore",
|
||||||
stdout: "inherit",
|
stdout: "inherit",
|
||||||
stderr: "inherit",
|
stderr: "inherit",
|
||||||
})`mpv${pausePlayer ? " --pause" : ""} ${file}`;
|
})`mpv ${file}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await koko;
|
await koko;
|
||||||
if (!waitAndCombine) return;
|
if (!wait) return;
|
||||||
|
|
||||||
const stop = Date.now();
|
const stop = Date.now();
|
||||||
const duration = ((stop - start) / 1000).toFixed(2);
|
const duration = ((stop - start) / 1000).toFixed(2);
|
||||||
|
|
@ -154,7 +162,7 @@ async function main() {
|
||||||
try {
|
try {
|
||||||
await execa({ cwd, shell: true })`sox ${files} output.wav`;
|
await execa({ cwd, shell: true })`sox ${files} output.wav`;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ExecaError && err.code === "ENOENT") {
|
if (err instanceof Object && "code" in err && err.code === "ENOENT") {
|
||||||
console.error("Sox not found. Unable to combine outputs.");
|
console.error("Sox not found. Unable to combine outputs.");
|
||||||
console.log(chalk.yellow(`Audio available in ${cwd}`));
|
console.log(chalk.yellow(`Audio available in ${cwd}`));
|
||||||
return;
|
return;
|
||||||
|
|
@ -174,7 +182,10 @@ async function main() {
|
||||||
stdin: "ignore",
|
stdin: "ignore",
|
||||||
stdout: "inherit",
|
stdout: "inherit",
|
||||||
stderr: "inherit",
|
stderr: "inherit",
|
||||||
})`mpv${pausePlayer ? " --pause" : ""} output.wav`;
|
})`mpv --pause output.wav`;
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(handleTopLevel);
|
main().catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue