Skip to content

Implement MuGlyph M1 foundation plus M2–M4 subsystems - #1

Merged
HarryCordewener merged 2 commits into
mainfrom
claude/muglyph-implementation-5f51l3
Jul 27, 2026
Merged

Implement MuGlyph M1 foundation plus M2–M4 subsystems#1
HarryCordewener merged 2 commits into
mainfrom
claude/muglyph-implementation-5f51l3

Conversation

@HarryCordewener

@HarryCordewener HarryCordewener commented Jul 27, 2026

Copy link
Copy Markdown
Member

Build out the MuGlyph client from the skeleton into a working, UI-agnostic
core with a Terminal.Gui v2 front end. All of MuClient.Core is unit-tested
(195 tests across Core, Graphics, and Scripting).

Solution

  • MuGlyph.slnx with six projects on net10.0, central package management,
    shared build props, and a GitHub Actions CI matrix (ubuntu + windows).

Core (UI-agnostic, fully testable)

  • AnsiParser: incremental SGR (16/256/24-bit, colon + semicolon forms),
    CSI/OSC recognition-and-discard; styled-span line model; ScrollbackBuffer.
  • TcpTransport (TLS via SslStream, IPv6) behind ITransport; TelnetSession
    wrapping TelnetNegotiationCore 2.5.3 with GMCP/MSSP/MSDP routing and
    GA/EOR prompt detection.
  • Automation: regex TriggerEngine (gag/highlight/rewrite/respond/spawn/script),
    AliasEngine, MacroEngine, IntervalScheduler.
  • Plain-text and HTML logging; JSON configuration + best-effort BeipMU importer.
  • Theme model + ThemeLibrary (Dark/Light/Solarized) with palette overrides.
  • WorldSession/SessionManager orchestrating the full pipeline.

Graphics (no UI dependency)

  • Kitty graphics encoder + Unicode placeholders, Sixel and half-block
    fallbacks, and an environment-based capability probe.

Scripting

  • Sandboxed MoonSharp ScriptHost exposing world/output/trigger/alias/timer/
    gmcp/log with hot-reload.

Tui

  • Terminal.Gui v2 app: truecolor OutputView with wrapping/scrollback,
    CommandInput with history + tab-completion, status line, key routing,
    theming.

Tests use TUnit on Microsoft.Testing.Platform; run via dotnet run --project.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_015w2WJ5M22CZmg9md67zpeB

Summary by CodeRabbit

  • New Features
    • Added continuous integration that builds and runs the full test suite on Linux and Windows using .NET 10.
    • Delivered end-to-end client foundation: telnet/TLS connectivity, ANSI/SGR styling, prompts + scrollback, automation (aliases/macros/triggers), theming, and Lua scripting with hot reload.
    • Added graphics protocol capability detection and inline image rendering (Kitty, Sixel, half-block), plus plain-text/HTML session logging.
    • Introduced configuration persistence and saved-world import (including BeipMU XML).
  • Documentation
    • Updated README status, build/run/test instructions, and scripting examples.
  • Tests
    • Expanded unit and integration coverage across automation, parsing, graphics, configuration, logging, telnet/session, and scripting.
  • Chores
    • Centralized MSBuild package/version management and introduced shared solution build props.

Build out the MuGlyph client from the skeleton into a working, UI-agnostic
core with a Terminal.Gui v2 front end. All of MuClient.Core is unit-tested
(195 tests across Core, Graphics, and Scripting).

Solution
- MuGlyph.slnx with six projects on net10.0, central package management,
  shared build props, and a GitHub Actions CI matrix (ubuntu + windows).

Core (UI-agnostic, fully testable)
- AnsiParser: incremental SGR (16/256/24-bit, colon + semicolon forms),
  CSI/OSC recognition-and-discard; styled-span line model; ScrollbackBuffer.
- TcpTransport (TLS via SslStream, IPv6) behind ITransport; TelnetSession
  wrapping TelnetNegotiationCore 2.5.3 with GMCP/MSSP/MSDP routing and
  GA/EOR prompt detection.
- Automation: regex TriggerEngine (gag/highlight/rewrite/respond/spawn/script),
  AliasEngine, MacroEngine, IntervalScheduler.
- Plain-text and HTML logging; JSON configuration + best-effort BeipMU importer.
- Theme model + ThemeLibrary (Dark/Light/Solarized) with palette overrides.
- WorldSession/SessionManager orchestrating the full pipeline.

Graphics (no UI dependency)
- Kitty graphics encoder + Unicode placeholders, Sixel and half-block
  fallbacks, and an environment-based capability probe.

Scripting
- Sandboxed MoonSharp ScriptHost exposing world/output/trigger/alias/timer/
  gmcp/log with hot-reload.

Tui
- Terminal.Gui v2 app: truecolor OutputView with wrapping/scrollback,
  CommandInput with history + tab-completion, status line, key routing,
  theming.

Tests use TUnit on Microsoft.Testing.Platform; run via `dotnet run --project`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015w2WJ5M22CZmg9md67zpeB
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e332636-0e6d-4aa3-95f2-a00bfa70d9ef

📥 Commits

Reviewing files that changed from the base of the PR and between 74d07b2 and 8519373.

📒 Files selected for processing (14)
  • .github/workflows/ci.yml
  • CLAUDE.md
  • src/MuClient.Core/Automation/Alias.cs
  • src/MuClient.Core/Automation/AliasEngine.cs
  • src/MuClient.Core/Automation/AutomationDefaults.cs
  • src/MuClient.Core/Automation/IntervalScheduler.cs
  • src/MuClient.Core/Automation/Trigger.cs
  • src/MuClient.Core/Automation/TriggerEngine.cs
  • src/MuClient.Core/Logging/HtmlLogSink.cs
  • src/MuClient.Core/Text/AnsiParser.cs
  • src/MuClient.Core/Transport/TcpTransport.cs
  • src/MuClient.Tui/MuGlyphApp.cs
  • tests/MuClient.Core.Tests/Automation/IntervalSchedulerTests.cs
  • tests/MuClient.Core.Tests/Text/AnsiParserTests.cs
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/MuClient.Core/Automation/Alias.cs
  • src/MuClient.Core/Automation/AliasEngine.cs
  • .github/workflows/ci.yml
  • tests/MuClient.Core.Tests/Automation/IntervalSchedulerTests.cs
  • src/MuClient.Core/Automation/TriggerEngine.cs
  • src/MuClient.Core/Transport/TcpTransport.cs
  • CLAUDE.md
  • src/MuClient.Tui/MuGlyphApp.cs
  • src/MuClient.Core/Automation/IntervalScheduler.cs
  • tests/MuClient.Core.Tests/Text/AnsiParserTests.cs
  • src/MuClient.Core/Text/AnsiParser.cs
  • src/MuClient.Core/Logging/HtmlLogSink.cs

📝 Walkthrough

Walkthrough

MuGlyph adds a .NET 10 solution with shared build configuration, core terminal/session functionality, graphics rendering, Lua scripting, a Terminal.Gui application, automated tests, documentation, an example script, and cross-platform GitHub Actions CI.

Changes

MuGlyph runtime and application stack

Layer / File(s) Summary
Core text, themes, configuration, logging, and transport
Directory.Build.props, src/MuClient.Core/Text/*, src/MuClient.Core/Configuration/*, src/MuClient.Core/Logging/*, src/MuClient.Core/Transport/*
Adds ANSI text models and parsing, scrollback, themes, JSON configuration, log sinks, connection options, and TCP/TLS transport.
Automation, Telnet, and world sessions
src/MuClient.Core/Automation/*, src/MuClient.Core/Telnet/*, src/MuClient.Core/Session/*
Adds aliases, macros, triggers, timers, Telnet protocol events, session lifecycle management, and the WorldSession processing pipeline.
Graphics capability and rendering pipeline
src/MuClient.Graphics/*
Adds terminal capability detection, image sources, Kitty and Sixel encoders, half-block rendering, and protocol-based inline image output.
Sandboxed Lua scripting integration
src/MuClient.Scripting/*, examples/example.lua
Adds the Lua host API, sandbox restrictions, callback dispatch, timers, reload handling, session bridging, and an example script.
Terminal UI application flow
src/MuClient.Tui/*
Adds the Terminal.Gui executable, command input history/completion, styled scrollback rendering, session startup, CLI parsing, and application shutdown handling.
Core runtime validation
tests/MuClient.Core.Tests/*
Adds TUnit tests and test doubles covering automation, ANSI parsing, scrollback, configuration, logging, Telnet sessions, and WorldSession behavior.
Graphics validation
tests/MuClient.Graphics.Tests/*
Adds tests for capability detection, image storage, renderers, Kitty protocol output, and Sixel encoding.
Scripting validation
tests/MuClient.Scripting.Tests/*
Adds tests for Lua API routing, callbacks, timers, sandbox behavior, script errors, and reload cleanup.
Solution, documentation, and CI integration
MuGlyph.slnx, README.md, CLAUDE.md, .github/workflows/ci.yml
Adds solution organization, repository guidance, build/run/test documentation, and Ubuntu/Windows .NET 10 CI execution.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR’s main scope: foundational MuGlyph work plus the M2–M4 subsystems.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/muglyph-implementation-5f51l3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Nitpick comments (1)
src/MuClient.Scripting/ScriptException.cs (1)

27-49: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Parse DecoratedMessage for the runtime-error line format too.

MoonSharp 2.0.0 runtime errors use DecoratedMessage entries like [string "chunk"]:4: Division by zero!, which contain no ( range. Since this method only succeeds when it finds parentheses, ordinary error(...)/runtime errors leave ScriptException.Line as null. Update the extraction to handle the standard [source]:LINE: message format without changing the safe fallback for unknown message shapes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MuClient.Scripting/ScriptException.cs` around lines 27 - 49, Update
TryExtractLine to also parse MoonSharp runtime messages in the “[source]:LINE:
message” format when no parenthesized range is present. Preserve the existing
parenthesized-range parsing and return null safely for malformed or unrecognized
DecoratedMessage values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 20-21: Update the actions/checkout step in the CI workflow to
disable persisted credentials by setting its persist-credentials option to
false, while leaving the existing checkout behavior unchanged.

In `@CLAUDE.md`:
- Around line 30-31: Update the project count in the status text beginning “M1
delivered, plus substantial M2–M4 work.” to state that MuGlyph.slnx builds all
seven projects, while preserving the surrounding build and test information.

In `@src/MuClient.Core/Automation/Alias.cs`:
- Around line 29-32: Update the Alias.Regex property to construct the compiled
Regex with a finite execution timeout for persisted patterns. In
AliasEngine.Expand, catch RegexMatchTimeoutException around
alias.Regex.Match(input) and handle the timeout by skipping or safely rejecting
that alias without hanging user input processing.

In `@src/MuClient.Core/Automation/IntervalScheduler.cs`:
- Around line 56-74: Update the Timer callback in IntervalScheduler to catch
exceptions thrown by callback(), preventing scheduled script/user callbacks from
escaping the ThreadPool thread and terminating the process. Report or swallow
the exception according to the scheduler’s existing error-handling conventions,
while preserving the finally block that disposes one-shot handles.

In `@src/MuClient.Core/Automation/Trigger.cs`:
- Around line 61-65: Update the Trigger.Regex property to construct the compiled
Regex with an explicit finite match timeout, preserving the existing
case-sensitivity options. Also validate or compile Pattern when triggers are
added so malformed user-supplied expressions fail immediately with a clear error
rather than during TriggerEngine.Process.

In `@src/MuClient.Core/Logging/HtmlLogSink.cs`:
- Around line 147-171: Make HtmlLogSink disposal and flushing idempotent by
updating Dispose and Flush to return without touching _writer once _closed is
true. Preserve the existing first-dispose behavior, including writing the
closing HTML, flushing, and disposing the writer when _ownsWriter is set.

In `@src/MuClient.Core/Session/WorldSession.cs`:
- Around line 144-152: Serialize trigger response sends in
WorldSession.ProcessOutputLine by awaiting each SendRawAsync call or routing
responses through a single-writer queue, and observe/log any resulting
exceptions instead of discarding tasks; update
src/MuClient.Core/Session/WorldSession.cs lines 144-152. Add synchronization
around the stream.WriteAsync operation in TcpTransport.SendAsync, using a
SemaphoreSlim or equivalent to prevent overlapping writes from any caller;
update src/MuClient.Core/Transport/TcpTransport.cs lines 74-78.
- Around line 80-110: Update ConnectAsync to clean up any existing _telnet
before replacing it with a newly created transport, including detaching all
handlers registered in ConnectAsync and disposing the previous instance. Ensure
this cleanup applies when reconnecting from Faulted or Disconnected states while
preserving the existing connection-state guard and new connection setup.

In `@src/MuClient.Core/Telnet/TelnetSession.cs`:
- Around line 69-85: Update ConnectAsync to clean up the connected transport
when BuildInterpreterAsync or subsequent setup fails before the read loop is
established. Wrap the post-connect initialization around BuildInterpreterAsync,
ByteCallbackProperty.SetValue, and linked cancellation setup in failure handling
that disconnects the transport, preserves the original exception, and leaves the
session state unconnected.
- Around line 231-253: Update DisconnectAsync to clear _interpreter after the
disconnect cleanup completes, matching the reset performed by DisposeAsync.
Ensure subsequent ConnectAsync calls can reconnect and SendAsync, SendGmcpAsync,
and SetWindowSizeAsync observe the “not connected” state.
- Around line 206-223: Update TelnetSession.SendAsync to forward its
cancellationToken to the interpreter send operation, and ensure SendLineAsync
preserves that forwarding through SendAsync. For SendGmcpAsync, pass the token
to the interpreter if supported; otherwise remove the unused cancellation-token
parameter from the affected methods so their API does not imply cancellable
writes.

In `@src/MuClient.Core/Text/AnsiParser.cs`:
- Around line 143-171: Update ProcessEscape to route ESC P, ESC X, ESC ^, and
ESC _ into the existing string-consuming state so their payloads are consumed
through ST instead of emitted as text. Preserve the current OSC behavior,
including its termination handling, and add coverage in AnsiParserTests
confirming a DCS payload followed by ST leaves only the subsequent visible text.

In `@src/MuClient.Graphics/InlineImage.cs`:
- Around line 105-111: Update the Kitty branch in InlineImageRenderer to
transmit the virtual placement before the image data and placeholder grid,
preserving the required U+10EEEE anchoring behavior. Extend InlineImageOutput to
represent the combined virtual-placement and image transmission, then update
affected Kitty tests to validate the transmission order and placeholder result.

In `@src/MuClient.Graphics/KittyGraphicsProtocol.cs`:
- Around line 155-172: Update BuildPlaceholder to prevent image IDs above 24
bits from being silently truncated: either validate and reject values greater
than 0x00FFFFFF at the public API boundary, or encode the high byte using
Kitty’s supported third cell diacritic. Preserve correct round-trip identity for
every accepted image ID.

In `@src/MuClient.Tui/MuGlyphApp.cs`:
- Around line 88-93: Update the connection flow around session.ConnectAsync in
MuGlyphApp so the continuation that reads _output.Viewport and calls
SetWindowSizeAsync executes on the UI thread. Remove the ConfigureAwait(false)
from this UI-dependent path or explicitly marshal the post-connect work through
the same Application.Invoke mechanism used by the session-driven UI updates
elsewhere in the file.
- Around line 121-130: Update OnCommandEntered so failures from the
fire-and-forget session.SendUserInputAsync call are observed and surfaced to the
user through the existing TUI feedback or error-reporting mechanism. Preserve
the current null-session early return and command dispatch behavior while
ensuring transport exceptions do not remain unhandled.
- Around line 146-153: Update the Ctrl+Q check in the key handling switch’s
default branch to compare against Terminal.Gui’s uppercase Q key code, such as
KeyCode.Q, instead of casting the lowercase character 'q'. Preserve the existing
Application.RequestStop and key.Handled behavior.

In `@tests/MuClient.Core.Tests/Automation/IntervalSchedulerTests.cs`:
- Around line 12-14: Update both scheduler tests, including After_FiresOnce and
the additionally referenced test, to replace fixed Task.Delay waits with a
TaskCompletionSource completed by each timer callback. Await the callback signal
with a bounded timeout, then retain the existing count assertions.

---

Nitpick comments:
In `@src/MuClient.Scripting/ScriptException.cs`:
- Around line 27-49: Update TryExtractLine to also parse MoonSharp runtime
messages in the “[source]:LINE: message” format when no parenthesized range is
present. Preserve the existing parenthesized-range parsing and return null
safely for malformed or unrecognized DecoratedMessage values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f584113b-77f8-4be6-8a9e-db29db599f7b

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7a3d8 and 74d07b2.

📒 Files selected for processing (90)
  • .github/workflows/ci.yml
  • CLAUDE.md
  • Directory.Build.props
  • Directory.Packages.props
  • MuGlyph.slnx
  • README.md
  • examples/example.lua
  • src/MuClient.Core/Automation/Alias.cs
  • src/MuClient.Core/Automation/AliasEngine.cs
  • src/MuClient.Core/Automation/IntervalScheduler.cs
  • src/MuClient.Core/Automation/Macro.cs
  • src/MuClient.Core/Automation/MacroEngine.cs
  • src/MuClient.Core/Automation/Trigger.cs
  • src/MuClient.Core/Automation/TriggerEngine.cs
  • src/MuClient.Core/Configuration/AppConfiguration.cs
  • src/MuClient.Core/Configuration/BeipMuImporter.cs
  • src/MuClient.Core/Configuration/ConfigurationStore.cs
  • src/MuClient.Core/Configuration/RgbJsonConverter.cs
  • src/MuClient.Core/Configuration/TerminalColorJsonConverter.cs
  • src/MuClient.Core/Configuration/WorldDefinition.cs
  • src/MuClient.Core/Logging/HtmlLogSink.cs
  • src/MuClient.Core/Logging/ILogSink.cs
  • src/MuClient.Core/Logging/PlainTextLogSink.cs
  • src/MuClient.Core/MuClient.Core.csproj
  • src/MuClient.Core/Session/SessionEvents.cs
  • src/MuClient.Core/Session/SessionManager.cs
  • src/MuClient.Core/Session/WorldSession.cs
  • src/MuClient.Core/Telnet/ITelnetSession.cs
  • src/MuClient.Core/Telnet/MsspConfigReader.cs
  • src/MuClient.Core/Telnet/TelnetEvents.cs
  • src/MuClient.Core/Telnet/TelnetSession.cs
  • src/MuClient.Core/Text/AnsiPalette.cs
  • src/MuClient.Core/Text/AnsiParser.cs
  • src/MuClient.Core/Text/ScrollbackBuffer.cs
  • src/MuClient.Core/Text/StyledLine.cs
  • src/MuClient.Core/Text/StyledSpan.cs
  • src/MuClient.Core/Text/StyledText.cs
  • src/MuClient.Core/Text/TerminalColor.cs
  • src/MuClient.Core/Text/TextStyle.cs
  • src/MuClient.Core/Theming/Theme.cs
  • src/MuClient.Core/Theming/ThemeLibrary.cs
  • src/MuClient.Core/Transport/ConnectionOptions.cs
  • src/MuClient.Core/Transport/ITransport.cs
  • src/MuClient.Core/Transport/TcpTransport.cs
  • src/MuClient.Graphics/CapabilityProbe.cs
  • src/MuClient.Graphics/GraphicsProtocol.cs
  • src/MuClient.Graphics/HalfBlockRenderer.cs
  • src/MuClient.Graphics/IImageSource.cs
  • src/MuClient.Graphics/InlineImage.cs
  • src/MuClient.Graphics/KittyGraphicsProtocol.cs
  • src/MuClient.Graphics/MuClient.Graphics.csproj
  • src/MuClient.Graphics/SixelEncoder.cs
  • src/MuClient.Graphics/TerminalCapabilities.cs
  • src/MuClient.Scripting/IScriptWorld.cs
  • src/MuClient.Scripting/MuClient.Scripting.csproj
  • src/MuClient.Scripting/ScriptException.cs
  • src/MuClient.Scripting/ScriptHost.cs
  • src/MuClient.Scripting/WorldSessionScriptBridge.cs
  • src/MuClient.Tui/ColorMapper.cs
  • src/MuClient.Tui/MuClient.Tui.csproj
  • src/MuClient.Tui/MuGlyphApp.cs
  • src/MuClient.Tui/Program.cs
  • src/MuClient.Tui/Views/CommandInput.cs
  • src/MuClient.Tui/Views/OutputView.cs
  • tests/MuClient.Core.Tests/Automation/AliasAndMacroTests.cs
  • tests/MuClient.Core.Tests/Automation/IntervalSchedulerTests.cs
  • tests/MuClient.Core.Tests/Automation/TriggerEngineTests.cs
  • tests/MuClient.Core.Tests/Configuration/ConfigurationTests.cs
  • tests/MuClient.Core.Tests/Logging/LogSinkTests.cs
  • tests/MuClient.Core.Tests/MuClient.Core.Tests.csproj
  • tests/MuClient.Core.Tests/Session/FakeTelnetSession.cs
  • tests/MuClient.Core.Tests/Session/WorldSessionTests.cs
  • tests/MuClient.Core.Tests/Telnet/FakeTransport.cs
  • tests/MuClient.Core.Tests/Telnet/TelnetSessionTests.cs
  • tests/MuClient.Core.Tests/Text/AnsiParserTests.cs
  • tests/MuClient.Core.Tests/Text/ScrollbackBufferTests.cs
  • tests/MuClient.Core.Tests/Text/StyledTextTests.cs
  • tests/MuClient.Core.Tests/Theming/ThemeTests.cs
  • tests/MuClient.Graphics.Tests/CapabilityProbeTests.cs
  • tests/MuClient.Graphics.Tests/HalfBlockRendererTests.cs
  • tests/MuClient.Graphics.Tests/InlineImageRendererTests.cs
  • tests/MuClient.Graphics.Tests/KittyGraphicsProtocolTests.cs
  • tests/MuClient.Graphics.Tests/MemoryImageSourceTests.cs
  • tests/MuClient.Graphics.Tests/MuClient.Graphics.Tests.csproj
  • tests/MuClient.Graphics.Tests/SixelEncoderTests.cs
  • tests/MuClient.Scripting.Tests/FakeScriptWorld.cs
  • tests/MuClient.Scripting.Tests/MuClient.Scripting.Tests.csproj
  • tests/MuClient.Scripting.Tests/ReloadTests.cs
  • tests/MuClient.Scripting.Tests/SandboxTests.cs
  • tests/MuClient.Scripting.Tests/ScriptHostTests.cs

Comment thread .github/workflows/ci.yml
Comment thread CLAUDE.md Outdated
Comment thread src/MuClient.Core/Automation/Alias.cs Outdated
Comment on lines +29 to +32
[JsonIgnore]
public Regex Regex => _compiled ??= new Regex(
Pattern,
RegexOptions.Compiled | (CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files matching Alias.cs:\n'
fd -a 'Alias\.cs$' . || true

printf '\nGit status/stat:\n'
git diff --stat || true

alias_file="$(fd 'Alias\.cs$' . | head -n 1 || true)"
if [ -n "${alias_file:-}" ]; then
  printf '\nFile outline:\n'
  ast-grep outline "$alias_file" || true
  printf '\nRelevant source:\n'
  cat -n "$alias_file"
fi

printf '\nSearch for Pattern/Regex usage in project:\n'
rg -n "class Alias|Pattern|Regex|RegexMatchTimeoutException|new Regex|RegexTimeout" -S --glob '*.cs' . || true

printf '\nSearch for expanded command handling / Alias Regex calls:\n'
rg -n "Regex\.(Match|IsMatch|Matches|Replace|Expand)|\.Regex|Alias" -S --glob '*.cs' . | head -n 200 || true

Repository: HarryCordewener/MuGlyph

Length of output: 16603


🏁 Script executed:

#!/bin/bash
set -euo pipefail

alias_engine="$(fd 'AliasEngine\.cs$' . | head -n 1 || true)"
world_session="$(fd 'WorldSession\.cs$' . | head -n 1 || true)"

if [ -n "${alias_engine:-}" ]; then
  printf 'AliasEngine.cs:\n'
  cat -n "$alias_engine"
fi

if [ -n "${world_session:-}" ]; then
  printf '\nWorldSession.cs around Expand/Command handling:\n'
  sed -n '130,185p' "$world_session" | cat -n
fi

printf '\nCheck whether RegexMatchTimeoutException is caught anywhere:\n'
rg -n "RegexMatchTimeoutException|catch\s*\(" --glob '*.cs' . || true

printf '\nSearch for command send handling around alias expansion call:\n'
rg -n -C 4 "Aliases\.Expand|commands\.Select|Send|SendCommands|input" src tests --glob '*.cs' || true

Repository: HarryCordewener/MuGlyph

Length of output: 47446


Bound alias-regex execution time.

Pattern is persisted/imported configuration, and AliasEngine.Expand() uses alias.Regex.Match(input) before sending user input. Give Regex a timeout, and handle RegexMatchTimeoutException in AliasEngine so a pathological alias does not hang user input handling.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 29-31: A Regex is constructed from a non-literal (variable) pattern without a matchTimeout. The .NET regex engine backtracks, so an attacker-controlled pattern can cause catastrophic backtracking (ReDoS) and hang the thread. Pass a TimeSpan matchTimeout (e.g. new Regex(pattern, RegexOptions.None, TimeSpan.FromSeconds(1))), set AppDomain RegexMatchTimeout, or avoid compiling untrusted patterns at all.
Context: new Regex(
Pattern,
RegexOptions.Compiled | (CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase))
Note: [CWE-1333] Inefficient Regular Expression Complexity.

(redos-regex-untrusted-pattern-no-timeout-csharp)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MuClient.Core/Automation/Alias.cs` around lines 29 - 32, Update the
Alias.Regex property to construct the compiled Regex with a finite execution
timeout for persisted patterns. In AliasEngine.Expand, catch
RegexMatchTimeoutException around alias.Regex.Match(input) and handle the
timeout by skipping or safely rejecting that alias without hanging user input
processing.

Source: Linters/SAST tools

Comment thread src/MuClient.Core/Automation/IntervalScheduler.cs
Comment thread src/MuClient.Core/Automation/Trigger.cs
Comment on lines +155 to +172
// Carry the 24-bit image id in the foreground colour, per the Kitty spec.
var idColor = TerminalColor.FromRgb(
(byte)((imageId >> 16) & 0xFF),
(byte)((imageId >> 8) & 0xFF),
(byte)(imageId & 0xFF));
var style = TextStyle.Default.WithForeground(idColor);

var lines = new StyledLine[rows];
for (var row = 0; row < rows; row++)
{
var spans = new StyledSpan[cols];
for (var col = 0; col < cols; col++)
{
var cell = new StringBuilder(4);
cell.Append(char.ConvertFromUtf32(PlaceholderCodePoint));
cell.Append(char.ConvertFromUtf32(RowColumnDiacritics[row]));
cell.Append(char.ConvertFromUtf32(RowColumnDiacritics[col]));
spans[col] = new StyledSpan(cell.ToString(), style);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

rg -n -C3 'BuildPlaceholder|imageId >> 16|RowColumnDiacritics' \
  src/MuClient.Graphics/KittyGraphicsProtocol.cs \
  tests/MuClient.Graphics.Tests/KittyGraphicsProtocolTests.cs

Repository: HarryCordewener/MuGlyph

Length of output: 8077


🌐 Web query:

Kitty graphics protocol placeholder image ID diacritics high byte foreground color rowcolumn-diacritics

💡 Result:

The Kitty graphics protocol uses a Unicode placeholder mechanism (enabled by U=1 in the virtual placement command) to allow images to be displayed in environments that support text but not arbitrary graphics, such as multiplexers (tmux) or text editors (vim, Emacs) [1][2]. Placeholder Character The terminal uses the Unicode character U+10EEEE as a placeholder for an image [1][3]. This character is in the Private Use Area (PUA) and is designed to be easily distinguished from normal text [2]. Encoding Image IDs The image ID is encoded in the foreground color of the placeholder character [1]. By default, this uses the terminal's foreground color attribute [2]. - 8-bit IDs can be represented using 256-color mode [1]. - 24-bit IDs can be represented using true-color mode [1]. - For image IDs exceeding 24 bits, the most significant byte can be specified using a third diacritic [1]. Row and Column Encoding The specific row and column position of the image within the placeholder grid is encoded using combining diacritics [1]. These diacritics are appended to the placeholder character [1][3]. The terminal refers to a specific set of diacritics, documented in the protocol as rowcolumn-diacritics (often provided in a file named rowcolumn-diacritics.txt) [1][2][4]. Example Usage To print a placeholder, you emit the placeholder character followed by its diacritics: 1. Foreground color (encoding the image ID) [5][3]. 2. The placeholder character (U+10EEEE) [1][3]. 3. The row diacritic [1][5]. 4. The column diacritic [1][5]. 5. Optionally, a third diacritic for the high byte of the image ID if needed [1][4]. This approach allows host applications to manage image placement while the terminal handles the actual rendering based on the decoded ID and position [1][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '1,220p' src/MuClient.Graphics/KittyGraphicsProtocol.cs

Repository: HarryCordewener/MuGlyph

Length of output: 7530


Reject or fully encode image IDs above 24 bits.

BuildPlaceholder encodes only bits 0–23 in the RGBA foreground, so imageId = 0x01010001 emits placeholders for image id 1. Kitty supports encoding the high byte with a third diacritic, so either limit the public API to 0x00FFFFFF or carry the high byte in the cell diacritics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MuClient.Graphics/KittyGraphicsProtocol.cs` around lines 155 - 172,
Update BuildPlaceholder to prevent image IDs above 24 bits from being silently
truncated: either validate and reject values greater than 0x00FFFFFF at the
public API boundary, or encode the high byte using Kitty’s supported third cell
diacritic. Preserve correct round-trip identity for every accepted image ID.

Comment on lines +88 to +93
try
{
await session.ConnectAsync().ConfigureAwait(false);
var size = _output.Viewport;
await session.SetWindowSizeAsync(Math.Max(1, size.Width), Math.Max(1, size.Height)).ConfigureAwait(false);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

ConfigureAwait(false) moves post-connect UI work off the UI thread.

After ConnectAsync().ConfigureAwait(false), the continuation (reading _output.Viewport, then calling SetWindowSizeAsync) resumes on a thread-pool thread instead of the UI thread. Everywhere else in this file (Lines 106-118), session-driven UI touches are explicitly marshaled back via Application.Invoke; this call site reads View state off-thread instead.

🔧 Proposed fix
-            await session.ConnectAsync().ConfigureAwait(false);
-            var size = _output.Viewport;
-            await session.SetWindowSizeAsync(Math.Max(1, size.Width), Math.Max(1, size.Height)).ConfigureAwait(false);
+            await session.ConnectAsync();
+            var size = _output.Viewport;
+            await session.SetWindowSizeAsync(Math.Max(1, size.Width), Math.Max(1, size.Height));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try
{
await session.ConnectAsync().ConfigureAwait(false);
var size = _output.Viewport;
await session.SetWindowSizeAsync(Math.Max(1, size.Width), Math.Max(1, size.Height)).ConfigureAwait(false);
}
try
{
await session.ConnectAsync();
var size = _output.Viewport;
await session.SetWindowSizeAsync(Math.Max(1, size.Width), Math.Max(1, size.Height));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MuClient.Tui/MuGlyphApp.cs` around lines 88 - 93, Update the connection
flow around session.ConnectAsync in MuGlyphApp so the continuation that reads
_output.Viewport and calls SetWindowSizeAsync executes on the UI thread. Remove
the ConfigureAwait(false) from this UI-dependent path or explicitly marshal the
post-connect work through the same Application.Invoke mechanism used by the
session-driven UI updates elsewhere in the file.

Comment on lines +121 to +130
private void OnCommandEntered(string command)
{
var session = _active;
if (session is null)
{
return;
}

_ = session.SendUserInputAsync(command);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fire-and-forget SendUserInputAsync swallows failures silently.

If sending fails (e.g. transport error), the exception is unobserved and the user gets no feedback that their command didn't go out.

🔧 Proposed fix
-        _ = session.SendUserInputAsync(command);
+        _ = SendCommandAsync(session, command);
     }
+
+    private async Task SendCommandAsync(WorldSession session, string command)
+    {
+        try
+        {
+            await session.SendUserInputAsync(command);
+        }
+        catch (Exception ex)
+        {
+            session.PrintSystem($"*** Send failed: {ex.Message}");
+        }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void OnCommandEntered(string command)
{
var session = _active;
if (session is null)
{
return;
}
_ = session.SendUserInputAsync(command);
}
private void OnCommandEntered(string command)
{
var session = _active;
if (session is null)
{
return;
}
_ = SendCommandAsync(session, command);
}
private async Task SendCommandAsync(WorldSession session, string command)
{
try
{
await session.SendUserInputAsync(command);
}
catch (Exception ex)
{
session.PrintSystem($"*** Send failed: {ex.Message}");
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MuClient.Tui/MuGlyphApp.cs` around lines 121 - 130, Update
OnCommandEntered so failures from the fire-and-forget session.SendUserInputAsync
call are observed and surfaced to the user through the existing TUI feedback or
error-reporting mechanism. Preserve the current null-session early return and
command dispatch behavior while ensuring transport exceptions do not remain
unhandled.

Comment thread src/MuClient.Tui/MuGlyphApp.cs
Comment thread tests/MuClient.Core.Tests/Automation/IntervalSchedulerTests.cs Outdated
CI failed on the timing-sensitive scheduler tests (fixed Task.Delay waits
were too tight on the runner). Replace them with signal-based waits and fold
in the safe, high-value CodeRabbit review findings.

Tests / CI
- IntervalScheduler tests now await a TaskCompletionSource signalled by the
  callback with a generous bounded timeout, instead of racing a fixed delay.
- CI checkout uses persist-credentials: false.

Correctness
- IntervalScheduler: guard timer callbacks so a throwing callback can neither
  crash the ThreadPool thread/process nor stop a recurring schedule.
- Trigger/Alias: compile user regexes with a 250ms match timeout and skip a
  pattern that times out (ReDoS guard) rather than blocking output/input.
- TcpTransport: serialize SendAsync with a semaphore so concurrent negotiation,
  command, and trigger-response writes cannot interleave and corrupt framing.
- AnsiParser: consume DCS/SOS/PM/APC (ESC P/X/^/_) string sequences through ST
  instead of leaking their payloads as text (APC covers Kitty graphics).
- HtmlLogSink: make Dispose/Flush idempotent (no writes after close).
- Tui: Ctrl+Q now compares against KeyCode.Q (uppercase) so quit actually fires.

Docs
- Correct the project count (seven) in CLAUDE.md.

198 tests pass (Core 102, Graphics 54, Scripting 42).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015w2WJ5M22CZmg9md67zpeB
@HarryCordewener
HarryCordewener merged commit a9d8504 into main Jul 27, 2026
3 checks passed
@HarryCordewener
HarryCordewener deleted the claude/muglyph-implementation-5f51l3 branch July 27, 2026 23:33
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.

2 participants