4e521cf8988b13af4b88d0a94ac286b7cf9b0ab0

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Rename to kaiwari

Diff

  1diff --git a/.gitignore b/.gitignore
  2index 8fc497423bd20060b1ff9f0995e4cc0724be0e63..ad5a59025f74622229708ff3fc03dcadaa758e8f 100644
  3--- a/.gitignore
  4+++ b/.gitignore
  5@@ -1,17 +1,13 @@
  6 # Go build artifacts
  7-/jp
  8+/kaiwari
  9 /bin/
 10 *.test
 11 
 12-# Local temporary output
 13-/tmp/
 14-jp_raw.log
 15-
 16 # Agent files
 17 .pi/
 18 /plan.md
 19 /PLAN.md
 20 /todo.md
 21 
 22-# Model weights (fetched by download-qwen3.sh)
 23+# Model weights
 24 /models/
 25diff --git a/AGENTS.md b/AGENTS.md
 26index 20b6fffba42db0b9dc55437fd343b5c61454b0ae..2cda111e89a163f78359f9c1ea1450df4a6295ca 100644
 27--- a/AGENTS.md
 28+++ b/AGENTS.md
 29@@ -1,4 +1,4 @@
 30-# jp — Japanese learning RPG (TUI + HTTP server)
 31+# kaiwari — Japanese learning RPG (TUI + HTTP server)
 32 
 33 ## Overview
 34 
 35@@ -13,10 +13,10 @@ consistent across visits. Only romaji is ever shown to the player; kana and kanj
 36 Two entry points share one game core (`internal/game`), the LLM layer
 37 (`internal/llm`), config, and bootstrap:
 38 
 39-- **`cmd/jp`** — a Bubble Tea TUI. Runs the game in-process: it builds its own
 40+- **`cmd/kaiwari`** — a Bubble Tea TUI. Runs the game in-process: it builds its own
 41   state + orchestrator with local adapters (mic capture via `arecord`, local
 42   audio playback) and talks straight to the model services.
 43-- **`cmd/jp-server`** — an HTTP server exposing in-memory game sessions for a
 44+- **`cmd/kaiwari-server`** — an HTTP server exposing in-memory game sessions for a
 45   thin external client (e.g. iOS). The client records and plays audio; the
 46   server does ASR transcription, the game loop, the judge, and TTS synthesis
 47   (returns the NPC line as WAV bytes; replay is client-side). Turns serialize
 48@@ -38,7 +38,7 @@ transcription, and the judge; scratch questions only touch the scratch model
 49 and never enter world state. Per-location history is compacted into summaries
 50 when it grows past budget (`internal/game/state.go`,
 51 `internal/llm/history.go`). Raw model I/O for debugging is appended to
 52-`jp_raw.log` in the working directory.
 53+`kaiwari_raw.log` in the working directory.
 54 
 55 ## How things are done here
 56 
 57@@ -52,7 +52,7 @@ almost fully allocated to the chat model.
 58 
 59 ### Product invariants
 60 
 61-- Render romaji only; never expose kana or kanji to the player — not in the TUI and not across the jp-server wire.
 62+- Render romaji only; never expose kana or kanji to the player — not in the TUI and not across the kaiwari-server wire.
 63 - NPCs behave as normal people, not language teachers. Grading stays separate
 64   from NPC dialogue; judge output never enters the NPC prompt.
 65 - Persona data contains no game, player, NPC, quest, or scenario context.
 66@@ -64,7 +64,7 @@ almost fully allocated to the chat model.
 67   interfaces defined at their point of use in `internal/game/orchestrator.go`.
 68 - Speech has two adapter pairs over the same interface: local (mic via
 69   `arecord` + player) in `internal/adapters` for the TUI, and remote (uploaded
 70-  WAV in, returned WAV out) in `internal/server/speech.go` for jp-server.
 71+  WAV in, returned WAV out) in `internal/server/speech.go` for kaiwari-server.
 72 - LLM replies are parsed from strict `FIELD|value` contracts, never freeform
 73   JSON.
 74 - Errors wrap with `%w`. Startup failures print one stderr line naming the
 75diff --git a/Makefile b/Makefile
 76index 6c24344555e10a32e18fd097d9101ae2827e1965..bc240eca5af3158c1de0e80a350551e9a5893ff4 100644
 77--- a/Makefile
 78+++ b/Makefile
 79@@ -8,10 +8,10 @@ fmt:
 80 	golangci-lint fmt ./...
 81 
 82 build:
 83-	go build -o jp ./cmd/jp
 84+	go build -o kaiwari ./cmd/kaiwari
 85 
 86 run:
 87-	go run ./cmd/jp
 88+	go run ./cmd/kaiwari
 89 
 90 llama:
 91 	llama-server \
 92diff --git a/README.md b/README.md
 93index 6f0a5ff147c215047a5a4f9db47263bf3c3d10c7..cd5b691b319da6461cf9fcd3b87f36bb84389bc3 100644
 94--- a/README.md
 95+++ b/README.md
 96@@ -1,4 +1,4 @@
 97-# jp — Japanese learning RPG (TUI)
 98+# kaiwari — Japanese learning RPG (TUI)
 99 
100 A terminal game for practicing spoken Japanese. You type English actions to
101 explore an LLM-narrated world and push-to-talk lines of Japanese; your words
102@@ -31,7 +31,7 @@ downloads a model for any of them. You run those services yourself.
103 
104 ```bash
105 go build ./...             # or: make build
106-jp                         # or: make run (go run ./cmd/jp)
107+kaiwari                    # or: make run (go run ./cmd/kaiwari)
108 ```
109 
110 On startup the game waits up to 30s for the LLM model to finish loading
111@@ -55,7 +55,7 @@ which win over defaults. Defaults match a local setup.
112 | Audio base URL | `--audio.url`           | `JP_AUDIO_BASE_URL`      | `http://127.0.0.1:8080`                | audio.cpp server (TTS + ASR)                            |
113 | Scenario brief | `--scenario`            | `JP_SCENARIO_PATH`       | `assets/scenarios/small_city.md`       | plain-text scenario brief file                          |
114 
115-Run `jp -help` for the full flag list.
116+Run `kaiwari -help` for the full flag list.
117 
118 ## Controls
119 
120diff --git a/cmd/jp-server/main.go b/cmd/kaiwari-server/main.go
121rename from cmd/jp-server/main.go
122rename to cmd/kaiwari-server/main.go
123index e488c5fa7bb4477d2a70441e6011091863f410ee..b1bdaea2e4be69982e8472b7934920f75af1890b 100644
124--- a/cmd/jp-server/main.go
125+++ b/cmd/kaiwari-server/main.go
126@@ -6,9 +6,9 @@ import (
127 	"net/http"
128 	"os"
129 
130-	"japanese/internal/bootstrap"
131-	"japanese/internal/config"
132-	"japanese/internal/server"
133+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/bootstrap"
134+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/config"
135+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/server"
136 )
137 
138 func main() {
139@@ -24,13 +24,13 @@ func main() {
140 	}
141 
142 	srv := server.New(svc, cfg.Token)
143-	fmt.Printf("jp-server: listening on %s\n", cfg.Listen)
144+	fmt.Printf("kaiwari-server: listening on %s\n", cfg.Listen)
145 	if err := http.ListenAndServe(cfg.Listen, srv.Handler()); err != nil {
146 		fatalf("%v", err)
147 	}
148 }
149 
150 func fatalf(format string, args ...any) {
151-	fmt.Fprintf(os.Stderr, "jp-server: "+format+"\n", args...)
152+	fmt.Fprintf(os.Stderr, "kaiwari-server: "+format+"\n", args...)
153 	os.Exit(1)
154 }
155diff --git a/cmd/jp/main.go b/cmd/kaiwari/main.go
156rename from cmd/jp/main.go
157rename to cmd/kaiwari/main.go
158index 02057132812065acb68a653de824856b4c73934a..68f1d699a43147774ce2733578f41f5eb06d27cd 100644
159--- a/cmd/jp/main.go
160+++ b/cmd/kaiwari/main.go
161@@ -7,13 +7,13 @@ import (
162 
163 	"github.com/charmbracelet/bubbletea"
164 
165-	"japanese/internal/adapters"
166-	"japanese/internal/bootstrap"
167-	"japanese/internal/config"
168-	"japanese/internal/game"
169-	"japanese/internal/stt"
170-	"japanese/internal/tts"
171-	"japanese/internal/ui"
172+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/adapters"
173+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/bootstrap"
174+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/config"
175+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/game"
176+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/stt"
177+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/tts"
178+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/ui"
179 )
180 
181 // recordCap bounds a single push-to-talk capture. It is passed to the recorder
182@@ -61,6 +61,6 @@ func buildOrchestrator(svc *bootstrap.Services, state *game.State) *game.Orchest
183 }
184 
185 func fatalf(format string, args ...any) {
186-	fmt.Fprintf(os.Stderr, "jp: "+format+"\n", args...)
187+	fmt.Fprintf(os.Stderr, "kaiwari: "+format+"\n", args...)
188 	os.Exit(1)
189 }
190diff --git a/go.mod b/go.mod
191index 9eb7802f39776007ca032c68ceed94919cecde05..26c6f3ac629abc0fc01fd15fee04c413bebfbd21 100644
192--- a/go.mod
193+++ b/go.mod
194@@ -1,4 +1,4 @@
195-module japanese
196+module git.theedgeofrage.com/TheEdgeOfRage/kaiwari
197 
198 go 1.27.0
199 
200diff --git a/internal/adapters/flashcards.go b/internal/adapters/flashcards.go
201index 5a44d55ec92f01290fa54c50e827f8a121408b8c..9b473c1422369c0473e2a14ab7ad7af3d36b79f3 100644
202--- a/internal/adapters/flashcards.go
203+++ b/internal/adapters/flashcards.go
204@@ -7,7 +7,7 @@ import (
205 	"os"
206 	"sync"
207 
208-	"japanese/internal/llm"
209+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/llm"
210 )
211 
212 // flashcardsPath is where generated vocabulary cards are persisted, next to the
213diff --git a/internal/adapters/llm.go b/internal/adapters/llm.go
214index 2d8132af494f6d807ac36c0f338d5a7eb004adf5..131e6327202aacfcf01dd2b4859e80f792866815 100644
215--- a/internal/adapters/llm.go
216+++ b/internal/adapters/llm.go
217@@ -12,12 +12,12 @@ import (
218 	"strings"
219 	"time"
220 
221-	"japanese/internal/llm"
222+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/llm"
223 )
224 
225 // rawLogPath is where raw model outputs are dumped for debugging, since the TUI
226 // owns the terminal and direct stderr writes get mangled by redraws.
227-const rawLogPath = "jp_raw.log"
228+const rawLogPath = "kaiwari_raw.log"
229 
230 func dumpRaw(label, transcript, raw string) {
231 	f, err := os.OpenFile(rawLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
232diff --git a/internal/adapters/speech.go b/internal/adapters/speech.go
233index 03508667b35a6778cfe5c0af3f5665db12f4f4c4..10031c1260706364e3c3c88c02bf35e9e2049c7b 100644
234--- a/internal/adapters/speech.go
235+++ b/internal/adapters/speech.go
236@@ -7,8 +7,8 @@ import (
237 	"os"
238 	"sync"
239 
240-	"japanese/internal/stt"
241-	"japanese/internal/tts"
242+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/stt"
243+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/tts"
244 )
245 
246 // SpeechInput adapts the stt recorder and ASR client to game.SpeechInput.
247diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go
248index 608e3dc9bb7662b581fd4e32bc0aa1f0e2e42aae..14edc838b5c780bf7136e2dbdc2567670abb6fa6 100644
249--- a/internal/bootstrap/bootstrap.go
250+++ b/internal/bootstrap/bootstrap.go
251@@ -1,4 +1,4 @@
252-// Package bootstrap builds the service clients shared by the jp entry points:
253+// Package bootstrap builds the service clients shared by the kaiwari entry points:
254 // scenario load, one LLM client per prompt slot, the audio availability check,
255 // the LLM readiness wait, and the prompt warmup. Warnings (unavailable audio,
256 // not-ready LLM, failed warmup) print to stderr; only a bad scenario file is
257@@ -12,12 +12,12 @@ import (
258 	"os"
259 	"time"
260 
261-	"japanese/internal/availability"
262-	"japanese/internal/config"
263-	"japanese/internal/llm"
264-	"japanese/internal/scenario"
265-	"japanese/internal/stt"
266-	"japanese/internal/tts"
267+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/availability"
268+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/config"
269+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/llm"
270+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/scenario"
271+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/stt"
272+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/tts"
273 )
274 
275 const (
276@@ -36,7 +36,7 @@ const (
277 	scratchSlot    = 4
278 )
279 
280-// Services is the set of shared clients one jp entry point builds at startup.
281+// Services is the set of shared clients one kaiwari entry point builds at startup.
282 type Services struct {
283 	Cfg        *config.Config
284 	Brief      string
285@@ -64,12 +64,12 @@ func New(cfg *config.Config) (*Services, error) {
286 	scratchClient := newLLMClient(cfg, hc, scratchSlot)
287 
288 	if err := availability.CheckAudio(hc, cfg.AudioConfig.AudioBaseURL, []string{tts.ModelName, stt.ASRModelName}); err != nil {
289-		fmt.Fprintf(os.Stderr, "jp: audio service unavailable: %v\n", err)
290+		fmt.Fprintf(os.Stderr, "kaiwari: audio service unavailable: %v\n", err)
291 	}
292 
293 	readyCtx, cancelReady := context.WithTimeout(context.Background(), llmReadyTimeout)
294 	if err := gameClient.WaitReady(readyCtx); err != nil {
295-		fmt.Fprintf(os.Stderr, "jp: LLM service not ready: %v\n", err)
296+		fmt.Fprintf(os.Stderr, "kaiwari: LLM service not ready: %v\n", err)
297 	}
298 	cancelReady()
299 
300@@ -91,7 +91,7 @@ func New(cfg *config.Config) (*Services, error) {
301 		}
302 	}
303 	if warmErr != nil {
304-		fmt.Fprintf(os.Stderr, "jp: LLM warmup failed: %v\n", warmErr)
305+		fmt.Fprintf(os.Stderr, "kaiwari: LLM warmup failed: %v\n", warmErr)
306 	}
307 
308 	return &Services{
309diff --git a/internal/config/config.go b/internal/config/config.go
310index d09e76bc5d4e3ffb4fcf3230097a725556ab25a2..b8f367f99ba4dfaea5798a129000394cd02404d4 100644
311--- a/internal/config/config.go
312+++ b/internal/config/config.go
313@@ -1,5 +1,5 @@
314 // Package config holds the service endpoint and recorder configuration shared
315-// by all jp subcommands. Values come from command-line flags, environment
316+// by all kaiwari subcommands. Values come from command-line flags, environment
317 // variables, or built-in defaults; flags win over env vars, which win over
318 // defaults.
319 package config
320@@ -30,7 +30,7 @@ type Config struct {
321 	Scenario string `long:"scenario" env:"JP_SCENARIO_PATH" default:"assets/scenarios/small_city.md" description:"path to the scenario brief file"`
322 }
323 
324-// ServerConfig is the jp-server option group: the shared Config plus the HTTP
325+// ServerConfig is the kaiwari-server option group: the shared Config plus the HTTP
326 // listen address and bearer token.
327 type ServerConfig struct {
328 	Config
329@@ -41,14 +41,14 @@ type ServerConfig struct {
330 
331 func ParseConfig() *Config {
332 	var cfg Config
333-	parse("jp", &cfg)
334+	parse("kaiwari", &cfg)
335 	return &cfg
336 }
337 
338-// ParseServerConfig parses the jp-server command line into a ServerConfig.
339+// ParseServerConfig parses the kaiwari-server command line into a ServerConfig.
340 func ParseServerConfig() *ServerConfig {
341 	var cfg ServerConfig
342-	parse("jp-server", &cfg)
343+	parse("kaiwari-server", &cfg)
344 	return &cfg
345 }
346 
347diff --git a/internal/game/orchestrator.go b/internal/game/orchestrator.go
348index ed2ae60b2f1805a3844ed46a4debd6f17da3ae54..5cedf5964db1c78d6d8d722628e5fbfeb33decbd 100644
349--- a/internal/game/orchestrator.go
350+++ b/internal/game/orchestrator.go
351@@ -7,7 +7,7 @@ import (
352 	"sync"
353 	"unicode/utf8"
354 
355-	"japanese/internal/llm"
356+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/llm"
357 )
358 
359 // SpeechInput captures push-to-talk audio and returns the raw transcript. Begin
360diff --git a/internal/game/state.go b/internal/game/state.go
361index b843cc44027e88994ea4b408d44b0fa532dda320..716cf54093a19ff6da867e35e80217c8be2e0efb 100644
362--- a/internal/game/state.go
363+++ b/internal/game/state.go
364@@ -5,7 +5,7 @@ import (
365 	"strings"
366 	"unicode/utf8"
367 
368-	"japanese/internal/llm"
369+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/llm"
370 )
371 
372 // Message is a role/content pair owned by the game layer so session and display
373diff --git a/internal/llm/client.go b/internal/llm/client.go
374index 9c1ff27e52eb35f0997190e5852eeb02c76cae18..ba313bb46727018727ac51a87534bd29ca8ef945 100644
375--- a/internal/llm/client.go
376+++ b/internal/llm/client.go
377@@ -11,7 +11,7 @@ import (
378 	"encoding/json"
379 	"fmt"
380 	"io"
381-	"japanese/internal/config"
382+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/config"
383 	"net/http"
384 	"strings"
385 	"time"
386diff --git a/internal/server/handlers.go b/internal/server/handlers.go
387index c92f3f8ba783ee4e1b21b00b083267391e2f2bfe..16002a2e56d090505997043a6a8b70cf9c5a3195 100644
388--- a/internal/server/handlers.go
389+++ b/internal/server/handlers.go
390@@ -11,7 +11,7 @@ import (
391 	"net/http"
392 	"strings"
393 
394-	"japanese/internal/game"
395+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/game"
396 )
397 
398 type judgeWire struct {
399diff --git a/internal/server/server.go b/internal/server/server.go
400index 67bc2feb49b077c481dfbaadbddb5bcfba4cb174..ed31fd963ddab1dcfda10bfcbc2fb5a3b408c2a2 100644
401--- a/internal/server/server.go
402+++ b/internal/server/server.go
403@@ -1,4 +1,4 @@
404-// Package server exposes jp game sessions over HTTP for a thin client.
405+// Package server exposes kaiwari game sessions over HTTP for a thin client.
406 // Sessions are in-memory and die with the process; turns serialize per
407 // session. Kana never crosses the wire.
408 package server
409@@ -13,11 +13,11 @@ import (
410 	"sync"
411 	"time"
412 
413-	"japanese/internal/adapters"
414-	"japanese/internal/bootstrap"
415-	"japanese/internal/game"
416-	"japanese/internal/stt"
417-	"japanese/internal/tts"
418+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/adapters"
419+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/bootstrap"
420+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/game"
421+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/stt"
422+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/tts"
423 )
424 
425 const (
426@@ -25,7 +25,7 @@ const (
427 	maxUpload   = int64(2 << 20)
428 )
429 
430-// Server serves jp game sessions over HTTP.
431+// Server serves kaiwari game sessions over HTTP.
432 type Server struct {
433 	svc   *bootstrap.Services
434 	token []byte
435diff --git a/internal/server/speech.go b/internal/server/speech.go
436index da6d2b66889dd43d342e7618d64425f069d84c98..dd169ab3ce89d5b235d260d9346478874e98dc64 100644
437--- a/internal/server/speech.go
438+++ b/internal/server/speech.go
439@@ -5,8 +5,8 @@ import (
440 	"errors"
441 	"sync"
442 
443-	"japanese/internal/stt"
444-	"japanese/internal/tts"
445+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/stt"
446+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/tts"
447 )
448 
449 // remoteInput adapts the uploaded WAV of one spoken turn to game.SpeechInput.
450diff --git a/internal/stt/recorder.go b/internal/stt/recorder.go
451index 40eab3aa4a1e5772c6cbefa4c5de1b85691e4452..b02e8545ad94cad23a0503de91f764947dc5b1bf 100644
452--- a/internal/stt/recorder.go
453+++ b/internal/stt/recorder.go
454@@ -56,7 +56,7 @@ func (r *Recorder) Start(ctx context.Context) (*Recording, error) {
455 		return nil, errors.New("stt: empty recorder command")
456 	}
457 
458-	f, err := os.CreateTemp("", "jp-record-*.wav")
459+	f, err := os.CreateTemp("", "kaiwari-record-*.wav")
460 	if err != nil {
461 		return nil, fmt.Errorf("stt: create temp wav: %w", err)
462 	}
463diff --git a/internal/ui/app.go b/internal/ui/app.go
464index c4e54340ecd56b8283db27df8728de59f1e8758e..47a85e879235243f5cb6f31584ee3389326be829 100644
465--- a/internal/ui/app.go
466+++ b/internal/ui/app.go
467@@ -12,7 +12,7 @@ import (
468 	"github.com/charmbracelet/bubbletea"
469 	"github.com/charmbracelet/lipgloss"
470 
471-	"japanese/internal/game"
472+	"git.theedgeofrage.com/TheEdgeOfRage/kaiwari/internal/game"
473 )
474 
475 // tickInterval refreshes the recording elapsed indicator while a capture is open.