- TypeScript 82.2%
- Python 17.8%
kittycharactersheet's hand-rolled brainstorm loop caches its large, stable game-ruleset text for an hour so multi-turn/tool-loop follow-ups don't re-bill it as fresh input tokens every call. The client's system option only took a plain string, with no way to set a cache_control breakpoint, which would have been a real cost regression on migration. Widen system to string | TextBlockParam[] and add cachedTextBlock() to build the cached block. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| packages | ||
| .gitignore | ||
| package.json | ||
| README.md | ||
claudechatwindow
Shared packages extracted from the Claude chat feature that's been independently built (and independently bugged) in careboard, homeclaude, HomeManagement, and kittycharactersheet — plus the one about to be added to medsync.
Two packages, not one central service. Each app keeps its own API key and model (no shared credential, no proxy to run/secure/monitor), so per-app usage tracking works exactly as it does today. Fixes — like the Android speech-recognition duplication bug — land here once, and each app picks them up by bumping a dependency version.
packages/ui(@brian/claude-chat-ui) — ReactuseSpeechToTexthook,ChatInput,MessageList. Seepackages/ui/README.md.packages/client-node(@brian/claude-chat-client) — Node/TS Anthropic client + tool-use loop, for the two Node backends. Seepackages/client-node/README.md.packages/client-python(claude-chat-client-py, import nameclaude_chat_client) — Python equivalent, for the three Python backends. Seepackages/client-python/README.md.
All three were typechecked/built/import-tested in node:20-slim and
python:3.12-slim containers (no Node/npm/pip available on this machine
directly). Re-run that after any further changes:
docker run --rm -v "$(pwd)":/work -w /work node:20-slim \
sh -c "npm install && npm run typecheck --workspaces && npm run build --workspaces"
docker run --rm -v "$(pwd)":/work -w /work python:3.12-slim \
sh -c "pip install anthropic && pip install -e packages/client-python"
Publishing (Forgejo package registry)
.forgejo/workflows/publish.yml publishes all three packages to this
Forgejo instance's own package registry on push to main — same
git.thenymans.com/brian namespace the other apps already push Docker
images to, reusing the same REGISTRY_USER/REGISTRY_PASSWORD repo
secrets. Nothing has been pushed yet; before any consuming app's Docker
build can succeed, this repo needs to actually exist on
git.thenymans.com and have those secrets configured, and a push to
main needs to run the workflow at least once so v0.1.0 of each package
lands in the registry.
- npm packages install from
https://git.thenymans.com/api/packages/brian/npm/(scoped as@brian/...). - The Python package installs from
https://git.thenymans.com/api/packages/brian/pypi/simple/. REGISTRY_PASSWORDis used both as the npm_authTokenand the PyPI upload password — confirm that token actually has package write scope in Forgejo (Docker registry auth and package registry auth are separate permission checks even though the existing workflows reuse the same secret name for both).
What's not here yet
- No unified chat-drawer component. careboard, homeclaude,
HomeManagement, and kittycharactersheet each have differently-shaped
chrome (bottom sheet / vanilla-JS monolith / drawer / floating
launcher) and different wire formats (NDJSON vs. SSE-like).
ChatInputandMessageListare the pieces common enough to extract without forcing a redesign; the surrounding layout stays app-specific for now. - homeclaude is vanilla JS/HTML, not React.
@brian/claude-chat-client(Node) still applies to its backend. For the frontend, either bundle@brian/claude-chat-uias a web component later, or leave homeclaude's hand-rolled speech code as the one holdout — it already has a correct fix in place, it's just not shared.
Per-app migration
homeclaude (server/index.js, already @anthropic-ai/sdk)
Backend only: replace the .messages.create(...) call sites (index.js
lines 372, 446, 474, 2033) and getClient() with createClaudeClient({ apiKey, model }) from @brian/claude-chat-client, keeping its existing DB-backed
key/model config as the source of those two values. Frontend
(public/index.html) can stay as-is — it already has a correct,
if unshared, fix for the voice bug.
kittycharactersheet (backend/anthropic.js, frontend/src/components/BrainstormChat.jsx)
Backend: swap runBrainstorm()'s hand-rolled stream/tool loop for
@brian/claude-chat-client, keeping its existing 3-tier key resolution
(per-user → admin default → env var) and its MODEL constant as the
model passed in. Frontend: no voice input exists yet here — if you want
to add it, wire up @brian/claude-chat-ui's ChatInput/useSpeechToText rather
than writing a fourth version of the fix.
careboard (api/app/services/assistant.py, raw httpx)
Backend: replace _call_anthropic/run_turn in assistant.py with
ClaudeClient from claude-chat-client-py, keeping the existing
DB-backed settings_service lookups for anthropic_api_key /
anthropic_model as what gets passed into ClaudeClientConfig. This also
moves careboard off raw httpx onto the official SDK, matching the other
Python apps. Frontend (web/src/lib/speech.ts,
web/src/components/AssistantPanel.tsx): swap for @brian/claude-chat-ui;
careboard's current continuous=false approach avoids the Android bug by
never entering continuous mode, so switching to the shared
useSpeechToText (continuous, with the fix applied) is a behavior
upgrade, not just a dedupe — continuous mode means users don't have to
tap the mic again between sentences.
HomeManagement (backend/app/routers/chat.py, frontend/src/components/ChatDrawer.tsx)
This one's already the closest match to both new packages — its
streaming loop and its speech-recognition fix are what claude-chat-client-py
and useSpeechToText are modeled on. Backend: replace _run_chat with
ClaudeClient.chat(...), keeping HM_ANTHROPIC_API_KEY/HM_ANTHROPIC_MODEL
(via appconfig.get_app_config) as the config source. Frontend: replace
ChatDrawer.tsx's toggleListening (lines 111-164) with
useSpeechToText/ChatInput.
medsync (new)
Since this is being built fresh rather than migrated, start directly on top of both packages instead of writing a fourth bespoke version:
- Backend (
backend/app/routes/): new chat router built onClaudeClientfromclaude-chat-client-py, with its ownMEDSYNC_ANTHROPIC_API_KEY/MEDSYNC_ANTHROPIC_MODELconfig, following HomeManagement'sappconfigpattern. - Frontend (
frontend/src/components/): new chat panel built onChatInput/MessageList/useSpeechToTextfrom@brian/claude-chat-ui, styled to match medsync's existingScanModal.jsxconventions.