34cbecf975ce83a62bfb00af208d1fd12c98e98f
Pattern lift from eyaltoledano/claude-task-master (MIT + Commons Clause — pattern only, no code lift). Adds BOOCODE_TOOLS env var with three tiers: - core (4 tools): view_file, list_dir, grep, find_files. ~2k token schema cost. - standard (15 tools): core + web_search, web_fetch, git_status, all 8 codecontext_* tools. ~10k token schema cost. - all (default; current behavior): every tool in ALL_TOOLS (20). ~21k token schema cost. The env var is a CEILING — narrows agent whitelists, never expands. Default behavior unchanged when var is unset. resolveToolTier is case-insensitive and falls back to 'all' on unknown values. CORE_TOOL_NAMES + STANDARD_TOOL_NAMES validated at module load against TOOLS_BY_NAME via two top-level for-loops that throw on the first missing name. Module fails to import if a tier references a tool that doesn't exist in the registry — catches typos and stale tier definitions at boot rather than silently filtering valid tools out of agent whitelists. Wiring: agents.ts parseAgentBlock now reads BOOCODE_TOOLS from process.env per parse, intersects with the agent's declared frontmatter tools (or DEFAULT_TOOLS when frontmatter omits the field). Per-parse read is fine — agents are re-parsed on the existing 60s cache TTL. Tests: tools.test.ts grows from 1 to 10 tests. Covers resolveToolTier across tiers/case/unknown values + the CORE-subset-of-STANDARD invariant + TOOLS_BY_NAME existence for both tier sets. 204/204 pass (was 195; +9 new). Deviation from the brief: the codecontext tools in the actual registry have NO codecontext_* prefix (the brief's STANDARD list assumed it). Used the actual names (get_codebase_overview, search_symbols, etc.). Module-load validation would have failed boot with the prefixed names. Smoke: with BOOCODE_TOOLS unset, agents return their full 12-tool whitelists. With BOOCODE_TOOLS=core in .env + container restart, the same agents narrow to 4 tools (find_files, grep, list_dir, view_file) — intersection of declared whitelist ∩ core tier. Reverted after confirmation. CLAUDE.md updated with BOOCODE_TOOLS in the Environment section's Optional list. .env.example gained a commented BOOCODE_TOOLS=all line with the per-tier token-cost table. ~110 LoC across 5 files (4 modified + 1 test expansion). Under the brief's ~30 LoC estimate for code; the test suite expansion drove most of the growth.
boocode
Self-hosted single-user developer chat app. v1: chat only.
Stack
- Node 20, Fastify, postgres (porsager/postgres), ws, zod
- React 18, Vite, TypeScript, Tailwind v4, shadcn/ui
- Postgres 16
- pnpm workspaces
Layout
apps/server— Fastify API + WebSocket + inference loop + file-read toolsapps/web— React frontend; served by Fastify in production, Vite in dev
Local dev
Requires Node 20, pnpm, Docker (for Postgres), and ripgrep.
# install
pnpm install
# bring up postgres only
cp .env.example .env
# edit POSTGRES_PASSWORD if you like; default DATABASE_URL points at the container
docker compose up -d boocode_db
# run server (port 3000) and web (port 5173) in two shells
DATABASE_URL=postgres://boocode:devpass@127.0.0.1:5500/boocode \
LLAMA_SWAP_URL=http://100.101.41.16:8401 \
pnpm dev:server
pnpm dev:web
The Vite dev server proxies /api and /api/ws/* to the Fastify backend with a
synthetic Remote-User: sam header so the Authelia auth layer can be skipped
during development.
Production
cd /opt/boocode
docker compose up --build -d
Binds to 100.114.205.53:9500 (Tailscale). Authelia is expected to gate the
upstream and inject Remote-User. Postgres binds loopback only.
What v1 has
Project sidebar, sessions per project, chat with streaming responses over
WebSocket, four file-read tools scoped to the project root (view_file,
list_dir, grep, find_files), and a model picker driven by llama-swap's
/v1/models.
What v1 does not have lives in v2 (terminal pane) and v3 (Coder pane).
Languages
TypeScript
94.3%
CSS
1.9%
JavaScript
1.2%
Shell
0.8%
Go
0.6%
Other
1.2%