agentk8 2.1.4 → 2.2.0

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/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { render } from 'ink';
4
4
  import meow from 'meow';
5
5
  import { App } from './components/App.js';
6
6
  import { checkClaudeInstalled } from './lib/claude.js';
7
- const VERSION = '2.1.4';
7
+ const VERSION = '2.2.0';
8
8
  const cli = meow(`
9
9
  Usage
10
10
  $ agentk8 [options]
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export type AgentName = 'Orchestrator' | 'Engineer' | 'Tester' | 'Security' | 'Scout' | 'Researcher' | 'ML Engineer' | 'Data Engineer' | 'Evaluator';
3
+ export type AgentState = 'idle' | 'active' | 'done';
4
+ interface AgentPanelProps {
5
+ mode: 'dev' | 'ml';
6
+ activeAgent?: AgentName;
7
+ completedAgents: AgentName[];
8
+ }
9
+ export declare const AgentPanel: React.FC<AgentPanelProps>;
10
+ export default AgentPanel;
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ const theme = {
5
+ border: '#2d3748',
6
+ accent: '#4fd1c5',
7
+ highlight: '#81e6d9',
8
+ dim: '#4a5568',
9
+ success: '#48bb78',
10
+ active: '#f6e05e',
11
+ };
12
+ // Animated border characters
13
+ const borderFrames = ['◢', '◣', '◤', '◥'];
14
+ const pulseChars = ['░', '▒', '▓', '█', '▓', '▒'];
15
+ const AgentBox = ({ name, icon, state, width }) => {
16
+ const [frame, setFrame] = useState(0);
17
+ useEffect(() => {
18
+ if (state !== 'active')
19
+ return;
20
+ const interval = setInterval(() => {
21
+ setFrame(f => (f + 1) % borderFrames.length);
22
+ }, 150);
23
+ return () => clearInterval(interval);
24
+ }, [state]);
25
+ const borderColor = state === 'active' ? theme.active : state === 'done' ? theme.success : theme.border;
26
+ const textColor = state === 'active' ? theme.highlight : state === 'done' ? theme.success : theme.dim;
27
+ const corner = state === 'active' ? borderFrames[frame] : state === 'done' ? '✓' : '○';
28
+ const innerWidth = width - 4;
29
+ const paddedName = name.length > innerWidth ? name.slice(0, innerWidth) : name.padEnd(innerWidth);
30
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: borderColor, children: corner }), _jsx(Text, { color: borderColor, children: '─'.repeat(width - 2) }), _jsx(Text, { color: borderColor, children: corner })] }), _jsxs(Box, { children: [_jsx(Text, { color: borderColor, children: "\u2502" }), _jsxs(Text, { color: textColor, children: [" ", icon, " "] }), _jsx(Text, { color: textColor, children: paddedName }), _jsx(Text, { color: borderColor, children: "\u2502" })] }), _jsxs(Box, { children: [_jsx(Text, { color: borderColor, children: state === 'active' ? borderFrames[(frame + 2) % 4] : '└' }), _jsx(Text, { color: borderColor, children: '─'.repeat(width - 2) }), _jsx(Text, { color: borderColor, children: state === 'active' ? borderFrames[(frame + 2) % 4] : '┘' })] })] }));
31
+ };
32
+ export const AgentPanel = ({ mode, activeAgent, completedAgents }) => {
33
+ const devAgents = [
34
+ { name: 'Orchestrator', icon: '◆' },
35
+ { name: 'Engineer', icon: '⚙' },
36
+ { name: 'Tester', icon: '✓' },
37
+ { name: 'Security', icon: '⛨' },
38
+ ];
39
+ const mlAgents = [
40
+ { name: 'Orchestrator', icon: '◆' },
41
+ { name: 'Researcher', icon: '◈' },
42
+ { name: 'ML Engineer', icon: '⬡' },
43
+ { name: 'Evaluator', icon: '◉' },
44
+ ];
45
+ const agents = mode === 'dev' ? devAgents : mlAgents;
46
+ const boxWidth = 16;
47
+ const getState = (name) => {
48
+ if (activeAgent === name)
49
+ return 'active';
50
+ if (completedAgents.includes(name))
51
+ return 'done';
52
+ return 'idle';
53
+ };
54
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { justifyContent: "center", children: [_jsx(Text, { color: theme.dim, children: "\u2500\u2500\u2500 " }), _jsx(Text, { color: theme.accent, children: "Agents" }), _jsx(Text, { color: theme.dim, children: " \u2500\u2500\u2500" })] }), _jsx(Box, { justifyContent: "center", marginTop: 1, children: agents.map((agent, i) => (_jsx(Box, { marginRight: i < agents.length - 1 ? 1 : 0, children: _jsx(AgentBox, { name: agent.name, icon: agent.icon, state: getState(agent.name), width: boxWidth }) }, agent.name))) })] }));
55
+ };
56
+ export default AgentPanel;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ export type AgentName = 'Orchestrator' | 'Engineer' | 'Tester' | 'Security' | 'Scout' | 'Researcher' | 'ML Engineer' | 'Data Engineer' | 'Evaluator';
3
+ export type AgentState = 'idle' | 'thinking' | 'working' | 'done';
4
+ interface Agent {
5
+ name: AgentName;
6
+ state: AgentState;
7
+ }
8
+ interface AgentStatusProps {
9
+ agents: Agent[];
10
+ mode: 'dev' | 'ml';
11
+ }
12
+ export declare const AgentStatus: React.FC<AgentStatusProps>;
13
+ export default AgentStatus;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ const theme = {
4
+ accent: '#4fd1c5',
5
+ highlight: '#81e6d9',
6
+ dim: '#4a5568',
7
+ border: '#2d3748',
8
+ success: '#48bb78',
9
+ warning: '#ecc94b',
10
+ };
11
+ const agentIcons = {
12
+ Orchestrator: '◆',
13
+ Engineer: '⚙',
14
+ Tester: '✓',
15
+ Security: '⛨',
16
+ Scout: '◎',
17
+ Researcher: '◈',
18
+ 'ML Engineer': '⬡',
19
+ 'Data Engineer': '⬢',
20
+ Evaluator: '◉',
21
+ };
22
+ const stateColors = {
23
+ idle: theme.dim,
24
+ thinking: theme.warning,
25
+ working: theme.accent,
26
+ done: theme.success,
27
+ };
28
+ const stateIndicators = {
29
+ idle: '○',
30
+ thinking: '◐',
31
+ working: '●',
32
+ done: '✓',
33
+ };
34
+ export const AgentStatus = ({ agents, mode }) => {
35
+ if (agents.length === 0)
36
+ return null;
37
+ return (_jsxs(Box, { flexDirection: "column", marginY: 1, marginLeft: 2, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: theme.dim, children: "\u250C\u2500 " }), _jsx(Text, { color: theme.accent, children: "Active Agents" }), _jsx(Text, { color: theme.dim, children: " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" })] }), agents.map((agent, i) => (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: theme.dim, children: "\u2502 " }), _jsxs(Text, { color: stateColors[agent.state], children: [stateIndicators[agent.state], " "] }), _jsxs(Text, { color: theme.highlight, children: [agentIcons[agent.name], " "] }), _jsx(Text, { color: agent.state === 'idle' ? theme.dim : theme.highlight, children: agent.name }), agent.state !== 'idle' && agent.state !== 'done' && (_jsxs(Text, { color: theme.dim, children: [" ", agent.state, "..."] }))] }, agent.name))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.dim, children: "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) })] }));
38
+ };
39
+ export default AgentStatus;
@@ -6,6 +6,7 @@ import { ChatMessage } from './ChatMessage.js';
6
6
  import { Input } from './Input.js';
7
7
  import { StatusBar } from './StatusBar.js';
8
8
  import { ThinkingIndicator } from './ThinkingIndicator.js';
9
+ import { AgentPanel } from './AgentPanel.js';
9
10
  import { runClaude } from '../lib/claude.js';
10
11
  export const App = ({ mode, version }) => {
11
12
  const { exit } = useApp();
@@ -15,6 +16,24 @@ export const App = ({ mode, version }) => {
15
16
  const [totalTokens, setTotalTokens] = useState(0);
16
17
  const [startTime] = useState(new Date());
17
18
  const [error, setError] = useState(null);
19
+ const [executionMode, setExecutionMode] = useState('plan');
20
+ const [activeAgent, setActiveAgent] = useState(undefined);
21
+ const [completedAgents, setCompletedAgents] = useState([]);
22
+ const [pendingPlan, setPendingPlan] = useState(null);
23
+ const [awaitingApproval, setAwaitingApproval] = useState(false);
24
+ // Detect agents mentioned in response
25
+ const detectMentionedAgents = (content) => {
26
+ const agents = [];
27
+ const devAgents = ['Engineer', 'Tester', 'Security', 'Scout'];
28
+ const mlAgents = ['Researcher', 'ML Engineer', 'Data Engineer', 'Evaluator'];
29
+ const relevantAgents = mode === 'dev' ? devAgents : mlAgents;
30
+ for (const agent of relevantAgents) {
31
+ if (content.toLowerCase().includes(agent.toLowerCase())) {
32
+ agents.push(agent);
33
+ }
34
+ }
35
+ return agents;
36
+ };
18
37
  // Handle input submission
19
38
  const handleSubmit = async (input) => {
20
39
  // Check for commands
@@ -22,6 +41,24 @@ export const App = ({ mode, version }) => {
22
41
  handleCommand(input);
23
42
  return;
24
43
  }
44
+ // Handle approval response
45
+ if (awaitingApproval) {
46
+ if (input.toLowerCase() === 'y' || input.toLowerCase() === 'yes') {
47
+ setAwaitingApproval(false);
48
+ // Execute the pending plan
49
+ if (pendingPlan) {
50
+ await executeTask(pendingPlan);
51
+ setPendingPlan(null);
52
+ }
53
+ return;
54
+ }
55
+ else if (input.toLowerCase() === 'n' || input.toLowerCase() === 'no') {
56
+ setAwaitingApproval(false);
57
+ setPendingPlan(null);
58
+ addSystemMessage('Plan cancelled. What would you like to do instead?');
59
+ return;
60
+ }
61
+ }
25
62
  // Add user message
26
63
  const userMessage = {
27
64
  id: Date.now().toString(),
@@ -30,13 +67,75 @@ export const App = ({ mode, version }) => {
30
67
  timestamp: new Date(),
31
68
  };
32
69
  setMessages(prev => [...prev, userMessage]);
70
+ if (executionMode === 'plan') {
71
+ await generatePlan(input);
72
+ }
73
+ else {
74
+ await executeTask(input);
75
+ }
76
+ };
77
+ // Generate a plan for approval
78
+ const generatePlan = async (input) => {
33
79
  setIsProcessing(true);
34
80
  setProcessingStartTime(new Date());
81
+ setActiveAgent('Orchestrator');
82
+ setError(null);
83
+ try {
84
+ const planPrompt = `Analyze this request and create a detailed execution plan. Do NOT execute yet - just analyze and plan.
85
+
86
+ Request: ${input}
87
+
88
+ Respond with:
89
+ 1. Task Analysis (complexity, scope)
90
+ 2. Agents Required (list which specialists are needed)
91
+ 3. Step-by-Step Plan (numbered steps)
92
+ 4. Questions (if any clarification needed)
93
+
94
+ Format your response clearly with headers.`;
95
+ const result = await runClaude(planPrompt, mode);
96
+ // Mark orchestrator done, detect other agents
97
+ const mentioned = detectMentionedAgents(result.response);
98
+ setCompletedAgents(['Orchestrator', ...mentioned]);
99
+ setActiveAgent(undefined);
100
+ // Add plan message
101
+ const planMessage = {
102
+ id: (Date.now() + 1).toString(),
103
+ role: 'agent',
104
+ agentName: 'Orchestrator',
105
+ content: result.response,
106
+ tokens: result.tokens,
107
+ timestamp: new Date(),
108
+ };
109
+ setMessages(prev => [...prev, planMessage]);
110
+ // Update tokens
111
+ if (result.tokens) {
112
+ setTotalTokens(prev => prev + result.tokens.input + result.tokens.output);
113
+ }
114
+ // Ask for approval
115
+ setPendingPlan(input);
116
+ setAwaitingApproval(true);
117
+ addSystemMessage('Execute this plan? (y/n)');
118
+ }
119
+ catch (err) {
120
+ setError(err instanceof Error ? err.message : 'Unknown error');
121
+ }
122
+ finally {
123
+ setIsProcessing(false);
124
+ setProcessingStartTime(null);
125
+ }
126
+ };
127
+ // Execute task directly
128
+ const executeTask = async (input) => {
129
+ setIsProcessing(true);
130
+ setProcessingStartTime(new Date());
131
+ setActiveAgent('Orchestrator');
132
+ setCompletedAgents([]);
35
133
  setError(null);
36
134
  try {
37
- // Call Claude
38
135
  const result = await runClaude(input, mode);
39
- // Add agent response
136
+ const mentioned = detectMentionedAgents(result.response);
137
+ setCompletedAgents(['Orchestrator', ...mentioned]);
138
+ setActiveAgent(undefined);
40
139
  const agentMessage = {
41
140
  id: (Date.now() + 1).toString(),
42
141
  role: 'agent',
@@ -46,7 +145,6 @@ export const App = ({ mode, version }) => {
46
145
  timestamp: new Date(),
47
146
  };
48
147
  setMessages(prev => [...prev, agentMessage]);
49
- // Update token count
50
148
  if (result.tokens) {
51
149
  setTotalTokens(prev => prev + result.tokens.input + result.tokens.output);
52
150
  }
@@ -59,9 +157,20 @@ export const App = ({ mode, version }) => {
59
157
  setProcessingStartTime(null);
60
158
  }
61
159
  };
160
+ // Add system message helper
161
+ const addSystemMessage = (content) => {
162
+ const msg = {
163
+ id: Date.now().toString(),
164
+ role: 'system',
165
+ agentName: 'System',
166
+ content,
167
+ timestamp: new Date(),
168
+ };
169
+ setMessages(prev => [...prev, msg]);
170
+ };
62
171
  // Handle slash commands
63
172
  const handleCommand = (cmd) => {
64
- const [command] = cmd.slice(1).split(' ');
173
+ const [command, ...args] = cmd.slice(1).split(' ');
65
174
  switch (command) {
66
175
  case 'exit':
67
176
  case 'quit':
@@ -69,6 +178,29 @@ export const App = ({ mode, version }) => {
69
178
  break;
70
179
  case 'clear':
71
180
  setMessages([]);
181
+ setActiveAgent(undefined);
182
+ setCompletedAgents([]);
183
+ break;
184
+ case 'plan':
185
+ setExecutionMode('plan');
186
+ addSystemMessage('Plan mode enabled. I will show plans for approval before executing.');
187
+ break;
188
+ case 'auto':
189
+ setExecutionMode('auto');
190
+ addSystemMessage('Auto mode enabled. I will execute tasks directly without approval.');
191
+ break;
192
+ case 'mode':
193
+ addSystemMessage(`Current execution mode: ${executionMode}\nUse /plan or /auto to switch.`);
194
+ break;
195
+ case 'agents':
196
+ if (!activeAgent && completedAgents.length === 0) {
197
+ addSystemMessage('No agents currently active.');
198
+ }
199
+ else {
200
+ const status = activeAgent ? `Active: ${activeAgent}\n` : '';
201
+ const completed = completedAgents.length > 0 ? `Completed: ${completedAgents.join(', ')}` : '';
202
+ addSystemMessage(`Agent Status:\n${status}${completed}`);
203
+ }
72
204
  break;
73
205
  case 'help':
74
206
  const helpMessage = {
@@ -79,13 +211,17 @@ export const App = ({ mode, version }) => {
79
211
  /help - Show this help
80
212
  /clear - Clear chat history
81
213
  /status - Show session status
214
+ /plan - Enable plan mode (ask before executing)
215
+ /auto - Enable auto mode (execute directly)
216
+ /mode - Show current execution mode
217
+ /agents - Show active agents
82
218
  /exit - Exit AGENT-K
83
219
 
84
220
  Keyboard shortcuts:
85
221
  ↑/↓ - Browse command history
222
+ Tab - Autocomplete commands
86
223
  Ctrl+C - Exit
87
- Ctrl+U - Clear input line
88
- Ctrl+A/E - Jump to start/end`,
224
+ Ctrl+U - Clear input line`,
89
225
  timestamp: new Date(),
90
226
  };
91
227
  setMessages(prev => [...prev, helpMessage]);
@@ -97,8 +233,10 @@ Ctrl+A/E - Jump to start/end`,
97
233
  agentName: 'System',
98
234
  content: `Session Status:
99
235
  ◇ Mode: ${mode === 'dev' ? 'Development' : 'ML Research'}
236
+ ◇ Execution: ${executionMode === 'plan' ? 'Plan (approval required)' : 'Auto (direct execution)'}
100
237
  ◇ Messages: ${messages.length}
101
238
  ◇ Total Tokens: ${totalTokens}
239
+ ◇ Active Agent: ${activeAgent || 'None'}
102
240
  ◇ Session Time: ${formatElapsed(startTime)}`,
103
241
  timestamp: new Date(),
104
242
  };
@@ -114,7 +252,16 @@ Ctrl+A/E - Jump to start/end`,
114
252
  exit();
115
253
  }
116
254
  });
117
- return (_jsxs(Box, { flexDirection: "column", children: [messages.length === 0 && _jsx(Banner, { version: version }), _jsx(Static, { items: messages, children: (msg) => (_jsx(ChatMessage, { role: msg.role, agentName: msg.agentName, content: msg.content, tokens: msg.tokens }, msg.id)) }), isProcessing && processingStartTime && (_jsx(ThinkingIndicator, { startTime: processingStartTime })), error && (_jsx(Box, { marginY: 1, marginLeft: 1, children: _jsxs(Text, { color: "#e53e3e", children: ["\u2717 Error: ", error] }) })), _jsx(Input, { onSubmit: handleSubmit, disabled: isProcessing, placeholder: "Type a message or /help for commands..." }), _jsx(StatusBar, { mode: mode, tokens: totalTokens, startTime: startTime, isProcessing: isProcessing })] }));
255
+ // Prepare items for Static (include banner as first item)
256
+ const staticItems = messages.length === 0
257
+ ? [{ id: 'banner', isBanner: true, role: 'system', content: '', timestamp: new Date() }]
258
+ : messages;
259
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item) => {
260
+ if ('isBanner' in item && item.isBanner) {
261
+ return _jsx(Banner, { version: version }, "banner");
262
+ }
263
+ return (_jsx(ChatMessage, { role: item.role, agentName: item.agentName, content: item.content, tokens: item.tokens }, item.id));
264
+ } }), isProcessing && processingStartTime && (_jsx(ThinkingIndicator, { startTime: processingStartTime })), error && (_jsx(Box, { marginY: 1, marginLeft: 1, children: _jsxs(Text, { color: "#e53e3e", children: ["\u2717 Error: ", error] }) })), _jsx(Input, { onSubmit: handleSubmit, disabled: isProcessing, placeholder: awaitingApproval ? "Execute plan? (y/n)" : "Type a message or /help for commands..." }), _jsx(AgentPanel, { mode: mode, activeAgent: activeAgent, completedAgents: completedAgents }), _jsx(StatusBar, { mode: mode, tokens: totalTokens, startTime: startTime, isProcessing: isProcessing })] }));
118
265
  };
119
266
  function formatElapsed(start) {
120
267
  const secs = Math.floor((Date.now() - start.getTime()) / 1000);
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  interface ChatMessageProps {
3
- role: 'user' | 'agent';
3
+ role: 'user' | 'agent' | 'system';
4
4
  agentName?: string;
5
5
  content: string;
6
6
  tokens?: {
@@ -9,10 +9,12 @@ const theme = {
9
9
  dim: '#4a5568',
10
10
  user: '#9f7aea', // Purple for user
11
11
  agent: '#4fd1c5', // Teal for agent
12
+ system: '#f6e05e', // Yellow for system
12
13
  };
13
14
  export const ChatMessage = ({ role, agentName = 'Agent', content, tokens, }) => {
14
15
  const isUser = role === 'user';
15
- const symbolColor = isUser ? theme.user : theme.agent;
16
+ const isSystem = role === 'system';
17
+ const symbolColor = isUser ? theme.user : isSystem ? theme.system : theme.agent;
16
18
  const title = isUser ? 'You' : agentName;
17
19
  const termWidth = process.stdout.columns || 80;
18
20
  const contentWidth = termWidth - 6;
@@ -8,7 +8,7 @@ const theme = {
8
8
  suggestion: '#718096',
9
9
  };
10
10
  // Available commands for autocomplete
11
- const COMMANDS = ['/help', '/status', '/clear', '/exit', '/mode'];
11
+ const COMMANDS = ['/help', '/status', '/clear', '/exit', '/plan', '/auto', '/mode', '/agents'];
12
12
  // Store history globally so it persists across re-renders
13
13
  const commandHistory = [];
14
14
  const MAX_HISTORY = 100;
@@ -5,3 +5,6 @@ export { Input } from './Input.js';
5
5
  export { StatusBar } from './StatusBar.js';
6
6
  export { ThinkingIndicator } from './ThinkingIndicator.js';
7
7
  export { RetroBox } from './Box.js';
8
+ export { AgentStatus } from './AgentStatus.js';
9
+ export { AgentPanel } from './AgentPanel.js';
10
+ export type { AgentName, AgentState } from './AgentPanel.js';
@@ -5,3 +5,5 @@ export { Input } from './Input.js';
5
5
  export { StatusBar } from './StatusBar.js';
6
6
  export { ThinkingIndicator } from './ThinkingIndicator.js';
7
7
  export { RetroBox } from './Box.js';
8
+ export { AgentStatus } from './AgentStatus.js';
9
+ export { AgentPanel } from './AgentPanel.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentk8",
3
- "version": "2.1.4",
3
+ "version": "2.2.0",
4
4
  "description": "Multi-Agent Claude Code Terminal Suite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",