@agentforge-ai/cli 0.4.3 → 0.5.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.
Files changed (67) hide show
  1. package/dist/default/convex/agents.ts +204 -0
  2. package/dist/default/convex/apiKeys.ts +133 -0
  3. package/dist/default/convex/cronJobs.ts +224 -0
  4. package/dist/default/convex/files.ts +103 -0
  5. package/dist/default/convex/folders.ts +110 -0
  6. package/dist/default/convex/heartbeat.ts +371 -0
  7. package/dist/default/convex/logs.ts +66 -0
  8. package/dist/default/convex/mastraIntegration.ts +185 -0
  9. package/dist/default/convex/mcpConnections.ts +127 -0
  10. package/dist/default/convex/messages.ts +90 -0
  11. package/dist/default/convex/projects.ts +114 -0
  12. package/dist/default/convex/schema.ts +150 -83
  13. package/dist/default/convex/sessions.ts +174 -0
  14. package/dist/default/convex/settings.ts +79 -0
  15. package/dist/default/convex/skills.ts +178 -0
  16. package/dist/default/convex/threads.ts +100 -0
  17. package/dist/default/convex/usage.ts +195 -0
  18. package/dist/default/convex/vault.ts +397 -0
  19. package/dist/default/dashboard/app/main.tsx +7 -3
  20. package/dist/default/dashboard/app/routes/agents.tsx +103 -161
  21. package/dist/default/dashboard/app/routes/chat.tsx +163 -317
  22. package/dist/default/dashboard/app/routes/connections.tsx +247 -386
  23. package/dist/default/dashboard/app/routes/cron.tsx +127 -286
  24. package/dist/default/dashboard/app/routes/files.tsx +184 -167
  25. package/dist/default/dashboard/app/routes/index.tsx +63 -96
  26. package/dist/default/dashboard/app/routes/projects.tsx +106 -225
  27. package/dist/default/dashboard/app/routes/sessions.tsx +87 -253
  28. package/dist/default/dashboard/app/routes/settings.tsx +316 -532
  29. package/dist/default/dashboard/app/routes/skills.tsx +329 -216
  30. package/dist/default/dashboard/app/routes/usage.tsx +107 -150
  31. package/dist/default/dashboard/tsconfig.json +3 -2
  32. package/dist/default/dashboard/vite.config.ts +6 -0
  33. package/dist/index.js +256 -49
  34. package/dist/index.js.map +1 -1
  35. package/package.json +1 -1
  36. package/templates/default/convex/agents.ts +204 -0
  37. package/templates/default/convex/apiKeys.ts +133 -0
  38. package/templates/default/convex/cronJobs.ts +224 -0
  39. package/templates/default/convex/files.ts +103 -0
  40. package/templates/default/convex/folders.ts +110 -0
  41. package/templates/default/convex/heartbeat.ts +371 -0
  42. package/templates/default/convex/logs.ts +66 -0
  43. package/templates/default/convex/mastraIntegration.ts +185 -0
  44. package/templates/default/convex/mcpConnections.ts +127 -0
  45. package/templates/default/convex/messages.ts +90 -0
  46. package/templates/default/convex/projects.ts +114 -0
  47. package/templates/default/convex/schema.ts +150 -83
  48. package/templates/default/convex/sessions.ts +174 -0
  49. package/templates/default/convex/settings.ts +79 -0
  50. package/templates/default/convex/skills.ts +178 -0
  51. package/templates/default/convex/threads.ts +100 -0
  52. package/templates/default/convex/usage.ts +195 -0
  53. package/templates/default/convex/vault.ts +397 -0
  54. package/templates/default/dashboard/app/main.tsx +7 -3
  55. package/templates/default/dashboard/app/routes/agents.tsx +103 -161
  56. package/templates/default/dashboard/app/routes/chat.tsx +163 -317
  57. package/templates/default/dashboard/app/routes/connections.tsx +247 -386
  58. package/templates/default/dashboard/app/routes/cron.tsx +127 -286
  59. package/templates/default/dashboard/app/routes/files.tsx +184 -167
  60. package/templates/default/dashboard/app/routes/index.tsx +63 -96
  61. package/templates/default/dashboard/app/routes/projects.tsx +106 -225
  62. package/templates/default/dashboard/app/routes/sessions.tsx +87 -253
  63. package/templates/default/dashboard/app/routes/settings.tsx +316 -532
  64. package/templates/default/dashboard/app/routes/skills.tsx +329 -216
  65. package/templates/default/dashboard/app/routes/usage.tsx +107 -150
  66. package/templates/default/dashboard/tsconfig.json +3 -2
  67. package/templates/default/dashboard/vite.config.ts +6 -0
@@ -1,207 +1,149 @@
1
-
2
1
  import { createFileRoute } from '@tanstack/react-router';
3
2
  import { DashboardLayout } from '../components/DashboardLayout';
4
3
  import { useState, useMemo } from 'react';
5
- import { Bot, Plus, Edit, Trash2, Search, Settings, Zap, X, ChevronDown, Star } from 'lucide-react';
6
-
7
- // MOCK DATA (to be replaced by Convex)
8
- const initialAgents = [
9
- {
10
- id: 'agent-1',
11
- name: 'Research Assistant',
12
- description: 'An agent specialized in gathering and summarizing information from the web.',
13
- instructions: 'You are a research assistant. Your goal is to find the most relevant and up-to-date information on any given topic. Use search tools and summarize your findings clearly.',
14
- model: 'gpt-4.1-mini',
15
- provider: 'OpenAI',
16
- temperature: 0.7,
17
- maxTokens: 4096,
18
- status: 'active',
19
- },
20
- {
21
- id: 'agent-2',
22
- name: 'Code Generator',
23
- description: 'Generates code in various programming languages based on user requirements.',
24
- instructions: 'You are a senior software engineer. Write clean, efficient, and well-documented code. Always ask for clarification if the requirements are ambiguous.',
25
- model: 'gemini-2.5-flash',
26
- provider: 'Google',
27
- temperature: 0.5,
28
- maxTokens: 8192,
29
- status: 'inactive',
30
- },
31
- {
32
- id: 'agent-3',
33
- name: 'Creative Writer',
34
- description: 'A creative partner for brainstorming and writing stories, scripts, and more.',
35
- instructions: 'You are a creative writer. Help users brainstorm ideas, develop characters, and write compelling narratives. Be imaginative and inspiring.',
36
- model: 'grok-4',
37
- provider: 'xAI',
38
- temperature: 0.9,
39
- maxTokens: 2048,
40
- status: 'active',
41
- },
42
- ];
43
-
44
- // CONVEX PLACEHOLDERS
45
- // import { useQuery, useMutation } from 'convex/react';
46
- // import { api } from '../../convex/_generated/api';
4
+ import { useQuery, useMutation } from 'convex/react';
5
+ import { api } from '@convex/_generated/api';
6
+ import { Bot, Plus, Edit, Trash2, Search, X, Star } from 'lucide-react';
47
7
 
48
8
  export const Route = createFileRoute('/agents')({ component: AgentsPage });
49
9
 
10
+ const providers = ['openai', 'anthropic', 'openrouter', 'google', 'xai'];
11
+ const modelsByProvider: Record<string, string[]> = {
12
+ openai: ['gpt-4.1-mini', 'gpt-4o', 'gpt-4o-mini'],
13
+ google: ['gemini-2.5-flash', 'gemini-1.5-pro'],
14
+ anthropic: ['claude-3.5-sonnet', 'claude-3-haiku'],
15
+ openrouter: ['openrouter/auto'],
16
+ xai: ['grok-4', 'grok-3'],
17
+ };
18
+
50
19
  function AgentsPage() {
51
- // const agents = useQuery(api.agents.list) || [];
52
- // const createAgent = useMutation(api.agents.create);
53
- // const updateAgent = useMutation(api.agents.update);
54
- // const deleteAgent = useMutation(api.agents.delete);
20
+ const agents = useQuery(api.agents.list, {}) ?? [];
21
+ const createAgent = useMutation(api.agents.create);
22
+ const updateAgent = useMutation(api.agents.update);
23
+ const removeAgent = useMutation(api.agents.remove);
24
+ const toggleActive = useMutation(api.agents.toggleActive);
55
25
 
56
- const [agents, setAgents] = useState(initialAgents);
57
26
  const [searchQuery, setSearchQuery] = useState('');
58
27
  const [isModalOpen, setIsModalOpen] = useState(false);
59
- const [editingAgent, setEditingAgent] = useState(null);
60
-
61
- const filteredAgents = useMemo(() =>
62
- agents.filter(agent =>
63
- agent.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
64
- agent.description.toLowerCase().includes(searchQuery.toLowerCase())
65
- ),
66
- [agents, searchQuery]
67
- );
28
+ const [editingAgent, setEditingAgent] = useState<any>(null);
68
29
 
69
- const openModal = (agent = null) => {
70
- setEditingAgent(agent);
71
- setIsModalOpen(true);
72
- };
30
+ const filtered = useMemo(() => {
31
+ if (!searchQuery) return agents;
32
+ const q = searchQuery.toLowerCase();
33
+ return agents.filter((a: any) => a.name.toLowerCase().includes(q) || (a.description || '').toLowerCase().includes(q));
34
+ }, [agents, searchQuery]);
73
35
 
74
- const closeModal = () => {
75
- setEditingAgent(null);
76
- setIsModalOpen(false);
77
- };
78
-
79
- const handleSaveAgent = (agentData) => {
36
+ const handleSave = async (formData: any) => {
80
37
  if (editingAgent) {
81
- // updateAgent({ id: editingAgent.id, ...agentData });
82
- setAgents(agents.map(a => a.id === editingAgent.id ? { ...a, ...agentData } : a));
38
+ await updateAgent({ id: editingAgent.id, ...formData });
83
39
  } else {
84
- // createAgent(agentData);
85
- setAgents([...agents, { id: `agent-${Date.now()}`, status: 'active', ...agentData }]);
40
+ const agentId = `agent-${Date.now()}`;
41
+ await createAgent({ id: agentId, ...formData });
86
42
  }
87
- closeModal();
43
+ setIsModalOpen(false);
44
+ setEditingAgent(null);
88
45
  };
89
46
 
90
- const handleDeleteAgent = (agentId) => {
91
- if (window.confirm('Are you sure you want to delete this agent?')) {
92
- // deleteAgent({ id: agentId });
93
- setAgents(agents.filter(a => a.id !== agentId));
47
+ const handleDelete = async (id: string) => {
48
+ if (confirm('Are you sure you want to delete this agent?')) {
49
+ await removeAgent({ id });
94
50
  }
95
51
  };
96
52
 
53
+ const handleToggle = async (id: string) => {
54
+ await toggleActive({ id });
55
+ };
56
+
97
57
  return (
98
58
  <DashboardLayout>
99
- <div className="p-6 bg-background text-foreground">
100
- <div className="flex justify-between items-center mb-6">
101
- <h1 className="text-2xl font-bold flex items-center"><Bot className="mr-2" /> Agents</h1>
102
- <button onClick={() => openModal()} className="bg-primary text-primary-foreground px-4 py-2 rounded-lg flex items-center hover:bg-primary/90">
103
- <Plus className="mr-2 h-4 w-4" /> Create Agent
59
+ <div className="space-y-6">
60
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
61
+ <div>
62
+ <h1 className="text-3xl font-bold">Agents</h1>
63
+ <p className="text-muted-foreground">Create and manage your AI agents.</p>
64
+ </div>
65
+ <button onClick={() => { setEditingAgent(null); setIsModalOpen(true); }} className="bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-primary/90 flex items-center gap-2">
66
+ <Plus className="w-4 h-4" /> Create Agent
104
67
  </button>
105
68
  </div>
106
69
 
107
- <div className="mb-6">
108
- <div className="relative">
109
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground h-5 w-5" />
110
- <input
111
- type="text"
112
- placeholder="Search agents..."
113
- value={searchQuery}
114
- onChange={(e) => setSearchQuery(e.target.value)}
115
- className="w-full bg-card border border-border rounded-lg pl-10 pr-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary"
116
- />
117
- </div>
70
+ <div className="relative max-w-sm">
71
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
72
+ <input type="text" placeholder="Search agents..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="pl-9 pr-3 py-2 bg-card border border-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary w-full" />
118
73
  </div>
119
74
 
120
- {filteredAgents.length > 0 ? (
121
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
122
- {filteredAgents.map(agent => (
123
- <AgentCard key={agent.id} agent={agent} onEdit={openModal} onDelete={handleDeleteAgent} />
124
- ))}
75
+ {filtered.length === 0 ? (
76
+ <div className="text-center py-16 bg-card border border-border rounded-lg">
77
+ <Bot className="w-16 h-16 text-muted-foreground/30 mx-auto mb-4" />
78
+ <h3 className="text-lg font-semibold mb-2">{agents.length === 0 ? 'No agents yet' : 'No matching agents'}</h3>
79
+ <p className="text-muted-foreground mb-4">{agents.length === 0 ? 'Create your first agent to get started.' : 'Try a different search term.'}</p>
80
+ {agents.length === 0 && (
81
+ <button onClick={() => { setEditingAgent(null); setIsModalOpen(true); }} className="bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-primary/90">
82
+ <Plus className="w-4 h-4 inline mr-2" />Create Agent
83
+ </button>
84
+ )}
125
85
  </div>
126
86
  ) : (
127
- <div className="text-center py-16 border-2 border-dashed border-border rounded-lg">
128
- <Bot className="mx-auto h-12 w-12 text-muted-foreground" />
129
- <h3 className="mt-4 text-lg font-medium">No agents found</h3>
130
- <p className="mt-1 text-sm text-muted-foreground">Create a new agent to get started.</p>
87
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
88
+ {filtered.map((agent: any) => (
89
+ <div key={agent._id} className="bg-card border border-border rounded-lg p-5 shadow-sm hover:shadow-md transition-shadow">
90
+ <div className="flex items-start justify-between mb-3">
91
+ <div className="flex items-center gap-2">
92
+ <Bot className="w-5 h-5 text-primary" />
93
+ <h3 className="font-semibold text-foreground">{agent.name}</h3>
94
+ </div>
95
+ <span className={`text-xs px-2 py-0.5 rounded-full ${agent.isActive ? 'bg-green-500/10 text-green-500' : 'bg-muted text-muted-foreground'}`}>
96
+ {agent.isActive ? 'Active' : 'Inactive'}
97
+ </span>
98
+ </div>
99
+ <p className="text-sm text-muted-foreground mb-3 line-clamp-2">{agent.description || 'No description'}</p>
100
+ <div className="flex items-center gap-2 text-xs text-muted-foreground mb-4">
101
+ <span className="bg-muted px-2 py-0.5 rounded">{agent.provider}</span>
102
+ <span className="bg-muted px-2 py-0.5 rounded">{agent.model}</span>
103
+ </div>
104
+ <div className="flex items-center justify-between pt-3 border-t border-border">
105
+ <button onClick={() => handleToggle(agent.id)} className="text-xs text-muted-foreground hover:text-foreground">
106
+ {agent.isActive ? 'Deactivate' : 'Activate'}
107
+ </button>
108
+ <div className="flex items-center gap-2">
109
+ <button onClick={() => { setEditingAgent(agent); setIsModalOpen(true); }} className="p-1.5 rounded hover:bg-muted"><Edit className="w-4 h-4 text-muted-foreground" /></button>
110
+ <button onClick={() => handleDelete(agent.id)} className="p-1.5 rounded hover:bg-destructive/10"><Trash2 className="w-4 h-4 text-destructive" /></button>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ ))}
131
115
  </div>
132
116
  )}
133
- </div>
134
- {isModalOpen && <AgentModal agent={editingAgent} onSave={handleSaveAgent} onClose={closeModal} />}
135
- </DashboardLayout>
136
- );
137
- }
138
117
 
139
- function AgentCard({ agent, onEdit, onDelete }) {
140
- return (
141
- <div className="bg-card border border-border rounded-lg p-4 flex flex-col justify-between hover:shadow-lg transition-shadow">
142
- <div>
143
- <div className="flex justify-between items-start">
144
- <div className="flex items-center mb-2">
145
- <Bot className="h-8 w-8 text-primary mr-3" />
146
- <div>
147
- <h2 className="text-lg font-bold">{agent.name}</h2>
148
- <span className={`text-xs px-2 py-0.5 rounded-full ${agent.status === 'active' ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'}`}>
149
- {agent.status}
150
- </span>
151
- </div>
152
- </div>
153
- <div className="flex items-center space-x-2">
154
- <button onClick={() => onEdit(agent)} className="text-muted-foreground hover:text-foreground"><Edit className="h-4 w-4" /></button>
155
- <button onClick={() => onDelete(agent.id)} className="text-muted-foreground hover:text-destructive"><Trash2 className="h-4 w-4" /></button>
156
- </div>
157
- </div>
158
- <p className="text-sm text-muted-foreground mb-4 h-10 overflow-hidden">{agent.description}</p>
159
- <div className="flex items-center text-xs text-muted-foreground space-x-4 mb-4">
160
- <div className="flex items-center"><Settings className="h-3 w-3 mr-1" /> {agent.model}</div>
161
- <div className="flex items-center"><Zap className="h-3 w-3 mr-1" /> {agent.provider}</div>
162
- </div>
118
+ {isModalOpen && <AgentModal agent={editingAgent} onSave={handleSave} onClose={() => { setIsModalOpen(false); setEditingAgent(null); }} />}
163
119
  </div>
164
- <button className="w-full bg-background border border-border text-center py-2 rounded-lg hover:bg-primary/10 text-sm">View Details</button>
165
- </div>
120
+ </DashboardLayout>
166
121
  );
167
122
  }
168
123
 
169
- function AgentModal({ agent, onSave, onClose }) {
124
+ function AgentModal({ agent, onSave, onClose }: { agent: any; onSave: (data: any) => void; onClose: () => void }) {
170
125
  const [formData, setFormData] = useState({
171
126
  name: agent?.name || '',
172
127
  description: agent?.description || '',
173
128
  instructions: agent?.instructions || '',
174
129
  model: agent?.model || 'gpt-4.1-mini',
175
- provider: agent?.provider || 'OpenAI',
176
- temperature: agent?.temperature || 0.7,
177
- maxTokens: agent?.maxTokens || 4096,
130
+ provider: agent?.provider || 'openai',
131
+ temperature: agent?.temperature ?? 0.7,
132
+ maxTokens: agent?.maxTokens ?? 4096,
178
133
  });
179
134
 
180
- const handleChange = (e) => {
135
+ const handleChange = (e: any) => {
181
136
  const { name, value } = e.target;
182
- setFormData(prev => ({ ...prev, [name]: value }));
137
+ setFormData(prev => ({ ...prev, [name]: name === 'maxTokens' ? parseInt(value) || 0 : value }));
183
138
  };
184
139
 
185
- const handleSliderChange = (e) => {
186
- setFormData(prev => ({ ...prev, temperature: parseFloat(e.target.value) }));
187
- };
188
-
189
- const handleSubmit = (e) => {
140
+ const handleSubmit = (e: any) => {
190
141
  e.preventDefault();
191
- onSave(formData);
192
- };
193
-
194
- const providers = ['OpenAI', 'Anthropic', 'OpenRouter', 'Google', 'xAI'];
195
- const models = {
196
- OpenAI: ['gpt-4.1-mini', 'gpt-4o', 'gpt-3.5-turbo'],
197
- Google: ['gemini-2.5-flash', 'gemini-1.5-pro', 'gemini-1.0-pro'],
198
- Anthropic: ['claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'],
199
- OpenRouter: ['openrouter/auto'],
200
- xAI: ['grok-4', 'grok-4-314b'],
142
+ onSave({ ...formData, temperature: parseFloat(String(formData.temperature)) });
201
143
  };
202
144
 
203
145
  return (
204
- <div className="fixed inset-0 bg-black/60 z-50 flex justify-center items-center">
146
+ <div className="fixed inset-0 bg-black/60 z-50 flex justify-center items-center p-4">
205
147
  <div className="bg-card border border-border rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] flex flex-col">
206
148
  <div className="flex justify-between items-center p-4 border-b border-border">
207
149
  <h2 className="text-lg font-bold">{agent ? 'Edit Agent' : 'Create Agent'}</h2>
@@ -214,30 +156,30 @@ function AgentModal({ agent, onSave, onClose }) {
214
156
  </div>
215
157
  <div>
216
158
  <label className="block text-sm font-medium mb-1">Description</label>
217
- <input type="text" name="description" value={formData.description} onChange={handleChange} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary" required />
159
+ <input type="text" name="description" value={formData.description} onChange={handleChange} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary" />
218
160
  </div>
219
161
  <div>
220
162
  <label className="block text-sm font-medium mb-1">Instructions</label>
221
- <textarea name="instructions" value={formData.instructions} onChange={handleChange} rows="6" className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary" required />
163
+ <textarea name="instructions" value={formData.instructions} onChange={handleChange} rows={6} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary" required />
222
164
  </div>
223
165
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
224
166
  <div>
225
167
  <label className="block text-sm font-medium mb-1">Provider</label>
226
- <select name="provider" value={formData.provider} onChange={handleChange} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
168
+ <select name="provider" value={formData.provider} onChange={(e) => { handleChange(e); setFormData(prev => ({ ...prev, provider: e.target.value, model: (modelsByProvider[e.target.value] || [])[0] || '' })); }} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
227
169
  {providers.map(p => <option key={p} value={p}>{p}</option>)}
228
170
  </select>
229
171
  </div>
230
172
  <div>
231
173
  <label className="block text-sm font-medium mb-1">Model</label>
232
174
  <select name="model" value={formData.model} onChange={handleChange} className="w-full bg-background border border-border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
233
- {(models[formData.provider] || []).map(m => <option key={m} value={m}>{m}</option>)}
175
+ {(modelsByProvider[formData.provider] || []).map(m => <option key={m} value={m}>{m}</option>)}
234
176
  </select>
235
177
  </div>
236
178
  </div>
237
179
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
238
180
  <div>
239
181
  <label className="block text-sm font-medium mb-1">Temperature: {formData.temperature}</label>
240
- <input type="range" min="0" max="1" step="0.1" value={formData.temperature} onChange={handleSliderChange} className="w-full h-2 bg-background rounded-lg appearance-none cursor-pointer" />
182
+ <input type="range" min="0" max="1" step="0.1" value={formData.temperature} onChange={(e) => setFormData(prev => ({ ...prev, temperature: parseFloat(e.target.value) }))} className="w-full h-2 bg-background rounded-lg appearance-none cursor-pointer" />
241
183
  </div>
242
184
  <div>
243
185
  <label className="block text-sm font-medium mb-1">Max Tokens</label>
@@ -245,8 +187,8 @@ function AgentModal({ agent, onSave, onClose }) {
245
187
  </div>
246
188
  </div>
247
189
  </form>
248
- <div className="flex justify-end p-4 border-t border-border">
249
- <button onClick={onClose} className="bg-background border border-border px-4 py-2 rounded-lg mr-2 hover:bg-primary/10">Cancel</button>
190
+ <div className="flex justify-end p-4 border-t border-border gap-2">
191
+ <button onClick={onClose} className="bg-background border border-border px-4 py-2 rounded-lg hover:bg-muted">Cancel</button>
250
192
  <button onClick={handleSubmit} className="bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-primary/90">Save Agent</button>
251
193
  </div>
252
194
  </div>