Class: Rubino::CLI::Chat::CompletionBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/cli/chat/completion_builder.rb

Overview

Builds the composer’s CompletionSource: the ‘/command` + `@file` candidates plus the per-command ARGUMENT grammars (the dropdown that completes the argument of /skills, /agents, /mcp, /sessions, /memory, /config, … the same way it completes a command or a file). Extracted out of ChatCommand — a self-contained candidate/data-generation block (#17 collaborator pattern).

Given the command loader it needs; every source is best-effort (a DB or registry hiccup degrades to no candidates, never a broken prompt) and read lazily on each dropdown open so a /model, /config or skill change is reflected immediately.

Constant Summary collapse

AGENTS_SUBCOMMANDS =

The /agents subcommand grammar offered by the dropdown (#39): first an id, then what you can do to it.

["steer", "probe", "--stop"].freeze
MCP_SUBCOMMANDS =

The /mcp subcommand grammar (#182): configured server names + reload first, then the on/off verbs for a named server.

%w[on off].freeze
SESSIONS_SUBCOMMANDS =

The /sessions subcommand grammar (#183): verbs + recent session ids first (bare id resumes, verb then id shows/deletes), then ids after a verb. Mirrors the /agents grammar so the picker teaches the surface.

["show", "delete", "--all"].freeze
MEMORY_SUBCOMMANDS =

The /memory subcommand grammar (#184): verbs first, then recent fact ids after show/forget (short ids — the store resolves prefixes) or the registered backend names after backend.

["search", "show", "forget", "backend", "--all"].freeze
SKILLS_SUBCOMMANDS =

The /skills grammar (#188): position one mixes the ‘✗ none` clear entry (CompletionSource keeps its special matching), the enable/disable verbs and the activate-by-name skill list; after a toggle verb, the names complete again. Activate-by-name and `✗ none` behave exactly as before.

%w[enable disable].freeze
CONFIG_SUBCOMMANDS =

The /config grammar (#187): verbs + the known config keys first (a bare key gets, key+value sets), keys again after get/set.

%w[get set show path].freeze

Instance Method Summary collapse

Constructor Details

#initialize(cmd_loader) ⇒ CompletionBuilder

Returns a new instance of CompletionBuilder.



45
46
47
# File 'lib/rubino/cli/chat/completion_builder.rb', line 45

def initialize(cmd_loader)
  @cmd_loader = cmd_loader
end

Instance Method Details

#buildObject



49
50
51
52
53
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
# File 'lib/rubino/cli/chat/completion_builder.rb', line 49

def build
  custom = begin
    @cmd_loader.names
  rescue StandardError
    []
  end
  names  = (::Rubino::Commands::BuiltIns::NAMES + custom).uniq
  files  = -> { Rubino::Workspace.primary_root }
  # ARGUMENT sources: the dropdown completes the argument of these commands
  # the same way it completes `/command` and `@file`.
  #   * /skills <partial> — a skill name (lazily re-read each open so a
  #     freshly-authored skill appears), TRUST-aligned with the prompt
  #     assembler (#63) so the picker never offers a skill that won't pin.
  #   * /agents (alias /tasks) — the live subagent ids, then the
  #     steer/probe/--stop subcommand grammar, so the comm surface is
  #     discoverable from the composer (#39).
  #   * /reply — the ids of children blocked waiting on the human.
  #   * /mcp — the configured server names (+ reload), then on/off for a
  #     named server (#182), same grammar shape as /agents.
  #   * /mode, /reasoning, /think — the closed enums (#185), via the
  #     positional shape so no `✗ none` clear entry is injected (there
  #     is no "clear" for a mode — see CompletionSource#initialize).
  #   * /model — the ruby_llm-registry model ids for the active provider
  #     (empty for custom backends like minimax/gateway, which aren't
  #     enumerable — the dropdown just shows nothing extra there).
  #   * /add-dir — filesystem DIRECTORY candidates from the typed
  #     partial (#185), via the partial-aware two-arg shape.
  #   * /sessions, /memory — verbs + recent ids (#183/#184), the same
  #     per-position grammar /agents ships.
  #   * /jobs — recent job ids (#187); /config — the get/set/show/path
  #     verbs + the known config keys flattened from the defaults tree.
  #   * /skills — the `✗ none` clear entry + the enable/disable verbs +
  #     the skill names (#188); after a toggle verb, the names again.
  arg_sources = {
    "skills" => ->(args) { skills_arg_candidates(args) },
    "agents" => ->(args) { agents_arg_candidates(args) },
    "tasks" => ->(args) { agents_arg_candidates(args) },
    "reply" => ->(args) { args.empty? ? blocked_subagent_ids : [] },
    "mcp" => ->(args) { mcp_arg_candidates(args) },
    "mode" => ->(args) { args.empty? ? Rubino::Modes::ALL.map(&:to_s) : [] },
    "model" => ->(args) { args.empty? ? model_arg_candidates : [] },
    "reasoning" => ->(args) { args.empty? ? Rubino::Config::ReasoningPrefs::RENDER_MODES.map(&:to_s) : [] },
    "think" => ->(args) { args.empty? ? Rubino::Config::ReasoningPrefs::EFFORTS.map(&:to_s) : [] },
    "add-dir" => lambda { |args, partial|
      args.empty? ? Rubino::UI::CompletionSource.directory_candidates(partial) : []
    },
    "sessions" => ->(args) { sessions_arg_candidates(args) },
    "memory" => ->(args) { memory_arg_candidates(args) },
    "jobs" => ->(args) { args.empty? ? recent_job_ids : [] },
    "config" => ->(args) { config_arg_candidates(args) }
  }
  Rubino::UI::CompletionSource.new(commands: names, files: files,
                                   arg_sources: arg_sources,
                                   descriptions: completion_descriptions)
end