mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-27 17:58:07 +00:00
feat(cli): /init — generate or update AGENTS.md from a project scan
Cross-surface slash command (CLI, gateway, TUI) following the /learn prompt-injection pattern. Port of Codex /init.
This commit is contained in:
parent
40dc36a842
commit
95b7ea5e5d
8 changed files with 340 additions and 2 deletions
2
cli.py
2
cli.py
|
|
@ -9462,6 +9462,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
|||
self._handle_skills_command(cmd_original)
|
||||
elif canonical == "learn":
|
||||
self._handle_learn_command(cmd_original)
|
||||
elif canonical == "init":
|
||||
self._handle_init_command(cmd_original)
|
||||
elif canonical == "memory":
|
||||
self._handle_memory_command(cmd_original)
|
||||
elif canonical == "platforms":
|
||||
|
|
|
|||
|
|
@ -11792,6 +11792,34 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
except Exception:
|
||||
return "Could not start /learn — please try again."
|
||||
|
||||
if canonical == "init":
|
||||
# /init: rewrite the turn to a guidance-laden prompt and fall
|
||||
# through to normal agent processing (same fall-through as /learn
|
||||
# so role alternation is preserved). The live agent scans the
|
||||
# project with its own read-only tools and writes/updates
|
||||
# AGENTS.md via write_file. No engine, works on any backend.
|
||||
from hermes_cli.init_command import build_init_prompt_for_cwd
|
||||
|
||||
_init_notes = event.get_command_args().strip()
|
||||
try:
|
||||
_init_prompt = build_init_prompt_for_cwd(extra=_init_notes)
|
||||
except Exception:
|
||||
return "Could not start /init — please try again."
|
||||
_ack = (
|
||||
"Updating AGENTS.md from a project scan…"
|
||||
if "UPDATE the existing AGENTS.md" in _init_prompt
|
||||
else "Generating AGENTS.md from a project scan…"
|
||||
)
|
||||
try:
|
||||
adapter = self._adapter_for_source(source)
|
||||
if adapter:
|
||||
_ack_meta = self._thread_metadata_for_source(source)
|
||||
await adapter.send(str(source.chat_id), _ack, metadata=_ack_meta)
|
||||
except Exception:
|
||||
logger.debug("init ack send failed", exc_info=True)
|
||||
event.text = _init_prompt
|
||||
# fall through to agent processing
|
||||
|
||||
if canonical == "fast":
|
||||
return await self._handle_fast_command(event)
|
||||
|
||||
|
|
|
|||
|
|
@ -1642,6 +1642,32 @@ class CLICommandsMixin:
|
|||
else: # pragma: no cover - defensive (no live input loop)
|
||||
print(" /learn needs an active chat session to run.")
|
||||
|
||||
def _handle_init_command(self, cmd: str):
|
||||
"""Handle /init — generate or update AGENTS.md from a project scan.
|
||||
|
||||
Mirrors /learn: build a guidance-laden prompt and inject it onto the
|
||||
agent's input queue as a normal user turn. The live agent scans the
|
||||
project with its own read-only tools and writes/updates AGENTS.md via
|
||||
``write_file``. No engine, no model-tool footprint, works on any
|
||||
terminal backend, and preserves prompt-cache invariants (no system
|
||||
prompt or history mutation).
|
||||
"""
|
||||
from hermes_cli.init_command import build_init_prompt_for_cwd
|
||||
|
||||
# Everything after the command word is optional user emphasis.
|
||||
parts = cmd.strip().split(None, 1)
|
||||
extra = parts[1].strip() if len(parts) > 1 else ""
|
||||
|
||||
msg = build_init_prompt_for_cwd(extra=extra)
|
||||
if "UPDATE the existing AGENTS.md" in msg:
|
||||
print("\n⚡ Updating AGENTS.md from a project scan...")
|
||||
else:
|
||||
print("\n⚡ Generating AGENTS.md from a project scan...")
|
||||
if hasattr(self, "_pending_input"):
|
||||
self._pending_input.put(msg)
|
||||
else: # pragma: no cover - defensive (no live input loop)
|
||||
print(" /init needs an active chat session to run.")
|
||||
|
||||
def _handle_memory_command(self, cmd: str):
|
||||
"""Handle /memory slash command — pending review + approval-gate toggle."""
|
||||
from hermes_cli.write_approval_commands import handle_pending_subcommand
|
||||
|
|
|
|||
|
|
@ -196,6 +196,8 @@ COMMAND_REGISTRY: list[CommandDef] = [
|
|||
"Tools & Skills", cli_only=True, aliases=("generate-pet",), args_hint="[description]"),
|
||||
CommandDef("learn", "Learn a reusable skill from anything you describe (dirs, URLs, this chat, notes)",
|
||||
"Tools & Skills", args_hint="<what to learn from>"),
|
||||
CommandDef("init", "Generate or update AGENTS.md project instructions from a repo scan",
|
||||
"Tools & Skills", args_hint="[notes]"),
|
||||
CommandDef("cron", "Manage scheduled tasks", "Tools & Skills",
|
||||
cli_only=True, args_hint="[subcommand]",
|
||||
subcommands=("list", "add", "create", "edit", "pause", "resume", "run", "remove")),
|
||||
|
|
@ -1172,7 +1174,10 @@ _SLACK_PRIORITY_ALIASES = ("btw", "bg")
|
|||
# displacing existing native Slack slash commands at the 50-command cap.
|
||||
# - debug: the log/report upload surface; reached via /hermes debug on Slack.
|
||||
# - egress: Docker-only proxy status; reachable as /hermes egress on Slack.
|
||||
_SLACK_VIA_HERMES_ONLY = frozenset({"topup", "moa", "debug", "egress"})
|
||||
# - init: repo-scan AGENTS.md bootstrap — a cwd-centric dev command that is
|
||||
# rare from Slack; reachable as /hermes init. Without this entry, adding
|
||||
# /init clamps /version off the native list and breaks Telegram parity.
|
||||
_SLACK_VIA_HERMES_ONLY = frozenset({"topup", "moa", "debug", "egress", "init"})
|
||||
|
||||
|
||||
def _sanitize_slack_name(raw: str) -> str:
|
||||
|
|
|
|||
150
hermes_cli/init_command.py
Normal file
150
hermes_cli/init_command.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/env python3
|
||||
"""``/init`` — build the prompt that generates or updates a project AGENTS.md.
|
||||
|
||||
Port of Codex ``/init`` (Claude Code has the same for CLAUDE.md). Hermes
|
||||
already *loads* AGENTS.md / CLAUDE.md / .cursorrules as project context, but
|
||||
had no command to bootstrap one. ``/init`` hands the live agent ONE
|
||||
guidance-laden prompt instructing it to:
|
||||
|
||||
1. Inspect the project with its own read-only tools (``read_file`` /
|
||||
``search_files`` on manifests, CI configs, lockfiles, existing docs) to
|
||||
learn the layout, toolchain, and the exact build/test/lint commands.
|
||||
2. Write a CONCISE ``AGENTS.md`` (target under 100 lines) with the sections
|
||||
an agent actually needs — overview, setup, commands, conventions,
|
||||
pitfalls — not an essay.
|
||||
3. If an AGENTS.md already exists, UPDATE it: preserve the user's existing
|
||||
content and merge in what's missing, never blow it away.
|
||||
|
||||
There is no engine and no model-tool footprint: the agent does the work with
|
||||
its existing toolset, so this works identically on local, Docker, and remote
|
||||
terminal backends. Every surface (CLI ``/init``, gateway ``/init``, TUI
|
||||
``/init``) calls :func:`build_init_prompt` and feeds the result to the agent
|
||||
as a normal user turn — the same prompt-injection pattern as ``/learn`` and
|
||||
``/blueprint``, which preserves prompt-cache invariants (no system-prompt or
|
||||
history mutation).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# The quality bar, embedded in every prompt so the generated file reads like a
|
||||
# maintainer wrote it — concrete and command-exact, not generic advice.
|
||||
_QUALITY_BAR = """\
|
||||
Quality bar for the file you write (this is what separates a useful AGENTS.md
|
||||
from noise):
|
||||
- CONCISE: target under 100 lines. Agents load this file every session — every
|
||||
line costs context. No essays, no marketing prose, no filler.
|
||||
- Commands must be EXACT invocations you verified from the repo (package.json
|
||||
scripts, Makefile targets, pyproject/tox/CI config, existing docs). Write
|
||||
`npm run test:unit` or `scripts/run_tests.sh tests/foo`, never "run the
|
||||
tests". NEVER invent a command you didn't see evidence for.
|
||||
- No generic advice. "Write tests for new code" and "follow best practices"
|
||||
are banned — if a line would be true of any repo, cut it.
|
||||
- Conventions must be OBSERVED, not assumed: naming patterns, module layout,
|
||||
error-handling style, commit-message format — only what the code actually
|
||||
shows.
|
||||
- Include pitfalls that would genuinely trip up a newcomer or an agent
|
||||
(required env vars, generated files not to hand-edit, slow test suites,
|
||||
ports already in use), if you found any. Skip the section if you found none.
|
||||
- Markdown structure: a short title + one-paragraph overview, then focused
|
||||
sections (e.g. "Dev environment", "Build & test", "Conventions",
|
||||
"Pitfalls"). Flat and scannable — no deep nesting."""
|
||||
|
||||
|
||||
def build_init_prompt(
|
||||
cwd: str,
|
||||
existing_file: str | None = None,
|
||||
extra: str = "",
|
||||
) -> str:
|
||||
"""Build the agent prompt for a ``/init`` request.
|
||||
|
||||
Args:
|
||||
cwd: the project directory the agent should scan and write
|
||||
``AGENTS.md`` into (usually the session working directory).
|
||||
existing_file: the current content of ``AGENTS.md`` if one already
|
||||
exists, else ``None``. When present the prompt switches to
|
||||
update-and-merge discipline instead of fresh generation.
|
||||
extra: free-text the user gave after ``/init`` — emphasis or notes to
|
||||
honor while authoring (e.g. "focus on the test setup").
|
||||
|
||||
Returns:
|
||||
A complete instruction the agent runs as a normal turn.
|
||||
"""
|
||||
extra = (extra or "").strip()
|
||||
|
||||
parts: list[str] = [
|
||||
"[/init] The user wants you to "
|
||||
+ (
|
||||
"UPDATE the existing AGENTS.md project-instructions file"
|
||||
if existing_file is not None
|
||||
else "generate an AGENTS.md project-instructions file"
|
||||
)
|
||||
+ f" for the project at: {cwd}\n",
|
||||
"AGENTS.md is the instruction file coding agents (Hermes included) "
|
||||
"load as project context every session. It should teach an agent how "
|
||||
"to work in THIS repo: what the project is, how to set up, the exact "
|
||||
"build/test/lint commands, the conventions the code actually follows, "
|
||||
"and the pitfalls that waste time.\n",
|
||||
"Do this:\n"
|
||||
"1. Inspect the project with your read-only tools (`read_file`, "
|
||||
"`search_files`) — start with manifests and toolchain files "
|
||||
"(package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, "
|
||||
"CI workflow configs, lockfiles), then the directory layout, existing "
|
||||
"README/docs, and test/lint configuration. Learn the real commands, "
|
||||
"don't guess them.\n"
|
||||
"2. Write the file to "
|
||||
f"{cwd.rstrip('/')}/AGENTS.md with `write_file`"
|
||||
+ (
|
||||
" — but this is an UPDATE, so follow the merge discipline below."
|
||||
if existing_file is not None
|
||||
else "."
|
||||
)
|
||||
+ "\n"
|
||||
"3. Confirm to the user the exact path you wrote and summarize in one "
|
||||
"or two lines what the file covers.\n",
|
||||
]
|
||||
|
||||
if existing_file is not None:
|
||||
parts.append(
|
||||
"MERGE DISCIPLINE — an AGENTS.md already exists (its current "
|
||||
"content is below). Do NOT overwrite or regenerate it from "
|
||||
"scratch. Preserve the user's existing content — their wording, "
|
||||
"their sections, their rules — and merge in only what is missing "
|
||||
"or verifiably stale (e.g. a command that no longer exists in the "
|
||||
"repo). When existing content conflicts with what you observed, "
|
||||
"prefer minimal surgical edits over rewrites, and keep the "
|
||||
"user's intent. The result must still meet the quality bar.\n\n"
|
||||
"CURRENT AGENTS.md CONTENT:\n"
|
||||
"<<<EXISTING_AGENTS_MD\n"
|
||||
f"{existing_file}\n"
|
||||
"EXISTING_AGENTS_MD\n"
|
||||
)
|
||||
|
||||
parts.append(_QUALITY_BAR)
|
||||
|
||||
if extra:
|
||||
parts.append(
|
||||
"\nUSER NOTES — honor these while authoring (they override the "
|
||||
f"defaults above where they conflict):\n{extra}"
|
||||
)
|
||||
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def build_init_prompt_for_cwd(cwd: str | None = None, extra: str = "") -> str:
|
||||
"""Convenience wrapper used by the dispatch surfaces.
|
||||
|
||||
Resolves ``cwd`` (defaults to the process working directory), reads an
|
||||
existing ``AGENTS.md`` there if present, and returns the full prompt.
|
||||
"""
|
||||
import os
|
||||
|
||||
resolved = os.path.abspath(cwd or os.getcwd())
|
||||
existing: str | None = None
|
||||
agents_path = os.path.join(resolved, "AGENTS.md")
|
||||
try:
|
||||
if os.path.isfile(agents_path):
|
||||
with open(agents_path, encoding="utf-8", errors="replace") as fh:
|
||||
existing = fh.read()
|
||||
except OSError:
|
||||
existing = None
|
||||
return build_init_prompt(resolved, existing_file=existing, extra=extra)
|
||||
117
tests/hermes_cli/test_init_command.py
Normal file
117
tests/hermes_cli/test_init_command.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"""Tests for /init — generate or update AGENTS.md from a project scan.
|
||||
|
||||
Covers the shared prompt builder (hermes_cli.init_command.build_init_prompt)
|
||||
and the slash-command registry wiring. /init has no engine and no model tool:
|
||||
it builds a guidance-laden prompt that the live agent runs as a normal turn
|
||||
(the /learn pattern), so these are the load-bearing behavior contracts.
|
||||
"""
|
||||
|
||||
from hermes_cli.init_command import (
|
||||
_QUALITY_BAR,
|
||||
build_init_prompt,
|
||||
build_init_prompt_for_cwd,
|
||||
)
|
||||
|
||||
|
||||
class TestBuildInitPrompt:
|
||||
def test_includes_the_cwd(self):
|
||||
prompt = build_init_prompt("/home/alice/projects/acme")
|
||||
assert "/home/alice/projects/acme" in prompt
|
||||
|
||||
def test_mentions_agents_md(self):
|
||||
prompt = build_init_prompt("/tmp/proj")
|
||||
assert "AGENTS.md" in prompt
|
||||
|
||||
def test_fresh_generation_when_no_existing_file(self):
|
||||
prompt = build_init_prompt("/tmp/proj", existing_file=None)
|
||||
assert "generate an AGENTS.md" in prompt
|
||||
# No merge discipline block for a fresh file.
|
||||
assert "MERGE DISCIPLINE" not in prompt
|
||||
|
||||
def test_merge_not_overwrite_when_existing_file_passed(self):
|
||||
existing = "# My Project\n\nAlways run `make lint` before committing.\n"
|
||||
prompt = build_init_prompt("/tmp/proj", existing_file=existing)
|
||||
low = prompt.lower()
|
||||
# Update mode, with explicit merge-not-overwrite discipline.
|
||||
assert "UPDATE the existing AGENTS.md" in prompt
|
||||
assert "merge" in low
|
||||
assert "do not overwrite" in low or "not overwrite" in low
|
||||
assert "preserve" in low
|
||||
# And it carries the current content so the agent can merge.
|
||||
assert existing.strip() in prompt
|
||||
|
||||
def test_empty_existing_file_still_triggers_update_mode(self):
|
||||
# An existing-but-empty AGENTS.md is still an update, not a fresh gen
|
||||
# (None means "no file"; "" means "empty file").
|
||||
prompt = build_init_prompt("/tmp/proj", existing_file="")
|
||||
assert "UPDATE the existing AGENTS.md" in prompt
|
||||
|
||||
def test_includes_extra_notes_verbatim(self):
|
||||
notes = "focus on the test setup, and mention the flaky e2e suite"
|
||||
prompt = build_init_prompt("/tmp/proj", extra=notes)
|
||||
assert notes in prompt
|
||||
|
||||
def test_no_notes_section_when_extra_empty(self):
|
||||
assert "USER NOTES" not in build_init_prompt("/tmp/proj", extra=" ")
|
||||
assert "USER NOTES" in build_init_prompt("/tmp/proj", extra="hi")
|
||||
|
||||
def test_always_includes_the_quality_bar(self):
|
||||
for existing in (None, "# old content"):
|
||||
prompt = build_init_prompt("/tmp/proj", existing_file=existing)
|
||||
assert _QUALITY_BAR in prompt
|
||||
|
||||
def test_quality_bar_demands_exact_commands_and_conciseness(self):
|
||||
low = _QUALITY_BAR.lower()
|
||||
assert "100 lines" in low
|
||||
assert "never invent" in low
|
||||
assert "no generic advice" in low
|
||||
|
||||
def test_references_read_only_scan_tools(self):
|
||||
prompt = build_init_prompt("/tmp/proj")
|
||||
for tool in ("read_file", "search_files", "write_file"):
|
||||
assert tool in prompt
|
||||
|
||||
|
||||
class TestBuildInitPromptForCwd:
|
||||
def test_defaults_to_process_cwd(self, tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
prompt = build_init_prompt_for_cwd()
|
||||
assert str(tmp_path) in prompt
|
||||
assert "generate an AGENTS.md" in prompt
|
||||
|
||||
def test_reads_existing_agents_md(self, tmp_path):
|
||||
(tmp_path / "AGENTS.md").write_text(
|
||||
"# Existing\n\nRun `tox -e py311`.\n", encoding="utf-8"
|
||||
)
|
||||
prompt = build_init_prompt_for_cwd(cwd=str(tmp_path))
|
||||
assert "UPDATE the existing AGENTS.md" in prompt
|
||||
assert "Run `tox -e py311`." in prompt
|
||||
|
||||
def test_passes_extra_through(self, tmp_path):
|
||||
prompt = build_init_prompt_for_cwd(cwd=str(tmp_path), extra="keep it short")
|
||||
assert "keep it short" in prompt
|
||||
|
||||
|
||||
class TestInitRegistryWiring:
|
||||
def test_init_is_registered_and_resolves(self):
|
||||
from hermes_cli.commands import resolve_command
|
||||
|
||||
cmd = resolve_command("init")
|
||||
assert cmd is not None
|
||||
assert cmd.name == "init"
|
||||
|
||||
def test_init_is_in_tools_and_skills_category(self):
|
||||
from hermes_cli.commands import resolve_command
|
||||
|
||||
assert resolve_command("init").category == "Tools & Skills"
|
||||
|
||||
def test_init_works_on_the_gateway(self):
|
||||
# /init is a both-surfaces command like /learn, not CLI-only.
|
||||
from hermes_cli.commands import GATEWAY_KNOWN_COMMANDS
|
||||
|
||||
assert "init" in GATEWAY_KNOWN_COMMANDS
|
||||
|
||||
def test_init_is_not_cli_only(self):
|
||||
from hermes_cli.commands import resolve_command
|
||||
|
||||
assert not resolve_command("init").cli_only
|
||||
|
|
@ -14987,6 +14987,7 @@ _PENDING_INPUT_COMMANDS: frozenset[str] = frozenset(
|
|||
"moa",
|
||||
"undo",
|
||||
"learn",
|
||||
"init",
|
||||
"compress",
|
||||
"compact",
|
||||
}
|
||||
|
|
@ -15338,6 +15339,14 @@ def _(rid, params: dict) -> dict:
|
|||
from agent.learn_prompt import build_learn_prompt
|
||||
|
||||
return _ok(rid, {"type": "send", "message": build_learn_prompt(arg)})
|
||||
if name == "init":
|
||||
# Generate-or-update AGENTS.md: build the guidance-laden prompt and
|
||||
# submit it as a normal agent turn (same pattern as /learn). The live
|
||||
# agent scans the project with its own read-only tools and writes or
|
||||
# merge-updates AGENTS.md via write_file. Works on any backend.
|
||||
from hermes_cli.init_command import build_init_prompt_for_cwd
|
||||
|
||||
return _ok(rid, {"type": "send", "message": build_init_prompt_for_cwd(extra=arg)})
|
||||
if name == "moa":
|
||||
# /moa is one-shot sugar only: run a single prompt through the default
|
||||
# MoA preset, then restore the prior model. To *switch* to a MoA preset
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ Type `/` in the CLI to open the autocomplete menu. Built-in commands are case-in
|
|||
| `/memory [pending\|approve\|reject\|approval]` | Review pending memory writes staged by the write-approval gate (`memory.write_approval`) and toggle the gate. See [Controlling memory writes](/user-guide/features/memory#controlling-memory-writes-write_approval). |
|
||||
| `/bundles` | List configured skill bundles — `/<name>` slash aliases that preload several skills at once. Configure under `bundles:` in `~/.hermes/config.yaml`. See [Skill Bundles](/user-guide/features/skills#skill-bundles). |
|
||||
| `/learn <what to learn from>` | Distill a reusable skill from anything you describe — a directory, a URL, the workflow you just walked the agent through, or pasted notes. Open-ended: the agent gathers the sources with its own tools and authors a `SKILL.md` following the house authoring standards. Works in the CLI, the messaging gateway, the TUI, and the dashboard Skills page. |
|
||||
| `/init [notes]` | Generate or update `AGENTS.md` project instructions from a repo scan (port of Codex `/init`). The agent inspects manifests, layout, and toolchain configs with its read-only tools, then writes a concise `AGENTS.md` — or, if one exists, merge-updates it preserving your content. Optional notes steer the emphasis. Works in the CLI, the messaging gateway, and the TUI. |
|
||||
| `/cron` | Manage scheduled tasks (list, add/create, edit, pause, resume, run, remove) |
|
||||
| `/suggestions [accept\|dismiss N\|catalog\|clear]` (alias: `/suggest`) | Review suggested automations. Use `/suggestions` to list pending suggestions, `/suggestions accept <id>` to create the proposed automation, `/suggestions dismiss <id>` to reject one, `/suggestions catalog` to add curated starter automations, and `/suggestions clear` to clear resolved suggestion records. Accepted jobs preserve the current surface as the delivery origin. |
|
||||
| `/blueprint [name] [slot=value ...]` (alias: `/bp`) | Set up an automation from a blueprint template. Bare `/blueprint` lists the catalog; `/blueprint <name>` starts a guided slot-filling flow on the next agent turn; `/blueprint <name> slot=value ...` creates the job directly. |
|
||||
|
|
@ -259,7 +260,7 @@ The messaging gateway supports the following built-in commands inside Telegram,
|
|||
- `/skills` is **CLI-only for search/browse/install**; its write-approval review subcommands (`pending`, `approve`, `reject`, `diff`, `approval`) also work on messaging platforms when `skills.write_approval` is on. `/memory` works on **both** surfaces.
|
||||
- `/verbose` is **CLI-only by default**, but can be enabled for messaging platforms by setting `display.tool_progress_command: true` in `config.yaml`. When enabled, it cycles the `display.tool_progress` mode and saves to config.
|
||||
- `/sethome`, `/update`, `/restart`, `/approve`, `/deny`, `/topic`, `/platform`, and `/commands` are **messaging-only** commands.
|
||||
- `/status`, `/egress`, `/version`, `/background`, `/queue`, `/steer`, `/voice`, `/reload-mcp`, `/reload-skills`, `/rollback`, `/debug`, `/fast`, `/footer`, `/curator`, `/kanban`, `/credits`, `/suggestions`, `/blueprint`, `/learn`, `/sessions`, and `/yolo` work in **both** the CLI and the messaging gateway.
|
||||
- `/status`, `/egress`, `/version`, `/background`, `/queue`, `/steer`, `/voice`, `/reload-mcp`, `/reload-skills`, `/rollback`, `/debug`, `/fast`, `/footer`, `/curator`, `/kanban`, `/credits`, `/suggestions`, `/blueprint`, `/learn`, `/init`, `/sessions`, and `/yolo` work in **both** the CLI and the messaging gateway.
|
||||
- `/voice join`, `/voice channel`, and `/voice leave` are only meaningful on Discord.
|
||||
- In the TUI, `/sessions` shows live sessions in the current TUI process. Use `/resume [name]` or `hermes --tui --resume <id-or-title>` for saved or closed transcripts.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue