forked from ultraworkers/claw-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseSwarmBanner.ts
More file actions
155 lines (143 loc) · 5.22 KB
/
Copy pathuseSwarmBanner.ts
File metadata and controls
155 lines (143 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import * as React from 'react'
import { useAppState, useAppStateStore } from '../../state/AppState.js'
import {
getActiveAgentForInput,
getViewedTeammateTask,
} from '../../state/selectors.js'
import {
AGENT_COLOR_TO_THEME_COLOR,
AGENT_COLORS,
type AgentColorName,
getAgentColor,
} from '../../tools/AgentTool/agentColorManager.js'
import { getStandaloneAgentName } from '../../utils/standaloneAgent.js'
import { isInsideTmux } from '../../utils/swarm/backends/detection.js'
import {
getCachedDetectionResult,
isInProcessEnabled,
} from '../../utils/swarm/backends/registry.js'
import { getSwarmSocketName } from '../../utils/swarm/constants.js'
import {
getAgentName,
getTeammateColor,
getTeamName,
isTeammate,
} from '../../utils/teammate.js'
import { isInProcessTeammate } from '../../utils/teammateContext.js'
import type { Theme } from '../../utils/theme.js'
type SwarmBannerInfo = {
text: string
bgColor: keyof Theme
} | null
/**
* Hook that returns banner information for swarm, standalone agent, or --agent CLI context.
* - Leader (not in tmux): Returns "tmux -L ... attach" command with cyan background
* - Leader (in tmux / in-process): Falls through to standalone-agent check — shows
* /rename name + /color background if set, else null
* - Teammate: Returns "teammate@team" format with their assigned color background
* - Viewing a background agent (CoordinatorTaskPanel): Returns agent name with its color
* - Standalone agent: Returns agent name with their color background (no @team)
* - --agent CLI flag: Returns "@agentName" with cyan background
*/
export function useSwarmBanner(): SwarmBannerInfo {
const teamContext = useAppState(s => s.teamContext)
const standaloneAgentContext = useAppState(s => s.standaloneAgentContext)
const agent = useAppState(s => s.agent)
// Subscribe so the banner updates on enter/exit teammate view even though
// getActiveAgentForInput reads it from store.getState().
useAppState(s => s.viewingAgentTaskId)
const store = useAppStateStore()
const [insideTmux, setInsideTmux] = React.useState<boolean | null>(null)
React.useEffect(() => {
void isInsideTmux().then(setInsideTmux)
}, [])
const state = store.getState()
// Teammate process: show @agentName with assigned color.
// In-process teammates run headless — their banner shows in the leader UI instead.
if (isTeammate() && !isInProcessTeammate()) {
const agentName = getAgentName()
if (agentName && getTeamName()) {
return {
text: `@${agentName}`,
bgColor: toThemeColor(
teamContext?.selfAgentColor ?? getTeammateColor(),
),
}
}
}
// Leader with spawned teammates: tmux-attach hint when external, else show
// the viewed teammate's name when inside tmux / native panes / in-process.
const hasTeammates =
teamContext?.teamName &&
teamContext.teammates &&
Object.keys(teamContext.teammates).length > 0
if (hasTeammates) {
const viewedTeammate = getViewedTeammateTask(state)
const viewedColor = toThemeColor(viewedTeammate?.identity.color)
const inProcessMode = isInProcessEnabled()
const nativePanes = getCachedDetectionResult()?.isNative ?? false
if (insideTmux === false && !inProcessMode && !nativePanes) {
return {
text: `View teammates: \`tmux -L ${getSwarmSocketName()} a\``,
bgColor: viewedColor,
}
}
if (
(insideTmux === true || inProcessMode || nativePanes) &&
viewedTeammate
) {
return {
text: `@${viewedTeammate.identity.agentName}`,
bgColor: viewedColor,
}
}
// insideTmux === null: still loading — fall through.
// Not viewing a teammate: fall through so /rename and /color are honored.
}
// Viewing a background agent (CoordinatorTaskPanel): local_agent tasks aren't
// InProcessTeammates, so getViewedTeammateTask misses them. Reverse-lookup the
// name from agentNameRegistry the same way CoordinatorAgentStatus does.
const active = getActiveAgentForInput(state)
if (active.type === 'named_agent') {
const task = active.task
let name: string | undefined
for (const [n, id] of state.agentNameRegistry) {
if (id === task.id) {
name = n
break
}
}
return {
text: name ? `@${name}` : task.description,
bgColor: getAgentColor(task.agentType) ?? 'cyan_FOR_SUBAGENTS_ONLY',
}
}
// Standalone agent (/rename, /color): name and/or custom color, no @team.
const standaloneName = getStandaloneAgentName(state)
const standaloneColor = standaloneAgentContext?.color
if (standaloneName || standaloneColor) {
return {
text: standaloneName ?? '',
bgColor: toThemeColor(standaloneColor),
}
}
// --agent CLI flag (when not handled above).
if (agent) {
const agentDef = state.agentDefinitions.activeAgents.find(
a => a.agentType === agent,
)
return {
text: agent,
bgColor: toThemeColor(agentDef?.color, 'promptBorder'),
}
}
return null
}
function toThemeColor(
colorName: string | undefined,
fallback: keyof Theme = 'cyan_FOR_SUBAGENTS_ONLY',
): keyof Theme {
return colorName && AGENT_COLORS.includes(colorName as AgentColorName)
? AGENT_COLOR_TO_THEME_COLOR[colorName as AgentColorName]
: fallback
}