From e7a9466bb4421f70671983949d0fc2f090ff427a Mon Sep 17 00:00:00 2001 From: nicodes Date: Tue, 27 Oct 2026 00:10:00 +0100 Subject: [PATCH] Use lowercase product names in user-facing text --- .github/workflows/ci.yml | 4 ++-- .github/workflows/dependency-policy.yml | 2 +- AGENTS.md | 5 +++++ README.md | 6 +++--- account.go | 2 +- internal/registry/device.go | 2 +- internal/registry/device_test.go | 14 +++++++------- internal/shell/product.go | 4 ++-- internal/shell/product_test.go | 8 ++++---- product_service.go | 2 +- 10 files changed, 27 insertions(+), 22 deletions(-) create mode 100644 AGENTS.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b466545..9df5383 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - - name: Set up locked Komizo + - name: Set up locked komizo uses: nicodes/komizo/setup@40086036977a5fc0119d6c4c1d5eb6ae3ab3b4b9 env: GH_TOKEN: ${{ github.token }} @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - - name: Set up locked Komizo + - name: Set up locked komizo uses: nicodes/komizo/setup@40086036977a5fc0119d6c4c1d5eb6ae3ab3b4b9 env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/dependency-policy.yml b/.github/workflows/dependency-policy.yml index 88b3233..81a841d 100644 --- a/.github/workflows/dependency-policy.yml +++ b/.github/workflows/dependency-policy.yml @@ -44,7 +44,7 @@ jobs: persist-credentials: false ref: main timeout-minutes: 3 - - name: Set up locked Komizo + - name: Set up locked komizo uses: nicodes/komizo/setup@40086036977a5fc0119d6c4c1d5eb6ae3ab3b4b9 env: GH_TOKEN: ${{ github.token }} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9ab7d63 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ +# Repository instructions + +# Product naming + +Use `termcade` in lowercase in user-facing text. Keep language identifiers, environment variables and protocol fields in their technical spelling. Usage documentation is generated from private `nicodes/docs`. diff --git a/README.md b/README.md index 1ceb47d..5663926 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ - + -# Termcade +# termcade A terminal arcade with sandboxed WebAssembly games. It includes Asteroid, Tetris and Brickough. Use a truecolor terminal at least 80×24 cells. @@ -16,7 +16,7 @@ To create a game, run `termcade dev new you/mygame`, then use `termcade dev buil ## Development -Install the [Komizo release](https://github.com/nicodes/komizo#install) matching `komizo.lock`. +Install the [komizo release](https://github.com/nicodes/komizo#install) matching `komizo.lock`. ```sh komizo check # Complete project checks diff --git a/account.go b/account.go index 6ac1364..44346f3 100644 --- a/account.go +++ b/account.go @@ -22,7 +22,7 @@ func cmdLoginContext(ctx context.Context, args []string, out io.Writer) error { return fmt.Errorf("usage: termcade login") } client := registry.New(registry.URL(nil), "") - session, err := client.DeviceLogin(ctx, "Termcade CLI", func(uri, code string) { + session, err := client.DeviceLogin(ctx, "termcade CLI", func(uri, code string) { fmt.Fprintf(out, "\nUsing a browser, visit:\n %s\n\nAnd enter this code:\n %s\n\nWaiting for approval (Ctrl+C to cancel)…\n", uri, code) }) if err != nil { diff --git a/internal/registry/device.go b/internal/registry/device.go index c372482..9d21d8a 100644 --- a/internal/registry/device.go +++ b/internal/registry/device.go @@ -81,7 +81,7 @@ func PairingURLWithCode(base, code string) string { func (c *Client) StartDevice(ctx context.Context, deviceName string) (DeviceRound, error) { deviceName = safeText(deviceName, 200) if deviceName == "" { - deviceName = "Termcade CLI" + deviceName = "termcade CLI" } var out deviceStartResponse err := c.doContext(ctx, http.MethodPost, "/v1/device/start", map[string]string{ diff --git a/internal/registry/device_test.go b/internal/registry/device_test.go index 8aa6f34..f69cf6e 100644 --- a/internal/registry/device_test.go +++ b/internal/registry/device_test.go @@ -52,7 +52,7 @@ func TestDeviceRoundPendingThenApproved(t *testing.T) { case "/v1/device/start": var body map[string]string json.NewDecoder(r.Body).Decode(&body) - if body["client_id"] != clientID || body["device_name"] != "Termcade CLI" { + if body["client_id"] != clientID || body["device_name"] != "termcade CLI" { t.Errorf("start body = %#v", body) } json.NewEncoder(w).Encode(startResponse(nil)) @@ -74,7 +74,7 @@ func TestDeviceRoundPendingThenApproved(t *testing.T) { defer srv.Close() client := New(srv.URL, "") - round, err := client.StartDevice(context.Background(), "Termcade CLI") + round, err := client.StartDevice(context.Background(), "termcade CLI") if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestExpiredRoundRestartsWithBackoff(t *testing.T) { defer srv.Close() displays := 0 - session, err := New(srv.URL, "").deviceLogin(context.Background(), "Termcade CLI", func(uri, code string) { + session, err := New(srv.URL, "").deviceLogin(context.Background(), "termcade CLI", func(uri, code string) { displays++ if uri != PairingURI || !userCodeRE.MatchString(code) { t.Errorf("unsafe display %q %q", uri, code) @@ -154,7 +154,7 @@ func TestServerErrorTextIsTerminalSafe(t *testing.T) { json.NewEncoder(w).Encode(map[string]string{"message": "bad\x1b[2J\nrequest"}) })) defer srv.Close() - _, err := New(srv.URL, "").StartDevice(context.Background(), "Termcade CLI") + _, err := New(srv.URL, "").StartDevice(context.Background(), "termcade CLI") if err == nil || strings.ContainsAny(err.Error(), "\x1b\n\r") || err.Error() != "bad[2Jrequest" { t.Fatalf("unsafe server error = %q", err) } @@ -165,7 +165,7 @@ func TestOversizedDeviceResponseIsRejected(t *testing.T) { w.Write([]byte(`{"device_code":"` + strings.Repeat("A", maxJSONResponse) + `"}`)) })) defer srv.Close() - _, err := New(srv.URL, "").StartDevice(context.Background(), "Termcade CLI") + _, err := New(srv.URL, "").StartDevice(context.Background(), "termcade CLI") if err == nil || err.Error() != "the marketplace returned malformed data" { t.Fatalf("oversized response error = %v", err) } @@ -191,7 +191,7 @@ func TestHostileDeviceStartFieldsAreRejected(t *testing.T) { json.NewEncoder(w).Encode(startResponse(map[string]any{test.field: test.value})) })) defer srv.Close() - _, err := New(srv.URL, "").StartDevice(context.Background(), "Termcade CLI") + _, err := New(srv.URL, "").StartDevice(context.Background(), "termcade CLI") if err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("error=%v, want %q", err, test.want) } @@ -253,7 +253,7 @@ func TestRevokedCredentialIsNotReturnedAsSession(t *testing.T) { } })) defer srv.Close() - _, err := New(srv.URL, "").deviceLogin(context.Background(), "Termcade CLI", func(string, string) {}, fakePolicy()) + _, err := New(srv.URL, "").deviceLogin(context.Background(), "termcade CLI", func(string, string) {}, fakePolicy()) if !errors.Is(err, ErrLoginRequired) { t.Fatalf("revocation error = %v, want ErrLoginRequired", err) } diff --git a/internal/shell/product.go b/internal/shell/product.go index 837589e..7bdb5ce 100644 --- a/internal/shell/product.go +++ b/internal/shell/product.go @@ -860,7 +860,7 @@ func (m Model) productItems() []productItem { case "pairing": items = append(items, productItem{Label: "Secure browser approval", Detail: "Never enter an account password or email verification code here."}, productItem{Label: m.app.pairingURL, Kind: "open-url", ID: m.app.pairingURL}, productItem{Label: "Pairing code: " + m.app.round.UserCode, Detail: "Waiting for approval. No browser is opened automatically."}) case "docs": - items = append(items, productItem{Label: "TERMCADE", Detail: "One arcade in your terminal and browser."}, productItem{Label: "Library", Detail: "Games you add appear in the navigation. Play installs a missing package. Local installations remain playable offline."}, productItem{Label: "Two removal actions", Detail: "Remove from Library changes your account. Uninstall here removes only this machine's copy."}, productItem{Label: "Build a game", Detail: "termcade dev new author/slug\ntermcade dev build\ntermcade dev install game.tcade"}, productItem{Label: "Publish", Detail: "termcade publish [asset]"}, productItem{Label: "Keyboard", Detail: "Ctrl+N toggles navigation. Tab is next and Shift+Tab is previous in lists, actions, forms, and dialogs. Enter activates; Esc goes back/pauses; Ctrl+C quits. PgUp/PgDown scrolls long text."}, productItem{Label: "Gameplay", Detail: "Arrows/WASD to move · Space/Z action A · X action B · Enter start. Pause retains keyboard navigation and pixel selection."}) + items = append(items, productItem{Label: "termcade", Detail: "One arcade in your terminal and browser."}, productItem{Label: "Library", Detail: "Games you add appear in the navigation. Play installs a missing package. Local installations remain playable offline."}, productItem{Label: "Two removal actions", Detail: "Remove from Library changes your account. Uninstall here removes only this machine's copy."}, productItem{Label: "Build a game", Detail: "termcade dev new author/slug\ntermcade dev build\ntermcade dev install game.tcade"}, productItem{Label: "Publish", Detail: "termcade publish [asset]"}, productItem{Label: "Keyboard", Detail: "Ctrl+N toggles navigation. Tab is next and Shift+Tab is previous in lists, actions, forms, and dialogs. Enter activates; Esc goes back/pauses; Ctrl+C quits. PgUp/PgDown scrolls long text."}, productItem{Label: "Gameplay", Detail: "Arrows/WASD to move · Space/Z action A · X action B · Enter start. Pause retains keyboard navigation and pixel selection."}) } return items } @@ -1661,7 +1661,7 @@ func (m Model) productNavView(width, height int) string { accountIndex := len(items) - 1 identity := renderItem(accountIndex) bodyHeight := max(0, height-lipgloss.Height(identity)) - lines := []string{lipgloss.NewStyle().Foreground(lipgloss.Color("#e6c945")).Bold(true).Render("TERMCADE"), ""} + lines := []string{lipgloss.NewStyle().Foreground(lipgloss.Color("#e6c945")).Bold(true).Render("termcade"), ""} visibleRows := max(0, bodyHeight-len(lines)) start := 0 if m.app.nav < accountIndex { diff --git a/internal/shell/product_test.go b/internal/shell/product_test.go index 5670fef..102e5c6 100644 --- a/internal/shell/product_test.go +++ b/internal/shell/product_test.go @@ -191,7 +191,7 @@ func TestProductCtrlNTogglesNavigationWithoutFooterStop(t *testing.T) { if cmd != nil || m.app.loc != location || m.app.selection != 1 || m.app.gen != generation || len(fixture.calls) != calls { t.Fatal("navigation toggle changed page, selection, or service state") } - if width < sidebarBreakpoint && strings.Contains(ansi.Strip(view(m)), "TERMCADE") != (want == 0) { + if width < sidebarBreakpoint && strings.Contains(ansi.Strip(view(m)), "termcade") != (want == 0) { t.Fatal("compact navigation did not open/close with the toggle") } } @@ -1094,7 +1094,7 @@ func TestProductSidebarBreakpointAndContentCap(t *testing.T) { m, _ = step(t, m, tea.WindowSizeMsg{Width: 80, Height: 24}) m.app.focus = 0 first = strings.Split(ansi.Strip(view(m)), "\n")[0] - if !strings.HasPrefix(first, "TERMCADE") || []rune(first)[21] != '│' { + if !strings.HasPrefix(first, "termcade") || []rune(first)[21] != '│' { t.Fatal("compact navigation is not an overlay") } } @@ -1117,7 +1117,7 @@ func TestProductNavigationLeavesMainFooterUncovered(t *testing.T) { t.Fatal("open navigation missing the shared footer") } for i := openCut; i < len(opened); i++ { - if strings.Contains(opened[i], "TERMCADE") || (len([]rune(opened[i])) > sidebarWidth-1 && []rune(opened[i])[sidebarWidth-1] == '│' && strings.Count(opened[i], "─") >= 8) { + if strings.Contains(opened[i], "termcade") || (len([]rune(opened[i])) > sidebarWidth-1 && []rune(opened[i])[sidebarWidth-1] == '│' && strings.Count(opened[i], "─") >= 8) { t.Fatalf("navigation covered main footer row %d: %q", i, opened[i]) } } @@ -1150,7 +1150,7 @@ func TestProductNavigationOverlaysSmallAndPushesLargeOnlyWhenOpen(t *testing.T) t.Fatalf("overlay reflowed content on row %d", i) } } - if !strings.HasPrefix(opened[0], "TERMCADE") { + if !strings.HasPrefix(opened[0], "termcade") { t.Fatal("small navigation is not an overlay") } } else { diff --git a/product_service.go b/product_service.go index ed25cc4..d923c06 100644 --- a/product_service.go +++ b/product_service.go @@ -132,7 +132,7 @@ func (p *productBackend) request(ctx context.Context, req shell.ProductRequest) } c := registry.New(p.store.Registry, "").WithContext(ctx) if req.Kind == "pair-start" { - round, err := c.StartDevice(ctx, "Termcade TUI") + round, err := c.StartDevice(ctx, "termcade TUI") return shell.ProductReply{Round: round, PairingURL: p.store.PairingURL}, err } s, err := c.CompleteDevice(ctx, req.Round)