ink について
ink は、Ink を使ってターミナルにアシスタントチャット UI を構築する assistant-ui のスキルとランタイムパッケージです。assistant-ui のランタイム、ツール、状態、AI SDK トランスポートを共有し、ターミナル向けプリミティブと Markdown 表示を提供します。
主な機能
- 01Ink ベースのターミナル UI プリミティブ
- 02assistant-ui ランタイムと状態の共有
- 03ターミナル対応のメッセージパーツ表示
- 04制御可能な入力欄
- 05ANSI スタイルの Markdown 出力
- 06ファイルストレージとスレッドアダプター
活用シーン
- 01Node プロジェクトでアシスタントチャット CLI を構築する
- 02アシスタントの Markdown をターミナルに表示する
- 03スレッドデータをローカルファイルに保存する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ assistant-ui/ skills --skill inkインストール後、Claude Code で/inkを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/adapters.mdcustom-backend.mdhooks.mdmigration.mdprimitives.md詳細
assistant-ui Ink
最新の API については、常に assistant-ui.com/llms.txt を参照してください。
@assistant-ui/react-ink は、assistant-ui ランタイムを Ink のターミナルレンダラーに接続します。ランタイム、ツール、状態、AI SDK トランスポートを Web アプリと共有しますが、DOM、CSS、コピーされた要素はありません。Ink の Box と Text、およびランタイム対応のプリミティブを使って画面を構成します。@assistant-ui/react-ink-markdown は、アシスタントのテキストを ANSI スタイルのターミナル Markdown としてレンダリングします。
リファレンス
- ./references/primitives.md -- すべてのターミナルプリミティブ名前空間、そのパーツ、スコープ付きコンテキスト、および制御された
TextInput - ./references/hooks.md -- ランタイム、状態、ツール、通知、音声、チェックリストのフック
- ./references/adapters.md -- ファイルストレージ、添付ファイル、タイトルのアダプター
- ./references/custom-backend.md -- ローカル推論、ローカルディスクの永続化、バックエンドスレッドの所有権
- ./references/migration.md -- Web アプリから移行できるものと、Ink 向けに作り直す必要があるもの
ターミナルアプリを開始する
新規で始める場合は、ターミナルのサンプルをスキャフォールドします:
npx assistant-ui@latest create --ink my-app
cd my-app
--ink は with-react-ink CLI サンプルに解決されます。サンプル一覧には with-react-ink-web も含まれていますが、これは --ink のスキャフォールド対象ではありません。
既存の Node プロジェクトの場合は、ランタイム、ターミナルレンダラー、AI SDK トランスポートをまとめてインストールします:
npm install @assistant-ui/react-ink @assistant-ui/react-ink-markdown ink react @assistant-ui/ai-sdk
API ルートは別のバックエンドプロジェクトで実行する必要があります。ブラウザアプリとは異なり、ターミナルプロセスは相対的な /api/chat URL を使用できません。AssistantChatTransport には、そのプロセスから到達可能な完全な URL を指定してください。
import { AssistantRuntimeProvider } from "@assistant-ui/react-ink";
import { AssistantChatTransport, useChatRuntime } from "@assistant-ui/ai-sdk";
import { Box } from "ink";
import { TerminalThread } from "./components/terminal-thread.js";
const CHAT_API_URL = "http://localhost:3000/api/chat";
export function App() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: CHAT_API_URL }),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Box flexDirection="column">
<TerminalThread />
</Box>
</AssistantRuntimeProvider>
);
}
useChatRuntime と AssistantChatTransport は @assistant-ui/ai-sdk から提供されます。そのバックエンドは AI SDK v7 の UI メッセージストリームを使用します。ルートの形式については、ランタイムの AI SDK v7 ガイドを参照してください。
ターミナルスレッドを構成する
ThreadPrimitive.Messages は、各子が実行される前に現在のメッセージスコープを作成します。メッセージコンポーネント内で s.message を読み取ったうえで、Web 要素ではなく Ink のプリミティブを使用してください。ランタイム状態のゲートには AuiIf を、実行中の処理には LoadingPrimitive を使用します。
import { Box, Text } from "ink";
import {
AuiIf,
ComposerPrimitive,
LoadingPrimitive,
ThreadPrimitive,
useAuiState,
} from "@assistant-ui/react-ink";
import { MarkdownText } from "@assistant-ui/react-ink-markdown";
function Message() {
const message = useAuiState((s) => s.message);
const text = message.content
.filter((part) => part.type === "text")
.map((part) => ("text" in part ? part.text : ""))
.join("");
if (message.role === "user") {
return <Text color="green">You: {text}</Text>;
}
return (
<Box flexDirection="column" marginBottom={1}>
<Text color="blue">Assistant:</Text>
<MarkdownText text={text} />
</Box>
);
}
export function TerminalThread() {
return (
<ThreadPrimitive.Root flexDirection="column">
<AuiIf condition={(s) => s.thread.isEmpty}>
<Text dimColor>Send a message to begin.</Text>
</AuiIf>
<ThreadPrimitive.Messages>{() => <Message />}</ThreadPrimitive.Messages>
<LoadingPrimitive.Root gap={1}>
<LoadingPrimitive.Spinner variant="bar" />
<LoadingPrimitive.Text />
<LoadingPrimitive.ElapsedTime />
</LoadingPrimitive.Root>
<Box borderStyle="round" borderColor="gray" paddingX={1}>
<Text color="gray">{"> "}</Text>
<ComposerPrimitive.Input submitOnEnter placeholder="Message..." autoFocus />
</Box>
</ThreadPrimitive.Root>
);
}
シンプルなメッセージのサンプルは、意図的にテキストパーツのみをレンダリングします。ツール呼び出し、添付ファイル、データ、ソース、推論が必要な場合は MessagePrimitive.Parts を使用してください。そのデフォルトのターミナルセーフレンダラーがこれらのパーツタイプを処理し、primitives にコンポーネントマップが示されています。
ターミナル入力の動作
ComposerPrimitive.Input はコンポーザーにバインドされた TextInput です。これは制御されたラインエディタであり、Ink には存在しないネイティブ入力ではありません。submitOnEnter は現在のコンポーザーのテキストを送信します。multiLine の場合、Enter は改行を挿入しますが、submitOnEnter が有効な場合は、ターミナルが区別できる場合に Shift Enter が改行を挿入します。Ctrl J はマルチラインモードでのみ改行を挿入し、シングルライン入力では決して送信しません。
- Left、Right、Backspace、Delete、および
Ctrl Dは 1 つの書記素単位で操作するため、絵文字、ZWJ シーケンス、結合文字、CJK 表意文字が分割されることはありません。 - Home と End は、シングルラインモードではバッファ全体を、マルチラインモードでは現在の行を選択します。
Ctrl AとCtrl Eは常に現在の行の境界を選択します。 - Up と Down は、UTF 16 オフセットではなく書記素の幅から計算されたターミナル表示列を維持しながら、マルチラインの行を移動します。
Ctrl W、Alt B、Alt F、およびAlt Dは、Intl.Segmenterの単語境界によって移動または削除します。Ctrl UとCtrl Kは現在の境界まで削除します。マルチラインの行末では、Ctrl Kは次の行を結合します。
Meta バインドには、Escape プレフィックス付きシーケンスを出力するターミナルが必要です。macOS の Terminal では、Alt バインドのために「Option を Meta キーとして使用」を有効にしてください。Shift Enter には CSI u のサポートが必要で、iTerm2 3.4 以降、kitty、foot などが該当します。その他のターミナルでは Enter として扱われ、submitOnEnter の動作を使用します。コンポーザー外での TextInput については、primitives を参照してください。
ターミナルでの Markdown
蓄積されたテキストを @assistant-ui/react-ink-markdown の MarkdownText に渡します。これは HTML や React DOM ツリーではなく、ターミナルでスタイル設定された出力を生成します。このパッケージはカスタムレンダラー用に MarkdownTextPrimitive、useShikiHighlighter、およびテーマ型もエクスポートしますが、レンダリングパイプラインを変更する必要がない限り、MarkdownText から始めてください。
import { MarkdownText } from "@assistant-ui/react-ink-markdown";
export function AssistantReply({ text }: { text: string }) {
return <MarkdownText text={text} />;
}
よくある落とし穴
ターミナルアプリがバックエンドではなく自身のプロセスにリクエストを送信する
AssistantChatTransportには、http://localhost:3000/api/chatのような絶対apiURL が必要です。AI SDK のルートは Ink プロセスとは別にホストしてください。
Web の Thread や shadcn の要素が CLI で何もレンダリングしない
- ブラウザの要素には DOM と CSS が必要です。サーフェスを Ink の
Box、Text、および@assistant-ui/react-inkのプリミティブで再構築してください。
プリミティブや状態フックがランタイムコンテキストの欠如について例外をスローする
AssistantRuntimeProvider runtime={runtime}の下にマウントしてください。ThreadPrimitive.MessagesとMessagePrimitive.Partsも、その子が読み取るアイテムスコープを作成します。
Enter が送信しない、または予期せず改行を挿入する
submitOnEnterはデフォルトで false です。Enter で送信し、対応ターミナルで Shift Enter で改行を挿入する場合にのみ、multiLineと組み合わせてください。
絵文字の削除で表示中の入力が壊れる、または垂直ナビゲーションが誤った場所に移動する
ComposerPrimitive.InputまたはエクスポートされたTextInputを使用してください。これらのバッファは書記素のセグメンテーションとターミナルの表示幅を使用します。文字インデックスの演算で代用しないでください。
CLI を再起動するとスレッドデータが消える
useLocalRuntimeはプロセスメモリです。単一のローカルプロセスにはcreateFileStorageAdapterを、バックエンドが所有するメタデータにはRemoteThreadListAdapterを使用してください。
Markdown を Web パッケージからインポートした
@assistant-ui/react-ink-markdownからMarkdownTextをインポートしてください。これはターミナルの書式を出力します。Web の Markdown レンダラーは DOM を対象としています。
関連スキル
- setup -- CLI のスキャフォールディング、ブラウザのセットアップ、パッケージのインストール
- runtime -- 共有の assistant-ui ランタイム、状態、スレッド、トランスポートの設計
- primitives -- 対象がターミナルでない場合のブラウザ用のスタイルなしプリミティブ
- tools -- Ink でも動作するツールキット定義とツール呼び出しレンダラー
- markdown -- ブラウザの Markdown レンダラーとソース表示