@agentskit/ink 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @agentskit/ink
2
+
3
+ Build terminal AI chat interfaces with the exact same API as `@agentskit/react`.
4
+
5
+ ## Why
6
+
7
+ - **No context switching** — if you know `@agentskit/react`, you already know this; same hooks, same component names, different renderer
8
+ - **Real terminal UX** — keyboard navigation, ANSI colors, and proper TTY streaming so your CLI feels native, not like a web app in a box
9
+ - **Any local or cloud model** — pair with Ollama for fully offline CLI tools, or any other provider via `@agentskit/adapters`
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @agentskit/ink @agentskit/adapters
15
+ ```
16
+
17
+ ## Quick example
18
+
19
+ ```tsx
20
+ import React from 'react'
21
+ import { render } from 'ink'
22
+ import { ChatContainer, InputBar, Message, useChat } from '@agentskit/ink'
23
+ import { ollama } from '@agentskit/adapters'
24
+
25
+ function App() {
26
+ const chat = useChat({ adapter: ollama({ model: 'llama3.1' }) })
27
+ return (
28
+ <ChatContainer>
29
+ {chat.messages.map(msg => <Message key={msg.id} message={msg} />)}
30
+ <InputBar chat={chat} />
31
+ </ChatContainer>
32
+ )
33
+ }
34
+
35
+ render(<App />)
36
+ ```
37
+
38
+ ## Next steps
39
+
40
+ - Use **tools** and **memory** from [`@agentskit/tools`](https://www.npmjs.com/package/@agentskit/tools) and [`@agentskit/memory`](https://www.npmjs.com/package/@agentskit/memory) in `useChat` the same way as in React
41
+ - For a **browser** UI, swap to [`@agentskit/react`](https://www.npmjs.com/package/@agentskit/react); for a quick **terminal chat** without your own Ink app, run [`@agentskit/cli`](https://www.npmjs.com/package/@agentskit/cli) `agentskit chat`
42
+
43
+ ## Ecosystem
44
+
45
+ | Package | Role |
46
+ |---------|------|
47
+ | [@agentskit/react](https://www.npmjs.com/package/@agentskit/react) | Browser — same hooks, different renderer |
48
+ | [@agentskit/cli](https://www.npmjs.com/package/@agentskit/cli) | Interactive chat + `agentskit init` |
49
+ | [@agentskit/adapters](https://www.npmjs.com/package/@agentskit/adapters) | Providers (e.g. `ollama` for local models) |
50
+ | [@agentskit/core](https://www.npmjs.com/package/@agentskit/core) | Shared chat types and controller |
51
+
52
+ ## Docs
53
+
54
+ [Full documentation](https://emersonbraun.github.io/agentskit/)
package/dist/index.cjs CHANGED
@@ -25,7 +25,9 @@ function useChat(config) {
25
25
  stop: controllerRef.current.stop,
26
26
  retry: controllerRef.current.retry,
27
27
  setInput: controllerRef.current.setInput,
28
- clear: controllerRef.current.clear
28
+ clear: controllerRef.current.clear,
29
+ approve: controllerRef.current.approve,
30
+ deny: controllerRef.current.deny
29
31
  };
30
32
  }
31
33
  function ChatContainer({ children }) {
@@ -96,6 +98,22 @@ function ThinkingIndicator({ visible, label = "Thinking..." }) {
96
98
  if (!visible) return null;
97
99
  return /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "yellow", children: label });
98
100
  }
101
+ function ToolConfirmation({ toolCall, onApprove, onDeny }) {
102
+ ink.useInput((input) => {
103
+ if (toolCall.status !== "requires_confirmation") return;
104
+ if (input.toLowerCase() === "y") onApprove(toolCall.id);
105
+ if (input.toLowerCase() === "n") onDeny(toolCall.id);
106
+ });
107
+ if (toolCall.status !== "requires_confirmation") return null;
108
+ return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", borderStyle: "round", paddingX: 1, borderColor: "yellow", children: [
109
+ /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "yellow", bold: true, children: [
110
+ "Confirm: ",
111
+ toolCall.name
112
+ ] }),
113
+ /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: JSON.stringify(toolCall.args) }),
114
+ /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "yellow", children: "Allow? (y/n)" })
115
+ ] });
116
+ }
99
117
 
100
118
  Object.defineProperty(exports, "createChatController", {
101
119
  enumerable: true,
@@ -126,6 +144,7 @@ exports.InputBar = InputBar;
126
144
  exports.Message = Message;
127
145
  exports.ThinkingIndicator = ThinkingIndicator;
128
146
  exports.ToolCallView = ToolCallView;
147
+ exports.ToolConfirmation = ToolConfirmation;
129
148
  exports.useChat = useChat;
130
149
  //# sourceMappingURL=index.cjs.map
131
150
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useChat.ts","../src/components/ChatContainer.tsx","../src/components/Message.tsx","../src/components/InputBar.tsx","../src/components/ToolCallView.tsx","../src/components/ThinkingIndicator.tsx"],"names":["useRef","createChatController","useEffect","useSyncExternalStore","Box","jsxs","jsx","Text","useInput","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["// NOTE: This hook is identical in @agentskit/react and @agentskit/ink.\n// Changes here must be mirrored in packages/react/src/useChat.ts.\nimport { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { createChatController } from '@agentskit/core'\nimport type { ChatConfig, ChatController, ChatReturn } from '@agentskit/core'\n\nexport function useChat(config: ChatConfig): ChatReturn {\n const controllerRef = useRef<ChatController | null>(null)\n\n if (!controllerRef.current) {\n controllerRef.current = createChatController(config)\n }\n\n useEffect(() => {\n controllerRef.current?.updateConfig(config)\n }, [config])\n\n const state = useSyncExternalStore(\n controllerRef.current.subscribe,\n controllerRef.current.getState,\n controllerRef.current.getState\n )\n\n return {\n ...state,\n send: controllerRef.current.send,\n stop: controllerRef.current.stop,\n retry: controllerRef.current.retry,\n setInput: controllerRef.current.setInput,\n clear: controllerRef.current.clear,\n }\n}\n","import React, { type ReactNode } from 'react'\nimport { Box } from 'ink'\n\nexport interface ChatContainerProps {\n children: ReactNode\n}\n\nexport function ChatContainer({ children }: ChatContainerProps) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n {children}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { Message as MessageType } from '@agentskit/core'\n\nexport interface MessageProps {\n message: MessageType\n}\n\nfunction roleColor(role: MessageType['role']) {\n switch (role) {\n case 'assistant':\n return 'cyan'\n case 'user':\n return 'green'\n case 'system':\n return 'yellow'\n case 'tool':\n return 'magenta'\n default:\n return 'white'\n }\n}\n\nexport function Message({ message }: MessageProps) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color={roleColor(message.role)}>\n {message.role.toUpperCase()}\n </Text>\n <Text>{message.content || (message.status === 'streaming' ? '...' : '')}</Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ChatReturn } from '@agentskit/core'\n\nexport interface InputBarProps {\n chat: ChatReturn\n placeholder?: string\n disabled?: boolean\n}\n\nexport function InputBar({ chat, placeholder = 'Type a message...', disabled = false }: InputBarProps) {\n useInput((input, key) => {\n if (disabled || chat.status === 'streaming') return\n\n if (key.return) {\n if (chat.input.trim()) {\n void chat.send(chat.input)\n }\n return\n }\n\n if (key.backspace || key.delete) {\n chat.setInput(chat.input.slice(0, -1))\n return\n }\n\n if (input && !key.ctrl && !key.meta) {\n chat.setInput(`${chat.input}${input}`)\n }\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{placeholder}</Text>\n <Text color={disabled ? 'gray' : 'white'}>\n &gt; {chat.input}\n </Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolCallViewProps {\n toolCall: ToolCall\n expanded?: boolean\n}\n\nexport function ToolCallView({ toolCall, expanded = false }: ToolCallViewProps) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text color=\"magenta\">\n Tool: {toolCall.name} [{toolCall.status}]\n </Text>\n {expanded ? (\n <>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n {toolCall.result ? <Text>{toolCall.result}</Text> : null}\n {toolCall.error ? <Text color=\"red\">{toolCall.error}</Text> : null}\n </>\n ) : null}\n </Box>\n )\n}\n","import React from 'react'\nimport { Text } from 'ink'\n\nexport interface ThinkingIndicatorProps {\n visible: boolean\n label?: string\n}\n\nexport function ThinkingIndicator({ visible, label = 'Thinking...' }: ThinkingIndicatorProps) {\n if (!visible) return null\n return <Text color=\"yellow\">{label}</Text>\n}\n"]}
1
+ {"version":3,"sources":["../src/useChat.ts","../src/components/ChatContainer.tsx","../src/components/Message.tsx","../src/components/InputBar.tsx","../src/components/ToolCallView.tsx","../src/components/ThinkingIndicator.tsx","../src/components/ToolConfirmation.tsx"],"names":["useRef","createChatController","useEffect","useSyncExternalStore","Box","jsxs","jsx","Text","useInput","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["// NOTE: This hook is intentionally duplicated from @agentskit/react (packages/react/src/useChat.ts).\n// The two packages are React siblings and cannot cross-depend, so the hook is kept in sync\n// manually. A sync-guard test at packages/ink/tests/useChat-sync.test.ts will fail if they drift.\n// When changing this file, mirror the same change in packages/react/src/useChat.ts.\nimport { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { createChatController } from '@agentskit/core'\nimport type { ChatConfig, ChatController, ChatReturn } from '@agentskit/core'\n\nexport function useChat(config: ChatConfig): ChatReturn {\n const controllerRef = useRef<ChatController | null>(null)\n\n if (!controllerRef.current) {\n controllerRef.current = createChatController(config)\n }\n\n useEffect(() => {\n controllerRef.current?.updateConfig(config)\n }, [config])\n\n const state = useSyncExternalStore(\n controllerRef.current.subscribe,\n controllerRef.current.getState,\n controllerRef.current.getState\n )\n\n return {\n ...state,\n send: controllerRef.current.send,\n stop: controllerRef.current.stop,\n retry: controllerRef.current.retry,\n setInput: controllerRef.current.setInput,\n clear: controllerRef.current.clear,\n approve: controllerRef.current.approve,\n deny: controllerRef.current.deny,\n }\n}\n","import React, { type ReactNode } from 'react'\nimport { Box } from 'ink'\n\nexport interface ChatContainerProps {\n children: ReactNode\n}\n\nexport function ChatContainer({ children }: ChatContainerProps) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n {children}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { Message as MessageType } from '@agentskit/core'\n\nexport interface MessageProps {\n message: MessageType\n}\n\nfunction roleColor(role: MessageType['role']) {\n switch (role) {\n case 'assistant':\n return 'cyan'\n case 'user':\n return 'green'\n case 'system':\n return 'yellow'\n case 'tool':\n return 'magenta'\n default:\n return 'white'\n }\n}\n\nexport function Message({ message }: MessageProps) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color={roleColor(message.role)}>\n {message.role.toUpperCase()}\n </Text>\n <Text>{message.content || (message.status === 'streaming' ? '...' : '')}</Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ChatReturn } from '@agentskit/core'\n\nexport interface InputBarProps {\n chat: ChatReturn\n placeholder?: string\n disabled?: boolean\n}\n\nexport function InputBar({ chat, placeholder = 'Type a message...', disabled = false }: InputBarProps) {\n useInput((input, key) => {\n if (disabled || chat.status === 'streaming') return\n\n if (key.return) {\n if (chat.input.trim()) {\n void chat.send(chat.input)\n }\n return\n }\n\n if (key.backspace || key.delete) {\n chat.setInput(chat.input.slice(0, -1))\n return\n }\n\n if (input && !key.ctrl && !key.meta) {\n chat.setInput(`${chat.input}${input}`)\n }\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{placeholder}</Text>\n <Text color={disabled ? 'gray' : 'white'}>\n &gt; {chat.input}\n </Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolCallViewProps {\n toolCall: ToolCall\n expanded?: boolean\n}\n\nexport function ToolCallView({ toolCall, expanded = false }: ToolCallViewProps) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text color=\"magenta\">\n Tool: {toolCall.name} [{toolCall.status}]\n </Text>\n {expanded ? (\n <>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n {toolCall.result ? <Text>{toolCall.result}</Text> : null}\n {toolCall.error ? <Text color=\"red\">{toolCall.error}</Text> : null}\n </>\n ) : null}\n </Box>\n )\n}\n","import React from 'react'\nimport { Text } from 'ink'\n\nexport interface ThinkingIndicatorProps {\n visible: boolean\n label?: string\n}\n\nexport function ThinkingIndicator({ visible, label = 'Thinking...' }: ThinkingIndicatorProps) {\n if (!visible) return null\n return <Text color=\"yellow\">{label}</Text>\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolConfirmationProps {\n toolCall: ToolCall\n onApprove: (toolCallId: string) => void\n onDeny: (toolCallId: string, reason?: string) => void\n}\n\nexport function ToolConfirmation({ toolCall, onApprove, onDeny }: ToolConfirmationProps) {\n useInput((input) => {\n if (toolCall.status !== 'requires_confirmation') return\n if (input.toLowerCase() === 'y') onApprove(toolCall.id)\n if (input.toLowerCase() === 'n') onDeny(toolCall.id)\n })\n\n if (toolCall.status !== 'requires_confirmation') return null\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} borderColor=\"yellow\">\n <Text color=\"yellow\" bold>\n Confirm: {toolCall.name}\n </Text>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n <Text color=\"yellow\">Allow? (y/n)</Text>\n </Box>\n )\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -34,4 +34,11 @@ interface ThinkingIndicatorProps {
34
34
  }
35
35
  declare function ThinkingIndicator({ visible, label }: ThinkingIndicatorProps): react_jsx_runtime.JSX.Element | null;
36
36
 
37
- export { ChatContainer, type ChatContainerProps, InputBar, type InputBarProps, Message, type MessageProps, ThinkingIndicator, type ThinkingIndicatorProps, ToolCallView, type ToolCallViewProps, useChat };
37
+ interface ToolConfirmationProps {
38
+ toolCall: ToolCall;
39
+ onApprove: (toolCallId: string) => void;
40
+ onDeny: (toolCallId: string, reason?: string) => void;
41
+ }
42
+ declare function ToolConfirmation({ toolCall, onApprove, onDeny }: ToolConfirmationProps): react_jsx_runtime.JSX.Element | null;
43
+
44
+ export { ChatContainer, type ChatContainerProps, InputBar, type InputBarProps, Message, type MessageProps, ThinkingIndicator, type ThinkingIndicatorProps, ToolCallView, type ToolCallViewProps, ToolConfirmation, type ToolConfirmationProps, useChat };
package/dist/index.d.ts CHANGED
@@ -34,4 +34,11 @@ interface ThinkingIndicatorProps {
34
34
  }
35
35
  declare function ThinkingIndicator({ visible, label }: ThinkingIndicatorProps): react_jsx_runtime.JSX.Element | null;
36
36
 
37
- export { ChatContainer, type ChatContainerProps, InputBar, type InputBarProps, Message, type MessageProps, ThinkingIndicator, type ThinkingIndicatorProps, ToolCallView, type ToolCallViewProps, useChat };
37
+ interface ToolConfirmationProps {
38
+ toolCall: ToolCall;
39
+ onApprove: (toolCallId: string) => void;
40
+ onDeny: (toolCallId: string, reason?: string) => void;
41
+ }
42
+ declare function ToolConfirmation({ toolCall, onApprove, onDeny }: ToolConfirmationProps): react_jsx_runtime.JSX.Element | null;
43
+
44
+ export { ChatContainer, type ChatContainerProps, InputBar, type InputBarProps, Message, type MessageProps, ThinkingIndicator, type ThinkingIndicatorProps, ToolCallView, type ToolCallViewProps, ToolConfirmation, type ToolConfirmationProps, useChat };
package/dist/index.js CHANGED
@@ -24,7 +24,9 @@ function useChat(config) {
24
24
  stop: controllerRef.current.stop,
25
25
  retry: controllerRef.current.retry,
26
26
  setInput: controllerRef.current.setInput,
27
- clear: controllerRef.current.clear
27
+ clear: controllerRef.current.clear,
28
+ approve: controllerRef.current.approve,
29
+ deny: controllerRef.current.deny
28
30
  };
29
31
  }
30
32
  function ChatContainer({ children }) {
@@ -95,7 +97,23 @@ function ThinkingIndicator({ visible, label = "Thinking..." }) {
95
97
  if (!visible) return null;
96
98
  return /* @__PURE__ */ jsx(Text, { color: "yellow", children: label });
97
99
  }
100
+ function ToolConfirmation({ toolCall, onApprove, onDeny }) {
101
+ useInput((input) => {
102
+ if (toolCall.status !== "requires_confirmation") return;
103
+ if (input.toLowerCase() === "y") onApprove(toolCall.id);
104
+ if (input.toLowerCase() === "n") onDeny(toolCall.id);
105
+ });
106
+ if (toolCall.status !== "requires_confirmation") return null;
107
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", paddingX: 1, borderColor: "yellow", children: [
108
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
109
+ "Confirm: ",
110
+ toolCall.name
111
+ ] }),
112
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: JSON.stringify(toolCall.args) }),
113
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Allow? (y/n)" })
114
+ ] });
115
+ }
98
116
 
99
- export { ChatContainer, InputBar, Message, ThinkingIndicator, ToolCallView, useChat };
117
+ export { ChatContainer, InputBar, Message, ThinkingIndicator, ToolCallView, ToolConfirmation, useChat };
100
118
  //# sourceMappingURL=index.js.map
101
119
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useChat.ts","../src/components/ChatContainer.tsx","../src/components/Message.tsx","../src/components/InputBar.tsx","../src/components/ToolCallView.tsx","../src/components/ThinkingIndicator.tsx"],"names":["Box","jsx","jsxs","Text"],"mappings":"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","file":"index.js","sourcesContent":["// NOTE: This hook is identical in @agentskit/react and @agentskit/ink.\n// Changes here must be mirrored in packages/react/src/useChat.ts.\nimport { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { createChatController } from '@agentskit/core'\nimport type { ChatConfig, ChatController, ChatReturn } from '@agentskit/core'\n\nexport function useChat(config: ChatConfig): ChatReturn {\n const controllerRef = useRef<ChatController | null>(null)\n\n if (!controllerRef.current) {\n controllerRef.current = createChatController(config)\n }\n\n useEffect(() => {\n controllerRef.current?.updateConfig(config)\n }, [config])\n\n const state = useSyncExternalStore(\n controllerRef.current.subscribe,\n controllerRef.current.getState,\n controllerRef.current.getState\n )\n\n return {\n ...state,\n send: controllerRef.current.send,\n stop: controllerRef.current.stop,\n retry: controllerRef.current.retry,\n setInput: controllerRef.current.setInput,\n clear: controllerRef.current.clear,\n }\n}\n","import React, { type ReactNode } from 'react'\nimport { Box } from 'ink'\n\nexport interface ChatContainerProps {\n children: ReactNode\n}\n\nexport function ChatContainer({ children }: ChatContainerProps) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n {children}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { Message as MessageType } from '@agentskit/core'\n\nexport interface MessageProps {\n message: MessageType\n}\n\nfunction roleColor(role: MessageType['role']) {\n switch (role) {\n case 'assistant':\n return 'cyan'\n case 'user':\n return 'green'\n case 'system':\n return 'yellow'\n case 'tool':\n return 'magenta'\n default:\n return 'white'\n }\n}\n\nexport function Message({ message }: MessageProps) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color={roleColor(message.role)}>\n {message.role.toUpperCase()}\n </Text>\n <Text>{message.content || (message.status === 'streaming' ? '...' : '')}</Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ChatReturn } from '@agentskit/core'\n\nexport interface InputBarProps {\n chat: ChatReturn\n placeholder?: string\n disabled?: boolean\n}\n\nexport function InputBar({ chat, placeholder = 'Type a message...', disabled = false }: InputBarProps) {\n useInput((input, key) => {\n if (disabled || chat.status === 'streaming') return\n\n if (key.return) {\n if (chat.input.trim()) {\n void chat.send(chat.input)\n }\n return\n }\n\n if (key.backspace || key.delete) {\n chat.setInput(chat.input.slice(0, -1))\n return\n }\n\n if (input && !key.ctrl && !key.meta) {\n chat.setInput(`${chat.input}${input}`)\n }\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{placeholder}</Text>\n <Text color={disabled ? 'gray' : 'white'}>\n &gt; {chat.input}\n </Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolCallViewProps {\n toolCall: ToolCall\n expanded?: boolean\n}\n\nexport function ToolCallView({ toolCall, expanded = false }: ToolCallViewProps) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text color=\"magenta\">\n Tool: {toolCall.name} [{toolCall.status}]\n </Text>\n {expanded ? (\n <>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n {toolCall.result ? <Text>{toolCall.result}</Text> : null}\n {toolCall.error ? <Text color=\"red\">{toolCall.error}</Text> : null}\n </>\n ) : null}\n </Box>\n )\n}\n","import React from 'react'\nimport { Text } from 'ink'\n\nexport interface ThinkingIndicatorProps {\n visible: boolean\n label?: string\n}\n\nexport function ThinkingIndicator({ visible, label = 'Thinking...' }: ThinkingIndicatorProps) {\n if (!visible) return null\n return <Text color=\"yellow\">{label}</Text>\n}\n"]}
1
+ {"version":3,"sources":["../src/useChat.ts","../src/components/ChatContainer.tsx","../src/components/Message.tsx","../src/components/InputBar.tsx","../src/components/ToolCallView.tsx","../src/components/ThinkingIndicator.tsx","../src/components/ToolConfirmation.tsx"],"names":["Box","jsx","jsxs","Text","useInput"],"mappings":"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","file":"index.js","sourcesContent":["// NOTE: This hook is intentionally duplicated from @agentskit/react (packages/react/src/useChat.ts).\n// The two packages are React siblings and cannot cross-depend, so the hook is kept in sync\n// manually. A sync-guard test at packages/ink/tests/useChat-sync.test.ts will fail if they drift.\n// When changing this file, mirror the same change in packages/react/src/useChat.ts.\nimport { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { createChatController } from '@agentskit/core'\nimport type { ChatConfig, ChatController, ChatReturn } from '@agentskit/core'\n\nexport function useChat(config: ChatConfig): ChatReturn {\n const controllerRef = useRef<ChatController | null>(null)\n\n if (!controllerRef.current) {\n controllerRef.current = createChatController(config)\n }\n\n useEffect(() => {\n controllerRef.current?.updateConfig(config)\n }, [config])\n\n const state = useSyncExternalStore(\n controllerRef.current.subscribe,\n controllerRef.current.getState,\n controllerRef.current.getState\n )\n\n return {\n ...state,\n send: controllerRef.current.send,\n stop: controllerRef.current.stop,\n retry: controllerRef.current.retry,\n setInput: controllerRef.current.setInput,\n clear: controllerRef.current.clear,\n approve: controllerRef.current.approve,\n deny: controllerRef.current.deny,\n }\n}\n","import React, { type ReactNode } from 'react'\nimport { Box } from 'ink'\n\nexport interface ChatContainerProps {\n children: ReactNode\n}\n\nexport function ChatContainer({ children }: ChatContainerProps) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n {children}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { Message as MessageType } from '@agentskit/core'\n\nexport interface MessageProps {\n message: MessageType\n}\n\nfunction roleColor(role: MessageType['role']) {\n switch (role) {\n case 'assistant':\n return 'cyan'\n case 'user':\n return 'green'\n case 'system':\n return 'yellow'\n case 'tool':\n return 'magenta'\n default:\n return 'white'\n }\n}\n\nexport function Message({ message }: MessageProps) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color={roleColor(message.role)}>\n {message.role.toUpperCase()}\n </Text>\n <Text>{message.content || (message.status === 'streaming' ? '...' : '')}</Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ChatReturn } from '@agentskit/core'\n\nexport interface InputBarProps {\n chat: ChatReturn\n placeholder?: string\n disabled?: boolean\n}\n\nexport function InputBar({ chat, placeholder = 'Type a message...', disabled = false }: InputBarProps) {\n useInput((input, key) => {\n if (disabled || chat.status === 'streaming') return\n\n if (key.return) {\n if (chat.input.trim()) {\n void chat.send(chat.input)\n }\n return\n }\n\n if (key.backspace || key.delete) {\n chat.setInput(chat.input.slice(0, -1))\n return\n }\n\n if (input && !key.ctrl && !key.meta) {\n chat.setInput(`${chat.input}${input}`)\n }\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{placeholder}</Text>\n <Text color={disabled ? 'gray' : 'white'}>\n &gt; {chat.input}\n </Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolCallViewProps {\n toolCall: ToolCall\n expanded?: boolean\n}\n\nexport function ToolCallView({ toolCall, expanded = false }: ToolCallViewProps) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text color=\"magenta\">\n Tool: {toolCall.name} [{toolCall.status}]\n </Text>\n {expanded ? (\n <>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n {toolCall.result ? <Text>{toolCall.result}</Text> : null}\n {toolCall.error ? <Text color=\"red\">{toolCall.error}</Text> : null}\n </>\n ) : null}\n </Box>\n )\n}\n","import React from 'react'\nimport { Text } from 'ink'\n\nexport interface ThinkingIndicatorProps {\n visible: boolean\n label?: string\n}\n\nexport function ThinkingIndicator({ visible, label = 'Thinking...' }: ThinkingIndicatorProps) {\n if (!visible) return null\n return <Text color=\"yellow\">{label}</Text>\n}\n","import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { ToolCall } from '@agentskit/core'\n\nexport interface ToolConfirmationProps {\n toolCall: ToolCall\n onApprove: (toolCallId: string) => void\n onDeny: (toolCallId: string, reason?: string) => void\n}\n\nexport function ToolConfirmation({ toolCall, onApprove, onDeny }: ToolConfirmationProps) {\n useInput((input) => {\n if (toolCall.status !== 'requires_confirmation') return\n if (input.toLowerCase() === 'y') onApprove(toolCall.id)\n if (input.toLowerCase() === 'n') onDeny(toolCall.id)\n })\n\n if (toolCall.status !== 'requires_confirmation') return null\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} borderColor=\"yellow\">\n <Text color=\"yellow\" bold>\n Confirm: {toolCall.name}\n </Text>\n <Text dimColor>{JSON.stringify(toolCall.args)}</Text>\n <Text color=\"yellow\">Allow? (y/n)</Text>\n </Box>\n )\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,18 @@
1
1
  {
2
2
  "name": "@agentskit/ink",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Ink terminal components and hooks for AgentsKit.",
5
+ "keywords": [
6
+ "agentskit",
7
+ "ai",
8
+ "agents",
9
+ "llm",
10
+ "chat",
11
+ "typescript",
12
+ "ink",
13
+ "terminal",
14
+ "cli"
15
+ ],
5
16
  "type": "module",
6
17
  "main": "./dist/index.cjs",
7
18
  "module": "./dist/index.js",
@@ -17,19 +28,19 @@
17
28
  "dist"
18
29
  ],
19
30
  "dependencies": {
20
- "ink": "^5.1.1",
21
- "@agentskit/core": "0.4.0"
31
+ "ink": "^6.8.0",
32
+ "@agentskit/core": "0.4.2"
22
33
  },
23
34
  "peerDependencies": {
24
35
  "react": ">=18.0.0"
25
36
  },
26
37
  "devDependencies": {
27
- "@types/node": "^24.0.0",
28
- "@types/react": "^18.3.0",
38
+ "@types/node": "^25.5.2",
39
+ "@types/react": "^19.2.14",
29
40
  "ink-testing-library": "^4.0.0",
30
- "react": "^18.3.1",
41
+ "react": "^19.2.4",
31
42
  "tsup": "^8.5.0",
32
- "typescript": "^5.9.2",
43
+ "typescript": "^6.0.2",
33
44
  "vitest": "^4.1.2"
34
45
  },
35
46
  "publishConfig": {