merge(pi/main): integrate upstream changes through v0.31.1

Merged 53 commits from pi/main including:
- Export HTML rewrite with tree sidebar and /share command
- Hooks API enhancements (custom status, editor, session management)
- Edit diff preview before tool execution
- TUI fixes (Unicode format chars, OSC 8 hyperlinks, emoji optimization)
- Gemini CLI rate limit retry with server-provided delay
- Various bug fixes and documentation updates

Resolved conflicts by preserving Bun migration (spawn→Bun.spawn,
fs→Bun.file) while accepting upstream's new features and APIs.
This commit is contained in:
can1357
2026-01-02 13:52:12 +01:00
92 changed files with 6332 additions and 2520 deletions
+250
View File
@@ -21,6 +21,256 @@ read README.md, then ask which module(s) to work on. Based on the answer, read t
- NEVER remove or downgrade code to fix type errors from outdated dependencies; upgrade the dependency instead
- Always ask before removing functionality or code that appears to be intentional
## Bun Over Node
This project uses Bun as its runtime. Always prefer Bun APIs over Node.js equivalents.
### Process Spawning
```typescript
// GOOD: Bun.spawn with ReadableStream API
import type { Subprocess } from "bun";
const child: Subprocess = Bun.spawn(["cmd", ...args], {
stdin: "ignore",
stdout: "pipe",
stderr: "pipe",
});
const reader = (child.stdout as ReadableStream<Uint8Array>).getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// process Buffer.from(value)
}
const exitCode = await child.exited;
// BAD: Node child_process
import { spawn } from "node:child_process";
const child = spawn("cmd", args);
child.stdout.on("data", (chunk) => { ... });
child.on("close", (code) => { ... });
```
### Sync Process Execution
```typescript
// GOOD: Bun.spawnSync
const result = Bun.spawnSync(["cmd", ...args], {
stdin: "ignore",
stdout: "pipe",
stderr: "pipe",
});
if (result.exitCode === 0) { ... }
// BAD: Node execSync/spawnSync
import { spawnSync } from "node:child_process";
```
### File I/O
```typescript
// GOOD: Bun.file and Bun.write
const content = await Bun.file("path.txt").text();
const binary = await Bun.file("image.png").arrayBuffer();
const exists = await Bun.file("maybe.txt").exists();
await Bun.write("path.txt", content);
// BAD: Node fs
import { readFileSync, writeFileSync, existsSync } from "node:fs";
```
### Scripts and package.json
```json
// GOOD: Use bun in scripts
{
"scripts": {
"start": "bun run src/index.ts",
"test": "bun test",
"check": "bunx tsc --noEmit"
}
}
// BAD: Use node/npm/npx
{
"scripts": {
"start": "node dist/index.js",
"test": "npm test",
"check": "npx tsc --noEmit"
}
}
```
### Running Commands
```bash
# GOOD
bun run script.ts
bun test
bunx tsc --noEmit
bun install
# BAD
node script.js
npm test
npx tsc --noEmit
npm install
```
### Type Imports for Bun APIs
```typescript
// Import Bun types when needed
import type { Subprocess } from "bun";
// Bun globals are available without import
Bun.spawn(...)
Bun.file(...)
Bun.write(...)
Bun.stdin.stream()
```
### Casting Bun Subprocess Streams
Bun's `Subprocess.stdout`/`stderr` types can be `number | ReadableStream | undefined`. Cast when using pipe mode:
```typescript
const child = Bun.spawn(["cmd"], { stdout: "pipe", stderr: "pipe" });
const stdoutReader = (child.stdout as ReadableStream<Uint8Array>).getReader();
const stderrReader = (child.stderr as ReadableStream<Uint8Array>).getReader();
```
### Binary Existence Checks
```typescript
// GOOD: Bun.which (cross-platform)
const gitPath = Bun.which("git");
if (!gitPath) throw new Error("git not found");
// BAD: Spawning which/where (platform-specific)
Bun.spawnSync(["which", "git"]); // Unix only
Bun.spawnSync(["where", "git"]); // Windows only
```
### Path Resolution
```typescript
// GOOD: import.meta (Bun ESM)
const thisDir = import.meta.dir; // equivalent to __dirname
const thisFile = import.meta.path; // equivalent to __filename
// BAD: fileURLToPath dance
import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
```
### Crypto
```typescript
// GOOD: Web Crypto (standard) or Bun.hash
const uuid = crypto.randomUUID();
const bytes = crypto.getRandomValues(new Uint8Array(32));
const hash = Bun.hash("sha256", data);
// BAD: Node crypto
import { randomBytes, randomUUID } from "node:crypto";
```
### Environment Variables
Bun auto-loads `.env` files. Do NOT import dotenv.
```typescript
// GOOD: Direct access
const apiKey = process.env.API_KEY;
// BAD: dotenv
import dotenv from "dotenv";
dotenv.config();
```
### HTTP Servers
```typescript
// GOOD: Bun.serve
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("OK");
},
});
// later: server.stop();
// BAD: Node http
import http from "node:http";
const server = http.createServer((req, res) => { ... });
```
### HTTP Requests
```typescript
// GOOD: Native fetch (built into Bun)
const response = await fetch("https://api.example.com/data");
const json = await response.json();
// BAD: External packages
import fetch from "node-fetch";
import axios from "axios";
```
### Password Hashing
```typescript
// GOOD: Bun.password (bcrypt/argon2 built-in)
const hash = await Bun.password.hash("password", "bcrypt");
const valid = await Bun.password.verify("password", hash);
// BAD: External packages
import bcrypt from "bcrypt";
import argon2 from "argon2";
```
### Watch Mode
```bash
# GOOD: Bun built-in watch/hot reload
bun --watch src/index.ts # Restart on changes
bun --hot src/index.ts # Hot reload without restart
# BAD: External tools
npx nodemon src/index.ts
npx ts-node-dev src/index.ts
```
### SQLite
```typescript
// GOOD: Bun built-in SQLite
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite");
db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)");
// BAD: External packages
import Database from "better-sqlite3";
```
### Testing
```bash
# GOOD: Bun built-in test runner
bun test
# BAD: External test runners
npx jest
npx vitest
npx mocha
```
See `docs/bun-migration-guide.md` for full migration reference.
## Commands
- After code changes: `npm run check` (get full output, no tail)