rax-flow 0.1.6 → 0.1.8
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/bin.js +3 -2
- package/dist/bin.js.map +1 -1
- package/dist/tui/App.d.ts +2 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +63 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/components/ChatPanel.d.ts +15 -0
- package/dist/tui/components/ChatPanel.d.ts.map +1 -0
- package/dist/tui/components/ChatPanel.js +31 -0
- package/dist/tui/components/ChatPanel.js.map +1 -0
- package/dist/tui/components/DAGPanel.d.ts +24 -0
- package/dist/tui/components/DAGPanel.d.ts.map +1 -0
- package/dist/tui/components/DAGPanel.js +30 -0
- package/dist/tui/components/DAGPanel.js.map +1 -0
- package/dist/tui/components/Header.d.ts +11 -0
- package/dist/tui/components/Header.d.ts.map +1 -0
- package/dist/tui/components/Header.js +25 -0
- package/dist/tui/components/Header.js.map +1 -0
- package/dist/tui/components/HelpOverlay.d.ts +6 -0
- package/dist/tui/components/HelpOverlay.d.ts.map +1 -0
- package/dist/tui/components/HelpOverlay.js +42 -0
- package/dist/tui/components/HelpOverlay.js.map +1 -0
- package/dist/tui/components/InputBar.d.ts +8 -0
- package/dist/tui/components/InputBar.d.ts.map +1 -0
- package/dist/tui/components/InputBar.js +16 -0
- package/dist/tui/components/InputBar.js.map +1 -0
- package/dist/tui/components/StatusPanel.d.ts +21 -0
- package/dist/tui/components/StatusPanel.d.ts.map +1 -0
- package/dist/tui/components/StatusPanel.js +21 -0
- package/dist/tui/components/StatusPanel.js.map +1 -0
- package/dist/tui/hooks/useAppState.d.ts +55 -0
- package/dist/tui/hooks/useAppState.d.ts.map +1 -0
- package/dist/tui/hooks/useAppState.js +190 -0
- package/dist/tui/hooks/useAppState.js.map +1 -0
- package/dist/tui/index.d.ts +3 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +8 -0
- package/dist/tui/index.js.map +1 -0
- package/package.json +7 -1
- package/src/bin.ts +3 -2
- package/src/tui/App.tsx +113 -0
- package/src/tui/components/ChatPanel.tsx +83 -0
- package/src/tui/components/DAGPanel.tsx +116 -0
- package/src/tui/components/Header.tsx +76 -0
- package/src/tui/components/HelpOverlay.tsx +83 -0
- package/src/tui/components/InputBar.tsx +55 -0
- package/src/tui/components/StatusPanel.tsx +90 -0
- package/src/tui/hooks/useAppState.ts +264 -0
- package/src/tui/index.ts +9 -0
- package/tsconfig.json +0 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
|
|
4
|
+
interface HelpOverlayProps {
|
|
5
|
+
onDismiss: () => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function HelpOverlay({ onDismiss }: HelpOverlayProps) {
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (key.escape || input === "q") {
|
|
11
|
+
onDismiss();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const sections = [
|
|
16
|
+
{
|
|
17
|
+
title: "COMMANDES",
|
|
18
|
+
items: [
|
|
19
|
+
["/run <prompt>", "Exécuter un workflow"],
|
|
20
|
+
["/status", "État du système"],
|
|
21
|
+
["/agents", "Liste des agents"],
|
|
22
|
+
["/providers", "Liste des providers"],
|
|
23
|
+
["/workflows", "Blueprints disponibles"],
|
|
24
|
+
["/help, ?", "Cette aide"],
|
|
25
|
+
["/exit", "Quitter"],
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: "NAVIGATION",
|
|
30
|
+
items: [
|
|
31
|
+
["Tab", "Changer de panel actif"],
|
|
32
|
+
["F1-F8", "Aller au panel spécifique"],
|
|
33
|
+
["Ctrl+C", "Quitter"],
|
|
34
|
+
["Esc", "Fermer l'aide"],
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: "PANELS",
|
|
39
|
+
items: [
|
|
40
|
+
["CHAT", "Messages et interaction"],
|
|
41
|
+
["DAG", "Visualisation du workflow"],
|
|
42
|
+
["STATUS", "Agents et providers"],
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Box
|
|
49
|
+
flexDirection="column"
|
|
50
|
+
flexGrow={1}
|
|
51
|
+
borderStyle="double"
|
|
52
|
+
borderColor="#f97316"
|
|
53
|
+
paddingX={2}
|
|
54
|
+
>
|
|
55
|
+
<Box marginBottom={1}>
|
|
56
|
+
<Text color="#f97316" bold>■ RAX-FLOW HELP</Text>
|
|
57
|
+
<Text color="gray"> — [Esc] pour fermer</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
|
|
60
|
+
<Box flexDirection="row" flexGrow={1}>
|
|
61
|
+
{sections.map((section) => (
|
|
62
|
+
<Box key={section.title} flexDirection="column" width={30}>
|
|
63
|
+
<Text color="gray" bold underline>
|
|
64
|
+
{section.title}
|
|
65
|
+
</Text>
|
|
66
|
+
<Box flexDirection="column" marginTop={1}>
|
|
67
|
+
{section.items.map(([cmd, desc]) => (
|
|
68
|
+
<Box key={cmd} flexDirection="row" marginBottom={1}>
|
|
69
|
+
<Text color="cyan">{cmd.padEnd(16)}</Text>
|
|
70
|
+
<Text color="white">{desc}</Text>
|
|
71
|
+
</Box>
|
|
72
|
+
))}
|
|
73
|
+
</Box>
|
|
74
|
+
</Box>
|
|
75
|
+
))}
|
|
76
|
+
</Box>
|
|
77
|
+
|
|
78
|
+
<Box borderStyle="single" borderColor="gray" marginTop={1}>
|
|
79
|
+
<Text color="gray">Pour exécuter un workflow, tapez simplement votre prompt ou /run "..."</Text>
|
|
80
|
+
</Box>
|
|
81
|
+
</Box>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import TextInput from "ink-text-input";
|
|
4
|
+
|
|
5
|
+
interface InputBarProps {
|
|
6
|
+
onSubmit: (input: string) => void;
|
|
7
|
+
suggestions: string[];
|
|
8
|
+
isProcessing: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function InputBar({ onSubmit, suggestions, isProcessing }: InputBarProps) {
|
|
12
|
+
const [input, setInput] = useState("");
|
|
13
|
+
|
|
14
|
+
const handleSubmit = (value: string) => {
|
|
15
|
+
if (value.trim()) {
|
|
16
|
+
onSubmit(value);
|
|
17
|
+
setInput("");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const filteredSuggestions = suggestions.filter((s) =>
|
|
22
|
+
s.toLowerCase().startsWith(input.toLowerCase())
|
|
23
|
+
).slice(0, 5);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Box flexDirection="column" borderStyle="single" borderColor="gray" paddingX={1}>
|
|
27
|
+
{input.startsWith("/") && filteredSuggestions.length > 0 && (
|
|
28
|
+
<Box flexDirection="row" marginBottom={1}>
|
|
29
|
+
{filteredSuggestions.map((s) => (
|
|
30
|
+
<Box key={s} marginRight={2}>
|
|
31
|
+
<Text color="cyan">{s}</Text>
|
|
32
|
+
</Box>
|
|
33
|
+
))}
|
|
34
|
+
</Box>
|
|
35
|
+
)}
|
|
36
|
+
<Box flexDirection="row" alignItems="center">
|
|
37
|
+
<Text color="#f97316" bold>▶</Text>
|
|
38
|
+
<Box marginLeft={1} flexGrow={1}>
|
|
39
|
+
<TextInput
|
|
40
|
+
value={input}
|
|
41
|
+
onChange={setInput}
|
|
42
|
+
onSubmit={handleSubmit}
|
|
43
|
+
placeholder="Tapez /help ou votre prompt..."
|
|
44
|
+
showCursor={true}
|
|
45
|
+
/>
|
|
46
|
+
</Box>
|
|
47
|
+
{isProcessing && <Text color="yellow"> ●</Text>}
|
|
48
|
+
</Box>
|
|
49
|
+
<Box flexDirection="row" marginTop={1}>
|
|
50
|
+
<Text color="gray" dimColor>[Ctrl+C] Quitter</Text>
|
|
51
|
+
<Text color="gray" dimColor> │ [/cmd] Commandes</Text>
|
|
52
|
+
</Box>
|
|
53
|
+
</Box>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
|
|
4
|
+
interface Agent {
|
|
5
|
+
name: string;
|
|
6
|
+
role: string;
|
|
7
|
+
status: "idle" | "running" | "queued" | "done";
|
|
8
|
+
provider: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Provider {
|
|
12
|
+
name: string;
|
|
13
|
+
status: "active" | "idle";
|
|
14
|
+
latency: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface StatusPanelProps {
|
|
18
|
+
agents: Agent[];
|
|
19
|
+
providers: Provider[];
|
|
20
|
+
fitness: number;
|
|
21
|
+
currentWorkflow: string | null;
|
|
22
|
+
isActive: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const statusIcons: Record<string, { icon: string; color: string }> = {
|
|
26
|
+
idle: { icon: "○", color: "gray" },
|
|
27
|
+
running: { icon: "▶", color: "yellow" },
|
|
28
|
+
queued: { icon: "◐", color: "cyan" },
|
|
29
|
+
done: { icon: "●", color: "green" },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function AgentRow({ agent }: { agent: Agent }) {
|
|
33
|
+
const { icon, color } = statusIcons[agent.status];
|
|
34
|
+
return (
|
|
35
|
+
<Box flexDirection="row" justifyContent="space-between">
|
|
36
|
+
<Text color={color}>{icon}</Text>
|
|
37
|
+
<Text color="white">{agent.name.slice(0, 12).padEnd(12)}</Text>
|
|
38
|
+
<Text color="gray">[{agent.provider}]</Text>
|
|
39
|
+
</Box>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ProviderRow({ provider }: { provider: Provider }) {
|
|
44
|
+
return (
|
|
45
|
+
<Box flexDirection="row" justifyContent="space-between">
|
|
46
|
+
<Text color={provider.status === "active" ? "green" : "gray"}>
|
|
47
|
+
{provider.status === "active" ? "●" : "○"}
|
|
48
|
+
</Text>
|
|
49
|
+
<Text color="white">{provider.name}</Text>
|
|
50
|
+
<Text color="gray">{provider.latency > 0 ? `${provider.latency}ms` : "--"}</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function StatusPanel({ agents, providers, fitness, currentWorkflow, isActive }: StatusPanelProps) {
|
|
56
|
+
const fitnessColor = fitness >= 0.9 ? "green" : fitness >= 0.7 ? "yellow" : "red";
|
|
57
|
+
const borderColor = isActive ? "#f97316" : "gray";
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Box flexDirection="column" width={30} borderStyle="single" borderColor={borderColor}>
|
|
61
|
+
<Box borderStyle="single" borderColor="#f97316" marginBottom={1}>
|
|
62
|
+
<Text color="#f97316" bold>STATUS</Text>
|
|
63
|
+
{isActive && <Text color="yellow"> [ACTIF]</Text>}
|
|
64
|
+
</Box>
|
|
65
|
+
|
|
66
|
+
<Box flexDirection="column" paddingX={1}>
|
|
67
|
+
<Text color="gray" bold>WORKFLOW</Text>
|
|
68
|
+
<Text color="white">{currentWorkflow || "Aucun"}</Text>
|
|
69
|
+
<Box flexDirection="row">
|
|
70
|
+
<Text color="gray">Fitness: </Text>
|
|
71
|
+
<Text color={fitnessColor} bold>{fitness.toFixed(2)}</Text>
|
|
72
|
+
</Box>
|
|
73
|
+
</Box>
|
|
74
|
+
|
|
75
|
+
<Box flexDirection="column" paddingX={1} marginTop={1}>
|
|
76
|
+
<Text color="gray" bold>AGENTS</Text>
|
|
77
|
+
{agents.slice(0, 6).map((agent) => (
|
|
78
|
+
<AgentRow key={agent.name} agent={agent} />
|
|
79
|
+
))}
|
|
80
|
+
</Box>
|
|
81
|
+
|
|
82
|
+
<Box flexDirection="column" paddingX={1} marginTop={1}>
|
|
83
|
+
<Text color="gray" bold>PROVIDERS</Text>
|
|
84
|
+
{providers.map((provider) => (
|
|
85
|
+
<ProviderRow key={provider.name} provider={provider} />
|
|
86
|
+
))}
|
|
87
|
+
</Box>
|
|
88
|
+
</Box>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
interface Message {
|
|
4
|
+
id: string;
|
|
5
|
+
type: "user" | "system" | "agent" | "error" | "success";
|
|
6
|
+
content: string;
|
|
7
|
+
timestamp: Date;
|
|
8
|
+
agent?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Agent {
|
|
12
|
+
name: string;
|
|
13
|
+
role: string;
|
|
14
|
+
status: "idle" | "running" | "queued" | "done";
|
|
15
|
+
provider: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Provider {
|
|
19
|
+
name: string;
|
|
20
|
+
status: "active" | "idle";
|
|
21
|
+
latency: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface DAGNode {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
status: "pending" | "running" | "done" | "error";
|
|
28
|
+
agent?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface DAGLevel {
|
|
32
|
+
name: string;
|
|
33
|
+
progress: number;
|
|
34
|
+
nodes: DAGNode[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface WorkflowState {
|
|
38
|
+
levels: DAGLevel[];
|
|
39
|
+
currentLevel: number;
|
|
40
|
+
totalProgress: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface AppState {
|
|
44
|
+
projectName: string;
|
|
45
|
+
agentCount: number;
|
|
46
|
+
provider: string;
|
|
47
|
+
status: "ready" | "running" | "error";
|
|
48
|
+
messages: Message[];
|
|
49
|
+
agents: Agent[];
|
|
50
|
+
providers: Provider[];
|
|
51
|
+
fitness: number;
|
|
52
|
+
currentWorkflow: string | null;
|
|
53
|
+
suggestions: string[];
|
|
54
|
+
isProcessing: boolean;
|
|
55
|
+
workflowState: WorkflowState;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const COMMAND_SUGGESTIONS = [
|
|
59
|
+
"/run", "/status", "/agents", "/providers", "/workflows",
|
|
60
|
+
"/logs", "/metrics", "/memory", "/help", "/exit",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const DEFAULT_AGENTS: Agent[] = [
|
|
64
|
+
{ name: "IntentClassifier", role: "Classify intent", status: "idle", provider: "H" },
|
|
65
|
+
{ name: "TaskPlanner", role: "Decompose tasks", status: "idle", provider: "H" },
|
|
66
|
+
{ name: "SpecAgent", role: "Write specs", status: "idle", provider: "H" },
|
|
67
|
+
{ name: "CodeGenerator", role: "Generate code", status: "idle", provider: "H" },
|
|
68
|
+
{ name: "TestAgent", role: "Run tests", status: "idle", provider: "H" },
|
|
69
|
+
{ name: "FixAgent", role: "Fix issues", status: "idle", provider: "H" },
|
|
70
|
+
{ name: "MutationAgent", role: "Evolve workflow", status: "idle", provider: "H" },
|
|
71
|
+
{ name: "ValidatorAgent", role: "Validate outputs", status: "idle", provider: "H" },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const DEFAULT_PROVIDERS: Provider[] = [
|
|
75
|
+
{ name: "Host-Native", status: "active", latency: 12 },
|
|
76
|
+
{ name: "Claude Code", status: "active", latency: 8 },
|
|
77
|
+
{ name: "OpenCode", status: "idle", latency: 0 },
|
|
78
|
+
{ name: "Anthropic", status: "idle", latency: 0 },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const DEFAULT_WORKFLOW_STATE: WorkflowState = {
|
|
82
|
+
levels: [
|
|
83
|
+
{
|
|
84
|
+
name: "L1: SPEC",
|
|
85
|
+
progress: 0,
|
|
86
|
+
nodes: [
|
|
87
|
+
{ id: "intent", name: "IntentClassifier", status: "pending", agent: "H" },
|
|
88
|
+
{ id: "spec", name: "SpecAgent", status: "pending", agent: "H" },
|
|
89
|
+
{ id: "arch", name: "ArchitectureAgent", status: "pending", agent: "H" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "L2: CODE",
|
|
94
|
+
progress: 0,
|
|
95
|
+
nodes: [
|
|
96
|
+
{ id: "task", name: "TaskPlanner", status: "pending", agent: "H" },
|
|
97
|
+
{ id: "codegen", name: "CodeGenerator", status: "pending", agent: "H" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "L3: TEST",
|
|
102
|
+
progress: 0,
|
|
103
|
+
nodes: [
|
|
104
|
+
{ id: "test", name: "TestAgent", status: "pending", agent: "H" },
|
|
105
|
+
{ id: "fix", name: "FixAgent", status: "pending", agent: "H" },
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
currentLevel: 0,
|
|
110
|
+
totalProgress: 0,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function generateId(): string {
|
|
114
|
+
return Math.random().toString(36).slice(2, 9);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function useAppState() {
|
|
118
|
+
const [state, setState] = useState<AppState>({
|
|
119
|
+
projectName: process.cwd().split("/").pop() || "project",
|
|
120
|
+
agentCount: 12,
|
|
121
|
+
provider: "HOST-NATIVE",
|
|
122
|
+
status: "ready",
|
|
123
|
+
messages: [
|
|
124
|
+
{
|
|
125
|
+
id: generateId(),
|
|
126
|
+
type: "system",
|
|
127
|
+
content: "■ RAX-FLOW HUB prêt. Tapez votre prompt ou /help pour les commandes.",
|
|
128
|
+
timestamp: new Date(),
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
agents: DEFAULT_AGENTS,
|
|
132
|
+
providers: DEFAULT_PROVIDERS,
|
|
133
|
+
fitness: 0.87,
|
|
134
|
+
currentWorkflow: null,
|
|
135
|
+
suggestions: COMMAND_SUGGESTIONS,
|
|
136
|
+
isProcessing: false,
|
|
137
|
+
workflowState: DEFAULT_WORKFLOW_STATE,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const addMessage = useCallback((type: Message["type"], content: string, agent?: string) => {
|
|
141
|
+
setState((prev: AppState) => ({
|
|
142
|
+
...prev,
|
|
143
|
+
messages: [
|
|
144
|
+
...prev.messages,
|
|
145
|
+
{ id: generateId(), type, content, timestamp: new Date(), agent },
|
|
146
|
+
],
|
|
147
|
+
}));
|
|
148
|
+
}, []);
|
|
149
|
+
|
|
150
|
+
const updateWorkflowProgress = useCallback((level: number, nodeIndex: number, status: DAGNode["status"]) => {
|
|
151
|
+
setState((prev: AppState) => {
|
|
152
|
+
const newLevels = prev.workflowState.levels.map((l, i) => {
|
|
153
|
+
if (i !== level) return l;
|
|
154
|
+
const newNodes = l.nodes.map((n, j) =>
|
|
155
|
+
j === nodeIndex ? { ...n, status } : n
|
|
156
|
+
);
|
|
157
|
+
const doneCount = newNodes.filter((n) => n.status === "done").length;
|
|
158
|
+
return {
|
|
159
|
+
...l,
|
|
160
|
+
nodes: newNodes,
|
|
161
|
+
progress: Math.round((doneCount / newNodes.length) * 100),
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const totalDone = newLevels.reduce((acc, l) =>
|
|
166
|
+
acc + l.nodes.filter((n) => n.status === "done").length, 0
|
|
167
|
+
);
|
|
168
|
+
const totalNodes = newLevels.reduce((acc, l) => acc + l.nodes.length, 0);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
...prev,
|
|
172
|
+
workflowState: {
|
|
173
|
+
levels: newLevels,
|
|
174
|
+
currentLevel: level,
|
|
175
|
+
totalProgress: Math.round((totalDone / totalNodes) * 100),
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
181
|
+
const processCommand = useCallback((input: string) => {
|
|
182
|
+
if (input.startsWith("/")) {
|
|
183
|
+
const cmd = input.slice(1).toLowerCase();
|
|
184
|
+
|
|
185
|
+
switch (cmd) {
|
|
186
|
+
case "help":
|
|
187
|
+
addMessage("system", `Commandes: /run /status /agents /providers /workflows /logs /metrics /memory /exit`);
|
|
188
|
+
break;
|
|
189
|
+
case "status":
|
|
190
|
+
addMessage("system", `Orchestrator: ● ACTIVE | Fitness: ${state.fitness.toFixed(2)} | Agents: ${state.agents.filter((a: Agent) => a.status !== "idle").length}/8 actifs`);
|
|
191
|
+
break;
|
|
192
|
+
case "agents":
|
|
193
|
+
const agentList = state.agents.map((a: Agent) => `${a.status === "running" ? "▶" : "○"} ${a.name} [${a.provider}]`).join("\n");
|
|
194
|
+
addMessage("system", agentList);
|
|
195
|
+
break;
|
|
196
|
+
case "providers":
|
|
197
|
+
const providerList = state.providers.map((p: Provider) => `${p.status === "active" ? "●" : "○"} ${p.name} ${p.latency > 0 ? `(${p.latency}ms)` : ""}`).join("\n");
|
|
198
|
+
addMessage("system", providerList);
|
|
199
|
+
break;
|
|
200
|
+
case "exit":
|
|
201
|
+
case "quit":
|
|
202
|
+
addMessage("system", "À bientôt !");
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
if (cmd.startsWith("run ")) {
|
|
206
|
+
const prompt = cmd.slice(4);
|
|
207
|
+
runWorkflow(prompt);
|
|
208
|
+
} else {
|
|
209
|
+
addMessage("error", `Commande inconnue: /${cmd}. Tapez /help pour l'aide.`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
runWorkflow(input);
|
|
214
|
+
}
|
|
215
|
+
}, [state, addMessage]);
|
|
216
|
+
|
|
217
|
+
const runWorkflow = useCallback((prompt: string) => {
|
|
218
|
+
setState((prev: AppState) => ({ ...prev, status: "running", isProcessing: true }));
|
|
219
|
+
addMessage("user", prompt);
|
|
220
|
+
|
|
221
|
+
let step = 0;
|
|
222
|
+
const steps = [
|
|
223
|
+
{ level: 0, node: 0, msg: "Analyse de l'intent...", agent: "IntentClassifier" },
|
|
224
|
+
{ level: 0, node: 1, msg: "Génération des specs...", agent: "SpecAgent" },
|
|
225
|
+
{ level: 0, node: 2, msg: "Design de l'architecture...", agent: "ArchitectureAgent" },
|
|
226
|
+
{ level: 1, node: 0, msg: "Planification des tâches...", agent: "TaskPlanner" },
|
|
227
|
+
{ level: 1, node: 1, msg: "Génération du code...", agent: "CodeGenerator" },
|
|
228
|
+
{ level: 2, node: 0, msg: "Exécution des tests...", agent: "TestAgent" },
|
|
229
|
+
{ level: 2, node: 1, msg: "Application des fixes...", agent: "FixAgent" },
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
function executeStep() {
|
|
233
|
+
if (step >= steps.length) {
|
|
234
|
+
setState((prev: AppState) => ({
|
|
235
|
+
...prev,
|
|
236
|
+
status: "ready",
|
|
237
|
+
isProcessing: false,
|
|
238
|
+
fitness: Math.min(prev.fitness + 0.05, 0.99),
|
|
239
|
+
currentWorkflow: prompt,
|
|
240
|
+
}));
|
|
241
|
+
addMessage("success", `✓ Workflow terminé! Fitness: ${(state.fitness + 0.05).toFixed(2)}`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const s = steps[step];
|
|
246
|
+
updateWorkflowProgress(s.level, s.node, "running");
|
|
247
|
+
addMessage("agent", s.msg, s.agent);
|
|
248
|
+
|
|
249
|
+
setTimeout(() => {
|
|
250
|
+
updateWorkflowProgress(s.level, s.node, "done");
|
|
251
|
+
step++;
|
|
252
|
+
executeStep();
|
|
253
|
+
}, 500 + Math.random() * 500);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
executeStep();
|
|
257
|
+
}, [addMessage, updateWorkflowProgress, state.fitness]);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
state,
|
|
261
|
+
dispatch: setState,
|
|
262
|
+
processCommand,
|
|
263
|
+
};
|
|
264
|
+
}
|
package/src/tui/index.ts
ADDED