Class: Textus::Surfaces::CLI::Verb
- Inherits:
-
Object
- Object
- Textus::Surfaces::CLI::Verb
- Defined in:
- lib/textus/surfaces/cli/verb.rb,
lib/textus/surfaces/cli/verb/get.rb,
lib/textus/surfaces/cli/verb/put.rb,
lib/textus/surfaces/cli/verb/init.rb,
lib/textus/surfaces/cli/verb/watch.rb,
lib/textus/surfaces/cli/verb/doctor.rb,
lib/textus/surfaces/cli/verb/mcp_serve.rb,
lib/textus/surfaces/cli/verb/schema_diff.rb,
lib/textus/surfaces/cli/verb/schema_init.rb,
lib/textus/surfaces/cli/verb/schema_migrate.rb
Overview
Subclasses must implement #call(store) and return an integer exit code. Use #emit(obj) for normal JSON output (returns 0). Subclasses that don’t need a Textus store (e.g. Init) override ‘.needs_store?` to return false; dispatch will pass nil instead.
Direct Known Subclasses
Group, Runner::Base, Doctor, Init, MCPServe, SchemaDiff, SchemaInit, SchemaMigrate, Watch
Defined Under Namespace
Classes: Doctor, Get, Init, MCPServe, Put, SchemaDiff, SchemaInit, SchemaMigrate, Watch
Instance Attribute Summary collapse
-
#positional ⇒ Object
readonly
Returns the value of attribute positional.
-
#stdin ⇒ Object
readonly
The input stream — the source for a ‘cli_stdin` envelope (ADR 0068).
Class Method Summary collapse
-
.command_name(name = nil) ⇒ Object
Declarative CLI name.
-
.descendants ⇒ Object
Recursive subclass enumeration.
- .inherited(subclass) ⇒ Object
- .needs_store? ⇒ Boolean
- .option(name, optspec) ⇒ Object
- .options ⇒ Object
-
.parent_group(group_klass = nil) ⇒ Object
Declares that this verb is a subcommand of ‘group_klass`.
Instance Method Summary collapse
-
#emit(obj, exit_code: 0) ⇒ Object
Hashes get “protocol” => PROTOCOL prepended unless they already carry one (Store envelopes do).
-
#gate_dispatch(cmd, store) ⇒ Object
Builds a Command from spec + inputs and dispatches through Gate.
-
#initialize(stdin:, stdout:, stderr:, cwd: nil) ⇒ Verb
constructor
A new instance of Verb.
- #parse(argv) ⇒ Object
-
#resolved_role(store, default: Role::DEFAULT) ⇒ Object
Resolves the active role for this invocation.
Constructor Details
#initialize(stdin:, stdout:, stderr:, cwd: nil) ⇒ Verb
Returns a new instance of Verb.
62 63 64 65 66 67 |
# File 'lib/textus/surfaces/cli/verb.rb', line 62 def initialize(stdin:, stdout:, stderr:, cwd: nil) @stdin = stdin @stdout = stdout @stderr = stderr @cwd = cwd end |
Instance Attribute Details
#positional ⇒ Object (readonly)
Returns the value of attribute positional.
83 84 85 |
# File 'lib/textus/surfaces/cli/verb.rb', line 83 def positional @positional end |
#stdin ⇒ Object (readonly)
The input stream — the source for a ‘cli_stdin` envelope (ADR 0068).
107 108 109 |
# File 'lib/textus/surfaces/cli/verb.rb', line 107 def stdin @stdin end |
Class Method Details
.command_name(name = nil) ⇒ Object
Declarative CLI name. Reader returns the registered name (or nil for verbs that aren’t directly invokable, like the abstract Verb/Group base classes). Writer registers it.
29 30 31 32 33 34 35 |
# File 'lib/textus/surfaces/cli/verb.rb', line 29 def command_name(name = nil) if name.nil? @command_name else @command_name = name.to_s end end |
.descendants ⇒ Object
Recursive subclass enumeration. Ruby 3.1 ships Class#subclasses but not Class#descendants, so we expand it ourselves.
57 58 59 |
# File 'lib/textus/surfaces/cli/verb.rb', line 57 def descendants subclasses.flat_map { |k| [k] + k.descendants } end |
.inherited(subclass) ⇒ Object
48 49 50 51 52 53 |
# File 'lib/textus/surfaces/cli/verb.rb', line 48 def inherited(subclass) super subclass.instance_variable_set(:@options, []) subclass.instance_variable_set(:@command_name, nil) subclass.instance_variable_set(:@parent_group, nil) end |
.needs_store? ⇒ Boolean
22 23 24 |
# File 'lib/textus/surfaces/cli/verb.rb', line 22 def needs_store? true end |
.option(name, optspec) ⇒ Object
13 14 15 16 |
# File 'lib/textus/surfaces/cli/verb.rb', line 13 def option(name, optspec) << [name, optspec] attr_accessor(name) end |
.options ⇒ Object
18 19 20 |
# File 'lib/textus/surfaces/cli/verb.rb', line 18 def @options ||= [] end |
.parent_group(group_klass = nil) ⇒ Object
Declares that this verb is a subcommand of ‘group_klass`. When set, the verb is NOT a top-level CLI verb — it’s listed under the group’s subcommands instead.
40 41 42 43 44 45 46 |
# File 'lib/textus/surfaces/cli/verb.rb', line 40 def parent_group(group_klass = nil) if group_klass.nil? @parent_group else @parent_group = group_klass end end |
Instance Method Details
#emit(obj, exit_code: 0) ⇒ Object
Hashes get “protocol” => PROTOCOL prepended unless they already carry one (Store envelopes do). Caller’s value wins on collision.
87 88 89 90 91 |
# File 'lib/textus/surfaces/cli/verb.rb', line 87 def emit(obj, exit_code: 0) payload = obj.is_a?(Hash) ? { "protocol" => PROTOCOL }.merge(obj) : obj @stdout.puts(JSON.generate(payload)) exit_code end |
#gate_dispatch(cmd, store) ⇒ Object
Builds a Command from spec + inputs and dispatches through Gate.
102 103 104 |
# File 'lib/textus/surfaces/cli/verb.rb', line 102 def gate_dispatch(cmd, store) store.gate.dispatch(cmd) end |
#parse(argv) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/textus/surfaces/cli/verb.rb', line 69 def parse(argv) fmt = "json" OptionParser.new do |o| self.class..each do |name, optspec| o.on(optspec) { |v| public_send(:"#{name}=", v) } end o.on("--output=FMT") { |v| fmt = v } o.on("--format=FMT") { |_v| raise FlagRenamed.new("--format", "--output") } end.permute!(argv) raise UsageError.new("only --output=json is supported in v1") unless fmt == "json" @positional = argv.dup end |
#resolved_role(store, default: Role::DEFAULT) ⇒ Object
Resolves the active role for this invocation. Honors the verb’s ‘–as` flag if declared, then TEXTUS_ROLE, then the project default. Pass `default:` to override the fallback (e.g. MCPServe uses AGENT).
96 97 98 99 |
# File 'lib/textus/surfaces/cli/verb.rb', line 96 def resolved_role(store, default: Role::DEFAULT) flag = respond_to?(:as_flag) ? as_flag : nil Role.resolve(flag: flag, env: ENV, root: store.root, default: default) end |