Status line entry listing agy/codex/ollama delegations in flight, and a warning when agy answers empty

Function-hook mods for Claude Code: shell safety, secret guarding, session telemetry, context panels, and workflow tools. Each mod is an isolated plugin with its own tests, and you enable only the ones you want.
| Mod | Description |
|---|---|
guarda | Intercepts risky Bash commands (rm -rf, git reset --hard, force push, new dependencies) and asks for confirmation explaining the impact. |
lixeira | Sends rm / Remove-Item targets to the Windows Recycle Bin instead of deleting them permanently. |
cofre | Keeps API keys out of the conversation: /chave stores them in .env, secret file reads display only variable names, outputs are masked, and pasted keys are quarantined. |
atomic-commit-guard | Blocks commits that mix top-level directories, violate Conventional Commits, or lack a Co-Authored-By trailer. |
universal-audit-log | A hook on * that logs every event (tool calls, prompts, turns, fs/http/process calls from other plugins) with secret redaction. |
| Mod | Description |
|---|---|
context-monitor | Context weather and token usage above the prompt, replay and activity panels, plain-language explanation before each edit, and ai-memory notes. |
cache-frio | Confirms before submitting a prompt to a large conversation whose prompt cache has gone cold. |
session-telemetry | Timestamp on each response, TTFT and tok/s per step, and visual notice whenever the responding model switches. |
session-time-machine | /timemachine: renders the session as a timeline of prompts, tool calls, and turn completions. |
turn-inspector | Side pane displaying the session turn-by-turn: every model request and what it carried. |
clean-view | Collapses each successful tool call into a single concise line (Edit file +12 −3); errors and in-flight calls retain the full block. |
webfetch-cache | Serves repeated WebFetch requests (identical URL and prompt) directly from the session cache. |
reflexo | Detects corrections and constraints in prompts, offering to save them as rules in CLAUDE.md. |
| Mod | Description |
|---|---|
agent-flow | Live diagram of agent flow: main loop context, subagent tree, and context passed/returned by each agent. |
agent-radar | One live line per running subagent above the prompt; /radar opens a pane with messages from each. |
delegation-dock | Status line indicator showing in-flight delegations (agy/codex/ollama) and alerting when one returns empty. |
phase-tracker | Tracks and displays the current development phase (Spec → Red Test → Surgical Code → Verification). |
next-prompts | Band with 3 next-step suggestions generated by a free Ollama cloud model; click one to draft. |
git-sidebar | Lazygit-style side pane: worktrees, local branches, branch switching, and worktree checkout. |
wait-quiz | Quizzes a flashcard in a band above the prompt while Claude is working. |
meus-mods | Central hub: quick-access bar above the prompt and /mods command displaying the status of each mod. |
Requires Claude Code with function-hook mods support.
git clone https://github.com/Covre81/claude-code-mods.git ~/.claude/mods
Enable the mods by listing their absolute paths in CLAUDE_CODE_PLUGIN_DIRS inside ~/.claude/settings.json. On Windows, paths are separated by ;:
{
"env": {
"CLAUDE_CODE_PLUGIN_DIRS": "C:/Users/me/.claude/mods/guarda;C:/Users/me/.claude/mods/cofre;C:/Users/me/.claude/mods/session-telemetry"
}
}
Order matters: the last plugin in the list sits closest to the engine. For instance, lixeira should come after guarda so guarda inspects the original rm -rf first.
Several mods include a command to toggle them on or off without restarting (/webfetch-cache on|off, /radar-toggle, /telemetria…); /mods displays the status of all mods.
Each mod follows the same structure:
<mod>/
.claude-plugin/plugin.json manifest
.claude-plugin/types/ engine types (git-ignored, see below)
hooks/hooks.json hooks registered by the mod
hooks/register.ts(x) code
hooks/*.test.ts(x) tests
Engine types are not committed to the repository. To typecheck and test a mod, copy .claude-plugin/types from an existing mod or generate them with your Claude Code instance.
Tests run directly within Claude Code, not via vitest/jest:
claude plugin test guarda # single mod
for d in */hooks/hooks.json; do claude plugin test "${d%%/*}"; done # all mods
Commits follow Conventional Commits, scoped to one mod per commit (enforced by atomic-commit-guard).
Six mods (agent-flow, git-sidebar, session-time-machine, turn-inspector, universal-audit-log, webfetch-cache) originate from davila7/claude-code-templates, and agent-radar originates from hamzafer/claude-code-mods, both under the MIT License and adapted here. Copyright notices are preserved in THIRD_PARTY_NOTICES.md.
hooks/register.ts 112 lines1import { atom, read, update } from 'claude-code'
2import type { Register, EngineInterface } from 'claude-code'
3
4const isEnabledRef = atom({ plugin: 'delegation-dock', key: 'isEnabled' } as const, true)
5
6type Job = { tool: string; startedAt: number; outputPath?: string; isRedirected: boolean }
7
8/** Which delegate a shell command starts, or undefined for any other command. */
9export function delegateOf(command: string): string | undefined {
10 if (/(^|[\s;&|(/\\"])agy(\.exe)?"?\s/.test(command) && !/agy(\.exe)?\s+(models|--help|-h\b|--version)/.test(command)) return 'agy'
11 if (/(^|[\s;&|(/\\"])codex(\.cmd)?"?\s+exec\b/.test(command)) return 'codex'
12 if (/(^|[\s;&|(/\\"])ollama(\.exe)?"?\s+run\b/.test(command) || /batch_llm\.py/.test(command)) return 'ollama'
13 return undefined
14}
15
16/** stdout sent to a file (`> out.txt`): an empty task output then says nothing about agy. */
17export const isRedirected = (command: string) => /(^|[^2&>])>>?\s*[^&\s]/.test(command)
18
19export const EMPTY_AGY =
20 'delegation-dock: agy terminou com saída VAZIA (exit 0). É a falha silenciosa conhecida do `agy -p` (CLAUDE.md, 2026-10-06): ' +
21 'não trate como resposta. Remonte como bundle via stdin, sem -p: `agy --mode plan --model gemini-3.1-pro-high --print-timeout 540s < bundle.txt`.'
22
23export function dockText(jobs: Iterable<Job>, now: number): string | undefined {
24 const parts = [...jobs].map(j => `${j.tool} ${Math.round((now - j.startedAt) / 1000)}s`)
25 return parts.length ? `⏳ ${parts.join(' · ')}` : undefined
26}
27
28// ponytail: module memory; a reload mid-delegation forgets the job (the model still gets its notification).
29const jobs = new Map<string, Job>()
30let ticker: { cancel: () => void } | undefined
31let fg = 0
32
33function refresh($: EngineInterface) {
34 $.ui.status(dockText(jobs.values(), Date.now()))
35 if (jobs.size && !ticker) ticker = $.clock.every(5000, () => refresh($))
36 if (!jobs.size && ticker) {
37 ticker.cancel()
38 ticker = undefined
39 }
40}
41
42export const register: Register = on => {
43 jobs.clear()
44
45 on('session.start', async ($, e, next) => {
46 const saved = await $.store.get('isEnabled').catch(() => undefined)
47 await update($, isEnabledRef, () => (typeof saved === 'boolean' ? saved : isEnabledRef.initial))
48 await $.command.register({
49 name: 'delegation-dock',
50 description: 'Liga/desliga a linha de status das delegações (agy/codex/ollama).',
51 argumentHint: 'on|off',
52 })
53 return next(e)
54 })
55
56 on('command.run', { command: 'delegation-dock' }, async ($, e) => {
57 const arg = e.args.trim().toLowerCase()
58 if (arg !== 'on' && arg !== 'off') {
59 const isEnabled = await read($, isEnabledRef)
60 return { text: `delegation-dock esta ${isEnabled ? 'ativado' : 'desativado'}. Uso: /delegation-dock on · /delegation-dock off` }
61 }
62 await update($, isEnabledRef, () => arg === 'on')
63 await $.store.set('isEnabled', arg === 'on') // $.state is per session; the store survives a restart
64 if (arg === 'off') $.ui.status(undefined)
65 return { text: `delegation-dock ${arg === 'on' ? 'ativado' : 'desativado'}.` }
66 })
67
68 on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
69 if (!(await read($, isEnabledRef))) return next(e)
70 const tool = delegateOf(e.command)
71 if (!tool) return next(e)
72
73 const key = `fg${++fg}`
74 jobs.set(key, { tool, startedAt: Date.now(), isRedirected: isRedirected(e.command) })
75 refresh($)
76 const ran = await next(e).finally(() => {
77 jobs.delete(key)
78 refresh($)
79 })
80 if (ran.deny !== undefined || ran.isError) return ran
81
82 const out = ran.result as { stdout?: string; backgroundTaskId?: string }
83 if (out.backgroundTaskId) {
84 const outputPath = /Output is being written to: (.+?\.output)/.exec(out.stdout ?? '')?.[1]
85 jobs.set(out.backgroundTaskId, { tool, startedAt: Date.now(), isRedirected: isRedirected(e.command), ...(outputPath ? { outputPath } : {}) })
86 refresh($)
87 return ran
88 }
89 if (tool === 'agy' && !isRedirected(e.command) && !out.stdout?.trim()) {
90 $.ui.toast('agy devolveu vazio')
91 return { ...ran, context: [...(ran.context ?? []), EMPTY_AGY] }
92 }
93 return ran
94 }).catch(($, e, next) => next(e)) // a dock bug must never block the command
95
96 on('prompt.submit', async ($, e, next) => {
97 if (e.origin.kind !== 'task-notification') return next(e)
98 const id = /<task-id>([^<]+)<\/task-id>/.exec(e.text)?.[1]
99 const job = id ? jobs.get(id) : undefined
100 if (!id || !job) return next(e)
101
102 jobs.delete(id)
103 refresh($)
104 const completed = /<status>completed<\/status>/.test(e.text)
105 if (job.tool !== 'agy' || job.isRedirected || !completed || !job.outputPath) return next(e)
106 const text = await $.fs.read(job.outputPath).catch(() => undefined)
107 if (text === undefined || text.trim()) return next(e)
108 $.ui.toast('agy devolveu vazio')
109 return next({ ...e, context: [...(e.context ?? []), EMPTY_AGY] })
110 }).catch(($, e, next) => next(e))
111}
112types/index.d.ts 12 lines1export type DelegationDockState = {
2 isEnabled: boolean
3}
4
5declare module 'claude-code' {
6 interface PluginState {
7 'delegation-dock': {
8 isEnabled: boolean
9 }
10 }
11}
12