Class: Rubino::Commands::Handlers::Skills

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

Overview

The ‘/skills` list/activate/enable/disable surface, extracted from Commands::Executor (batch B).

‘/skills` → list (unchanged behavior). `/skills <name>` → ACTIVATE that skill for the session (sticky).

The name is validated against the registry; an
unknown OR DISABLED name errors and leaves the
active skill unchanged.

‘/skills none` → CLEAR the active skill (also the `✗ none`

picker entry, whose spliced label is
normalized here).

‘/skills enable <name>` → persistently re-enable a skill (#188) — the `/skills disable <name>` same StateRepository write the HTTP API

toggle and the `rubino skills` CLI verbs run
(Skills::Toggle), affecting EVERY session,
unlike the session-scoped activation.

The active skill is stored in Rubino::ActiveSkill (a process-level slot, mirroring Rubino::Modes) so it survives across turns and is force-loaded into the system prompt each turn (Context::PromptAssembler).

Constant Summary collapse

TOGGLE_VERBS =

The /skills toggle verbs (#188) — the same registry-validated StateRepository write the HTTP API and ‘rubino skills` CLI run.

%w[enable disable].freeze

Instance Method Summary collapse

Constructor Details

#initialize(ui:) ⇒ Skills

Returns a new instance of Skills.



31
32
33
# File 'lib/rubino/commands/handlers/skills.rb', line 31

def initialize(ui:)
  @ui = ui
end

Instance Method Details

#handle_skills(arguments) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
# File 'lib/rubino/commands/handlers/skills.rb', line 35

def handle_skills(arguments)
  tokens = arguments.to_s.strip.split(/\s+/)
  if TOGGLE_VERBS.include?(tokens.first.to_s.downcase)
    toggle_skill(tokens[1], enabled: tokens.first.casecmp?("enable"))
    return
  end

  arg = normalize_skill_arg(arguments)

  return show_skills if arg.nil?

  if clear_skill_arg?(arg)
    previous = Rubino::ActiveSkill.current
    Rubino::ActiveSkill.clear
    if previous
      @ui.success("Cleared active skill (was: #{previous}).")
    else
      @ui.info("No active skill.")
    end
    return
  end

  # Trust-aligned discovery (#63): activate only skills the assembler
  # will actually pin — in an untrusted cwd a project-local skill is
  # refused (with a reason) instead of chip-active-but-not-injected.
  registry = Rubino::Skills::Registry.trusted
  skill = registry.find(arg)
  unless skill
    if Rubino::Skills::Registry.new.find(arg)
      @ui.error("skill #{arg} is in this directory's .rubino/skills, but the directory " \
                "isn't trusted — its SKILL.md would not be loaded, so it can't be activated")
    else
      @ui.error("unknown skill: #{arg}")
      available = registry.names
      @ui.info("Available: #{available.join(", ")}") unless available.empty?
    end
    return
  end

  # A disabled skill is EXCLUDED from activation (#188): the assembler
  # refuses to inject it (active_skill_block checks enabled?), so pinning
  # it would show an active chip with no effect.
  unless registry.enabled?(skill.name)
    @ui.error("skill #{skill.name} is disabled — /skills enable #{skill.name} to use it")
    return
  end

  Rubino::ActiveSkill.set(skill.name)
  @ui.success("Active skill: #{skill.name} (loaded into context for this session).")
end