candycore/sugar-crush

Chat-shell TUI for AI coding assistants — port of charmbracelet/crush. Pluggable Backend interface (ship your own Anthropic / OpenAI / Ollama / shell-out adapter). Markdown rendering of replies via CandyShine, scrollback viewport via SugarBits, input area via SugarBits TextArea.

Maintainers

Package info

github.com/sugarcraft/sugar-crush

Documentation

Type:project

pkg:composer/candycore/sugar-crush

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.0 2026-05-07 01:29 UTC

This package is not auto-updated.

Last update: 2026-05-07 16:32:13 UTC


README

sugar-crush

SugarCrush

CI codecov Packagist Version License PHP

demo

Chat-shell TUI for AI coding assistants — port of charmbracelet/crush. Pluggable backends (ship your own Anthropic / OpenAI / Ollama / shell-out adapter), Markdown rendering of replies via CandyShine, scrollback above a fixed input box.

┌─ SugarCrush ───────────────────────────────────────┐
│ user> explain fiber-based scheduling in PHP        │
│                                                    │
│ assistant                                          │
│ ## Fibers (PHP 8.1+)                               │
│                                                    │
│ Fibers are cooperative units of execution …        │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ > how do they relate to ReactPHP?█                 │
└────────────────────────────────────────────────────┘
 Enter to send · Esc / ^C to quit

Run it

composer install
./bin/sugarcrush

By default it ships with EchoBackend so the binary is runnable offline (the assistant just echoes what you typed). To wire it to a real LLM, set $SUGARCRUSH_BACKEND_CMD to a command that reads JSON history on stdin and writes the reply to stdout:

export SUGARCRUSH_BACKEND_CMD=~/bin/anthropic-stream.sh
./bin/sugarcrush

Sample wrapper script (Anthropic)

#!/usr/bin/env bash
# ~/bin/anthropic-stream.sh
payload=$(jq -nc --argjson h "$(cat)" \
  '{model: "claude-opus-4-7", max_tokens: 4096, messages: $h}')

curl -sN https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d "$payload" \
  | jq -r '.content[0].text'

chmod +x ~/bin/anthropic-stream.sh and you're done.

The wrapper-script approach is deliberate: keeps the PHP package network-dep-free, lets you swap providers without changing PHP code, and makes prompt-engineering iteration as fast as editing a shell script.

Writing a custom Backend

If you'd rather skip the shell-out dance and integrate the SDK directly, implement the Backend interface in PHP:

use SugarCraft\Crush\{Backend, Chat, Message};

final class MyBackend implements Backend {
    public function complete(array $history): Message {
        $reply = /* your call here, returning a string */;
        return Message::assistant($reply);
    }
}

(new Program(new Chat(backend: new MyBackend())))->run();

Architecture

File Role
Role enum system / user / assistant — matches every API's wire vocab
Message VO: role, content, createdAt; toWire() for adapters
Backend interface complete(list<Message>): Message
Backend\EchoBackend Offline default — echoes the last user message
Backend\CommandBackend Shells out via proc_open; JSON history → stdin → stdout reply
AssistantMsg Internal Msg — fires when a backend completion arrives
Chat SugarCraft Model — history, input buffer, inFlight gate
Renderer Pure view fn — CandyShine-rendered scrollback + input box

Test plan

  • 21 tests / 43 assertions
  • Message: factories, wire shape, custom timestamps
  • EchoBackend: echoes most recent user, handles empty history
  • CommandBackend: history is JSON-piped to stdin, exit code surfaced as error message, missing command handled gracefully
  • Chat: type accumulation, space, UTF-8-aware backspace, Enter submits + clears + arms inFlight, empty submit no-op, AssistantMsg appends + clears inFlight, keystrokes ignored while inFlight, Esc quits, full echo round-trip via the real EchoBackend

Status

Phase 9+ entry #17 — first cut. Single-shot replies (no streaming yet); StreamingBackend interface is the obvious follow-up. Markdown rendering, persistent input buffer, and the inFlight gate are all wired.