agentk8 1.0.4 → 2.0.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.d.ts +2 -0
- package/dist/cli.js +74 -0
- package/dist/components/App.d.ts +7 -0
- package/dist/components/App.js +120 -0
- package/dist/components/Banner.d.ts +6 -0
- package/dist/components/Banner.js +17 -0
- package/dist/components/Box.d.ts +9 -0
- package/dist/components/Box.js +13 -0
- package/dist/components/ChatMessage.d.ts +13 -0
- package/dist/components/ChatMessage.js +44 -0
- package/dist/components/Input.d.ts +9 -0
- package/dist/components/Input.js +49 -0
- package/dist/components/StatusBar.d.ts +9 -0
- package/dist/components/StatusBar.js +40 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +6 -0
- package/dist/lib/claude.d.ts +10 -0
- package/dist/lib/claude.js +113 -0
- package/dist/themes/retro.d.ts +46 -0
- package/dist/themes/retro.js +52 -0
- package/package.json +28 -39
- package/agentk +0 -497
- package/bin/agentk-wrapper.js +0 -35
- package/bin/postinstall.js +0 -97
- package/lib/core.sh +0 -291
- package/lib/ipc.sh +0 -503
- package/lib/spawn.sh +0 -402
- package/lib/ui.sh +0 -584
- package/lib/visual.sh +0 -351
- package/modes/dev/engineer.md +0 -118
- package/modes/dev/orchestrator.md +0 -110
- package/modes/dev/security.md +0 -221
- package/modes/dev/tester.md +0 -161
- package/modes/ml/data-engineer.md +0 -244
- package/modes/ml/evaluator.md +0 -265
- package/modes/ml/ml-engineer.md +0 -239
- package/modes/ml/orchestrator.md +0 -145
- package/modes/ml/researcher.md +0 -198
- package/modes/shared/scout.md +0 -270
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import { App } from './components/App.js';
|
|
6
|
+
import { checkClaudeInstalled } from './lib/claude.js';
|
|
7
|
+
const VERSION = '2.0.0';
|
|
8
|
+
const cli = meow(`
|
|
9
|
+
Usage
|
|
10
|
+
$ agentk [options]
|
|
11
|
+
|
|
12
|
+
Options
|
|
13
|
+
--mode, -m Set mode: dev (default) or ml
|
|
14
|
+
--version, -v Show version
|
|
15
|
+
--help, -h Show this help
|
|
16
|
+
|
|
17
|
+
Examples
|
|
18
|
+
$ agentk Start dev mode
|
|
19
|
+
$ agentk --mode ml Start ML mode
|
|
20
|
+
|
|
21
|
+
Commands (inside app)
|
|
22
|
+
/help Show available commands
|
|
23
|
+
/status Show session status
|
|
24
|
+
/clear Clear chat history
|
|
25
|
+
/exit Exit AGENT-K
|
|
26
|
+
|
|
27
|
+
Keyboard
|
|
28
|
+
Ctrl+C Exit
|
|
29
|
+
Ctrl+U Clear input line
|
|
30
|
+
`, {
|
|
31
|
+
importMeta: import.meta,
|
|
32
|
+
flags: {
|
|
33
|
+
mode: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
shortFlag: 'm',
|
|
36
|
+
default: 'dev',
|
|
37
|
+
},
|
|
38
|
+
version: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
shortFlag: 'v',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
async function main() {
|
|
45
|
+
// Handle version flag
|
|
46
|
+
if (cli.flags.version) {
|
|
47
|
+
console.log(`AGENT-K v${VERSION}`);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
// Validate mode
|
|
51
|
+
const mode = cli.flags.mode;
|
|
52
|
+
if (mode !== 'dev' && mode !== 'ml') {
|
|
53
|
+
console.error(`Error: Invalid mode '${mode}'. Use 'dev' or 'ml'.`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
// Check if Claude is installed
|
|
57
|
+
const claudeInstalled = await checkClaudeInstalled();
|
|
58
|
+
if (!claudeInstalled) {
|
|
59
|
+
console.error('Error: Claude Code CLI is not installed.');
|
|
60
|
+
console.error('Install it from: https://claude.ai/code');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// Clear screen
|
|
64
|
+
console.clear();
|
|
65
|
+
// Render the app
|
|
66
|
+
const { waitUntilExit } = render(_jsx(App, { mode: mode, version: VERSION }));
|
|
67
|
+
await waitUntilExit();
|
|
68
|
+
// Show goodbye message
|
|
69
|
+
console.log('\n✦ Goodbye!\n');
|
|
70
|
+
}
|
|
71
|
+
main().catch((err) => {
|
|
72
|
+
console.error('Error:', err.message);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useApp, useInput } from 'ink';
|
|
4
|
+
import { Banner } from './Banner.js';
|
|
5
|
+
import { ChatMessage } from './ChatMessage.js';
|
|
6
|
+
import { Input } from './Input.js';
|
|
7
|
+
import { StatusBar } from './StatusBar.js';
|
|
8
|
+
import { runClaude } from '../lib/claude.js';
|
|
9
|
+
import { colors, icons } from '../themes/retro.js';
|
|
10
|
+
export const App = ({ mode, version }) => {
|
|
11
|
+
const { exit } = useApp();
|
|
12
|
+
const [messages, setMessages] = useState([]);
|
|
13
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
14
|
+
const [totalTokens, setTotalTokens] = useState(0);
|
|
15
|
+
const [startTime] = useState(new Date());
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
// Handle input submission
|
|
18
|
+
const handleSubmit = async (input) => {
|
|
19
|
+
// Check for commands
|
|
20
|
+
if (input.startsWith('/')) {
|
|
21
|
+
handleCommand(input);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Add user message
|
|
25
|
+
const userMessage = {
|
|
26
|
+
id: Date.now().toString(),
|
|
27
|
+
role: 'user',
|
|
28
|
+
content: input,
|
|
29
|
+
timestamp: new Date(),
|
|
30
|
+
};
|
|
31
|
+
setMessages(prev => [...prev, userMessage]);
|
|
32
|
+
setIsProcessing(true);
|
|
33
|
+
setError(null);
|
|
34
|
+
try {
|
|
35
|
+
// Call Claude
|
|
36
|
+
const result = await runClaude(input, mode);
|
|
37
|
+
// Add agent response
|
|
38
|
+
const agentMessage = {
|
|
39
|
+
id: (Date.now() + 1).toString(),
|
|
40
|
+
role: 'agent',
|
|
41
|
+
agentName: 'Orchestrator',
|
|
42
|
+
content: result.response,
|
|
43
|
+
tokens: result.tokens,
|
|
44
|
+
timestamp: new Date(),
|
|
45
|
+
};
|
|
46
|
+
setMessages(prev => [...prev, agentMessage]);
|
|
47
|
+
// Update token count
|
|
48
|
+
if (result.tokens) {
|
|
49
|
+
setTotalTokens(prev => prev + result.tokens.input + result.tokens.output);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
setIsProcessing(false);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Handle slash commands
|
|
60
|
+
const handleCommand = (cmd) => {
|
|
61
|
+
const [command, ...args] = cmd.slice(1).split(' ');
|
|
62
|
+
switch (command) {
|
|
63
|
+
case 'exit':
|
|
64
|
+
case 'quit':
|
|
65
|
+
exit();
|
|
66
|
+
break;
|
|
67
|
+
case 'clear':
|
|
68
|
+
setMessages([]);
|
|
69
|
+
break;
|
|
70
|
+
case 'help':
|
|
71
|
+
const helpMessage = {
|
|
72
|
+
id: Date.now().toString(),
|
|
73
|
+
role: 'agent',
|
|
74
|
+
agentName: 'System',
|
|
75
|
+
content: `Available commands:
|
|
76
|
+
/help - Show this help
|
|
77
|
+
/clear - Clear chat history
|
|
78
|
+
/status - Show agent status
|
|
79
|
+
/exit - Exit AGENT-K
|
|
80
|
+
|
|
81
|
+
Keyboard shortcuts:
|
|
82
|
+
Ctrl+C - Exit
|
|
83
|
+
Ctrl+U - Clear input line`,
|
|
84
|
+
timestamp: new Date(),
|
|
85
|
+
};
|
|
86
|
+
setMessages(prev => [...prev, helpMessage]);
|
|
87
|
+
break;
|
|
88
|
+
case 'status':
|
|
89
|
+
const statusMessage = {
|
|
90
|
+
id: Date.now().toString(),
|
|
91
|
+
role: 'agent',
|
|
92
|
+
agentName: 'System',
|
|
93
|
+
content: `Session Status:
|
|
94
|
+
${icons.bullet} Mode: ${mode === 'dev' ? 'Development' : 'ML Research'}
|
|
95
|
+
${icons.bullet} Messages: ${messages.length}
|
|
96
|
+
${icons.bullet} Total Tokens: ${totalTokens}
|
|
97
|
+
${icons.bullet} Session Time: ${formatElapsed(startTime)}`,
|
|
98
|
+
timestamp: new Date(),
|
|
99
|
+
};
|
|
100
|
+
setMessages(prev => [...prev, statusMessage]);
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
setError(`Unknown command: /${command}`);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
// Handle Ctrl+C
|
|
107
|
+
useInput((input, key) => {
|
|
108
|
+
if (key.ctrl && input === 'c') {
|
|
109
|
+
exit();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Banner, { version: version }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [messages.map(msg => (_jsx(ChatMessage, { role: msg.role, agentName: msg.agentName, content: msg.content, tokens: msg.tokens }, msg.id))), isProcessing && (_jsx(Box, { marginY: 1, children: _jsxs(Text, { color: colors.warning, children: [icons.sparkle, " Orchestrator is thinking..."] }) })), error && (_jsx(Box, { marginY: 1, children: _jsxs(Text, { color: colors.error, children: [icons.cross, " 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 })] }));
|
|
113
|
+
};
|
|
114
|
+
function formatElapsed(start) {
|
|
115
|
+
const secs = Math.floor((Date.now() - start.getTime()) / 1000);
|
|
116
|
+
const mins = Math.floor(secs / 60);
|
|
117
|
+
const remainingSecs = secs % 60;
|
|
118
|
+
return mins > 0 ? `${mins}m ${remainingSecs}s` : `${secs}s`;
|
|
119
|
+
}
|
|
120
|
+
export default App;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors, boxDouble, icons } from '../themes/retro.js';
|
|
4
|
+
export const Banner = ({ version }) => {
|
|
5
|
+
const termWidth = process.stdout.columns || 80;
|
|
6
|
+
const bannerWidth = Math.min(termWidth - 2, 50);
|
|
7
|
+
const innerWidth = bannerWidth - 2;
|
|
8
|
+
const title = `${icons.star} A G E N T - K ${icons.star}`;
|
|
9
|
+
const subtitle = 'Multi-Agent Claude Code Suite';
|
|
10
|
+
const versionText = `v${version}`;
|
|
11
|
+
const centerPad = (text, width) => {
|
|
12
|
+
const pad = Math.max(0, Math.floor((width - text.length) / 2));
|
|
13
|
+
return ' '.repeat(pad) + text + ' '.repeat(width - pad - text.length);
|
|
14
|
+
};
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: colors.secondary, children: [boxDouble.topLeft, boxDouble.horizontal.repeat(innerWidth), boxDouble.topRight] }), _jsxs(Text, { color: colors.secondary, children: [boxDouble.vertical, _jsx(Text, { bold: true, color: colors.primary, children: centerPad(title, innerWidth) }), boxDouble.vertical] }), _jsxs(Text, { color: colors.secondary, children: [boxDouble.vertical, _jsx(Text, { dimColor: true, children: centerPad(subtitle, innerWidth) }), boxDouble.vertical] }), _jsxs(Text, { color: colors.secondary, children: [boxDouble.vertical, _jsx(Text, { dimColor: true, children: centerPad(versionText, innerWidth) }), boxDouble.vertical] }), _jsxs(Text, { color: colors.secondary, children: [boxDouble.bottomLeft, boxDouble.horizontal.repeat(innerWidth), boxDouble.bottomRight] })] }));
|
|
16
|
+
};
|
|
17
|
+
export default Banner;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box as InkBox, Text } from 'ink';
|
|
4
|
+
import { box, colors } from '../themes/retro.js';
|
|
5
|
+
export const RetroBox = ({ title, children, color = colors.secondary, width = '100%', }) => {
|
|
6
|
+
const termWidth = process.stdout.columns || 80;
|
|
7
|
+
const boxWidth = typeof width === 'number' ? width : Math.min(termWidth - 2, 80);
|
|
8
|
+
const titleText = title ? ` ${title} ` : '';
|
|
9
|
+
const topLineWidth = boxWidth - 2 - titleText.length;
|
|
10
|
+
const topLine = box.horizontal.repeat(Math.max(0, topLineWidth));
|
|
11
|
+
return (_jsxs(InkBox, { flexDirection: "column", width: boxWidth, children: [_jsxs(Text, { color: color, children: [box.topLeft, box.horizontal, title && _jsx(Text, { bold: true, children: titleText }), topLine, box.topRight] }), _jsx(InkBox, { flexDirection: "column", paddingX: 1, children: React.Children.map(children, (child) => (_jsxs(InkBox, { children: [_jsx(Text, { color: color, children: box.vertical }), _jsx(InkBox, { flexGrow: 1, paddingX: 1, children: child }), _jsx(Text, { color: color, children: box.vertical })] }))) }), _jsxs(Text, { color: color, children: [box.bottomLeft, box.horizontal.repeat(boxWidth - 2), box.bottomRight] })] }));
|
|
12
|
+
};
|
|
13
|
+
export default RetroBox;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface ChatMessageProps {
|
|
3
|
+
role: 'user' | 'agent';
|
|
4
|
+
agentName?: string;
|
|
5
|
+
content: string;
|
|
6
|
+
tokens?: {
|
|
7
|
+
input: number;
|
|
8
|
+
output: number;
|
|
9
|
+
};
|
|
10
|
+
timestamp?: Date;
|
|
11
|
+
}
|
|
12
|
+
export declare const ChatMessage: React.FC<ChatMessageProps>;
|
|
13
|
+
export default ChatMessage;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { box, colors, icons } from '../themes/retro.js';
|
|
4
|
+
export const ChatMessage = ({ role, agentName = 'Agent', content, tokens, timestamp, }) => {
|
|
5
|
+
const isUser = role === 'user';
|
|
6
|
+
const color = isUser ? colors.primary : colors.secondary;
|
|
7
|
+
const title = isUser ? 'You' : agentName;
|
|
8
|
+
const termWidth = process.stdout.columns || 80;
|
|
9
|
+
const boxWidth = Math.min(termWidth - 2, 78);
|
|
10
|
+
const contentWidth = boxWidth - 4;
|
|
11
|
+
// Word wrap content
|
|
12
|
+
const lines = wrapText(content, contentWidth);
|
|
13
|
+
const titleText = ` ${title} `;
|
|
14
|
+
const topLineWidth = boxWidth - 2 - titleText.length;
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { color: color, children: [box.topLeft, box.horizontal, _jsx(Text, { bold: true, children: titleText }), box.horizontal.repeat(Math.max(0, topLineWidth)), box.topRight] }), lines.map((line, i) => (_jsxs(Text, { color: color, children: [box.vertical, " ", _jsx(Text, { color: "white", children: line.padEnd(contentWidth) }), " ", box.vertical] }, i))), tokens && tokens.input + tokens.output > 0 && (_jsxs(Text, { color: color, children: [box.vertical, " ", _jsxs(Text, { dimColor: true, children: [icons.arrow, " ", tokens.input + tokens.output, " tokens (in: ", tokens.input, ", out: ", tokens.output, ")"] }), ' '.repeat(Math.max(0, contentWidth - 40)), " ", box.vertical] })), _jsxs(Text, { color: color, children: [box.bottomLeft, box.horizontal.repeat(boxWidth - 2), box.bottomRight] })] }));
|
|
16
|
+
};
|
|
17
|
+
// Simple word wrap function
|
|
18
|
+
function wrapText(text, width) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
const paragraphs = text.split('\n');
|
|
21
|
+
for (const para of paragraphs) {
|
|
22
|
+
if (para.length <= width) {
|
|
23
|
+
lines.push(para);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const words = para.split(' ');
|
|
27
|
+
let currentLine = '';
|
|
28
|
+
for (const word of words) {
|
|
29
|
+
if ((currentLine + ' ' + word).trim().length <= width) {
|
|
30
|
+
currentLine = (currentLine + ' ' + word).trim();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (currentLine)
|
|
34
|
+
lines.push(currentLine);
|
|
35
|
+
currentLine = word;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (currentLine)
|
|
39
|
+
lines.push(currentLine);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return lines.length > 0 ? lines : [''];
|
|
43
|
+
}
|
|
44
|
+
export default ChatMessage;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { colors, icons } from '../themes/retro.js';
|
|
5
|
+
export const Input = ({ onSubmit, placeholder = 'Type a message...', prefix = '>', disabled = false, }) => {
|
|
6
|
+
const [value, setValue] = useState('');
|
|
7
|
+
const [cursorPosition, setCursorPosition] = useState(0);
|
|
8
|
+
useInput((input, key) => {
|
|
9
|
+
if (disabled)
|
|
10
|
+
return;
|
|
11
|
+
if (key.return) {
|
|
12
|
+
if (value.trim()) {
|
|
13
|
+
onSubmit(value);
|
|
14
|
+
setValue('');
|
|
15
|
+
setCursorPosition(0);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else if (key.backspace || key.delete) {
|
|
19
|
+
if (cursorPosition > 0) {
|
|
20
|
+
setValue(prev => prev.slice(0, cursorPosition - 1) + prev.slice(cursorPosition));
|
|
21
|
+
setCursorPosition(pos => pos - 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (key.leftArrow) {
|
|
25
|
+
setCursorPosition(pos => Math.max(0, pos - 1));
|
|
26
|
+
}
|
|
27
|
+
else if (key.rightArrow) {
|
|
28
|
+
setCursorPosition(pos => Math.min(value.length, pos + 1));
|
|
29
|
+
}
|
|
30
|
+
else if (key.ctrl && input === 'c') {
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
else if (key.ctrl && input === 'u') {
|
|
34
|
+
// Clear line
|
|
35
|
+
setValue('');
|
|
36
|
+
setCursorPosition(0);
|
|
37
|
+
}
|
|
38
|
+
else if (!key.ctrl && !key.meta && input) {
|
|
39
|
+
setValue(prev => prev.slice(0, cursorPosition) + input + prev.slice(cursorPosition));
|
|
40
|
+
setCursorPosition(pos => pos + input.length);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Render input with cursor
|
|
44
|
+
const beforeCursor = value.slice(0, cursorPosition);
|
|
45
|
+
const atCursor = value[cursorPosition] || ' ';
|
|
46
|
+
const afterCursor = value.slice(cursorPosition + 1);
|
|
47
|
+
return (_jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: colors.primary, bold: true, children: [prefix, " "] }), value.length === 0 && !disabled ? (_jsx(Text, { dimColor: true, children: placeholder })) : (_jsxs(_Fragment, { children: [_jsx(Text, { children: beforeCursor }), _jsx(Text, { inverse: true, children: atCursor }), _jsx(Text, { children: afterCursor })] })), disabled && _jsxs(Text, { dimColor: true, children: [" ", icons.sparkle, " thinking..."] })] }));
|
|
48
|
+
};
|
|
49
|
+
export default Input;
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
import { colors, icons, box } from '../themes/retro.js';
|
|
5
|
+
export const StatusBar = ({ mode, tokens, startTime, isProcessing = false, }) => {
|
|
6
|
+
const [elapsed, setElapsed] = useState('0s');
|
|
7
|
+
const [spinnerFrame, setSpinnerFrame] = useState(0);
|
|
8
|
+
// Update elapsed time every second
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const interval = setInterval(() => {
|
|
11
|
+
const secs = Math.floor((Date.now() - startTime.getTime()) / 1000);
|
|
12
|
+
const mins = Math.floor(secs / 60);
|
|
13
|
+
const remainingSecs = secs % 60;
|
|
14
|
+
setElapsed(mins > 0 ? `${mins}m ${remainingSecs}s` : `${secs}s`);
|
|
15
|
+
}, 1000);
|
|
16
|
+
return () => clearInterval(interval);
|
|
17
|
+
}, [startTime]);
|
|
18
|
+
// Spinner animation
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!isProcessing)
|
|
21
|
+
return;
|
|
22
|
+
const interval = setInterval(() => {
|
|
23
|
+
setSpinnerFrame(f => (f + 1) % icons.spinner.length);
|
|
24
|
+
}, 100);
|
|
25
|
+
return () => clearInterval(interval);
|
|
26
|
+
}, [isProcessing]);
|
|
27
|
+
const formatTokens = (t) => {
|
|
28
|
+
if (t >= 1000000)
|
|
29
|
+
return `${(t / 1000000).toFixed(1)}M`;
|
|
30
|
+
if (t >= 1000)
|
|
31
|
+
return `${(t / 1000).toFixed(1)}k`;
|
|
32
|
+
return t.toString();
|
|
33
|
+
};
|
|
34
|
+
const modeIcon = mode === 'dev' ? icons.lightning : icons.brain;
|
|
35
|
+
const modeLabel = mode === 'dev' ? 'Development' : 'ML Research';
|
|
36
|
+
const termWidth = process.stdout.columns || 80;
|
|
37
|
+
const barWidth = Math.min(termWidth - 2, 78);
|
|
38
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.dim, children: box.horizontal.repeat(barWidth) }), _jsxs(Box, { justifyContent: "space-between", width: barWidth, children: [_jsxs(Text, { children: [_jsxs(Text, { color: colors.secondary, children: [modeIcon, " ", modeLabel] }), _jsx(Text, { dimColor: true, children: " \u2502 " }), _jsx(Text, { dimColor: true, children: "/help for commands" })] }), _jsxs(Text, { children: [isProcessing && (_jsxs(Text, { color: colors.warning, children: [icons.spinner[spinnerFrame], " "] })), _jsxs(Text, { dimColor: true, children: [elapsed, " \u2502 "] }), _jsxs(Text, { color: colors.primary, children: ["\u2191 ", formatTokens(tokens), " tokens"] })] })] })] }));
|
|
39
|
+
};
|
|
40
|
+
export default StatusBar;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface ClaudeResult {
|
|
2
|
+
response: string;
|
|
3
|
+
tokens: {
|
|
4
|
+
input: number;
|
|
5
|
+
output: number;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export declare function runClaude(prompt: string, mode: 'dev' | 'ml'): Promise<ClaudeResult>;
|
|
9
|
+
export declare function checkClaudeInstalled(): Promise<boolean>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
export async function runClaude(prompt, mode) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const systemPrompt = getSystemPrompt(mode);
|
|
5
|
+
// Run claude with JSON output to capture tokens
|
|
6
|
+
const args = [
|
|
7
|
+
'--print',
|
|
8
|
+
'--output-format', 'json',
|
|
9
|
+
'--system-prompt', systemPrompt,
|
|
10
|
+
prompt
|
|
11
|
+
];
|
|
12
|
+
const claude = spawn('claude', args, {
|
|
13
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
+
});
|
|
15
|
+
let stdout = '';
|
|
16
|
+
let stderr = '';
|
|
17
|
+
claude.stdout.on('data', (data) => {
|
|
18
|
+
stdout += data.toString();
|
|
19
|
+
});
|
|
20
|
+
claude.stderr.on('data', (data) => {
|
|
21
|
+
stderr += data.toString();
|
|
22
|
+
});
|
|
23
|
+
claude.on('close', (code) => {
|
|
24
|
+
if (code !== 0) {
|
|
25
|
+
reject(new Error(stderr || `Claude exited with code ${code}`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
// Try to parse as JSON
|
|
30
|
+
const result = parseClaudeOutput(stdout);
|
|
31
|
+
resolve(result);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
// If not JSON, return as plain text
|
|
35
|
+
resolve({
|
|
36
|
+
response: stdout.trim(),
|
|
37
|
+
tokens: { input: 0, output: 0 },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
claude.on('error', (err) => {
|
|
42
|
+
reject(new Error(`Failed to start Claude: ${err.message}`));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function parseClaudeOutput(output) {
|
|
47
|
+
try {
|
|
48
|
+
const json = JSON.parse(output);
|
|
49
|
+
// Extract response text
|
|
50
|
+
let response = '';
|
|
51
|
+
if (json.result) {
|
|
52
|
+
response = json.result;
|
|
53
|
+
}
|
|
54
|
+
else if (json.content) {
|
|
55
|
+
if (Array.isArray(json.content)) {
|
|
56
|
+
response = json.content.map((c) => c.text || '').join('\n');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
response = json.content;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (json.text) {
|
|
63
|
+
response = json.text;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
response = output;
|
|
67
|
+
}
|
|
68
|
+
// Extract tokens
|
|
69
|
+
const tokens = {
|
|
70
|
+
input: json.usage?.input_tokens || json.inputTokens || json.stats?.input_tokens || 0,
|
|
71
|
+
output: json.usage?.output_tokens || json.outputTokens || json.stats?.output_tokens || 0,
|
|
72
|
+
};
|
|
73
|
+
return { response, tokens };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return {
|
|
77
|
+
response: output.trim(),
|
|
78
|
+
tokens: { input: 0, output: 0 },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function getSystemPrompt(mode) {
|
|
83
|
+
const today = new Date().toISOString().split('T')[0];
|
|
84
|
+
if (mode === 'ml') {
|
|
85
|
+
return `You are the Orchestrator agent in AGENT-K, a multi-agent system for ML research.
|
|
86
|
+
Today's date: ${today}
|
|
87
|
+
|
|
88
|
+
Your role is to:
|
|
89
|
+
1. Analyze ML tasks and break them into subtasks
|
|
90
|
+
2. Coordinate between Researcher, ML Engineer, Data Engineer, and Evaluator
|
|
91
|
+
3. Ensure best practices in ML development
|
|
92
|
+
4. Provide clear, actionable responses
|
|
93
|
+
|
|
94
|
+
Be concise and practical in your responses.`;
|
|
95
|
+
}
|
|
96
|
+
return `You are the Orchestrator agent in AGENT-K, a multi-agent system for software development.
|
|
97
|
+
Today's date: ${today}
|
|
98
|
+
|
|
99
|
+
Your role is to:
|
|
100
|
+
1. Analyze development tasks and break them into subtasks
|
|
101
|
+
2. Coordinate between Engineer, Tester, and Security agents
|
|
102
|
+
3. Ensure code quality and security
|
|
103
|
+
4. Provide clear, actionable responses
|
|
104
|
+
|
|
105
|
+
Be concise and practical in your responses.`;
|
|
106
|
+
}
|
|
107
|
+
export async function checkClaudeInstalled() {
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
const claude = spawn('claude', ['--version'], { stdio: 'ignore' });
|
|
110
|
+
claude.on('close', (code) => resolve(code === 0));
|
|
111
|
+
claude.on('error', () => resolve(false));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const colors: {
|
|
2
|
+
primary: string;
|
|
3
|
+
secondary: string;
|
|
4
|
+
accent: string;
|
|
5
|
+
warning: string;
|
|
6
|
+
error: string;
|
|
7
|
+
dim: string;
|
|
8
|
+
bg: string;
|
|
9
|
+
text: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const box: {
|
|
12
|
+
topLeft: string;
|
|
13
|
+
topRight: string;
|
|
14
|
+
bottomLeft: string;
|
|
15
|
+
bottomRight: string;
|
|
16
|
+
horizontal: string;
|
|
17
|
+
vertical: string;
|
|
18
|
+
cross: string;
|
|
19
|
+
teeDown: string;
|
|
20
|
+
teeUp: string;
|
|
21
|
+
teeRight: string;
|
|
22
|
+
teeLeft: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const boxDouble: {
|
|
25
|
+
topLeft: string;
|
|
26
|
+
topRight: string;
|
|
27
|
+
bottomLeft: string;
|
|
28
|
+
bottomRight: string;
|
|
29
|
+
horizontal: string;
|
|
30
|
+
vertical: string;
|
|
31
|
+
};
|
|
32
|
+
export declare const icons: {
|
|
33
|
+
star: string;
|
|
34
|
+
sparkle: string;
|
|
35
|
+
check: string;
|
|
36
|
+
cross: string;
|
|
37
|
+
arrow: string;
|
|
38
|
+
bullet: string;
|
|
39
|
+
circle: string;
|
|
40
|
+
spinner: string[];
|
|
41
|
+
lightning: string;
|
|
42
|
+
brain: string;
|
|
43
|
+
};
|
|
44
|
+
export declare const ascii: {
|
|
45
|
+
banner: string;
|
|
46
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// AGENT-K Retro Theme
|
|
2
|
+
// Inspired by classic terminal aesthetics
|
|
3
|
+
export const colors = {
|
|
4
|
+
primary: '#00ff00', // Matrix green
|
|
5
|
+
secondary: '#00ffff', // Cyan
|
|
6
|
+
accent: '#ff00ff', // Magenta
|
|
7
|
+
warning: '#ffff00', // Yellow
|
|
8
|
+
error: '#ff0000', // Red
|
|
9
|
+
dim: '#666666', // Gray
|
|
10
|
+
bg: '#000000', // Black
|
|
11
|
+
text: '#ffffff', // White
|
|
12
|
+
};
|
|
13
|
+
export const box = {
|
|
14
|
+
topLeft: '╭',
|
|
15
|
+
topRight: '╮',
|
|
16
|
+
bottomLeft: '╰',
|
|
17
|
+
bottomRight: '╯',
|
|
18
|
+
horizontal: '─',
|
|
19
|
+
vertical: '│',
|
|
20
|
+
cross: '┼',
|
|
21
|
+
teeDown: '┬',
|
|
22
|
+
teeUp: '┴',
|
|
23
|
+
teeRight: '├',
|
|
24
|
+
teeLeft: '┤',
|
|
25
|
+
};
|
|
26
|
+
export const boxDouble = {
|
|
27
|
+
topLeft: '╔',
|
|
28
|
+
topRight: '╗',
|
|
29
|
+
bottomLeft: '╚',
|
|
30
|
+
bottomRight: '╝',
|
|
31
|
+
horizontal: '═',
|
|
32
|
+
vertical: '║',
|
|
33
|
+
};
|
|
34
|
+
export const icons = {
|
|
35
|
+
star: '✦',
|
|
36
|
+
sparkle: '✢',
|
|
37
|
+
check: '✓',
|
|
38
|
+
cross: '✗',
|
|
39
|
+
arrow: '→',
|
|
40
|
+
bullet: '●',
|
|
41
|
+
circle: '○',
|
|
42
|
+
spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
43
|
+
lightning: '⚡',
|
|
44
|
+
brain: '🧠',
|
|
45
|
+
};
|
|
46
|
+
export const ascii = {
|
|
47
|
+
banner: `
|
|
48
|
+
╔═══════════════════════════════════════════╗
|
|
49
|
+
║ ✦ A G E N T - K ✦ ║
|
|
50
|
+
║ Multi-Agent Claude Code Suite ║
|
|
51
|
+
╚═══════════════════════════════════════════╝`,
|
|
52
|
+
};
|