Skip to content

feat: SquidStd.ConsoleCommands - interactive console commands with a fixed prompt#55

Merged
tgiachi merged 5 commits into
developfrom
feature/consolecommands
Jul 3, 2026
Merged

feat: SquidStd.ConsoleCommands - interactive console commands with a fixed prompt#55
tgiachi merged 5 commits into
developfrom
feature/consolecommands

Conversation

@tgiachi

@tgiachi tgiachi commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

New package SquidStd.ConsoleCommands - interactive operator commands with a fixed prompt, ported (and generalized) from the moongatev2 console system.

The stack (container.AddConsoleCommands())

  • Command system: registry with | aliases, quoted-argument tokenizer (say "hello world"), case-insensitive dispatch, handler exceptions reported and logged but never fatal, ExecuteCommandWithOutputAsync output capture (ready for future remote sources), prefix + per-command autocomplete providers.
  • Interactive prompt UI (Spectre.Console): fixed prompt row with log lines rendered above it, locked mode with a configurable unlock character, TAB completion with cycling (Shift+Tab reverses), input history, graceful degradation to plain WriteLine in non-TTY environments (tests/CI).
  • ConsolePromptLogSink: registered as a container ILogEventSink, picked up automatically by the bootstrap logger hook - set logger.EnableConsole: false and the prompt owns the terminal.
  • Builtins: help/?, clear/cls, exit/quit (resolves the bootstrap and stops it - engine events and lifecycle logs included).
  • Config section consoleCommands: Prompt, StartLocked, UnlockCharacter.
  • [RegisterConsoleCommand("ping|p", "Replies pong.")] on IConsoleCommandExecutor classes + source generator emitting RegisterGeneratedConsoleCommands() (lazy wiring via RegisterInitializer<ICommandSystemService>, new SQDGEN006 diagnostic).

Also

  • Sample SquidStd.Samples.ConsoleCommands (runtime-verified: help list + pong, single log stream); package README + docs article + toc.
  • Post-review hardening: empty alias list throws, redefining an alias evicts the displaced entry (truthful help), sample log duplication fixed.

Test Plan

  • 25 package tests (tokenizer, registry/dispatch/aliases/redefine, output capture, autocomplete, builtins incl. exit-stop spy, registration with lazy config, non-TTY UI paths) + 6 generator tests
  • Full suite 1060/1060; build 0 errors; docfx 0 warnings; sample runtime verified
  • Manual interactive smoke pending on a real TTY (prompt, unlock, TAB, logs above prompt) - not automatable in CI

Follow-ups noted (non-blocking)

  • exit stops all services but a RunAsync-hosted app keeps waiting on its infinite delay - the bootstrap needs a shutdown signal (next batch candidate).
  • Align the permanent non-TTY fallback across all ConsoleUiService render paths.

NOTE: do NOT promote to main yet - more features are landing before the next release.

tgiachi added 5 commits July 3, 2026 15:57
- feat(generators): ConsoleCommandRegistrationGenerator emitting RegisterGeneratedConsoleCommands with singleton registration and deferred RegisterCommand wiring via DryIoc RegisterInitializer
- feat(generators): SQDGEN006 diagnostic for unsupported console command classes
- feat(samples): SquidStd.Samples.ConsoleCommands with attribute, delegate and builtin command usage
- test(generators): ConsoleCommandRegistrationGeneratorTests covering generation, description fallback, no-op and diagnostics
- docs: SquidStd.ConsoleCommands package README, docfx article and toc entry
…ries on redefine, single log stream in the sample
@tgiachi
tgiachi merged commit 106ab6f into develop Jul 3, 2026
2 checks passed
Comment on lines +56 to +73
foreach (var alias in aliases)
{
if (!_commands.TryAdd(alias, entry))
{
_logger.Warning("Console command '{Command}' redefined", alias);
var displaced = _commands[alias];
_commands[alias] = entry;

// Keep help output truthful: drop every other key that still points at the
// displaced entry, so its stale alias list is no longer reported.
foreach (var staleKey in _commands.Where(pair => ReferenceEquals(pair.Value, displaced))
.Select(pair => pair.Key)
.ToArray())
{
_commands.TryRemove(staleKey, out _);
}
}
}
Comment on lines +161 to +165
catch (Exception ex)
{
_logger.Error(ex, "Console command '{Command}' failed", name);
writeLine($"Command '{name}' failed: {ex.Message}");
}
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