用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Aradotso/hermes-skills --skill hermes-war-room-ui命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | hermes-war-room-ui |
| description | UI dashboard for visualizing and controlling Hermes multi-agent orchestration with kanban delegation |
| triggers | ["set up the hermes war room dashboard","visualize hermes agent orchestration","create a hermes team interface","monitor hermes kanban tasks","build a multi-agent control panel","manage hermes operative profiles","deploy the hermes war room","configure hermes orchestration UI"] |
Skill by ara.so — Hermes Skills collection.
Hermes War Room is a browser-based visual dashboard for managing Hermes Agent's multi-profile orchestration and kanban delegation system. It provides real-time monitoring of agent operatives, task boards, and mission coordination—transforming command-line agent orchestration into a visual "operations floor."
The War Room wraps the Hermes CLI with a Vue/Nuxt interface that:
It reads from ~/.hermes/kanban.db and ~/.hermes/profiles/, shells out to the hermes CLI, and persists UI state in its own SQLite database.
# Download and extract the latest release
mkdir -p ~/hermes-war-room && cd ~/hermes-war-room
curl -L -o hermes-war-room.tar.gz \
https://github.com/Naroh091/hermes-war-room/releases/latest/download/hermes-war-room.tar.gz
tar xzf hermes-war-room.tar.gz
# Run (requires Hermes CLI installed and Node 22+)
HERMES_HOME=$HOME/.hermes NITRO_HOST=127.0.0.1 NITRO_PORT=3000 \
node .output/server/index.mjs
Open http://localhost:3000 in your browser.
git clone https://github.com/Naroh091/hermes-war-room.git
cd hermes-war-room
pnpm install
pnpm dev
The dev server runs on http://localhost:3000.
# Required
HERMES_HOME=$HOME/.hermes # Path to Hermes profiles and kanban DB
NITRO_HOST=127.0.0.1 # Bind address
NITRO_PORT=3000 # Port
# Optional
WAR_ROOM_DB=./data/war-room.db # War Room's own SQLite database
NODE_ENV=production # Production/development mode
Each Hermes profile becomes an "operative" in the War Room:
<!-- Components displaying operatives -->
<OperativeWorkstation
:operative="operative"
:current-task="currentTask"
@click="openDossier"
/>
Operatives have:
~/.hermes/profiles/<slug>/Convention is to have one "leader" profile that routes but never executes:
<!-- profiles/lider/SOUL.md -->
You are the mission orchestrator. Your job is to:
1. Decompose user goals into discrete tasks
2. Delegate each task via kanban to the right specialist
3. Never do the work yourself
4. Summarize results when workers complete tasks
Always use the terminal tool to create kanban tasks:
hermes kanban create --assignee <worker> --title "..." --body "..." --json
Workers have full tool access and execute:
<!-- profiles/investigador/SOUL.md -->
You are a research specialist. When assigned a kanban task:
1. Execute the research using available tools
2. Complete the task with: hermes kanban complete <id> --summary "..."
A mission is a conversation thread with an orchestrator:
// server/api/missions/create.post.ts
export default defineEventHandler(async (event) => {
const { orchestratorId, title } = await readBody(event)
const mission = await db.insert(missions).values({
orchestrator_id: orchestratorId,
title: title,
status: 'open',
created_at: new Date()
}).returning()
return mission[0]
})
Messages are sent via ACP sessions that persist across turns.
// Create a new mission
POST /api/missions/create
{
"orchestratorId": 1,
"title": "Research competitor pricing"
}
// Send a message (returns SSE stream)
POST /api/missions/:id/message
{
"content": "Find pricing for Acme Corp products"
}
// Get mission history
GET /api/missions/:id
// List all operatives
GET /api/operatives
// Get operative details with current task
GET /api/operatives/:id
// Update operative (callsign, avatar, color, active)
PATCH /api/operatives/:id
{
"callsign": "RESEARCHER",
"color": "#3b82f6",
"active": true
}
// Retrain (update SOUL.md, skills, tools)
POST /api/operatives/:id/retrain
{
"soul": "You are a specialized legal analyst...",
"skills": ["research", "analysis"],
"mcpServers": ["filesystem", "brave-search"]
}
// Get all tasks for the board view
GET /api/kanban/tasks
// Get tasks for a specific operative
GET /api/kanban/tasks?assignee=investigador
// Watch a task (auto-nudge when complete)
POST /api/kanban/watch
{
"taskId": "task_abc123",
"missionId": 5
}
Create an orchestrator and workers via Hermes CLI:
# Create orchestrator
hermes profile create lider --model claude-3-5-sonnet-20241022
# Create specialist workers
hermes profile create investigador --model claude-3-5-sonnet-20241022
hermes profile create legal --model claude-3-5-sonnet-20241022
hermes profile create writer --model gpt-4o
Edit ~/.hermes/profiles/lider/SOUL.md:
# Mission Orchestrator
You coordinate a team of specialists. When the user gives you a goal:
1. Break it into discrete, parallelizable tasks
2. Assign each task to the right specialist via kanban
3. Never do the work yourself
4. Wait for task completion notifications
5. Synthesize results into a final answer
## Available Team
- **investigador**: Research, data gathering, fact-checking
- **legal**: Legal analysis, compliance review
- **writer**: Content creation, documentation
## Task Creation
Always use this exact format:
```bash
hermes kanban create \
--assignee <specialist> \
--title "<concise title>" \
--body "<detailed instructions>" \
--json
Edit `~/.hermes/profiles/investigador/SOUL.md`:
```markdown
# Research Specialist
You execute research tasks assigned via kanban.
When you receive a task:
1. Read the task body for full context
2. Use available tools (search, filesystem) to gather information
3. Complete with a detailed summary:
```bash
hermes kanban complete <task_id> --summary "<findings>"
### 3. Configure Skills in War Room UI
1. Navigate to `/team`
2. Click the operative's badge
3. Click "Retrain"
4. Enable skills:
- Orchestrator: `terminal` (for kanban), `reasoning`
- Workers: `terminal`, `filesystem`, `search`, `reasoning`, etc.
5. Add MCP servers as needed
6. Save
### 4. Start the Dispatcher
The dispatcher claims `ready` tasks and hands them to workers:
```bash
# In a tmux/screen session
hermes gateway
Without the dispatcher, tasks will stay in "ready" state.
// 1. Frontend: Create mission
const mission = await $fetch('/api/missions/create', {
method: 'POST',
body: {
orchestratorId: 1, // lider
title: 'Market research for Product X'
}
})
// 2. Frontend: Send initial brief
const eventSource = new EventSource(
`/api/missions/${mission.id}/message`,
{
method: 'POST',
body: JSON.stringify({
content: 'Research our top 3 competitors: pricing, features, and market position'
})
}
)
eventSource.addEventListener('tool_call', (event) => {
const data = JSON.parse(event.data)
if (data.name === 'terminal' && data.input.includes('kanban create')) {
console.log('Orchestrator delegating task...')
}
})
// 3. Backend: Orchestrator creates tasks
// (Automatically intercepted by War Room)
// hermes kanban create --assignee investigador --title "Research Competitor A" --json
// hermes kanban create --assignee investigador --title "Research Competitor B" --json
eventSource.(, {
data = .(event.)
.(, data.)
})
// server/utils/taskWatcher.ts
import { db } from './db'
import { watchedTasks, missions } from './schema'
export async function pollWatchedTasks() {
const watched = await db.select().from(watchedTasks).where(
eq(watchedTasks.notified, false)
)
for (const watch of watched) {
const task = await getKanbanTask(watch.task_id)
if (task.status === 'done' || task.status === 'blocked') {
// Inject system message into orchestrator session
const mission = await db.select().from(missions).where(
eq(missions.id, watch.mission_id)
).get()
await sendSystemMessage(mission.acp_session_id, {
role: 'system',
content: `Task ${watch.task_id} is now . Summary: . Please integrate this into your response to the user.`
})
db.(watchedTasks).({ : }).(
(watchedTasks., watch.)
)
}
}
}
(pollWatchedTasks, )
<script setup>
const { mission, messages, sendMessage, loading } = useMission(missionId)
async function briefOrchestrator() {
await sendMessage('Analyze legal compliance for EU markets')
}
</script>
<template>
<div>
<ChatMessage
v-for="msg in messages"
:key="msg.id"
:message="msg"
/>
<ChatInput @send="sendMessage" :disabled="loading" />
</div>
</template>
<script setup>
const { operatives, activeOperatives, refresh } = useOperatives()
const workers = computed(() =>
activeOperatives.value.filter(op => op.profile_slug !== 'lider')
)
</script>
<template>
<OperativeWorkstation
v-for="op in workers"
:key="op.id"
:operative="op"
/>
</template>
<script setup>
const { tasks, tasksByStatus, refresh } = useKanban()
const columns = {
todo: computed(() => tasksByStatus.value.todo),
ready: computed(() => tasksByStatus.value.ready),
running: computed(() => tasksByStatus.value.running),
blocked: computed(() => tasksByStatus.value.blocked)
}
</script>
<template>
<div class="kanban-board">
<KanbanColumn
v-for="(tasks, status) in columns"
:key="status"
:title="status"
:tasks="tasks"
/>
</div>
</template>
// POST /api/operatives/create
export default defineEventHandler(async (event) => {
const { slug, callsign, cloneFrom } = await readBody(event)
// Shell out to Hermes CLI
const { stdout } = await execAsync(
`hermes profile create ${slug} ${cloneFrom ? `--clone ${cloneFrom}` : ''}`
)
// Register in War Room DB
const operative = await db.insert(operatives).values({
profile_slug: slug,
callsign: callsign || slug.toUpperCase(),
avatar: randomAvatar(),
color: randomColor(),
active: true
}).returning()
return operative[0]
})
<!-- components/OperativeWorkstation.vue -->
<script setup>
const props = defineProps(['operative'])
const { data: currentTask } = useFetch(`/api/operatives/${props.operative.id}/current-task`, {
watch: true,
refreshInterval: 2000 // Poll every 2s
})
const status = computed(() => {
if (!currentTask.value) return 'Standing by'
if (currentTask.value.status === 'running') return `Working on: ${currentTask.value.title}`
if (currentTask.value.status === 'blocked') return 'Blocked'
return 'Standing by'
})
</script>
<template>
<div class="workstation" :style="{ borderColor: operative.color }">
<img :src="operative.avatar" />
<div class="status-pill">{{ status }}</div>
</div>
</template>
<script setup>
const props = defineProps(['taskId'])
const { data: chain } = useFetch(`/api/kanban/tasks/${props.taskId}/chain`)
</script>
<template>
<div class="delegation-chain">
<div v-for="task in chain" :key="task.id" class="chain-node">
<OperativeBadge :operative="task.assignee" mini />
<div class="task-info">
<strong>{{ task.title }}</strong>
<span class="status">{{ task.status }}</span>
</div>
<div v-if="task.children?.length" class="children">
<DelegationChain
v-for="child in task.children"
:key="child.id"
:task-id="child.id"
/>
</div>
</div>
</div>
</template>
Problem: Tasks stuck in "ready" state, workers never pick them up.
Solution: Start the Hermes gateway:
hermes gateway
Run in a persistent session (tmux/screen) or as a systemd service.
Problem: Orchestrator answers directly instead of creating kanban tasks.
Solution: Update the orchestrator's SOUL.md to be more explicit:
CRITICAL RULES:
1. You MUST delegate via kanban. NEVER answer directly.
2. Every user request becomes one or more kanban tasks.
3. Use the terminal tool: hermes kanban create --assignee <worker> --title "..." --body "..." --json
4. After delegating, say "Tasks assigned, waiting for results."
Also ensure the terminal skill is enabled and the orchestrator's chat includes the delegation preamble (injected automatically by War Room).
Problem: Worker completes task but orchestrator never sees it.
Solution: Verify the task watcher is running (built into War Room server). Check logs for:
[taskWatcher] Detected task task_abc123 completed, nudging orchestrator
If missing, restart the War Room server.
Problem: Updated SOUL.md or skills in UI but agent still behaves the same.
Solution: The ACP session caches profile config. Either:
pkill -f "hermes gateway" && hermes gatewayProblem: database is locked when reading kanban.db.
Solution: Hermes uses SQLite in WAL mode, but heavy concurrent access can still cause locks. Ensure:
HERMES_HOMEsqlite3 sessions are open on kanban.db# /etc/systemd/system/hermes-war-room.service
[Unit]
Description=Hermes War Room UI
After=network.target
[Service]
Type=simple
User=hermes
WorkingDirectory=/opt/hermes-war-room
Environment="HERMES_HOME=/home/hermes/.hermes"
Environment="NITRO_HOST=0.0.0.0"
Environment="NITRO_PORT=3000"
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node .output/server/index.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable hermes-war-room
sudo systemctl start hermes-war-room
server {
listen 80;
server_name war-room.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# SSE requires no buffering
proxy_buffering off;
proxy_cache off;
}
}
# .env.production
HERMES_HOME=/var/lib/hermes
NITRO_HOST=127.0.0.1
NITRO_PORT=3000
WAR_ROOM_DB=/var/lib/hermes-war-room/war-room.db
NODE_ENV=production
hermes-war-room/
├── server/
│ ├── api/
│ │ ├── missions/ # Mission CRUD + messaging
│ │ ├── operatives/ # Operative management + retrain
│ │ └── kanban/ # Kanban task queries + watch
│ ├── utils/
│ │ ├── db.ts # War Room SQLite connection
│ │ ├── hermes.ts # Hermes CLI wrappers
│ │ └── taskWatcher.ts # Auto-nudge poller
│ └── middleware/
├── components/
│ ├── OperativeWorkstation.vue
│ ├── KanbanBoard.vue
│ ├── MissionChat.vue
│ └── OperativeDossier.vue
├── composables/
│ ├── useMission.ts
│ ├── useOperatives.ts
│ └── useKanban.ts
├── pages/
│ ├── index.vue # War Room floor
│ ├── team.vue # Roster/badges
│ └── missions.vue # Archive
└── data/
└── war-room.db # War Room state (not Hermes kanban.db)
The War Room reads ~/.hermes/kanban.db and ~/.hermes/profiles/ directly but never writes to them—writes go through the hermes CLI.
基于 SOC 职业分类