Class: Rubino::Commands::Handlers::Help

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/commands/handlers/help.rb

Overview

The ‘/help` and `/commands` listings (and the unknown-command “Available:” roster), extracted from Commands::Executor (batch B). A plain collaborator given the command `loader` and the `ui` — it owns the built-in/keys/input reference text and the custom-command discovery copy.

Instance Method Summary collapse

Constructor Details

#initialize(ui:, loader:) ⇒ Help

Returns a new instance of Help.



11
12
13
14
# File 'lib/rubino/commands/handlers/help.rb', line 11

def initialize(ui:, loader:)
  @ui = ui
  @loader = loader
end

Instance Method Details

#available_commandsObject

All known slash commands (built-ins + discovered custom), used for the “Available:” hint on an unknown command (L6 — previously listed only custom commands, which is usually empty).



19
20
21
22
23
24
25
26
# File 'lib/rubino/commands/handlers/help.rb', line 19

def available_commands
  custom = begin
    @loader.names
  rescue StandardError
    []
  end
  (BuiltIns::NAMES + custom).uniq
end

#closest_command(name) ⇒ Object

Closest known slash command to a mistyped name (no leading slash), rendered WITH its slash for the “Did you mean /X?” hint, or nil when none is close enough (FRICTION-4). Uses Ruby’s stdlib SpellChecker (DidYouMean) — the same Levenshtein matcher Thor/Bundler use — so the distance threshold scales with command length. Best-effort: any matcher hiccup just yields no suggestion.



34
35
36
37
38
39
40
41
42
# File 'lib/rubino/commands/handlers/help.rb', line 34

def closest_command(name)
  require "did_you_mean"
  bare  = name.to_s.delete_prefix("/")
  names = available_commands.map { |c| c.to_s.delete_prefix("/") }
  match = DidYouMean::SpellChecker.new(dictionary: names).correct(bare).first
  match && "/#{match}"
rescue StandardError
  nil
end

#show_commandsObject



106
107
108
109
110
111
112
113
114
# File 'lib/rubino/commands/handlers/help.rb', line 106

def show_commands
  commands = @loader.all
  return explain_empty_commands if commands.empty?

  @ui.info("Custom commands  (run with /<name>; add --preview to see the prompt first):")
  commands.each do |cmd|
    @ui.info("  /#{cmd.name}#{custom_desc(cmd)}")
  end
end

#show_helpObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/rubino/commands/handlers/help.rb', line 54

def show_help
  @ui.info("Slash commands run actions or reusable prompts. Type /<name>; /help is this list.")
  @ui.blank_line
  @ui.info("Built-in:")
  rows  = help_builtin_rows
  width = rows.map { |name, _| name.length }.max
  rows.each do |name, desc|
    help_line("  #{name.ljust(width)}  - #{desc}")
  end
  @ui.blank_line

  # The `@` file-picker is a discoverable composer feature (type `@` to
  # autocomplete a workspace file) but was undocumented in /help (F14).
  # /paste and /clear-images already appear once under "Built-in" above,
  # so they're NOT repeated here — this section is image/file INPUT only,
  # no command rows (#87 de-dup).
  @ui.info("Input:")
  help_line("  ! <command>   - run a shell command yourself, no approval; output joins the context")
  help_line("  @<path>       - autocomplete a workspace file into the prompt")
  help_line("  @<image>      - attach an image (png/jpg/jpeg/gif/webp/bmp) to the turn")
  help_line("  <image path>  - drop or paste an image file path to attach it")
  @ui.blank_line

  # The keystroke vocabulary was invisible in /help (#87): a newcomer
  # couldn't learn how to cancel a turn, drive the approval menu, or that
  # Tab completes. One compact reference line covers it.
  @ui.info("Keys:")
  help_line("  ↑/↓ + Enter   - choose in the approval menu")
  help_line("  Enter         - send; during a turn, interrupt it and run this next")
  help_line("  Alt-Enter     - queue this to run after the current turn (or /queued <msg>)")
  help_line("  Shift-Tab     - cycle mode (default → plan → yolo)")
  help_line("  Tab           - complete the highlighted /command or @file (empty input: cycle agent)")
  help_line("  Ctrl-O        - reveal the last reasoning (collapsed or hidden)")
  help_line("  Ctrl-C        - cancel the turn (twice to exit)")
  help_line("  Esc Esc       - rewind to an earlier message (fork + edit & resend)")
  help_line("  /             - start a command;  @  attach a file/image")
  @ui.blank_line

  show_agents_help
  @ui.blank_line

  custom = @loader.all
  if custom.any?
    @ui.info("Custom commands  (run with /<name>; add --preview to see the prompt first):")
    custom.each do |cmd|
      @ui.info("  /#{cmd.name}#{custom_desc(cmd)}")
    end
  else
    @ui.info("Custom commands  (none yet — run /commands to learn how to add one)")
  end
end

#suggest_and_list(name) ⇒ Object

The unknown-command tail (FRICTION-4): a “Did you mean /X?” line for the closest match (when one is close enough) followed by the full Available roster. Lives here next to the command list it reads.



47
48
49
50
51
52
# File 'lib/rubino/commands/handlers/help.rb', line 47

def suggest_and_list(name)
  suggestion = closest_command(name)
  @ui.info("Did you mean #{suggestion}?") if suggestion
  @ui.info("Available: #{available_commands.join(", ")}")
  :handled
end