ModsCode / Claude Code Mods / Ranking / ant-farm
ant-farm
Mission control for your subagents: what each one is doing, its activity and its cost, live.
What it does
- Draws a band above the prompt, a pane
- Adds /antfarm
- Watches tool calls and can refuse them
- Hooks agent.spawn, session.end, session.start, turn.complete and 3 more
Good points
- No network calls
- Starts no processes
- Written in TypeScript
- Has a desktop-specific branch
Code to start from
A starter ModsCode writes from the drawing it read, and the mod's own lines that draw, where there are any.
Starter (register.tsx)
import type { Register } from 'claude-code'
// Drawn like ant-farm by modmyclaude (pane, band, status, toast), as ModsCode read it from the mod's code:
// https://modscode.com/claude-mods/mods/modmyclaude-ant-farm/
// Written by ModsCode from that reading, not copied from the mod: use it as you like.
// Each value below stands for one the mod works out at run time; put in your own.
export const register: Register = on => {
// the band above the prompt; what else goes there is drawn under it
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
const { Box, Text, Button } = $.ui.resolve(e)
const summary = '…'
const taskOf = '…'
const modelOf = '…'
const fmtDuration = '…'
const fmtTokens = '…'
const fmtCost = '…'
const tail = '…'
const taskOf2 = '…'
const modelOf2 = '…'
const fmtDuration2 = '…'
const fmtTokens2 = '…'
const fmtCost2 = '…'
const tail2 = '…'
const taskOf3 = '…'
const modelOf3 = '…'
const fmtDuration3 = '…'
const fmtTokens3 = '…'
const fmtCost3 = '…'
const tail3 = '…'
const hidden = '…'
return (
<Box flexDirection="column">
<Box flexDirection="column">
<Box flexDirection="row">
<Box flexDirection="row" flexGrow={1}>
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Text wrap="truncate-end">
{" "}
{summary}
</Text>
</Box>
<Button label="Hide" />
</Box>
<Box flexDirection="row">
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Text wrap="truncate-end">
{" "}
{taskOf}
{" · "}
{modelOf}
{" · "}
{fmtDuration}
{" · "}
{fmtTokens}
{" tok · "}
{fmtCost}
{" · "}
{tail}
</Text>
</Box>
<Box flexDirection="row">
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Text wrap="truncate-end">
{" "}
{taskOf2}
{" · "}
{modelOf2}
{" · "}
{fmtDuration2}
{" · "}
{fmtTokens2}
{" tok · "}
{fmtCost2}
{" · "}
{tail2}
</Text>
</Box>
<Box flexDirection="row">
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Text wrap="truncate-end">
{" "}
{taskOf3}
{" · "}
{modelOf3}
{" · "}
{fmtDuration3}
{" · "}
{fmtTokens3}
{" tok · "}
{fmtCost3}
{" · "}
{tail3}
</Text>
</Box>
<Text dimColor>
+
{hidden}
{" more - /antfarm shows them all"}
</Text>
</Box>
{await next(e)}
</Box>
)
})
// the pane: open it with $.ui.open({ id: 'my-pane', title: "Ant farm" })
on('ui.render', { component: 'Pane', requestId: 'my-pane' }, async ($, e) => {
const { Box, Text, Button } = $.ui.resolve(e)
const ch = '…'
const ch2 = '…'
const ch3 = '…'
const ch4 = '…'
const ch5 = '…'
const ch6 = '…'
const ch7 = '…'
const ch8 = '…'
const ch9 = '…'
const text = '…'
const clock = '…'
const length = '…'
const length2 = '…'
const failed = '…'
const waiting = '…'
const budgetLine = '…'
const fmtCost = '…'
const fmtTokens = '…'
const fmtDuration = '…'
const budgetFraction = '…'
const glyph = '…'
const glyph2 = '…'
const glyph3 = '…'
const glyph4 = '…'
const glyph5 = '…'
const glyph6 = '…'
const glyph7 = '…'
const glyph8 = '…'
const glyph9 = '…'
const glyph10 = '…'
const glyph11 = '…'
const glyph12 = '…'
const ch10 = '…'
const ch11 = '…'
const ch12 = '…'
const ch13 = '…'
const ch14 = '…'
const ch15 = '…'
const ch16 = '…'
const ch17 = '…'
const ch18 = '…'
const numberOf = '…'
const taskOf = '…'
const glyph13 = '…'
const word = '…'
const type = '…'
const modelOf = '…'
const met = '…'
const elapsed = '…'
const from = '…'
const text2 = '…'
const tally = '…'
const met2 = '…'
const who = '…'
const glyph14 = '…'
const word2 = '…'
const words = '…'
const met3 = '…'
const who2 = '…'
const glyph15 = '…'
const word3 = '…'
const words2 = '…'
const met4 = '…'
const who3 = '…'
const glyph16 = '…'
const word4 = '…'
const words3 = '…'
const id = '…'
const length3 = '…'
const length4 = '…'
const length5 = '…'
const left = '…'
const met5 = '…'
const who4 = '…'
const glyph17 = '…'
const word5 = '…'
const words4 = '…'
const met6 = '…'
const who5 = '…'
const glyph18 = '…'
const word6 = '…'
const words5 = '…'
const met7 = '…'
const who6 = '…'
const glyph19 = '…'
const word7 = '…'
const words6 = '…'
return (
<Box flexDirection="column">
<Box flexDirection="row" gap={1} marginBottom={1}>
<Box flexDirection="column" flexShrink={0}>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch}
</Text>
</Box>
<Box width={1}>
<Text>
{ch2}
</Text>
</Box>
<Box width={1}>
<Text>
{ch3}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch4}
</Text>
</Box>
<Box width={1}>
<Text>
{ch5}
</Text>
</Box>
<Box width={1}>
<Text>
{ch6}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch7}
</Text>
</Box>
<Box width={1}>
<Text>
{ch8}
</Text>
</Box>
<Box width={1}>
<Text>
{ch9}
</Text>
</Box>
</Box>
</Box>
<Box flexDirection="column" flexGrow={1} flexShrink={1} minWidth={0}>
<Box flexDirection="row">
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text bold>
{text}
</Text>
</Box>
<Box flexShrink={0} marginLeft={1}>
<Text dimColor>
{clock}
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Text bold>
{length}
</Text>
<Text dimColor>
{" running · "}
</Text>
<Text bold>
{length2}
</Text>
<Text dimColor>
{" done · "}
</Text>
<Text bold>
{failed}
</Text>
<Text dimColor>
{" failed"}
</Text>
<Text color="warning">
{" · "}
{waiting}
{" waiting"}
</Text>
</Box>
<Box minWidth={0}>
<Text>
{budgetLine}
</Text>
</Box>
<Box minWidth={0}>
<Text dimColor>
{"Subagents "}
{fmtCost}
{" (price table) · "}
{fmtTokens}
{" tokens · "}
{fmtDuration}
{" wall"}
</Text>
</Box>
<Box flexDirection="row">
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Box marginLeft={1} minWidth={0}>
<Text>
{budgetFraction}
%
</Text>
</Box>
</Box>
</Box>
<Box flexDirection="column" flexShrink={0} marginLeft={1}>
<Text dimColor>
last 60 s
</Text>
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
<Box width={1}>
<Text>
{glyph}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph2}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph3}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
<Box width={1}>
<Text>
{glyph4}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph5}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph6}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
<Box width={1}>
<Text>
{glyph7}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph8}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph9}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
<Box width={1}>
<Text>
{glyph10}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph11}
</Text>
</Box>
<Box width={1}>
<Text>
{glyph12}
</Text>
</Box>
</Box>
</Box>
</Box>
<Box flexDirection="row" gap={2} alignItems="center">
<Box flexDirection="column" flexShrink={0}>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch10}
</Text>
</Box>
<Box width={1}>
<Text>
{ch11}
</Text>
</Box>
<Box width={1}>
<Text>
{ch12}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch13}
</Text>
</Box>
<Box width={1}>
<Text>
{ch14}
</Text>
</Box>
<Box width={1}>
<Text>
{ch15}
</Text>
</Box>
</Box>
<Box flexDirection="row" flexWrap="nowrap">
<Box width={1}>
<Text>
{ch16}
</Text>
</Box>
<Box width={1}>
<Text>
{ch17}
</Text>
</Box>
<Box width={1}>
<Text>
{ch18}
</Text>
</Box>
</Box>
</Box>
<Text dimColor>
No subagents yet this session. Each one gets an ant here when it starts.
</Text>
</Box>
<Box flexDirection="column" borderStyle="round" borderColor="suggestion" paddingX={1} marginBottom={1} minWidth={0}>
<Box flexDirection="row">
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text bold>
{numberOf}
{" "}
{taskOf}
</Text>
</Box>
<Box flexShrink={0} marginLeft={1}>
<Text bold>
{glyph13}
{" "}
{word}
</Text>
</Box>
</Box>
<Text dimColor>
{type}
{" · "}
{modelOf}
{" · started "}
{met}
{" · "}
{elapsed}
{" · "}
{from}
</Text>
<Text bold>
{text2}
</Text>
<Text dimColor>
{tally}
</Text>
<Box flexDirection="row">
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0} />
<Box marginLeft={1} minWidth={0}>
<Text dimColor>
tokens per request
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met2}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph14}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word2}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words}
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met3}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who2}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph15}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word3}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words2}
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met4}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who3}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph16}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word4}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words3}
</Text>
</Box>
</Box>
<Box flexDirection="row" gap={2} marginTop={1}>
<Box>
<Button hotkey="p" label="previous" />
</Box>
<Box>
<Button hotkey="n" label="next" />
</Box>
<Box>
<Button hotkey="x" label="close" />
</Box>
<Box flexGrow={1} />
<Box flexShrink={1} minWidth={0}>
<Text dimColor>
{"id "}
{id}
</Text>
</Box>
</Box>
</Box>
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box flexDirection="row" marginTop={1}>
<Text dimColor>
{"Finished · "}
{length3}
{" "}
</Text>
<Box>
<Button hotkey="f" label="collapse" />
</Box>
</Box>
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box flexDirection="row" gap={1} alignItems="flex-start" />
<Box>
<Text dimColor>
+
{length4}
{" more finished"}
</Text>
</Box>
<Box flexDirection="column" marginTop={1}>
<Box flexDirection="row">
<Text bold>
Activity
</Text>
<Text color="success">
{" ●"}
</Text>
<Text dimColor>
{" "}
{length5}
{" "}
row
</Text>
<Box flexGrow={1} />
<Box marginRight={2}>
<Button label={`+${left} more`} />
</Box>
<Box>
<Button hotkey="t" label="show" />
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met5}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who4}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph17}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word5}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words4}
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met6}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who5}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph18}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word6}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words5}
</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width={9} flexShrink={0}>
<Text dimColor>
{met7}
</Text>
</Box>
<Box width={4} flexShrink={0}>
<Text dimColor>
{who6}
</Text>
</Box>
<Box width={2} flexShrink={0}>
<Text>
{glyph19}
</Text>
</Box>
<Box width={8} flexShrink={0}>
<Text>
{word7}
</Text>
</Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text>
{words6}
</Text>
</Box>
</Box>
<Text dimColor>
Nothing yet. Tool calls, edits, handoffs and endings land here.
</Text>
</Box>
<Box marginTop={1} minWidth={0}>
<Text dimColor>
Keys · 1-9 open · n next · p previous · x close · f finished · t activity
{" · m map"}
</Text>
</Box>
</Box>
)
})
}
The mod's own code
Shown under the mod's licence (MIT); its text is below. Keep the notice if you copy these lines.
mods/ant-farm/hooks/register.tsx lines 517–591 · Band
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
if (e.viewport?.isFullscreen !== undefined) docksPanes = e.viewport.isFullscreen
if (e.props.hasSurvey) return next(e)
const list = await read($, ants)
const v = await read($, view)
const { running, finished } = ordered(list)
const showList = v.isListInBand && list.length > 0 && !(await isPanePlaced($))
if (!e.props.isWorking && running.length === 0 && !showList) return next(e)
const { Box, Text, Button } = $.ui.resolve(e)
const now = await $.clock.now()
const t = await read($, turn)
const spend = await sessionSpend($)
const level = budgetLevel(spend.usd, budgetUsd)
const tone = level === 2 ? { color: 'error' } : level === 1 ? { color: 'warning' } : { dimColor: true }
const failed = finished.filter(a => a.status === 'failed').length
const since =
e.props.isWorking && t.isRunning ? t.startedAt
: running.length > 0 ? Math.min(...running.map(a => a.startedAt))
: undefined
const colors = modelColors(list)
const frame = Math.floor(now / TICK_MS)
const summary = [
...(since !== undefined ? [fmtDuration(now - since)] : []),
`${running.length} running / ${finished.length - failed} done`,
...(failed > 0 ? [`${failed} failed`] : []),
spendLabel(spend),
].join(' · ')
const items = [...running, ...finished]
const room = Math.max(1, e.props.maxRows - 2)
const shown = showList ? items.slice(0, room) : []
const hidden = showList ? items.length - shown.length : 0
// Other mods' bands are never erased by this one, and they draw first: an
// overflowing ant list can then never push another mod's buttons out of view.
// What is beneath may be the engine's own node, which the engine refuses
// under a Box that sizes, hides or moves it (width, height, display,
// overflow, position): only our own rows carry the band's width.
const below = await next(e)
return (
<Box flexDirection="column">
{below}
<Box flexDirection="column" key="ant-farm" width={e.props.bodyColumns}>
<Box flexDirection="row" key="band">
<Box key="summary" flexDirection="row" flexGrow={1}>
{glyphRow($.ui.resolve(e), e.surface, miniAnt(running.length > 0 ? 'running' : 'done', frame), tone)}
<Text wrap="truncate-end" {...tone}>
{' '}{summary}
</Text>
</Box>
{showList ? (
<Button key="hide-list" label="Hide" onPress={() => update($, view, x => ({ ...x, isListInBand: false }))} />
) : null}
</Box>
{shown.map(a => {
const p = progressOf(a, list, now)
const held = live.get(a.id)?.phase === 'waiting'
const tail = isRunning(a) ? (held ? 'waiting' : p.etaMs !== null ? fmtEta(p.etaMs) : 'running') : a.status
return (
<Box flexDirection="row" key={`row-${a.id}`}>
{glyphRow($.ui.resolve(e), e.surface, miniAnt(a.status, frame + phaseOf(a.id)), { color: colors.get(a.model) ?? 'subtle' })}
<Text wrap="truncate-end" dimColor={!isRunning(a)}>
{' '}{taskOf(a)} · {modelOf(a)} · {fmtDuration((a.endedAt ?? now) - a.startedAt)} · {fmtTokens(a.usage.tokens)} tok · {fmtCost(a.usage)} · {tail}
</Text>
</Box>
)
})}
{hidden > 0 ? <Text dimColor>+{hidden} more - /antfarm shows them all</Text> : null}
</Box>
</Box>
)
})mods/ant-farm/hooks/cells.tsx lines 12–30 · Band
export function glyphRow(elements: Elements, surface: string, text: string, paint: TextProps = {}): RenderElement {
const { Box, Text } = elements
if (surface !== 'desktop') {
return (
<Box flexShrink={0}>
<Text {...paint} wrap="truncate-end">{text}</Text>
</Box>
)
}
const cells: RenderElement[] = []
for (const run of text.match(/ +|[^ ]/gu) ?? []) {
cells.push(run.startsWith(' ') ? <Box width={run.length} /> : <Box width={1}><Text {...paint}>{run}</Text></Box>)
}
return (
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
{cells}
</Box>
)
}mods/ant-farm/hooks/register.tsx lines 593–637 · Pane
on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
if (e.viewport?.isFullscreen !== undefined) docksPanes = e.viewport.isFullscreen
const list = await read($, ants)
const v = await read($, view)
const now = await $.clock.now()
const spend = await sessionSpend($)
const select = (id: string | undefined) =>
void update($, view, x => {
const { selectedId: _, ...rest } = x
return id === undefined ? rest : { ...rest, selectedId: id }
})
return drawControl({
ui: $.ui.resolve(e),
surface: e.surface,
columns: e.props.bodyColumns,
isFocused: e.props.isFocused,
now,
frame: Math.floor(now / TICK_MS),
list,
events: await read($, events),
view: v,
live,
colors: modelColors(list),
spend,
budgetUsd,
level: budgetLevel(spend.usd, budgetUsd),
subagents: sumUsage(list.map(a => a.usage)),
on: {
select,
move: by => {
const { running, finished } = ordered(list)
const ids = [...running, ...finished].map(a => a.id)
void update($, view, x => {
const next = moveSelection(ids, x.selectedId, by)
const { selectedId: _, ...rest } = x
return next === undefined ? rest : { ...rest, selectedId: next }
})
},
toggleFinished: () => void update($, view, x => ({ ...x, isFinishedExpanded: !x.isFinishedExpanded })),
toggleTimeline: () => void update($, view, x => ({ ...x, isTimelineHidden: x.isTimelineHidden !== true })),
growTimeline: () => void update($, view, x => ({ ...x, isTimelineExpanded: true })),
toggleMap: () => void update($, view, x => ({ ...x, isMapHidden: x.isMapHidden !== true })),
},
})
})mods/ant-farm/hooks/control.tsx lines 116–275 · Pane
export function drawControl(c: ControlInput): RenderElement {
const { Box, Text, Button } = c.ui
const { running, finished } = ordered(c.list)
const failed = finished.filter(a => a.status === 'failed').length
const waiting = running.filter(a => c.live.get(a.id)?.phase === 'waiting').length
const compact = c.columns < COMPACT_BELOW
// The desktop draws words in a proportional font and clips a row at the pane's
// edge instead of cutting it with an ellipsis, so a narrow desktop pane wraps
// its lines; the terminal, where cells are exact, cuts them at the end.
const cut: TextProps['wrap'] = c.surface === 'desktop' && compact ? 'wrap' : 'truncate-end'
// Every Button is plain: the terminal draws `1: open` with the key hint in the accent
// colour, and a desktop draws its native button either way.
const plain = { plain: true as const }
// The desktop app keeps the press handle a Button was mounted with, and the engine hands
// out new handles on every drawing: a Button that never remounts there is dead after the
// first redraw. A wrapper keyed by the frame remounts it on every drawing.
const remount = (name: string) => (c.surface === 'desktop' ? { key: `press-${name}-${c.frame}` } : {})
const firstStart = c.list.length > 0 ? Math.min(...c.list.map(a => a.startedAt)) : c.now
const lastEnd = running.length > 0 ? c.now : Math.max(firstStart, ...finished.map(a => a.endedAt ?? a.startedAt))
const selected = c.view.selectedId !== undefined ? c.list.find(a => a.id === c.view.selectedId) : undefined
const { cols, cardWidth } = gridOf(c.columns)
const byId = new Map(c.list.map(a => [a.id, a] as const))
const antPaint = (a: AntFarmAnt): TextProps => {
if (a.status === 'failed') return { color: 'error' }
if (isRunning(a)) return { color: c.colors.get(a.model) ?? 'subtle' }
if (a.endedAt !== undefined && c.now - a.endedAt < SETTLE_MS) return { color: 'success' }
return { color: 'subtle', dimColor: true }
}
// The button that opens a subagent in the inspector, keyed by its mission number.
const openButton = (a: AntFarmAnt, isSelected: boolean) => {
const hotkey = hotkeyOf(a.n)
return (
<Button
key={`inspect-${a.id}`}
{...plain}
{...(hotkey !== undefined ? { hotkey } : {})}
dimColor={!isRunning(a) && !isSelected}
label={isSelected ? 'close' : 'open'}
onPress={() => c.on.select(isSelected ? undefined : a.id)}
/>
)
}
// ---------------------------------------------------------------- a card
const card = (a: AntFarmAnt): RenderElement => {
const live = c.live.get(a.id)
const isSelected = selected?.id === a.id
const justFinished = !isRunning(a) && a.endedAt !== undefined && c.now - a.endedAt < SETTLE_MS
const border = isSelected ? 'suggestion' : a.status === 'failed' ? 'error' : justFinished ? 'success' : isRunning(a) ? c.colors.get(a.model) ?? 'subtle' : 'subtle'
const rested = !isRunning(a) && !justFinished && !isSelected
const tone: TextProps = a.status === 'failed' ? { color: 'error' } : { dimColor: !isRunning(a) }
const p = progressOf(a, c.list, c.now)
// Held on the person, a subagent makes no progress: no time left to show.
const etaMs = live?.phase === 'waiting' ? null : p.etaMs
// The card's rows in cells: the art lane, one gap, the words beside it.
const inner = cardWidth - 4
const lane = ANT_WIDTH + (inner >= WIDE_CARD ? 3 : 2)
const meta = Math.max(1, inner - lane - 1)
const spark = Math.min(SPARK_CELLS, Math.max(SPARK_MIN, meta - 16))
const rows = antRows(a.status, c.frame + phaseOf(a.id), lane)
const state = stateLine(a, live, c.now, c.frame)
// On hover the card grows by a few rows in its own style: the task in full where the
// title had to cut it, then the subagent's last three rows. Drawn hidden in the flow
// inside the card, so the card's own hover reveals it; no offsets, no fill, nothing
// drawn over anything, and only this card grows.
const titleCut = [...`${numberOf(a)} ${taskOf(a)}`].length > inner - OPEN_CELLS - 1
const recent = c.events.filter(ev => ev.agentId === a.id).slice(-3).reverse()
const revealRow = (ev: AntFarmEvent, i: number): RenderElement => {
const look = eventLook(ev.kind)
return (
<Box flexDirection="row" key={`recent-${i}`}>
<Box width={8} flexShrink={0}><Text dimColor>{met(ev.at - firstStart)}</Text></Box>
<Box width={2} flexShrink={0}><Text {...(look.color !== undefined ? { color: look.color } : { dimColor: true })}>{look.glyph}</Text></Box>
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text dimColor wrap="truncate-end">{clean(ev.text)}{ev.detail !== undefined ? ` · ${clean(ev.detail)}` : ''}</Text>
</Box>
</Box>
)
}
const reveal = (
<Box display="none" hover={{ display: 'flex' }} flexDirection="column" marginTop={1}>
{titleCut ? <Text dimColor wrap="wrap">{taskOf(a)}</Text> : null}
{recent.length > 0 ? recent.map(revealRow) : <Text dimColor>No activity recorded yet</Text>}
</Box>
)
const money = [
fmtCost(a.usage),
...(etaMs !== null ? [fmtEta(etaMs)] : []),
...(inner >= COUNTS_CARD && (a.tools ?? 0) > 0 ? [`${a.tools} ${a.tools === 1 ? 'tool' : 'tools'}`] : []),
...(inner >= COUNTS_CARD && (a.edits ?? 0) > 0 ? [`${a.edits} ${a.edits === 1 ? 'edit' : 'edits'}`] : []),
...(inner >= COUNTS_CARD && (a.errors ?? 0) > 0 ? [`${a.errors} ${a.errors === 1 ? 'error' : 'errors'}`] : []),
].join(' · ')
return (
<Box
key={`ant-${a.id}`}
flexDirection="column"
width={cardWidth}
flexShrink={0}
minWidth={0}
borderStyle="round"
borderColor={border}
borderDimColor={rested}
paddingX={1}
hover={{ borderColor: 'suggestion' }}
>
<Box flexDirection="row">
<Box flexGrow={1} flexShrink={1} minWidth={0}>
<Text bold={isRunning(a) || isSelected} dimColor={rested} wrap="truncate-end">{numberOf(a)} {taskOf(a)}</Text>
</Box>
<Box flexShrink={0} marginLeft={1}>{openButton(a, isSelected)}</Box>
</Box>
<Box flexDirection="row">
<Box key={`art-${a.id}`} flexDirection="column" width={lane} flexShrink={0}>
{rows.map(row => glyphRow(c.ui, c.surface, row, antPaint(a)))}
</Box>
<Box flexDirection="column" width={meta} flexShrink={0} minWidth={0} marginLeft={1}>
<Text dimColor wrap="truncate-end">{modelOf(a)} · {fmtDuration((a.endedAt ?? c.now) - a.startedAt)}</Text>
<Text bold wrap="truncate-end" color={state.color} dimColor={rested}>{state.text}</Text>
<Box flexDirection="row">
{glyphRow(c.ui, c.surface, sparkline(a.history ?? [], spark), antPaint(a))}
<Box marginLeft={1} flexShrink={1} minWidth={0}>
<Text dimColor wrap="truncate-end">{a.usage.requests} req · {fmtTokens(a.usage.tokens)} tok</Text>
</Box>
</Box>
</Box>
</Box>
<Box flexDirection="row">
{glyphRow(c.ui, c.surface, bar(p.fraction, BAR_CELLS), tone)}
<Box marginLeft={1} flexGrow={1} flexShrink={1} minWidth={0}>
<Text wrap="truncate-end" {...tone}>{money}</Text>
</Box>
</Box>
{reveal}
</Box>
)
}
// ---------------------------------------------------------------- a line
const line = (a: AntFarmAnt): RenderElement => {
const look = statusLook(a.status, c.live.get(a.id)?.phase)
const isSelected = selected?.id === a.id
const paint = antPaint(a)
// The model name goes first on a narrow pane (the ant carries its colour); the rest stays whole
// wherever the pane has room for the mini ant, a few cells of task, the numbers, the mark and the button.
const numbers = `${c.columns >= LINE_MODEL_COLUMNS ? `${modelOf(a)} · ` : ''}${fmtDuration((a.endedAt ?? c.now) - a.startedAt)} · ${fmtTokens(a.usage.tokens)} tok · ${fmtCost(a.usage)}`
const numbersFit = c.columns >= MINI_WIDTH + 1 + 8 + 3 + [...numbers].length + 3 + OPEN_CELLS
if (compact) {
return (
<Box flexDirection="row" key={`line-${a.id}`}>
{glyphRow(c.ui, c.surface, miniAnt(a.status, isRunning(a) ? c.frame + phaseOf(a.id) : 0), paint)}
<Box marginLeft={1} flexGrow={1} flexShrink={1} minWidth={0}>
<Text dimColor={!isRunning(a)} wrap={cut}>{numberOf(a)} {a.status} · {taskOf(a)} · {numbers}</Text>
</Box>
</Box>
)
}mods/ant-farm/hooks/bolt-draw.tsx lines 29–65 · Pane
export function boltSprite(elements: Elements, surface: string, pick: BoltPick, key = 'bolt'): RenderElement {
const { Box, Text, Raster } = elements
const cells = boltOf(pick)
const columns = cells[0]?.length ?? 0
if (surface === 'terminal' && Raster !== undefined) {
return <Raster key={key} columns={columns} rows={cells.length} cells={rasterCells(cells)} />
}
const paint = (cell: BoltCell): TextProps => ({
...(cell.fg !== undefined ? { color: cell.fg } : {}),
...(cell.bg !== undefined ? { backgroundColor: cell.bg } : {}),
})
if (surface === 'desktop') {
return (
<Box key={key} flexDirection="column" flexShrink={0}>
{cells.map(line => (
<Box flexDirection="row" flexWrap="nowrap">
{line.map(cell =>
cell.fg === undefined && cell.bg === undefined ? <Box width={1} /> : <Box width={1}><Text {...paint(cell)}>{cell.ch}</Text></Box>,
)}
</Box>
))}
</Box>
)
}
return (
<Box key={key} flexDirection="column" flexShrink={0}>
{cells.map(line => (
<Text>
{boltRuns(line).map(run => {
const text = run.ch.replace(/ /g, ' ')
return run.fg === undefined && run.bg === undefined ? text : <Text {...paint(run)}>{text}</Text>
})}
</Text>
))}
</Box>
)
}mods/ant-farm/hooks/cells.tsx lines 36–50 · Pane
export function paintedRow(elements: Elements, surface: string, cells: readonly PaintedCell[]): RenderElement {
const { Box, Text } = elements
if (surface !== 'desktop') {
return (
<Box flexDirection="row" flexShrink={0}>
{cells.map(cell => <Text {...cell.paint}>{cell.glyph}</Text>)}
</Box>
)
}
return (
<Box flexDirection="row" flexWrap="nowrap" flexShrink={0}>
{cells.map(cell => <Box width={1}><Text {...cell.paint}>{cell.glyph}</Text></Box>)}
</Box>
)
}Licence text (MIT)
MIT License Copyright (c) 2026 modmyclaude contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
How to build this: Pane (panel), Deny a tool call
What its code calls
drives Claudetool calls
What its code shows
Read from the source at commit 1949bfa9cdc5. The first group decides whether a mod is listed; the rest is for you to weigh. A later commit is not read until the next crawl.
| Decides the listing: code you can check | |
|---|---|
| Entry read | Every module hooks/hooks.json names was read.mods/ant-farm/hooks/register.tsx |
| Parses | Every file parses as TypeScript or JavaScript.10 files parsed |
| All its code is in the mod | Every import is a file of the mod or the engine's own module; no packages, no require.all its code is in the mod |
| Readable | No minified lines, no eval or new Function, no import of a computed path, no encoded blobs.no minified code, no eval, no encoded blobs |
| Calls in the open | Network and programs are reached as $.http.fetch and $.process.run in place, never passed around.every call outside goes through $ in the open |
| For you to weigh before installing | |
| Where it draws | The terminal, the desktop app, both, or nothing (hooks only: a guard or a prompt rewriter), as its code says. Shown, never a reason to leave a mod out.draws in the terminal and the desktop app, with its own branch for the desktop |
| Install line | Whether its repository's own .claude-plugin/marketplace.json lists it, so it installs from that marketplace with the lines on its page.listed in its repository's .claude-plugin/marketplace.json: installs from there |
| Programs on Windows | Whether every program it starts through $.process exists on Windows (tail, date, open, osascript, /usr/… do not).runs no program a Windows PC lacks |
| Network | Whether every fetch names its https host in the code.no network |
| Programs | Whether every program it starts is named in the code and is not a shell, an interpreter or a network tool.starts no programs |
| Reads and sends | Whether it both reads something of yours (files, environment variables, the transcript, settings) and reaches outside (the network, an MCP server, another session, telemetry).reaches nothing outside |
| Keys in the source | Whether the source holds what looks like an API key or a private key.no keys in the source |
| Licence | An open licence (MIT, Apache-2.0, BSD, ISC, Unlicense, 0BSD, CC0) with its text lets ModsCode show its code; otherwise the code is linked.MIT |
| Settings hooks | Hooks declared in hooks/hooks.json or plugin.json beside the module: a command (a shell when it has no args), an HTTP request, an MCP tool or a model prompt at an event. They run outside $.no settings hooks: its function hooks alone |
| Servers it ships | MCP servers (a program, or a URL; a headersHelper is a shell command; a .mcpb bundle is unpacked at run time), LSP servers, monitors (a shell command for the whole session) and bin/ (on the PATH of the Bash tool).ships no MCP or LSP server, no monitor, no bin/ |
| Skills, commands, agents | What its Markdown declares beyond text: allowed-tools granted for the turn, hooks added when a skill is invoked, a command run before Claude reads the skill.no skills, commands, agents or workflows |
| Tools and MCP | Whether every tool it calls through $.tool.call and $.mcp.call is named in the code, and none is a shell (Bash through the tool runs a shell without $.process).calls no tool, MCP server or command |
| Asks of the model | Completions it runs ($.model.complete, fork, classify), subagents it spawns, prompts it submits as you, and the tools, commands and agents it adds.adds command antfarm |
| Events it hooks | By what a hook there can do: gates (refuse a tool call, a prompt, a setting, another plugin), shapes (what the model reads: the system prompt, tool descriptions, each request), reads (your input, the events of the settings hooks, every event with *), and the rest.gates: command.run (antfarm), tool.call · shapes: turn.step · also: session.start, session.end, agent.spawn, turn.start (+3) · timers: clock.every, clock.after |
| Reads, by name | The environment variables and files it names, and whether it reads the transcript, settings.json, the prompt box or the mouse selection.reads no files, environment variables, settings or transcript |
| Writes, by name | The files and environment variables it names, and whether it changes settings, the prompt box, the clipboard, the conversation (a row the model reads) or another session.writes no files, environment variables, settings or conversation rows |
This is a reading of the code text, not a security review: it does not run the mod, and it cannot see what a program the mod starts does. The check before installing is yours. How mods are chosen
Install it
Check it yourself before you install it. Mods run unsandboxed, with your permissions, and ModsCode does not vouch for what this code does. Read its code (or run claude plugin validate . in its folder), weigh what it reaches, and install the commit you read.
From its marketplace
Its repository is its marketplace. Add that marketplace (in the terminal with these commands, or in the desktop app's plugin settings) and install the mod from it:
/plugin marketplace add modmyclaude/claude-code-mods/plugin install ant-farm@modmyclaude/reload-pluginsThis installs the marketplace's latest commit, which may not be the one read here (1949bfa9cdc5). Compare them before you install. /reload-plugins loads it into a session that is already open.
Try the commit ModsCode read
No marketplace needed. In a terminal, clone the repository, check out the commit read, validate the mod, and load it for one session:
git clone https://github.com/modmyclaude/claude-code-modsgit -C claude-code-mods checkout 1949bfa9cdc52cf547f3ac0681cba15e9624c4b6claude plugin validate claude-code-mods/mods/ant-farmclaude --plugin-dir claude-code-mods/mods/ant-farmThe validate line lists the events the mod hooks and what it asks Claude Code to do, without running it. The last line loads the mod for this session only and writes nothing to your settings. If the author has rewritten history, the commit may no longer exist.
Ask Claude or ChatGPT about it
With ModsCode added as a connector, Claude or ChatGPT can read this mod's code, what it reaches and how its controls are wired, and build on them. Ask it like this:
Read the ModsCode mod "modmyclaude-ant-farm" with its code and make me one like itAdd the ModsCode MCP to Claude or ChatGPT
Similar mods
- code-modernizationAnthropic★ 37,809
- crit-tuiTomasz Tomczyk★ 1,205
- remctlFederico Viticci★ 595
- task-orchestrator-modJeff Picklyk★ 208
- localvoxtral-modlocalvoxtral★ 59
- localvoxtral-remotelocalvoxtral★ 59
More from this repository
- bouncermodmyclaude★ 1
- chaptersmodmyclaude★ 1
- cloakroommodmyclaude★ 1
- crossroadsmodmyclaude★ 1
- fine-printmodmyclaude★ 1
- footprintsmodmyclaude★ 1
See all 27 mods in this repository
Badge for your README
If this is your mod, paste this into your README. It links to this page.
[](https://modscode.com/claude-mods/mods/modmyclaude-ant-farm/)