@desplega.ai/agent-swarm 1.2.1 → 1.9.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.
Files changed (119) hide show
  1. package/.claude/settings.local.json +20 -1
  2. package/.env.docker.example +22 -1
  3. package/.env.example +17 -0
  4. package/.github/workflows/docker-publish.yml +92 -0
  5. package/CONTRIBUTING.md +270 -0
  6. package/DEPLOYMENT.md +391 -0
  7. package/Dockerfile.worker +29 -1
  8. package/FAQ.md +19 -0
  9. package/LICENSE +21 -0
  10. package/MCP.md +249 -0
  11. package/README.md +103 -207
  12. package/assets/agent-swarm-logo-orange.png +0 -0
  13. package/assets/agent-swarm-logo.png +0 -0
  14. package/docker-compose.example.yml +137 -0
  15. package/docker-entrypoint.sh +223 -7
  16. package/package.json +8 -3
  17. package/{cc-plugin → plugin}/.claude-plugin/plugin.json +1 -1
  18. package/plugin/README.md +1 -0
  19. package/plugin/agents/.gitkeep +0 -0
  20. package/plugin/agents/codebase-analyzer.md +143 -0
  21. package/plugin/agents/codebase-locator.md +122 -0
  22. package/plugin/agents/codebase-pattern-finder.md +227 -0
  23. package/plugin/agents/web-search-researcher.md +109 -0
  24. package/plugin/commands/create-plan.md +415 -0
  25. package/plugin/commands/implement-plan.md +89 -0
  26. package/plugin/commands/research.md +200 -0
  27. package/plugin/commands/start-leader.md +101 -0
  28. package/plugin/commands/start-worker.md +56 -0
  29. package/plugin/commands/swarm-chat.md +78 -0
  30. package/plugin/commands/todos.md +66 -0
  31. package/plugin/commands/work-on-task.md +44 -0
  32. package/plugin/skills/.gitkeep +0 -0
  33. package/scripts/generate-mcp-docs.ts +415 -0
  34. package/slack-manifest.json +69 -0
  35. package/src/be/db.ts +1431 -25
  36. package/src/cli.tsx +135 -11
  37. package/src/commands/lead.ts +13 -0
  38. package/src/commands/runner.ts +255 -0
  39. package/src/commands/worker.ts +8 -220
  40. package/src/hooks/hook.ts +102 -14
  41. package/src/http.ts +361 -5
  42. package/src/prompts/base-prompt.ts +131 -0
  43. package/src/server.ts +56 -0
  44. package/src/slack/app.ts +73 -0
  45. package/src/slack/commands.ts +88 -0
  46. package/src/slack/handlers.ts +281 -0
  47. package/src/slack/index.ts +3 -0
  48. package/src/slack/responses.ts +175 -0
  49. package/src/slack/router.ts +170 -0
  50. package/src/slack/types.ts +20 -0
  51. package/src/slack/watcher.ts +119 -0
  52. package/src/tools/create-channel.ts +80 -0
  53. package/src/tools/get-tasks.ts +54 -21
  54. package/src/tools/join-swarm.ts +28 -4
  55. package/src/tools/list-channels.ts +37 -0
  56. package/src/tools/list-services.ts +110 -0
  57. package/src/tools/poll-task.ts +46 -3
  58. package/src/tools/post-message.ts +87 -0
  59. package/src/tools/read-messages.ts +192 -0
  60. package/src/tools/register-service.ts +118 -0
  61. package/src/tools/send-task.ts +80 -7
  62. package/src/tools/store-progress.ts +9 -3
  63. package/src/tools/task-action.ts +211 -0
  64. package/src/tools/unregister-service.ts +110 -0
  65. package/src/tools/update-profile.ts +105 -0
  66. package/src/tools/update-service-status.ts +118 -0
  67. package/src/types.ts +110 -3
  68. package/src/utils/pretty-print.ts +224 -0
  69. package/thoughts/shared/plans/.gitkeep +0 -0
  70. package/thoughts/shared/plans/2025-12-18-inverse-teleport.md +1142 -0
  71. package/thoughts/shared/plans/2025-12-18-slack-integration.md +1195 -0
  72. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +732 -0
  73. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +361 -0
  74. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +501 -0
  75. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +560 -0
  76. package/thoughts/shared/research/.gitkeep +0 -0
  77. package/thoughts/shared/research/2025-12-18-slack-integration.md +442 -0
  78. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +339 -0
  79. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +390 -0
  80. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +376 -0
  81. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +264 -0
  82. package/tsconfig.json +3 -1
  83. package/ui/bun.lock +692 -0
  84. package/ui/index.html +22 -0
  85. package/ui/package.json +32 -0
  86. package/ui/pnpm-lock.yaml +3034 -0
  87. package/ui/postcss.config.js +6 -0
  88. package/ui/public/logo.png +0 -0
  89. package/ui/src/App.tsx +43 -0
  90. package/ui/src/components/ActivityFeed.tsx +415 -0
  91. package/ui/src/components/AgentDetailPanel.tsx +534 -0
  92. package/ui/src/components/AgentsPanel.tsx +549 -0
  93. package/ui/src/components/ChatPanel.tsx +1820 -0
  94. package/ui/src/components/ConfigModal.tsx +232 -0
  95. package/ui/src/components/Dashboard.tsx +534 -0
  96. package/ui/src/components/Header.tsx +168 -0
  97. package/ui/src/components/ServicesPanel.tsx +612 -0
  98. package/ui/src/components/StatsBar.tsx +288 -0
  99. package/ui/src/components/StatusBadge.tsx +124 -0
  100. package/ui/src/components/TaskDetailPanel.tsx +807 -0
  101. package/ui/src/components/TasksPanel.tsx +575 -0
  102. package/ui/src/hooks/queries.ts +170 -0
  103. package/ui/src/index.css +235 -0
  104. package/ui/src/lib/api.ts +161 -0
  105. package/ui/src/lib/config.ts +35 -0
  106. package/ui/src/lib/theme.ts +214 -0
  107. package/ui/src/lib/utils.ts +48 -0
  108. package/ui/src/main.tsx +32 -0
  109. package/ui/src/types/api.ts +164 -0
  110. package/ui/src/vite-env.d.ts +1 -0
  111. package/ui/tailwind.config.js +35 -0
  112. package/ui/tsconfig.json +31 -0
  113. package/ui/vite.config.ts +22 -0
  114. package/cc-plugin/README.md +0 -49
  115. package/cc-plugin/commands/setup-leader.md +0 -73
  116. package/cc-plugin/commands/start-worker.md +0 -64
  117. package/docker-compose.worker.yml +0 -35
  118. package/example-req-meta.json +0 -24
  119. /package/{cc-plugin → plugin}/hooks/hooks.json +0 -0
@@ -0,0 +1,288 @@
1
+ import Box from "@mui/joy/Box";
2
+ import Typography from "@mui/joy/Typography";
3
+ import { useColorScheme } from "@mui/joy/styles";
4
+ import { useStats } from "../hooks/queries";
5
+
6
+ interface HexStatProps {
7
+ label: string;
8
+ value: number;
9
+ color: string;
10
+ glowColor: string;
11
+ isActive?: boolean;
12
+ isDark: boolean;
13
+ onClick?: () => void;
14
+ }
15
+
16
+ function HexStat({ label, value, color, glowColor, isActive, isDark, onClick }: HexStatProps) {
17
+ return (
18
+ <Box
19
+ onClick={onClick}
20
+ role={onClick ? "button" : undefined}
21
+ tabIndex={onClick ? 0 : undefined}
22
+ onKeyDown={onClick ? (e) => { if (e.key === "Enter" || e.key === " ") onClick(); } : undefined}
23
+ sx={{
24
+ position: "relative",
25
+ width: { xs: 65, sm: 75, md: 90 },
26
+ height: { xs: 72, sm: 84, md: 100 },
27
+ transition: "all 0.3s ease",
28
+ cursor: onClick ? "pointer" : "default",
29
+ animation: isActive ? "breathe 3s ease-in-out infinite" : undefined,
30
+ flexShrink: 0,
31
+ "@keyframes breathe": {
32
+ "0%, 100%": { opacity: 0.9, transform: "scale(1)" },
33
+ "50%": { opacity: 1, transform: "scale(1.02)" },
34
+ },
35
+ "&:hover": {
36
+ filter: isDark ? `drop-shadow(0 0 15px ${glowColor})` : `drop-shadow(0 0 8px ${glowColor})`,
37
+ transform: onClick ? "scale(1.05)" : undefined,
38
+ },
39
+ "&:hover .hex-bg": {
40
+ bgcolor: isDark ? "#2F2419" : "#F5EDE4",
41
+ },
42
+ "& *": {
43
+ cursor: onClick ? "pointer" : "inherit",
44
+ },
45
+ }}
46
+ >
47
+ {/* Border layer */}
48
+ <Box
49
+ sx={{
50
+ position: "absolute",
51
+ inset: 0,
52
+ clipPath: "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)",
53
+ background: color,
54
+ }}
55
+ />
56
+ {/* Background layer */}
57
+ <Box
58
+ className="hex-bg"
59
+ sx={{
60
+ position: "absolute",
61
+ inset: 2,
62
+ clipPath: "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)",
63
+ bgcolor: isDark ? "#1A130E" : "#FFFFFF",
64
+ transition: "background-color 0.2s ease",
65
+ }}
66
+ />
67
+ {/* Content layer */}
68
+ <Box
69
+ sx={{
70
+ position: "relative",
71
+ width: "100%",
72
+ height: "100%",
73
+ display: "flex",
74
+ flexDirection: "column",
75
+ alignItems: "center",
76
+ justifyContent: "center",
77
+ zIndex: 1,
78
+ }}
79
+ >
80
+ <Typography
81
+ sx={{
82
+ fontFamily: "code",
83
+ fontSize: { xs: "1.1rem", sm: "1.25rem", md: "1.5rem" },
84
+ fontWeight: 700,
85
+ color,
86
+ textShadow: isDark ? `0 0 20px ${glowColor}` : "none",
87
+ lineHeight: 1,
88
+ }}
89
+ >
90
+ {value}
91
+ </Typography>
92
+ <Typography
93
+ sx={{
94
+ fontFamily: "body",
95
+ fontSize: { xs: "0.45rem", sm: "0.5rem", md: "0.55rem" },
96
+ color: "text.tertiary",
97
+ letterSpacing: "0.08em",
98
+ mt: 0.5,
99
+ textAlign: "center",
100
+ lineHeight: 1.1,
101
+ px: 0.5,
102
+ }}
103
+ >
104
+ {label}
105
+ </Typography>
106
+ </Box>
107
+ </Box>
108
+ );
109
+ }
110
+
111
+ interface StatsBarProps {
112
+ onFilterAgents?: (status: "all" | "busy" | "idle") => void;
113
+ onNavigateToTasks?: (status?: "pending" | "in_progress" | "completed" | "failed") => void;
114
+ }
115
+
116
+ export default function StatsBar({ onFilterAgents, onNavigateToTasks }: StatsBarProps) {
117
+ const { data: stats } = useStats();
118
+ const { mode } = useColorScheme();
119
+ const isDark = mode === "dark";
120
+
121
+ if (!stats) return null;
122
+
123
+ const colors = {
124
+ blue: "#3B82F6",
125
+ amber: isDark ? "#F5A623" : "#D48806",
126
+ gold: isDark ? "#D4A574" : "#8B6914",
127
+ tertiary: isDark ? "#8B7355" : "#6B5344",
128
+ rust: isDark ? "#A85454" : "#B54242",
129
+ blueGlow: isDark ? "rgba(59, 130, 246, 0.5)" : "rgba(59, 130, 246, 0.25)",
130
+ amberGlow: isDark ? "rgba(245, 166, 35, 0.5)" : "rgba(212, 136, 6, 0.25)",
131
+ goldGlow: isDark ? "rgba(212, 165, 116, 0.5)" : "rgba(139, 105, 20, 0.25)",
132
+ tertiaryGlow: isDark ? "rgba(139, 115, 85, 0.4)" : "rgba(107, 83, 68, 0.2)",
133
+ rustGlow: isDark ? "rgba(168, 84, 84, 0.5)" : "rgba(181, 66, 66, 0.25)",
134
+ };
135
+
136
+ // Honeycomb-style arrangement: two rows offset
137
+ const topRow = [
138
+ {
139
+ label: "AGENTS",
140
+ value: stats.agents.total,
141
+ color: colors.blue,
142
+ glowColor: colors.blueGlow,
143
+ onClick: onFilterAgents ? () => onFilterAgents("all") : undefined,
144
+ },
145
+ {
146
+ label: "BUSY",
147
+ value: stats.agents.busy,
148
+ color: colors.amber,
149
+ glowColor: colors.amberGlow,
150
+ isActive: stats.agents.busy > 0,
151
+ onClick: onFilterAgents ? () => onFilterAgents("busy") : undefined,
152
+ },
153
+ {
154
+ label: "IDLE",
155
+ value: stats.agents.idle,
156
+ color: colors.gold,
157
+ glowColor: colors.goldGlow,
158
+ onClick: onFilterAgents ? () => onFilterAgents("idle") : undefined,
159
+ },
160
+ ];
161
+
162
+ const bottomRow = [
163
+ {
164
+ label: "PENDING",
165
+ value: stats.tasks.pending,
166
+ color: colors.tertiary,
167
+ glowColor: colors.tertiaryGlow,
168
+ onClick: onNavigateToTasks ? () => onNavigateToTasks("pending") : undefined,
169
+ },
170
+ {
171
+ label: "RUNNING",
172
+ value: stats.tasks.in_progress,
173
+ color: colors.amber,
174
+ glowColor: colors.amberGlow,
175
+ isActive: stats.tasks.in_progress > 0,
176
+ onClick: onNavigateToTasks ? () => onNavigateToTasks("in_progress") : undefined,
177
+ },
178
+ {
179
+ label: "DONE",
180
+ value: stats.tasks.completed,
181
+ color: colors.gold,
182
+ glowColor: colors.goldGlow,
183
+ onClick: onNavigateToTasks ? () => onNavigateToTasks("completed") : undefined,
184
+ },
185
+ {
186
+ label: "FAILED",
187
+ value: stats.tasks.failed,
188
+ color: colors.rust,
189
+ glowColor: colors.rustGlow,
190
+ onClick: onNavigateToTasks ? () => onNavigateToTasks("failed") : undefined,
191
+ },
192
+ ];
193
+
194
+ return (
195
+ <Box
196
+ sx={{
197
+ bgcolor: "background.surface",
198
+ border: "1px solid",
199
+ borderColor: "neutral.outlinedBorder",
200
+ borderRadius: "8px",
201
+ py: { xs: 1, md: 1.5 },
202
+ flexShrink: 0,
203
+ overflowX: { xs: "auto", md: "visible" },
204
+ WebkitOverflowScrolling: "touch",
205
+ }}
206
+ >
207
+ {/* Desktop: Honeycomb layout */}
208
+ <Box
209
+ sx={{
210
+ display: { xs: "none", md: "flex" },
211
+ flexDirection: "column",
212
+ alignItems: "center",
213
+ gap: 0,
214
+ }}
215
+ >
216
+ {/* Top row - 3 hexagons */}
217
+ <Box
218
+ sx={{
219
+ display: "flex",
220
+ justifyContent: "center",
221
+ gap: 0.5,
222
+ }}
223
+ >
224
+ {topRow.map((stat) => (
225
+ <HexStat
226
+ key={stat.label}
227
+ label={stat.label}
228
+ value={stat.value}
229
+ color={stat.color}
230
+ glowColor={stat.glowColor}
231
+ isActive={stat.isActive}
232
+ isDark={isDark}
233
+ onClick={stat.onClick}
234
+ />
235
+ ))}
236
+ </Box>
237
+
238
+ {/* Bottom row - 4 hexagons, offset for honeycomb effect */}
239
+ <Box
240
+ sx={{
241
+ display: "flex",
242
+ justifyContent: "center",
243
+ gap: 0.5,
244
+ mt: -2.5, // Overlap for honeycomb effect
245
+ }}
246
+ >
247
+ {bottomRow.map((stat) => (
248
+ <HexStat
249
+ key={stat.label}
250
+ label={stat.label}
251
+ value={stat.value}
252
+ color={stat.color}
253
+ glowColor={stat.glowColor}
254
+ isActive={stat.isActive}
255
+ isDark={isDark}
256
+ onClick={stat.onClick}
257
+ />
258
+ ))}
259
+ </Box>
260
+ </Box>
261
+
262
+ {/* Mobile: Single row horizontal scroll */}
263
+ <Box
264
+ sx={{
265
+ display: { xs: "flex", md: "none" },
266
+ flexDirection: "row",
267
+ alignItems: "center",
268
+ gap: { xs: 0.5, sm: 1 },
269
+ px: 1,
270
+ minWidth: "max-content",
271
+ }}
272
+ >
273
+ {[...topRow, ...bottomRow].map((stat) => (
274
+ <HexStat
275
+ key={stat.label}
276
+ label={stat.label}
277
+ value={stat.value}
278
+ color={stat.color}
279
+ glowColor={stat.glowColor}
280
+ isActive={stat.isActive}
281
+ isDark={isDark}
282
+ onClick={stat.onClick}
283
+ />
284
+ ))}
285
+ </Box>
286
+ </Box>
287
+ );
288
+ }
@@ -0,0 +1,124 @@
1
+ import Chip from "@mui/joy/Chip";
2
+ import { useColorScheme } from "@mui/joy/styles";
3
+ import type { AgentStatus, TaskStatus } from "../types/api";
4
+
5
+ interface StatusBadgeProps {
6
+ status: AgentStatus | TaskStatus;
7
+ size?: "sm" | "md" | "lg";
8
+ }
9
+
10
+ interface StatusConfig {
11
+ color: "success" | "warning" | "neutral" | "danger";
12
+ label: string;
13
+ bgColor: { dark: string; light: string };
14
+ textColor: { dark: string; light: string };
15
+ glowColor: { dark: string; light: string };
16
+ }
17
+
18
+ const statusConfig: Record<AgentStatus | TaskStatus, StatusConfig> = {
19
+ // Agent statuses
20
+ idle: {
21
+ color: "success",
22
+ label: "IDLE",
23
+ bgColor: { dark: "rgba(212, 165, 116, 0.15)", light: "rgba(139, 105, 20, 0.12)" },
24
+ textColor: { dark: "#D4A574", light: "#8B6914" },
25
+ glowColor: { dark: "rgba(212, 165, 116, 0.4)", light: "rgba(139, 105, 20, 0.2)" },
26
+ },
27
+ busy: {
28
+ color: "warning",
29
+ label: "BUSY",
30
+ bgColor: { dark: "rgba(245, 166, 35, 0.15)", light: "rgba(212, 136, 6, 0.12)" },
31
+ textColor: { dark: "#F5A623", light: "#D48806" },
32
+ glowColor: { dark: "rgba(245, 166, 35, 0.5)", light: "rgba(212, 136, 6, 0.25)" },
33
+ },
34
+ offline: {
35
+ color: "neutral",
36
+ label: "OFFLINE",
37
+ bgColor: { dark: "rgba(107, 83, 68, 0.15)", light: "rgba(168, 154, 124, 0.15)" },
38
+ textColor: { dark: "#6B5344", light: "#8B7355" },
39
+ glowColor: { dark: "rgba(107, 83, 68, 0.3)", light: "rgba(168, 154, 124, 0.15)" },
40
+ },
41
+ // Task statuses
42
+ unassigned: {
43
+ color: "neutral",
44
+ label: "UNASSIGNED",
45
+ bgColor: { dark: "rgba(100, 100, 100, 0.15)", light: "rgba(150, 150, 150, 0.15)" },
46
+ textColor: { dark: "#888888", light: "#666666" },
47
+ glowColor: { dark: "rgba(100, 100, 100, 0.3)", light: "rgba(150, 150, 150, 0.15)" },
48
+ },
49
+ offered: {
50
+ color: "warning",
51
+ label: "OFFERED",
52
+ bgColor: { dark: "rgba(147, 112, 219, 0.15)", light: "rgba(128, 90, 213, 0.12)" },
53
+ textColor: { dark: "#9370DB", light: "#6B5B95" },
54
+ glowColor: { dark: "rgba(147, 112, 219, 0.4)", light: "rgba(128, 90, 213, 0.2)" },
55
+ },
56
+ pending: {
57
+ color: "neutral",
58
+ label: "PENDING",
59
+ bgColor: { dark: "rgba(107, 83, 68, 0.15)", light: "rgba(168, 154, 124, 0.15)" },
60
+ textColor: { dark: "#8B7355", light: "#6B5344" },
61
+ glowColor: { dark: "rgba(139, 115, 85, 0.3)", light: "rgba(168, 154, 124, 0.15)" },
62
+ },
63
+ in_progress: {
64
+ color: "warning",
65
+ label: "IN PROGRESS",
66
+ bgColor: { dark: "rgba(245, 166, 35, 0.15)", light: "rgba(212, 136, 6, 0.12)" },
67
+ textColor: { dark: "#F5A623", light: "#D48806" },
68
+ glowColor: { dark: "rgba(245, 166, 35, 0.5)", light: "rgba(212, 136, 6, 0.25)" },
69
+ },
70
+ completed: {
71
+ color: "success",
72
+ label: "COMPLETED",
73
+ bgColor: { dark: "rgba(212, 165, 116, 0.15)", light: "rgba(139, 105, 20, 0.12)" },
74
+ textColor: { dark: "#D4A574", light: "#8B6914" },
75
+ glowColor: { dark: "rgba(212, 165, 116, 0.4)", light: "rgba(139, 105, 20, 0.2)" },
76
+ },
77
+ failed: {
78
+ color: "danger",
79
+ label: "FAILED",
80
+ bgColor: { dark: "rgba(168, 84, 84, 0.15)", light: "rgba(181, 66, 66, 0.12)" },
81
+ textColor: { dark: "#A85454", light: "#B54242" },
82
+ glowColor: { dark: "rgba(168, 84, 84, 0.4)", light: "rgba(181, 66, 66, 0.2)" },
83
+ },
84
+ };
85
+
86
+ export default function StatusBadge({ status, size = "sm" }: StatusBadgeProps) {
87
+ const { mode } = useColorScheme();
88
+ const isDark = mode === "dark";
89
+ const config = statusConfig[status];
90
+ const isActive = status === "busy" || status === "in_progress" || status === "offered";
91
+
92
+ const bgColor = isDark ? config.bgColor.dark : config.bgColor.light;
93
+ const textColor = isDark ? config.textColor.dark : config.textColor.light;
94
+ const glowColor = isDark ? config.glowColor.dark : config.glowColor.light;
95
+
96
+ return (
97
+ <Chip
98
+ size={size}
99
+ variant="soft"
100
+ sx={{
101
+ fontFamily: "code",
102
+ fontWeight: 600,
103
+ fontSize: size === "sm" ? "0.65rem" : "0.75rem",
104
+ letterSpacing: "0.05em",
105
+ bgcolor: bgColor,
106
+ color: textColor,
107
+ border: "1px solid",
108
+ borderColor: isActive ? textColor : "transparent",
109
+ boxShadow: isDark ? `0 0 10px ${glowColor}` : "none",
110
+ animation: isActive ? "pulse-amber 2s ease-in-out infinite" : undefined,
111
+ "@keyframes pulse-amber": {
112
+ "0%, 100%": {
113
+ boxShadow: `0 0 5px ${glowColor}`,
114
+ },
115
+ "50%": {
116
+ boxShadow: `0 0 15px ${glowColor}, 0 0 25px ${glowColor}`,
117
+ },
118
+ },
119
+ }}
120
+ >
121
+ {config.label}
122
+ </Chip>
123
+ );
124
+ }