Class: Kward::Kwsh
- Inherits:
-
Object
- Object
- Kward::Kwsh
- Defined in:
- lib/kward/shell/kwsh.rb
Overview
Kward-native embedded shell command runner.
Defined Under Namespace
Classes: Completion, Result
Constant Summary collapse
- BUILTINS =
%w[alias capture cd pwd export source unset unalias clear exit logout pty].freeze
- DEFAULT_SHELL =
"/bin/sh"- DEFAULT_TIMEOUT_SECONDS =
300- DEFAULT_MAX_OUTPUT_BYTES =
1_048_576- DEFAULT_HISTORY_LIMIT =
1_000- CONTEXT_OUTPUT_BYTES =
32_000
Instance Attribute Summary collapse
-
#cwd ⇒ Object
readonly
Returns the value of attribute cwd.
-
#last_command ⇒ Object
readonly
Returns the value of attribute last_command.
-
#timeout_seconds ⇒ Object
readonly
Returns the value of attribute timeout_seconds.
Class Method Summary collapse
Instance Method Summary collapse
- #child_env(interactive: false) ⇒ Object
- #command_shell ⇒ Object
- #complete(input, cursor) ⇒ Object
-
#context_snapshot(max_output_bytes: CONTEXT_OUTPUT_BYTES) ⇒ Object
Returns bounded shell state for an explicit shell-agent prompt.
- #editor_command_result(command, display_command: command) ⇒ Object
- #expand_alias(command, interactive: false) ⇒ Object
-
#initialize(cwd: Dir.pwd, env: ENV.to_h, shell: DEFAULT_SHELL, configured_env: {}, aliases: {}, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES) ⇒ Kwsh
constructor
A new instance of Kwsh.
- #prompt_label ⇒ Object
-
#record_interactive_result(output:, exit_status:) ⇒ Object
Records the output of an interactive command after terminal handoff.
- #run(input, cancellation: nil, &block) ⇒ Object
-
#run_for_agent(input, timeout_seconds: nil, cancellation: nil) ⇒ Object
Runs a command for the shell assistant without handing it the terminal.
Constructor Details
#initialize(cwd: Dir.pwd, env: ENV.to_h, shell: DEFAULT_SHELL, configured_env: {}, aliases: {}, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES) ⇒ Kwsh
Returns a new instance of Kwsh.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/kward/shell/kwsh.rb', line 33 def initialize(cwd: Dir.pwd, env: ENV.to_h, shell: DEFAULT_SHELL, configured_env: {}, aliases: {}, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES) @cwd = File.(cwd.to_s.empty? ? Dir.pwd : cwd.to_s) @previous_cwd = nil @env = env.to_h.transform_keys(&:to_s).transform_values(&:to_s) @env.merge!(configured_env.to_h.transform_keys(&:to_s).transform_values(&:to_s)) @env["PWD"] = @cwd configure_rbenv_environment configure_color_environment @aliases = aliases.to_h.transform_keys(&:to_s).transform_values(&:to_s) @shell = shell.to_s.empty? ? DEFAULT_SHELL : shell.to_s @timeout_seconds = timeout_seconds.to_i.positive? ? timeout_seconds.to_i : DEFAULT_TIMEOUT_SECONDS @max_output_bytes = max_output_bytes.to_i.positive? ? max_output_bytes.to_i : DEFAULT_MAX_OUTPUT_BYTES @last_command = nil @last_result = nil end |
Instance Attribute Details
#cwd ⇒ Object (readonly)
Returns the value of attribute cwd.
23 24 25 |
# File 'lib/kward/shell/kwsh.rb', line 23 def cwd @cwd end |
#last_command ⇒ Object (readonly)
Returns the value of attribute last_command.
23 24 25 |
# File 'lib/kward/shell/kwsh.rb', line 23 def last_command @last_command end |
#timeout_seconds ⇒ Object (readonly)
Returns the value of attribute timeout_seconds.
23 24 25 |
# File 'lib/kward/shell/kwsh.rb', line 23 def timeout_seconds @timeout_seconds end |
Class Method Details
.valid_alias_name?(name) ⇒ Boolean
431 432 433 |
# File 'lib/kward/shell/kwsh.rb', line 431 def self.valid_alias_name?(name) name.to_s.match?(/\A[A-Za-z_][A-Za-z0-9_-]*\z/) && !BUILTINS.include?(name.to_s) end |
Instance Method Details
#child_env(interactive: false) ⇒ Object
29 30 31 |
# File 'lib/kward/shell/kwsh.rb', line 29 def child_env(interactive: false) @env.dup end |
#command_shell ⇒ Object
25 26 27 |
# File 'lib/kward/shell/kwsh.rb', line 25 def command_shell @shell end |
#complete(input, cursor) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/kward/shell/kwsh.rb', line 98 def complete(input, cursor) token = completion_token(input.to_s, cursor.to_i) return nil if token[:command] && token[:text].empty? completion_text = token[:path_text] || token[:text] candidates = if token[:command] && !path_like_token?(completion_text) && !token[:quote] command_candidates(completion_text) else path_candidates(completion_text, directories_only: cd_completion?(input, token), quote: token[:quote]) end return nil if candidates.empty? replacement = completion_replacement(token[:text], candidates) Completion.new(range: token[:range], replacement: replacement, candidates: candidates) end |
#context_snapshot(max_output_bytes: CONTEXT_OUTPUT_BYTES) ⇒ Object
Returns bounded shell state for an explicit shell-agent prompt.
89 90 91 92 93 94 95 96 |
# File 'lib/kward/shell/kwsh.rb', line 89 def context_snapshot(max_output_bytes: CONTEXT_OUTPUT_BYTES) { cwd: @cwd, last_command: @last_command, exit_status: @last_result&.exit_status, last_output: context_output(@last_result&.output, max_output_bytes) } end |
#editor_command_result(command, display_command: command) ⇒ Object
126 127 128 129 |
# File 'lib/kward/shell/kwsh.rb', line 126 def editor_command_result(command, display_command: command) = (command, interactive: true) kward_command_result(, display_command: display_command) end |
#expand_alias(command, interactive: false) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/kward/shell/kwsh.rb', line 114 def (command, interactive: false) words = shell_words(command) return command if words.empty? || BUILTINS.include?(words.first) return command unless @aliases[words.first] rest = command.sub(/\A\s*#{Regexp.escape(words.first)}\b\s*/, "") = [@aliases.fetch(words.first), rest].reject(&:empty?).join(" ") interactive ? .sub(/\A\s*(?:capture|pty)(?:\s+|\z)/, "") : rescue ArgumentError command end |
#prompt_label ⇒ Object
49 50 51 |
# File 'lib/kward/shell/kwsh.rb', line 49 def prompt_label "Shell #{display_cwd} $" end |
#record_interactive_result(output:, exit_status:) ⇒ Object
Records the output of an interactive command after terminal handoff.
82 83 84 85 86 |
# File 'lib/kward/shell/kwsh.rb', line 82 def record_interactive_result(output:, exit_status:) return unless @last_command @last_result = Result.new(output: ANSI.sanitize_transcript(output.to_s), exit_status: exit_status) end |
#run(input, cancellation: nil, &block) ⇒ Object
53 54 55 56 57 58 59 60 |
# File 'lib/kward/shell/kwsh.rb', line 53 def run(input, cancellation: nil, &block) command = input.to_s.strip return Result.new(output: "", exit_status: 0) if command.empty? result = (command, cancellation: cancellation, &block) remember_result(command, result) result end |
#run_for_agent(input, timeout_seconds: nil, cancellation: nil) ⇒ Object
Runs a command for the shell assistant without handing it the terminal. Built-ins still execute in this Kwsh instance so their state persists.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/kward/shell/kwsh.rb', line 64 def run_for_agent(input, timeout_seconds: nil, cancellation: nil) command = input.to_s.strip return run(command, cancellation: cancellation) if command.empty? result = run(command, cancellation: cancellation) return result unless result.interactive_command result = execute( result.interactive_command, display_command: command, timeout_seconds: timeout_seconds, cancellation: cancellation ) remember_result(command, result) result end |