Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hunvreus/heypi/llms.txt

Use this file to discover all available pages before exploring further.

This example builds a Slack DevOps assistant that manages Linux and VPS hosts directly from your workspace. It demonstrates Heypi’s full feature set in a realistic ops context: SSH key onboarding, file-backed host inventory, command policy enforcement with approval gates, Markdown runbook search, and scheduled jobs for daily health checks and idle incident follow-up—all coordinated through a single createHeypi call.

What’s Included

SSH Host Tools

File-backed host inventory with SSH key generation, host facts refresh, and remote command execution via host_exec.

Runbook Search

A custom runbook_search tool that scans Markdown runbooks under agent/runbooks/ for keyword matches.

Approval Gates

systemctl, docker, apt, ufw, and firewall commands require human approval before running remotely.

Admin UI

Local admin panel at http://127.0.0.1:3000/admin with live job, thread, and approval state.

Cron Health Check

A daily-health-check job fires at 09:00 UTC every day and posts a summary to a configured Slack channel.

Incident Heartbeat

An idle-incident-follow-up heartbeat checks every 6 hours whether a quiet incident thread still needs attention.

Setup

1

Clone or copy the example

cp examples/slack-devops/.env.example examples/slack-devops/.env
2

Install dependencies

pnpm install
3

Set environment variables

Edit examples/slack-devops/.env and fill in the required values:
SLACK_BOT_TOKEN=<slack-bot-token>
SLACK_APP_TOKEN=<slack-app-token>
# SLACK_SIGNING_SECRET=... # HTTP mode only
OPENAI_API_KEY=<openai-api-key>

# Optional. Empty means any allowed Slack user can approve.
HEYPI_APPROVERS=
HEYPI_SLACK_TEAMS=
HEYPI_SLACK_CHANNELS=
HEYPI_SLACK_USERS=
HEYPI_SLACK_JOB_CHANNEL=
Leave the HEYPI_SLACK_* allowlists empty to accept every event Slack delivers. Set comma-separated IDs to restrict which teams, channels, or users may trigger the agent.
4

Create your Slack app

You need two tokens:
  • SLACK_BOT_TOKEN — OAuth bot token from your Slack app’s OAuth & Permissions page.
  • SLACK_APP_TOKEN — App-level token with the connections:write scope, found under Basic Information → App-Level Tokens.
Required bot scopes: app_mentions:read, chat:write, channels:history, groups:history. Enable Socket Mode in your app settings.Verify your setup with:
pnpm heypi slack check --env examples/slack-devops/.env
pnpm heypi slack channels --env examples/slack-devops/.env
5

Optionally set a job channel

Run pnpm heypi slack channels to find a channel ID, then set:
HEYPI_SLACK_JOB_CHANNEL=C1234567890
When this variable is set, the example registers two jobs (see Scheduled Jobs below). If it is unset, Heypi logs a warning and starts normally without any jobs.
6

Run the agent

pnpm run dev:slack
# or directly:
node index.js
The admin panel is available at http://127.0.0.1:3000/admin. Heypi logs a one-time login link at startup. If it expires while the app is still running, regenerate it with:
pnpm heypi admin link --state examples/slack-devops/state

Configuration

The core createHeypi call wires together the Slack adapter, custom tools, agent folder, approval policy, and scheduled jobs:
const app = createHeypi({
  state: { root: stateRoot },
  logger: log,
  admin: true,
  adapters: [
    slack({
      botToken: required("SLACK_BOT_TOKEN"),
      mode: "socket",
      appToken: required("SLACK_APP_TOKEN"),
      allow: {
        teams: list("HEYPI_SLACK_TEAMS"),
        channels: list("HEYPI_SLACK_CHANNELS"),
        users: list("HEYPI_SLACK_USERS"),
      },
      trigger: "mention",
      reply: "thread",
      streaming: true,
    }),
  ],
  agent: agentFrom("./agent", {
    id: "slack-devops",
    model: "openai/gpt-5-mini",
    context: [hostContext],
    tools: [...coreTools({ bash: true }), ...runbookTools, ...hostTools],
  }),
  approval: {
    approvers: list("HEYPI_APPROVERS"),
    expiresInMs: 10 * 60 * 1000,
  },
  jobs: jobChannel ? [ /* see Scheduled Jobs */ ] : [],
  runtime: {
    root: workspace("./workspace"),
    scope: "channel",
  },
});

Custom Tools

createHostTools — SSH & Host Inspection

tools/host.ts exposes eight tools for the agent. All mutating or risky tools check the commandPolicy before executing:
ToolDescription
host_key_ensureCreates a named Ed25519 keypair if missing; returns only the public key.
host_key_publicReturns the public key for a named keypair.
hosts_listLists all configured hosts from state/hosts.json.
hosts_lookupFinds a host by id, name, alias, or tag.
hosts_upsertAdds or updates a host; requires approval.
hosts_removeRemoves a host from inventory; requires approval.
host_facts_refreshProbes each host over SSH and caches OS, disk, memory, ports, and more.
host_execRuns a shell command on one or more hosts over SSH; risky commands require approval.
const hostTools = createHostTools({
  root: stateRoot,
  commandPolicy,
  timeoutMs: 60_000,
});
tools/runbook.ts exposes a single runbook_search tool that walks all .md files under agent/runbooks/ and returns matching lines:
const runbookTools = createRunbookTools({ root: "./agent/runbooks" });
The agent’s AGENTS.md instructs it to search runbooks before suggesting remediation steps for any host, service, rollback, or disk issue.

createHostContext — Dynamic Host Inventory

createHostContext is a context provider that reads state/hosts.json and injects a brief host summary into the system prompt each turn. This lets the model recognize host IDs, tags, and aliases before choosing tools:
const hostContext = createHostContext({ root: stateRoot });

Agent Folder

agent/
  SOUL.md             # Role, tone, and safety constraints
  AGENTS.md           # Operating guidance: tool ordering, runbook-first policy
  skills/
    incident-triage/
      SKILL.md        # 5-step incident workflow with guardrails
  runbooks/
    deploy-rollback.md
    disk-space.md
    host-onboarding.md
    linux-health-check.md
    server-inventory.md
    service-debugging.md
SOUL.md defines the agent’s role (Slack DevOps assistant for configured Linux/VPS hosts) and hard safety constraints (never expose private key material, never modify agent source from chat). AGENTS.md defines operating order: clarify → search runbooks → check cached facts → run safe diagnostics → propose minimal remediation → call the tool for risky actions (don’t ask in chat, the app handles the gate). skills/incident-triage/SKILL.md provides a structured 5-step workflow: confirm scope, load runbook context, run safe diagnostics first, propose minimal remediation, close with a status summary.

Scheduled Jobs

When HEYPI_SLACK_JOB_CHANNEL is set, two jobs are registered:
jobs: [
  {
    id: "daily-health-check",
    schedule: { cron: "0 9 * * *", timezone: "UTC" },
    targets: { slack: { channels: [jobChannel] } },
    prompt: "Run a daily infrastructure health check and summarize anything that needs attention.",
    state: "active",
  },
  {
    id: "idle-incident-follow-up",
    kind: "heartbeat",
    everyMs: 6 * 60 * 60 * 1000,
    idleMs: 30 * 60 * 1000,
    scope: { slack: { channels: [jobChannel] } },
    prompt: "If an incident thread has gone quiet, ask whether follow-up is still needed.",
    state: "paused",
  },
],
  • daily-health-check — active cron job, fires at 09:00 UTC and posts to HEYPI_SLACK_JOB_CHANNEL. No external scheduler needed; Heypi runs it inside the Node process.
  • idle-incident-follow-up — paused heartbeat job. When enabled, it checks every 6 hours whether the channel has been idle for at least 30 minutes, then prompts the agent to follow up on any quiet incident threads.
Jobs run inside the Heypi Node process. No external cron service (crontab, Kubernetes CronJob, etc.) is required.

Command Policy

The commandPolicy object controls which remote commands the agent can run without approval, which require approval, and which are always blocked:
const commandPolicy = {
  allow: [
    // curl health checks to localhost only
    /^\s*curl\s+-I\b[^;&|]*\bhttps?:\/\/(?:127\.0\.0\.1|localhost)(?::\d+)?(?:\/|\b)\s*(?:\|\|\s*true\s*)?$/i,
    /^\s*curl\s+[^;&|]*--head\b[^;&|]*\bhttps?:\/\/(?:127\.0\.0\.1|localhost)(?::\d+)?(?:\/|\b)\s*(?:\|\|\s*true\s*)?$/i,
  ],
  approve: [
    /\bsystemctl\s+(restart|stop|start|reload|enable|disable|mask|unmask)\b/i,
    /\bdocker\s+(restart|stop|rm|compose\s+up|compose\s+down|prune)\b/i,
    /\bapt(?:-get)?\s+(install|remove|purge|upgrade|dist-upgrade|autoremove)\b/i,
    /\byum\s+(install|remove|update|upgrade)\b/i,
    /\bufw\s+(allow|deny|delete|enable|disable|reload|reset)\b/i,
    /\bfirewall-cmd\b/i,
    /\biptables\b/i,
    /\bnft\s+(add|delete|flush|insert|replace)\b/i,
  ],
  block: [
    /\bcat\s+.*(?:\.env|id_rsa|id_ed25519)\b/i,
    /\bchmod\s+777\b/i,
  ],
};
Commands not matched by any rule default to safe read-only execution without a confirmation prompt.

Production HTTP Mode

For a production deployment behind a public HTTPS endpoint, switch from Socket Mode to HTTP mode using the commented-out block in index.ts:
slack({
  botToken: required("SLACK_BOT_TOKEN"),
  signingSecret: required("SLACK_SIGNING_SECRET"),
  mode: "http",
  allow: {
    teams: list("HEYPI_SLACK_TEAMS"),
    channels: list("HEYPI_SLACK_CHANNELS"),
    users: list("HEYPI_SLACK_USERS"),
  },
  trigger: "mention",
  reply: "thread",
  streaming: true,
})
Set the Event Subscriptions and Interactivity request URLs in your Slack app settings to https://<host>/slack/slack/events. The admin panel shares the same HTTP listener and remains available at /admin.
SLACK_SIGNING_SECRET is only required for HTTP mode. Socket Mode uses SLACK_APP_TOKEN and requires no public URL.

Build docs developers (and LLMs) love