Skip to content

fix(cli): consistent 80-column box and content truncation for all slash commands - #109

Closed
antoaenono wants to merge 2 commits into
NousResearch:mainfrom
antoaenono:fix/slash-cmd-consistent-width-output
Closed

fix(cli): consistent 80-column box and content truncation for all slash commands#109
antoaenono wants to merge 2 commits into
NousResearch:mainfrom
antoaenono:fix/slash-cmd-consistent-width-output

Conversation

@antoaenono

@antoaenono antoaenono commented Feb 27, 2026

Copy link
Copy Markdown

Description

  • Adds a helper for printing the header box
  • Adds a few helpers for standardizing truncation for each slash command

Commit 1 - consistent header

  • Add some constants for fixing to 80 chars
  • Add _print_box_header() method shared by all 9 slash command handlers

Commit 2 - consistent truncation

  • Add BOX_RIGHT_PAD = 2 constant so truncation happens with a similar margin to the left side
  • Add _char_width(), _truncate_to_width(), _fit_to_box() helpers using unicodedata east-asian-width for correct emoji/wide char handling
  • Apply _fit_to_box() to content lines in /help, /tools, /toolsets, /personality, /cron, /prompt
  • Use some local prefix/suffix/name_len vars within the tool call handlers to encourage consistent calculations of spacing

Issue

Closes #37

Replace nine hand-rolled header blocks (widths 50/58/60/78) with a
shared _print_box_header() method and three constants:

  BOX_WIDTH = 80                     - total terminal columns
  BOX_BORDER = 2                     - '+' border chars (one on each side)
  BOX_INNER = BOX_WIDTH - BOX_BORDER - usable width between the borders
Add width-aware truncation helpers using unicodedata to correctly handle
wide characters (emoji, CJK) that occupy 2 terminal columns:

  _char_width()        - display width of a single character
  _truncate_to_width() - truncate a string to fit within N columns
  _fit_to_box()        - truncate given a prefix width and BOX_RIGHT_PAD

Also adds BOX_RIGHT_PAD = 2 as the right margin constant.

Apply truncation to all slash command content lines, replacing ad-hoc
hard-coded slices ([:45], [:40]) and old ellipsis style in /personality.
Use local prefix/suffix/name_len vars at each call site so the format
string and width math stay in sync.

/toolsets uses named length vars (marker_len, name_len, tool_len) instead
due to its more complex format string.
@teknium1

Copy link
Copy Markdown
Contributor

Already implemented sorry!

@teknium1 teknium1 closed this Feb 27, 2026
@antoaenono

Copy link
Copy Markdown
Author

You did? Just ran hermes update. Still seems broken.
Screenshot 2026-02-27 at 06 56 48

@antoaenono

Copy link
Copy Markdown
Author

Here's a side by side of /personality.

Screenshot 2026-02-27 at 09 47 49

@antoaenono

Copy link
Copy Markdown
Author
Screenshot 2026-02-27 at 17 45 57

This one shows tools, toolsets, personality side by side.

On the left you can see my branch where I'm running .venv/bin/py cli.py and on the right: hermes.

jarvis-stark-ops added a commit to 1Team-Engineering/hermes-agent that referenced this pull request Jun 16, 2026
* feat(mcp): scaffold hermes-tools-mcp stdio server with initialize

NousResearch#109

Minimal stdio JSON-RPC server that responds to MCP 'initialize' with
the expected protocol version + capabilities. Tools surface comes in
follow-up commits. Env-bound to (profile, project) scope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(mcp): tools/list returns filtered Hermes tool surface

NousResearch#109

Filters the live Hermes tool registry to the v1 surface defined in
spec §4.1. Includes special hermes_set_task tool for per-turn task
context updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(mcp): tools/call dispatches to Hermes tool registry

NousResearch#109

hermes_set_task updates per-turn task context (task_id, workspace).
All other tools/call requests filter through ALLOWED_TOOLS, then
dispatch via registry.dispatch() which handles both sync and async
tools. Denied tools (kanban_dispatch etc.) return isError=true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(kanban): add task_sessions table for relay scope tracking

NousResearch#113

Per spec §4.7 — tracks (profile, project) → tmux session, claude
session_id, FIFO path, MCP config path, project root, scope cwd,
task count, timestamps. Helpers: upsert_session, get_session,
touch_session, mark_compacted, evict_session, list_idle_sessions.

* feat(relay): project derivation + task header builder

NousResearch#114

derive_project maps a kanban workspace to its 'project' key:
  - scratch workspaces -> 'scratch'
  - git workspaces -> basename of git root
  - else raises ProviderError (B11: no silent default)

build_task_header prepends '[task_id=... workspace=...]' to each
user prompt so claude knows the current task without an MCP roundtrip.

* feat(provider): register claude-code-relay ProviderProfile

NousResearch#112

Registers the provider via the existing bundled-plugin pattern under
plugins/model-providers/claude-code-relay/__init__.py. api_mode=
'claude_code_relay' is a new tag for the transport defined in the
next commit. auth_type='none' because the claude binary handles its
own OAuth — the relay just shells out.

* feat(transport): scope spawn + send turn helpers

NousResearch#112

ScopeContext bundles (profile, project, workspace). ensure_scope
either finds a live tmux or invokes relay-spawn-scope.sh (writing
the MCP config first). send_turn prepends the task header and
relays through relay-send.sh, returning the captured response.
Also added monkeypatch for derive_project_root in spawn test since
tmp_path is not a git repo.

* feat(transport): OpenAI-compat ChatCompletion response shape

NousResearch#112

chat_completion() returns the standard OpenAI ChatCompletion object.
Only the latest user message is forwarded — prior conversation rides
on claude's in-session memory. Usage fields left null; /compact hook
in next task captures /usage and backfills.

* feat(kanban): /compact between-task hook for relay scopes

NousResearch#115

After a task with provider=claude-code-relay completes or blocks,
send /compact to its (profile, project) scope. Per B10, failures
are logged but never block the completion. Records last_compacted_at
on task_sessions.

Also: add tasks.provider column to SCHEMA_SQL + additive ALTER migration;
set conn.row_factory=sqlite3.Row in init_db test-fixture shortcut so
complete_task works correctly in :memory: tests.

Spike 2 (2026-06-15) confirmed /compact returns 'Not enough messages
to compact' on short sessions; that's handled cleanly via the timeout
in relay-send.sh slash-command extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(relay): idle-eviction cron step

NousResearch#116

evict_idle_scopes deletes task_sessions rows whose last_used_at is
older than HERMES_RELAY_IDLE_EVICT_SECS (default 86400 = 24h),
and tears down the tmux session via relay-kill-scope.sh.

Wire into nightly-hermes-maintenance follows separately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(relay): boot-check that Hermes Stop hook is installed

NousResearch#109

B3: Stop hook is mandatory for the relay to detect turn-end via FIFO.
Boot-check raises ProviderError with installer path if the hook is
missing — fail loud instead of silently hanging 180s per turn.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(relay): validate ScopeContext.profile + project against B8 regex

Per code review on PR #20 — the shell relay-spawn-scope.sh validates
^[a-zA-Z0-9_-]+$ but Python-side _write_mcp_config writes /tmp/hermes-mcp-{slug}.json
BEFORE invoking the shell. Slugs containing / or .. would have allowed
path traversal out of /tmp.

Add same regex check in ScopeContext.__post_init__. Closes B8 (spec §3).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): chmod 0o600 on /tmp MCP config files

Defense-in-depth: prevent workspace paths in the MCP config from leaking
to other users sharing /tmp. Per code review on PR #20.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(kanban): honor HERMES_RELAY_BIN in /compact hook for consistency

Transport reads HERMES_RELAY_BIN; the kanban_db /compact hook should
too. Per code review on PR #20.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): preserve task_sessions row when relay-kill-scope.sh fails

Per code review on PR #20 — previously evicted the DB row even on kill
failure, orphaning the tmux session with no record to retry. Now check
rc; only evict DB row on rc=0. Failed scopes get retried on next cron tick.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): log WARNING when relay-send exits 4 (Stop-hook timeout)

Per code review on PR #20 — exit 4 means partial response, but callers
had no signal. Add a logger.warning so operators see the partial in logs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Jarvis <jarvis@Kaipos-Mac-mini.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…updates (NousResearch#109)

Bumps the npm_and_yarn group with 1 update in the /scripts/whatsapp-bridge directory: [body-parser](https://github.com/expressjs/body-parser).
Bumps the npm_and_yarn group with 2 updates in the /ui-tui directory: [brace-expansion](https://github.com/juliangruber/brace-expansion) and [js-yaml](https://github.com/nodeca/js-yaml).
Bumps the npm_and_yarn group with 2 updates in the /web directory: [brace-expansion](https://github.com/juliangruber/brace-expansion) and [js-yaml](https://github.com/nodeca/js-yaml).
Bumps the npm_and_yarn group with 3 updates in the /website directory: [body-parser](https://github.com/expressjs/body-parser), [shell-quote](https://github.com/ljharb/shell-quote) and [webpack-dev-server](https://github.com/webpack/webpack-dev-server).


Updates `body-parser` from 1.20.5 to 1.20.6
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](expressjs/body-parser@1.20.5...1.20.6)

Updates `brace-expansion` from 1.1.13 to 1.1.16
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v1.1.13...v1.1.16)

Updates `js-yaml` from 4.2.0 to 4.3.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.2.0...4.3.0)

Updates `brace-expansion` from 1.1.14 to 1.1.16
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v1.1.13...v1.1.16)

Updates `js-yaml` from 4.2.0 to 4.3.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.2.0...4.3.0)

Updates `body-parser` from 1.20.5 to 1.20.6
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](expressjs/body-parser@1.20.5...1.20.6)

Updates `shell-quote` from 1.8.4 to 1.10.0
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](ljharb/shell-quote@v1.8.4...v1.10.0)

Updates `webpack-dev-server` from 5.2.5 to 5.2.6
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/v5.2.6/CHANGELOG.md)
- [Commits](webpack/webpack-dev-server@v5.2.5...v5.2.6)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-version: 1.20.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.16
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.16
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: body-parser
  dependency-version: 1.20.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: shell-quote
  dependency-version: 1.10.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack-dev-server
  dependency-version: 5.2.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
nicezic pushed a commit to nicezic/hermes-agent that referenced this pull request Aug 26, 2026
fix: 合并 dev-fix 稳定性修复到主分支
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

consistent slash command content

2 participants