swarmlancer-cli 0.2.0 → 0.2.1
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/agent.js +16 -16
- package/dist/app.js +7 -7
- package/dist/screens/agent-config.js +7 -8
- package/dist/screens/agent-editor.js +16 -11
- package/dist/screens/agent-list.js +3 -6
- package/dist/screens/agent-picker.js +1 -1
- package/dist/screens/agent-running.js +2 -2
- package/dist/screens/banner.js +7 -6
- package/dist/screens/dashboard.js +4 -7
- package/dist/screens/discovery-settings.js +3 -3
- package/dist/screens/message.d.ts +1 -1
- package/dist/screens/message.js +2 -7
- package/dist/screens/model-picker.js +1 -1
- package/dist/screens/name-editor.js +4 -4
- package/dist/screens/session-goal.js +4 -4
- package/dist/screens/settings.js +2 -2
- package/dist/screens/setup-wizard.js +9 -9
- package/dist/screens/status-panel.js +2 -2
- package/dist/theme.d.ts +0 -11
- package/dist/theme.js +9 -21
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -21,7 +21,7 @@ function resetIdleTimer(limits, log) {
|
|
|
21
21
|
if (idleTimer)
|
|
22
22
|
clearTimeout(idleTimer);
|
|
23
23
|
idleTimer = setTimeout(() => {
|
|
24
|
-
log(colors.
|
|
24
|
+
log(colors.lime(`⏱ Auto-stopping: idle for ${limits.autoStopIdleMinutes} minutes`));
|
|
25
25
|
stopAgent();
|
|
26
26
|
}, limits.autoStopIdleMinutes * 60 * 1000);
|
|
27
27
|
}
|
|
@@ -35,7 +35,7 @@ function resetIdleTimer(limits, log) {
|
|
|
35
35
|
export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = console.log) {
|
|
36
36
|
const config = getConfig();
|
|
37
37
|
if (!config.token) {
|
|
38
|
-
log(colors.
|
|
38
|
+
log(colors.lime("Not logged in. Run: swarmlancer login"));
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
resetCounters();
|
|
@@ -44,7 +44,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
44
44
|
// Log limits
|
|
45
45
|
log(colors.gray(`Limits: ${limits.maxConcurrentConversations} concurrent, ${limits.maxMessagesPerConversation} msgs/convo, ${limits.cooldownSeconds}s cooldown, ${limits.maxConversationsPerSession} max/session, ${limits.autoStopIdleMinutes}m idle`));
|
|
46
46
|
function connect() {
|
|
47
|
-
log(colors.
|
|
47
|
+
log(colors.lime(`Connecting to ${wsUrl}...`));
|
|
48
48
|
const ws = new WebSocket(wsUrl);
|
|
49
49
|
activeWs = ws;
|
|
50
50
|
// Start idle timer
|
|
@@ -56,7 +56,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
56
56
|
const msg = JSON.parse(raw.toString());
|
|
57
57
|
switch (msg.type) {
|
|
58
58
|
case "authenticated":
|
|
59
|
-
log(colors.
|
|
59
|
+
log(colors.lime("Agent online"));
|
|
60
60
|
log("");
|
|
61
61
|
log(colors.gray("Waiting for conversations..."));
|
|
62
62
|
ws.send(JSON.stringify({ type: "get_online_users" }));
|
|
@@ -64,7 +64,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
64
64
|
case "online_users": {
|
|
65
65
|
const users = msg.users;
|
|
66
66
|
if (users.length > 0) {
|
|
67
|
-
log(colors.
|
|
67
|
+
log(colors.lime(`📡 Online: ${users.map((u) => `${u.displayName} (@${u.githubUsername})`).join(", ")}`));
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
70
|
log(colors.gray("No other agents online right now."));
|
|
@@ -78,7 +78,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
78
78
|
// Check concurrent conversation limit
|
|
79
79
|
if (!activeConversations.has(conversationId) &&
|
|
80
80
|
activeConversations.size >= limits.maxConcurrentConversations) {
|
|
81
|
-
log(colors.
|
|
81
|
+
log(colors.lime(`⚠ Skipping: ${limits.maxConcurrentConversations} concurrent conversations active`));
|
|
82
82
|
ws.send(JSON.stringify({
|
|
83
83
|
type: "inference_error",
|
|
84
84
|
requestId,
|
|
@@ -89,7 +89,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
89
89
|
// Check session total limit
|
|
90
90
|
if (!activeConversations.has(conversationId) &&
|
|
91
91
|
totalConversations >= limits.maxConversationsPerSession) {
|
|
92
|
-
log(colors.
|
|
92
|
+
log(colors.lime(`⚠ Session limit reached (${limits.maxConversationsPerSession} conversations). Stopping.`));
|
|
93
93
|
stopAgent();
|
|
94
94
|
break;
|
|
95
95
|
}
|
|
@@ -99,7 +99,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
99
99
|
const elapsed = (Date.now() - lastConversationTime) / 1000;
|
|
100
100
|
if (lastConversationTime > 0 && elapsed < limits.cooldownSeconds) {
|
|
101
101
|
const wait = Math.ceil(limits.cooldownSeconds - elapsed);
|
|
102
|
-
log(colors.
|
|
102
|
+
log(colors.lime(`⚠ Cooldown: ${wait}s remaining`));
|
|
103
103
|
ws.send(JSON.stringify({
|
|
104
104
|
type: "inference_error",
|
|
105
105
|
requestId,
|
|
@@ -119,7 +119,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
119
119
|
const msgCount = (conversationMessageCounts.get(conversationId) ?? 0) + 1;
|
|
120
120
|
conversationMessageCounts.set(conversationId, msgCount);
|
|
121
121
|
if (msgCount > limits.maxMessagesPerConversation) {
|
|
122
|
-
log(colors.
|
|
122
|
+
log(colors.lime(`⚠ Conversation ${conversationId.slice(0, 8)} hit ${limits.maxMessagesPerConversation} message limit`));
|
|
123
123
|
activeConversations.delete(conversationId);
|
|
124
124
|
ws.send(JSON.stringify({
|
|
125
125
|
type: "inference_response",
|
|
@@ -128,7 +128,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
128
128
|
}));
|
|
129
129
|
break;
|
|
130
130
|
}
|
|
131
|
-
log(colors.
|
|
131
|
+
log(colors.lime(`💬 [${conversationId.slice(0, 8)}] msg ${msgCount}/${limits.maxMessagesPerConversation} (${activeConversations.size}/${limits.maxConcurrentConversations} active, ${totalConversations}/${limits.maxConversationsPerSession} total)`));
|
|
132
132
|
try {
|
|
133
133
|
let response = await runInference(systemPrompt, messages);
|
|
134
134
|
// Enforce response length limit
|
|
@@ -137,7 +137,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
137
137
|
log(colors.gray(` (truncated to ${limits.maxResponseLength} chars)`));
|
|
138
138
|
}
|
|
139
139
|
const preview = response.length > 80 ? response.slice(0, 80) + "..." : response;
|
|
140
|
-
log(colors.
|
|
140
|
+
log(colors.lime(`✓ Response: ${preview}`));
|
|
141
141
|
ws.send(JSON.stringify({
|
|
142
142
|
type: "inference_response",
|
|
143
143
|
requestId,
|
|
@@ -146,7 +146,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
146
146
|
}
|
|
147
147
|
catch (err) {
|
|
148
148
|
const errorMsg = err instanceof Error ? err.message : "Unknown error";
|
|
149
|
-
log(colors.
|
|
149
|
+
log(colors.lime(`✗ Inference failed: ${errorMsg}`));
|
|
150
150
|
ws.send(JSON.stringify({
|
|
151
151
|
type: "inference_error",
|
|
152
152
|
requestId,
|
|
@@ -157,7 +157,7 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
157
157
|
}
|
|
158
158
|
case "conversation_started": {
|
|
159
159
|
const { conversationId, withUser } = msg;
|
|
160
|
-
log(colors.
|
|
160
|
+
log(colors.lime(`🤝 Conversation started with ${withUser.displayName} (@${withUser.githubUsername})`));
|
|
161
161
|
resetIdleTimer(limits, log);
|
|
162
162
|
break;
|
|
163
163
|
}
|
|
@@ -169,18 +169,18 @@ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = co
|
|
|
169
169
|
break;
|
|
170
170
|
}
|
|
171
171
|
case "error":
|
|
172
|
-
log(colors.
|
|
172
|
+
log(colors.lime(`Server error: ${msg.message}`));
|
|
173
173
|
break;
|
|
174
174
|
}
|
|
175
175
|
});
|
|
176
176
|
ws.on("close", () => {
|
|
177
177
|
if (alive) {
|
|
178
|
-
log(colors.
|
|
178
|
+
log(colors.lime("Disconnected. Reconnecting in 5s..."));
|
|
179
179
|
reconnectTimer = setTimeout(connect, 5000);
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
182
|
ws.on("error", (err) => {
|
|
183
|
-
log(colors.
|
|
183
|
+
log(colors.lime(`WebSocket error: ${err.message}`));
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
connect();
|
package/dist/app.js
CHANGED
|
@@ -388,15 +388,15 @@ async function runAgentSession() {
|
|
|
388
388
|
screen.setStatus("waiting for conversations...");
|
|
389
389
|
}
|
|
390
390
|
else {
|
|
391
|
-
log(colors.
|
|
391
|
+
log(colors.lime(`📡 Found ${candidates.length} candidates, screening...`));
|
|
392
392
|
screen.setStatus(`screening ${candidates.length} profiles...`);
|
|
393
393
|
const results = await screenProfiles(candidates, agent.instructions, sessionGoal, discovery.matchThreshold, discovery.maxScreenPerSession, (screened, total, result) => {
|
|
394
394
|
const icon = result.score >= discovery.matchThreshold
|
|
395
|
-
? colors.
|
|
395
|
+
? colors.lime("✓")
|
|
396
396
|
: colors.gray("⊘");
|
|
397
397
|
const name = `@${result.profile.githubUsername}`;
|
|
398
398
|
const scoreStr = result.score >= discovery.matchThreshold
|
|
399
|
-
? colors.
|
|
399
|
+
? colors.lime(`${result.score}/10`)
|
|
400
400
|
: colors.gray(`${result.score}/10`);
|
|
401
401
|
log(` ${icon} ${name} ${scoreStr} — ${colors.gray(result.reason)}`);
|
|
402
402
|
screen.setStatus(`screening ${screened}/${total}...`);
|
|
@@ -404,10 +404,10 @@ async function runAgentSession() {
|
|
|
404
404
|
const matches = results.filter((r) => r.score >= discovery.matchThreshold);
|
|
405
405
|
log("");
|
|
406
406
|
if (matches.length === 0) {
|
|
407
|
-
log(colors.
|
|
407
|
+
log(colors.lime("No strong matches found. Waiting for incoming conversations..."));
|
|
408
408
|
}
|
|
409
409
|
else {
|
|
410
|
-
log(colors.
|
|
410
|
+
log(colors.lime(`Found ${matches.length} match${matches.length === 1 ? "" : "es"}. Starting conversations...`));
|
|
411
411
|
log("");
|
|
412
412
|
// Initiate conversations with matches
|
|
413
413
|
for (const match of matches) {
|
|
@@ -415,7 +415,7 @@ async function runAgentSession() {
|
|
|
415
415
|
log(colors.gray(` ⊘ @${match.profile.githubUsername} is offline, skipping`));
|
|
416
416
|
continue;
|
|
417
417
|
}
|
|
418
|
-
log(colors.
|
|
418
|
+
log(colors.lime(` 🤝 Reaching out to @${match.profile.githubUsername} (${match.score}/10: ${match.reason})`));
|
|
419
419
|
// Send start_conversation via WebSocket
|
|
420
420
|
sendToServer({
|
|
421
421
|
type: "start_conversation",
|
|
@@ -431,7 +431,7 @@ async function runAgentSession() {
|
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
433
|
catch (err) {
|
|
434
|
-
log(colors.
|
|
434
|
+
log(colors.lime(`Discovery failed: ${err instanceof Error ? err.message : err}`));
|
|
435
435
|
screen.setStatus("waiting for conversations...");
|
|
436
436
|
}
|
|
437
437
|
return done;
|
|
@@ -28,20 +28,19 @@ export class AgentConfigScreen {
|
|
|
28
28
|
this.container.addChild(new Text(truncateToWidth(theme.border("─".repeat(60)), 60), 0, 0));
|
|
29
29
|
this.container.addChild(new Spacer(1));
|
|
30
30
|
const items = [
|
|
31
|
-
{ value: "edit-name", label: "Rename
|
|
32
|
-
{ value: "edit-instructions", label: "
|
|
33
|
-
{ value: "edit-discovery", label: "Discovery
|
|
34
|
-
{ value: "edit-limits", label: "
|
|
35
|
-
{ value: "edit-model", label: "
|
|
36
|
-
{ value: "delete", label: "Delete
|
|
37
|
-
{ value: "back", label: "← Back", description: "" },
|
|
31
|
+
{ value: "edit-name", label: "Rename", description: agent.name },
|
|
32
|
+
{ value: "edit-instructions", label: "Instructions", description: "How your agent behaves" },
|
|
33
|
+
{ value: "edit-discovery", label: "Discovery", description: "Keywords, match threshold, filters" },
|
|
34
|
+
{ value: "edit-limits", label: "Limits", description: "Max conversations, cooldown, auto-stop" },
|
|
35
|
+
{ value: "edit-model", label: "Model", description: modelStr },
|
|
36
|
+
{ value: "delete", label: "Delete", description: "" },
|
|
38
37
|
];
|
|
39
38
|
this.selectList = new SelectList(items, items.length, {
|
|
40
39
|
selectedPrefix: (t) => theme.accent(t),
|
|
41
40
|
selectedText: (t) => theme.accent(t),
|
|
42
41
|
description: (t) => colors.gray(t),
|
|
43
42
|
scrollInfo: (t) => colors.gray(t),
|
|
44
|
-
noMatch: (t) => colors.
|
|
43
|
+
noMatch: (t) => colors.gray(t),
|
|
45
44
|
});
|
|
46
45
|
this.selectList.onSelect = (item) => {
|
|
47
46
|
this.onAction?.(item.value);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Editor, matchesKey, Key, truncateToWidth, } from "@mariozechner/pi-tui";
|
|
2
2
|
import { colors, theme } from "../theme.js";
|
|
3
3
|
const EDITOR_THEME = {
|
|
4
|
-
borderColor: (s) => colors.
|
|
4
|
+
borderColor: (s) => colors.lime(s),
|
|
5
5
|
selectList: {
|
|
6
|
-
selectedPrefix: (t) => colors.
|
|
7
|
-
selectedText: (t) => colors.
|
|
6
|
+
selectedPrefix: (t) => colors.lime(t),
|
|
7
|
+
selectedText: (t) => colors.lime(t),
|
|
8
8
|
description: (t) => colors.gray(t),
|
|
9
9
|
scrollInfo: (t) => colors.gray(t),
|
|
10
|
-
noMatch: (t) => colors.
|
|
10
|
+
noMatch: (t) => colors.gray(t),
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
13
|
export class AgentEditorScreen {
|
|
@@ -43,17 +43,22 @@ export class AgentEditorScreen {
|
|
|
43
43
|
return this.cachedLines;
|
|
44
44
|
const lines = [];
|
|
45
45
|
lines.push(truncateToWidth(theme.border("─".repeat(width)), width));
|
|
46
|
-
lines.push(truncateToWidth(` ${theme.title("
|
|
47
|
-
lines.push(truncateToWidth(colors.gray(" This controls how your agent behaves in conversations."), width));
|
|
46
|
+
lines.push(truncateToWidth(` ${theme.title("Instructions")}`, width));
|
|
48
47
|
lines.push(truncateToWidth(theme.border("─".repeat(width)), width));
|
|
49
|
-
lines
|
|
50
|
-
|
|
51
|
-
for (const line of
|
|
48
|
+
// Editor takes most of the space — pad with empty lines to double the height
|
|
49
|
+
const editorLines = this.editor.render(width);
|
|
50
|
+
for (const line of editorLines) {
|
|
52
51
|
lines.push(line);
|
|
53
52
|
}
|
|
54
|
-
lines
|
|
53
|
+
// Add padding lines to give the editor more vertical space
|
|
54
|
+
const termHeight = this.tui.terminal?.rows ?? 40;
|
|
55
|
+
const usedLines = lines.length + 3; // +3 for footer
|
|
56
|
+
const remaining = Math.max(0, termHeight - usedLines);
|
|
57
|
+
for (let i = 0; i < remaining; i++) {
|
|
58
|
+
lines.push("");
|
|
59
|
+
}
|
|
55
60
|
lines.push(truncateToWidth(theme.border("─".repeat(width)), width));
|
|
56
|
-
lines.push(truncateToWidth(colors.gray("
|
|
61
|
+
lines.push(truncateToWidth(colors.gray(" ctrl+s save • esc cancel"), width));
|
|
57
62
|
this.cachedLines = lines;
|
|
58
63
|
return lines;
|
|
59
64
|
}
|
|
@@ -12,7 +12,7 @@ export class AgentListScreen {
|
|
|
12
12
|
this.container = new Container();
|
|
13
13
|
this.container.addChild(new Spacer(1));
|
|
14
14
|
this.container.addChild(new Text(truncateToWidth(theme.border("─".repeat(60)), 60), 0, 0));
|
|
15
|
-
this.container.addChild(new Text(theme.title("
|
|
15
|
+
this.container.addChild(new Text(theme.title(" Agents"), 1, 0));
|
|
16
16
|
this.container.addChild(new Text(colors.gray(" Each agent has its own instructions, model, discovery & limits."), 1, 0));
|
|
17
17
|
this.container.addChild(new Text(truncateToWidth(theme.border("─".repeat(60)), 60), 0, 0));
|
|
18
18
|
this.container.addChild(new Spacer(1));
|
|
@@ -20,14 +20,11 @@ export class AgentListScreen {
|
|
|
20
20
|
// Create new agent always at top
|
|
21
21
|
items.push({
|
|
22
22
|
value: "__create__",
|
|
23
|
-
label: "+ Create new
|
|
23
|
+
label: "+ Create new",
|
|
24
24
|
description: "",
|
|
25
25
|
});
|
|
26
26
|
// Existing agents
|
|
27
27
|
for (const agent of agents) {
|
|
28
|
-
const modelStr = agent.modelPattern
|
|
29
|
-
? colors.gray(` (${agent.modelPattern})`)
|
|
30
|
-
: "";
|
|
31
28
|
const instrPreview = agent.instructions.length > 0
|
|
32
29
|
? `${agent.instructions.slice(0, 50).replace(/\n/g, " ").trim()}…`
|
|
33
30
|
: "(no instructions)";
|
|
@@ -43,7 +40,7 @@ export class AgentListScreen {
|
|
|
43
40
|
selectedText: (t) => theme.accent(t),
|
|
44
41
|
description: (t) => colors.gray(t),
|
|
45
42
|
scrollInfo: (t) => colors.gray(t),
|
|
46
|
-
noMatch: (t) => colors.
|
|
43
|
+
noMatch: (t) => colors.gray(t),
|
|
47
44
|
});
|
|
48
45
|
this.selectList.onSelect = (item) => {
|
|
49
46
|
if (item.value === "__create__") {
|
|
@@ -25,7 +25,7 @@ export class AgentPickerScreen {
|
|
|
25
25
|
selectedText: (t) => theme.accent(t),
|
|
26
26
|
description: (t) => colors.gray(t),
|
|
27
27
|
scrollInfo: (t) => colors.gray(t),
|
|
28
|
-
noMatch: (t) => colors.
|
|
28
|
+
noMatch: (t) => colors.gray(t),
|
|
29
29
|
});
|
|
30
30
|
this.selectList.onSelect = (item) => {
|
|
31
31
|
const agent = agents.find((a) => a.id === item.value);
|
|
@@ -45,9 +45,9 @@ export class AgentRunningScreen {
|
|
|
45
45
|
// Header
|
|
46
46
|
lines.push(theme.border("─".repeat(width)));
|
|
47
47
|
const modelInfo = `${this.model.provider}/${this.model.id}`;
|
|
48
|
-
lines.push(truncateToWidth(` ${theme.title("⚡ AGENT ONLINE")} ${colors.
|
|
48
|
+
lines.push(truncateToWidth(` ${theme.title("⚡ AGENT ONLINE")} ${colors.lime(this.agentName)} ${colors.gray("model:")} ${colors.lime(modelInfo)}`, width));
|
|
49
49
|
if (this.sessionGoal) {
|
|
50
|
-
lines.push(truncateToWidth(` ${colors.gray("goal:")} ${colors.
|
|
50
|
+
lines.push(truncateToWidth(` ${colors.gray("goal:")} ${colors.lime(this.sessionGoal)}`, width));
|
|
51
51
|
}
|
|
52
52
|
lines.push(theme.border("─".repeat(width)));
|
|
53
53
|
lines.push("");
|
package/dist/screens/banner.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Container, Text, Spacer } from "@mariozechner/pi-tui";
|
|
2
2
|
import { colors } from "../theme.js";
|
|
3
|
-
// "LET THE SWARM BEGIN" — figlet Mini font, 70 chars wide, single horizontal block
|
|
4
3
|
const BANNER_ART = [
|
|
5
|
-
`
|
|
6
|
-
` | |
|
|
7
|
-
` |
|
|
4
|
+
` _______ ________ _______ ______ _______ _____ _______ _______ ______ _______ ______`,
|
|
5
|
+
` | __| | | | _ | __ \\ | | |_| _ | | | | ___| __ \\`,
|
|
6
|
+
` |__ | | | | | < | | | | ---| ___| <`,
|
|
7
|
+
` |_______|________|___|___|___|__|__|_|__|_______|___|___|__|____|______|_______|___|__|`,
|
|
8
8
|
];
|
|
9
9
|
export class BannerComponent extends Container {
|
|
10
10
|
constructor() {
|
|
@@ -15,9 +15,10 @@ export class BannerComponent extends Container {
|
|
|
15
15
|
this.clear();
|
|
16
16
|
this.addChild(new Spacer(1));
|
|
17
17
|
for (const line of BANNER_ART) {
|
|
18
|
-
this.addChild(new Text(colors.
|
|
18
|
+
this.addChild(new Text(colors.limeBold(line), 1, 0));
|
|
19
19
|
}
|
|
20
|
-
this.addChild(new
|
|
20
|
+
this.addChild(new Spacer(0));
|
|
21
|
+
this.addChild(new Text(colors.gray(" Let the Swarm Begin"), 1, 0));
|
|
21
22
|
this.addChild(new Spacer(1));
|
|
22
23
|
}
|
|
23
24
|
invalidate() {
|
|
@@ -3,9 +3,9 @@ import { colors, theme } from "../theme.js";
|
|
|
3
3
|
import { StatusPanel } from "./status-panel.js";
|
|
4
4
|
import { BannerComponent } from "./banner.js";
|
|
5
5
|
const MENU_ITEMS = [
|
|
6
|
-
{ value: "start", label: "Start
|
|
7
|
-
{ value: "agents", label: "
|
|
8
|
-
{ value: "quit", label: "
|
|
6
|
+
{ value: "start", label: "Start", description: "Pick an agent → set goal → discover → connect" },
|
|
7
|
+
{ value: "agents", label: "Agents", description: "Create, edit, or delete agents" },
|
|
8
|
+
{ value: "quit", label: "Exit", description: "" },
|
|
9
9
|
];
|
|
10
10
|
export class DashboardScreen {
|
|
11
11
|
container;
|
|
@@ -22,15 +22,12 @@ export class DashboardScreen {
|
|
|
22
22
|
this.statusPanel = new StatusPanel(status);
|
|
23
23
|
this.container.addChild(this.statusPanel);
|
|
24
24
|
this.container.addChild(new Spacer(1));
|
|
25
|
-
// Menu
|
|
26
|
-
this.container.addChild(new Text(colors.bold(" What do you want to do?"), 1, 0));
|
|
27
|
-
this.container.addChild(new Spacer(1));
|
|
28
25
|
this.selectList = new SelectList(MENU_ITEMS, MENU_ITEMS.length, {
|
|
29
26
|
selectedPrefix: (t) => theme.accent(t),
|
|
30
27
|
selectedText: (t) => theme.accent(t),
|
|
31
28
|
description: (t) => colors.gray(t),
|
|
32
29
|
scrollInfo: (t) => colors.gray(t),
|
|
33
|
-
noMatch: (t) => colors.
|
|
30
|
+
noMatch: (t) => colors.gray(t),
|
|
34
31
|
});
|
|
35
32
|
this.selectList.onSelect = (item) => {
|
|
36
33
|
this.onAction?.(item.value);
|
|
@@ -146,14 +146,14 @@ export class DiscoverySettingsScreen {
|
|
|
146
146
|
const pct = ((value - (f.min ?? 0)) / ((f.max ?? 100) - (f.min ?? 0)));
|
|
147
147
|
const barW = 16;
|
|
148
148
|
const filled = Math.round(pct * barW);
|
|
149
|
-
const bar = colors.
|
|
149
|
+
const bar = colors.lime("█".repeat(filled)) + colors.gray("░".repeat(barW - filled));
|
|
150
150
|
const valStr = `${value}${f.suffix ?? ""}`;
|
|
151
151
|
lines.push(truncateToWidth(`${prefix}${label}`, width));
|
|
152
|
-
lines.push(truncateToWidth(` ${colors.gray("◀")} ${bar} ${colors.gray("▶")} ${isActive ? colors.
|
|
152
|
+
lines.push(truncateToWidth(` ${colors.gray("◀")} ${bar} ${colors.gray("▶")} ${isActive ? colors.lime(valStr) : colors.gray(valStr)}`, width));
|
|
153
153
|
}
|
|
154
154
|
else if (f.type === "boolean") {
|
|
155
155
|
const value = this.settings[f.key];
|
|
156
|
-
const valStr = value ? colors.
|
|
156
|
+
const valStr = value ? colors.lime("ON") : colors.gray("OFF");
|
|
157
157
|
lines.push(truncateToWidth(`${prefix}${label} ${colors.gray("◀")} ${valStr} ${colors.gray("▶")}`, width));
|
|
158
158
|
}
|
|
159
159
|
else if (f.type === "keywords") {
|
|
@@ -8,7 +8,7 @@ export declare class MessageScreen implements Component {
|
|
|
8
8
|
private container;
|
|
9
9
|
private tui;
|
|
10
10
|
onClose?: () => void;
|
|
11
|
-
constructor(tui: TUI, title: string, lines: string[],
|
|
11
|
+
constructor(tui: TUI, title: string, lines: string[], _style?: "info" | "error" | "success");
|
|
12
12
|
handleInput(_data: string): void;
|
|
13
13
|
render(width: number): string[];
|
|
14
14
|
invalidate(): void;
|
package/dist/screens/message.js
CHANGED
|
@@ -8,17 +8,12 @@ export class MessageScreen {
|
|
|
8
8
|
container;
|
|
9
9
|
tui;
|
|
10
10
|
onClose;
|
|
11
|
-
constructor(tui, title, lines,
|
|
11
|
+
constructor(tui, title, lines, _style = "info") {
|
|
12
12
|
this.tui = tui;
|
|
13
13
|
this.container = new Container();
|
|
14
|
-
const colorFn = style === "error"
|
|
15
|
-
? colors.red
|
|
16
|
-
: style === "success"
|
|
17
|
-
? colors.green
|
|
18
|
-
: colors.cyan;
|
|
19
14
|
this.container.addChild(new Spacer(1));
|
|
20
15
|
this.container.addChild(new Text(theme.border("─".repeat(50)), 0, 0));
|
|
21
|
-
this.container.addChild(new Text(
|
|
16
|
+
this.container.addChild(new Text(colors.bold(colors.lime(` ${title}`)), 1, 0));
|
|
22
17
|
this.container.addChild(new Spacer(1));
|
|
23
18
|
for (const line of lines) {
|
|
24
19
|
this.container.addChild(new Text(` ${line}`, 0, 0));
|
|
@@ -23,7 +23,7 @@ export class ModelPickerScreen {
|
|
|
23
23
|
selectedText: (t) => theme.accent(t),
|
|
24
24
|
description: (t) => colors.gray(t),
|
|
25
25
|
scrollInfo: (t) => colors.gray(t),
|
|
26
|
-
noMatch: (t) => colors.
|
|
26
|
+
noMatch: (t) => colors.gray(t),
|
|
27
27
|
});
|
|
28
28
|
this.selectList.onSelect = (item) => {
|
|
29
29
|
const model = models.find((m) => m.id === item.value);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Editor, matchesKey, Key, truncateToWidth, } from "@mariozechner/pi-tui";
|
|
2
2
|
import { colors, theme } from "../theme.js";
|
|
3
3
|
const EDITOR_THEME = {
|
|
4
|
-
borderColor: (s) => colors.
|
|
4
|
+
borderColor: (s) => colors.lime(s),
|
|
5
5
|
selectList: {
|
|
6
|
-
selectedPrefix: (t) => colors.
|
|
7
|
-
selectedText: (t) => colors.
|
|
6
|
+
selectedPrefix: (t) => colors.lime(t),
|
|
7
|
+
selectedText: (t) => colors.lime(t),
|
|
8
8
|
description: (t) => colors.gray(t),
|
|
9
9
|
scrollInfo: (t) => colors.gray(t),
|
|
10
|
-
noMatch: (t) => colors.
|
|
10
|
+
noMatch: (t) => colors.gray(t),
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
13
|
export class NameEditorScreen {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Editor, matchesKey, Key, truncateToWidth, } from "@mariozechner/pi-tui";
|
|
2
2
|
import { colors, theme } from "../theme.js";
|
|
3
3
|
const EDITOR_THEME = {
|
|
4
|
-
borderColor: (s) => colors.
|
|
4
|
+
borderColor: (s) => colors.lime(s),
|
|
5
5
|
selectList: {
|
|
6
|
-
selectedPrefix: (t) => colors.
|
|
7
|
-
selectedText: (t) => colors.
|
|
6
|
+
selectedPrefix: (t) => colors.lime(t),
|
|
7
|
+
selectedText: (t) => colors.lime(t),
|
|
8
8
|
description: (t) => colors.gray(t),
|
|
9
9
|
scrollInfo: (t) => colors.gray(t),
|
|
10
|
-
noMatch: (t) => colors.
|
|
10
|
+
noMatch: (t) => colors.gray(t),
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
13
|
export class SessionGoalScreen {
|
package/dist/screens/settings.js
CHANGED
|
@@ -107,9 +107,9 @@ export class SettingsScreen {
|
|
|
107
107
|
const pct = (value - s.min) / (s.max - s.min);
|
|
108
108
|
const barWidth = 20;
|
|
109
109
|
const filled = Math.round(pct * barWidth);
|
|
110
|
-
const bar = colors.
|
|
110
|
+
const bar = colors.lime("█".repeat(filled)) + colors.gray("░".repeat(barWidth - filled));
|
|
111
111
|
lines.push(truncateToWidth(`${prefix}${label}`, width));
|
|
112
|
-
lines.push(truncateToWidth(` ${colors.gray("◀")} ${bar} ${colors.gray("▶")} ${isActive ? colors.
|
|
112
|
+
lines.push(truncateToWidth(` ${colors.gray("◀")} ${bar} ${colors.gray("▶")} ${isActive ? colors.lime(valueStr) : colors.gray(valueStr)}`, width));
|
|
113
113
|
if (isActive) {
|
|
114
114
|
lines.push(truncateToWidth(` ${colors.gray(s.description)}`, width));
|
|
115
115
|
}
|
|
@@ -39,7 +39,7 @@ export class SetupWizardScreen {
|
|
|
39
39
|
selectedText: (t) => theme.accent(t),
|
|
40
40
|
description: (t) => colors.gray(t),
|
|
41
41
|
scrollInfo: (t) => colors.gray(t),
|
|
42
|
-
noMatch: (t) => colors.
|
|
42
|
+
noMatch: (t) => colors.gray(t),
|
|
43
43
|
});
|
|
44
44
|
this.promptList.onSelect = (item) => {
|
|
45
45
|
this.promptActive = false;
|
|
@@ -81,20 +81,20 @@ export class SetupWizardScreen {
|
|
|
81
81
|
line = colors.gray(step.label);
|
|
82
82
|
break;
|
|
83
83
|
case "running":
|
|
84
|
-
icon = colors.
|
|
85
|
-
line = colors.
|
|
84
|
+
icon = colors.lime("◉");
|
|
85
|
+
line = colors.lime(step.label + "...");
|
|
86
86
|
break;
|
|
87
87
|
case "done":
|
|
88
|
-
icon = colors.
|
|
89
|
-
line = colors.
|
|
88
|
+
icon = colors.lime("✓");
|
|
89
|
+
line = colors.lime(step.label);
|
|
90
90
|
break;
|
|
91
91
|
case "failed":
|
|
92
|
-
icon = colors.
|
|
93
|
-
line = colors.
|
|
92
|
+
icon = colors.lime("✗");
|
|
93
|
+
line = colors.lime(step.label);
|
|
94
94
|
break;
|
|
95
95
|
case "skipped":
|
|
96
|
-
icon = colors.
|
|
97
|
-
line = colors.
|
|
96
|
+
icon = colors.gray("⊘");
|
|
97
|
+
line = colors.gray(step.label);
|
|
98
98
|
break;
|
|
99
99
|
}
|
|
100
100
|
lines.push(truncateToWidth(` ${icon} ${line}`, width));
|
|
@@ -14,7 +14,7 @@ export class StatusPanel extends Container {
|
|
|
14
14
|
rebuild() {
|
|
15
15
|
this.clear();
|
|
16
16
|
const line = (label, value, ok) => {
|
|
17
|
-
const icon = ok ? colors.
|
|
17
|
+
const icon = ok ? colors.lime("✓") : colors.gray("✗");
|
|
18
18
|
const labelStr = colors.gray(label.padEnd(10));
|
|
19
19
|
return ` ${icon} ${labelStr} ${value}`;
|
|
20
20
|
};
|
|
@@ -24,7 +24,7 @@ export class StatusPanel extends Container {
|
|
|
24
24
|
? `${this.info.model.provider}/${this.info.model.id}`
|
|
25
25
|
: "(not detected)", !!this.info.model), 0, 0));
|
|
26
26
|
const agentStr = this.info.agentCount === 0
|
|
27
|
-
? "none — create one in
|
|
27
|
+
? "none — create one in Agents"
|
|
28
28
|
: `${this.info.agentCount} agent${this.info.agentCount === 1 ? "" : "s"}`;
|
|
29
29
|
this.addChild(new Text(line("Agents", agentStr, this.info.agentCount > 0), 0, 0));
|
|
30
30
|
this.addChild(new Text(line("Server", this.info.serverUrl, true), 0, 0));
|
package/dist/theme.d.ts
CHANGED
|
@@ -3,24 +3,13 @@ export declare const colors: {
|
|
|
3
3
|
bold: (s: string) => string;
|
|
4
4
|
dim: (s: string) => string;
|
|
5
5
|
italic: (s: string) => string;
|
|
6
|
-
cyan: (s: string) => string;
|
|
7
|
-
green: (s: string) => string;
|
|
8
|
-
red: (s: string) => string;
|
|
9
|
-
yellow: (s: string) => string;
|
|
10
|
-
magenta: (s: string) => string;
|
|
11
|
-
blue: (s: string) => string;
|
|
12
6
|
white: (s: string) => string;
|
|
13
7
|
gray: (s: string) => string;
|
|
14
|
-
brightCyan: (s: string) => string;
|
|
15
|
-
brightGreen: (s: string) => string;
|
|
16
|
-
brightYellow: (s: string) => string;
|
|
17
8
|
brightWhite: (s: string) => string;
|
|
18
9
|
lime: (s: string) => string;
|
|
19
10
|
limeBold: (s: string) => string;
|
|
20
11
|
bgLime: (s: string) => string;
|
|
21
12
|
bgGray: (s: string) => string;
|
|
22
|
-
bgCyan: (s: string) => string;
|
|
23
|
-
bgBlue: (s: string) => string;
|
|
24
13
|
inverse: (s: string) => string;
|
|
25
14
|
};
|
|
26
15
|
export declare const theme: {
|
package/dist/theme.js
CHANGED
|
@@ -11,46 +11,34 @@ export const colors = {
|
|
|
11
11
|
bold: (s) => `${ESC}1m${s}${ESC}22m`,
|
|
12
12
|
dim: (s) => `${ESC}2m${s}${ESC}22m`,
|
|
13
13
|
italic: (s) => `${ESC}3m${s}${ESC}23m`,
|
|
14
|
-
// Named colors
|
|
15
|
-
cyan: (s) => `${ESC}36m${s}${ESC}39m`,
|
|
16
|
-
green: (s) => `${ESC}32m${s}${ESC}39m`,
|
|
17
|
-
red: (s) => `${ESC}31m${s}${ESC}39m`,
|
|
18
|
-
yellow: (s) => `${ESC}33m${s}${ESC}39m`,
|
|
19
|
-
magenta: (s) => `${ESC}35m${s}${ESC}39m`,
|
|
20
|
-
blue: (s) => `${ESC}34m${s}${ESC}39m`,
|
|
14
|
+
// Named colors — kept for internal use but UI uses only lime
|
|
21
15
|
white: (s) => `${ESC}37m${s}${ESC}39m`,
|
|
22
16
|
gray: (s) => `${ESC}90m${s}${ESC}39m`,
|
|
23
|
-
// Bright
|
|
24
|
-
brightCyan: (s) => `${ESC}96m${s}${ESC}39m`,
|
|
25
|
-
brightGreen: (s) => `${ESC}92m${s}${ESC}39m`,
|
|
26
|
-
brightYellow: (s) => `${ESC}93m${s}${ESC}39m`,
|
|
27
17
|
brightWhite: (s) => `${ESC}97m${s}${ESC}39m`,
|
|
28
|
-
// Logo lime green — #C8F135
|
|
18
|
+
// Logo lime green — #C8F135 — THE accent color
|
|
29
19
|
lime: (s) => `${LIME_FG}${s}${ESC}39m`,
|
|
30
20
|
limeBold: (s) => `${LIME_FG}${ESC}1m${s}${ESC}22m${ESC}39m`,
|
|
31
21
|
bgLime: (s) => `${LIME_BG}${DARK_FG}${s}${RESET}`,
|
|
32
22
|
// Background
|
|
33
23
|
bgGray: (s) => `${ESC}48;5;236m${s}${ESC}49m`,
|
|
34
|
-
bgCyan: (s) => `${ESC}46m${s}${ESC}49m`,
|
|
35
|
-
bgBlue: (s) => `${ESC}44m${s}${ESC}49m`,
|
|
36
24
|
// Inverse
|
|
37
25
|
inverse: (s) => `${ESC}7m${s}${ESC}27m`,
|
|
38
26
|
};
|
|
39
|
-
// Semantic aliases —
|
|
27
|
+
// Semantic aliases — everything colorful is lime
|
|
40
28
|
export const theme = {
|
|
41
29
|
accent: colors.lime,
|
|
42
|
-
success: colors.
|
|
43
|
-
error: colors.
|
|
44
|
-
warning: colors.
|
|
30
|
+
success: colors.lime,
|
|
31
|
+
error: colors.lime,
|
|
32
|
+
warning: colors.lime,
|
|
45
33
|
muted: colors.gray,
|
|
46
34
|
highlight: colors.limeBold,
|
|
47
35
|
border: colors.lime,
|
|
48
36
|
title: (s) => colors.bold(colors.lime(s)),
|
|
49
37
|
subtitle: (s) => colors.bold(colors.white(s)),
|
|
50
38
|
status: {
|
|
51
|
-
ok: (s) => colors.
|
|
52
|
-
fail: (s) => colors.
|
|
53
|
-
warn: (s) => colors.
|
|
39
|
+
ok: (s) => colors.lime(`✓ ${s}`),
|
|
40
|
+
fail: (s) => colors.lime(`✗ ${s}`),
|
|
41
|
+
warn: (s) => colors.lime(`⚠ ${s}`),
|
|
54
42
|
info: (s) => colors.lime(`▸ ${s}`),
|
|
55
43
|
},
|
|
56
44
|
};
|