Class: Prescient::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/prescient/cli.rb

Overview

Command-line interface for common Prescient operations.

Defined Under Namespace

Classes: UsageError

Constant Summary collapse

FORMATS =

Supported output formats.

Returns:

  • (Array<String>)

    Output format names

['text', 'json'].freeze
CONFIGURATION_EXAMPLE =

Schema URL and annotated starter configuration for config example.

<<~YAML
  # yaml-language-server: $schema=https://raw.githubusercontent.com/kanutocd/prescient/refs/heads/main/schema/prescient.configuration.schema.json
  #
  # Prescient configuration example.
  #
  # Precedence, from lowest to highest:
  # 1. Built-in defaults and provider environment variables.
  # 2. Values in this YAML file.
  # 3. Per-operation CLI overrides such as --provider and --chat-model.
  #
  # Use `prescient config validate` after editing this file.
  # Keep credentials out of source control; use *_env references instead.
  version: 1

  # Global behavior.
  default_provider: ollama
  timeout: 30
  retry_attempts: 3
  retry_delay: 1.0
  fallback_providers: []
  sensitive_keys:
    - api_key
    - password
    - token
    - secret

  providers:
    # Local Ollama requires no API key.
    ollama:
      type: ollama
      url: http://localhost:11434
      embedding_model: nomic-embed-text
      chat_model: llama3.2:3b
      # prompt_templates:
      #   system_prompt: You are a concise assistant.
      #   no_context_template: "%<system_prompt>s\x5Cn\x5CnUser: %<query>s"
      #   with_context_template: "%<system_prompt>s\x5Cn\x5CnContext:\x5Cn%<context>s\x5Cn\x5CnUser: %<query>s"

    # Uncomment a cloud provider and set its credential in the environment.
    # openai:
    #   type: openai
    #   api_key_env: OPENAI_API_KEY
    #   embedding_model: text-embedding-3-small
    #   chat_model: gpt-4.1-mini
    #   prompt_templates:
    #     system_prompt: You are a concise assistant.
    #     no_context_template: "%<system_prompt>s\x5Cn\x5CnUser: %<query>s"

    # anthropic:
    #   type: anthropic
    #   api_key_env: ANTHROPIC_API_KEY
    #   model: claude-sonnet-4-20250514

    # gemini:
    #   type: gemini
    #   api_key_env: GEMINI_API_KEY
    #   embedding_model: gemini-embedding-001
    #   chat_model: gemini-2.5-flash

    # mistral:
    #   type: mistral
    #   api_key_env: MISTRAL_API_KEY
    #   embedding_model: mistral-embed
    #   chat_model: mistral-large-latest

    # DeepSeek supports text generation, but not embeddings.
    # deepseek:
    #   type: deepseek
    #   api_key_env: DEEPSEEK_API_KEY
    #   chat_model: deepseek-v4-flash

    # xai:
    #   type: xai
    #   api_key_env: XAI_API_KEY
    #   chat_model: grok-4.5

    # huggingface:
    #   type: huggingface
    #   api_key_env: HUGGINGFACE_API_KEY
    #   embedding_model: sentence-transformers/all-MiniLM-L6-v2
    #   chat_model: google/gemma-2-2b-it

  # External tools are opt-in and separate from AI providers. They can be
  # used directly with `prescient search`, or as context with
  # `prescient search --generate`.
  #
  # The CLI also registers `web_search` automatically when SEARXNG_URL is
  # set and no YAML tool configuration is provided.
  tools:
    # Local SearXNG example. Uncomment this block to configure a tool in YAML.
    # web_search:
    #   type: searxng
    #   url: http://localhost:8080
    #   timeout: 5
    #   max_results: 5
    #   language: en
    #   categories:
    #     - general
    #     - science
    #   max_response_bytes: 1048576

    # SearchApi example. It uses SearchApi's Google engine by default and
    # authenticates with a Bearer token from the environment.
    # searchapi_web:
    #   type: searchapi
    #   api_key_env: SEARCHAPI_API_KEY
    #   engine: google
    #   location: New York
    #   hl: en
    #   gl: us
    #   timeout: 10
    #   max_results: 5

    # Capability fallback. Adapters are tried in order, and fallback occurs
    # only for transient connection or rate-limit failures.
    # resilient_search:
    #   adapters:
    #     - type: searxng
    #       url_env: SEARXNG_URL
    #     - type: searchapi
    #       api_key_env: SEARCHAPI_API_KEY
    #       engine: google

    # Prefer an environment reference when the URL differs by environment
    # or should not be committed. Use `--tool research_search` to select a
    # tool with a custom name.
    # research_search:
    #   type: searxng
    #   url_env: SEARXNG_URL
    #   timeout_env: SEARXNG_TIMEOUT
    #   max_results_env: SEARXNG_MAX_RESULTS
    #   language_env: SEARXNG_LANGUAGE
    #   categories_env: SEARXNG_CATEGORIES

    # Search results are returned directly by default. Add `--generate` to
    # feed normalized results to the selected AI provider. Omit `--generate`
    # when the caller should handle the search results itself.
YAML

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(arguments, input:, output:, errors:) ⇒ CLI

Initialize a CLI runner with injectable streams.

Parameters:

  • arguments (Array<String>)

    Command-line arguments

  • input (IO)

    Input stream used for stdin prompts

  • output (IO)

    Output stream for command results

  • errors (IO)

    Output stream for diagnostics



180
181
182
183
184
185
# File 'lib/prescient/cli.rb', line 180

def initialize(arguments, input:, output:, errors:)
  @arguments = arguments.dup
  @input = input
  @output = output
  @errors = errors
end

Class Method Details

.run(arguments, input: $stdin, output: $stdout, errors: $stderr) ⇒ Integer

Run the CLI and return a process exit status.

Parameters:

  • arguments (Array<String>)

    Command-line arguments

  • input (IO) (defaults to: $stdin)

    Input stream used for stdin prompts

  • output (IO) (defaults to: $stdout)

    Output stream for command results

  • errors (IO) (defaults to: $stderr)

    Output stream for diagnostics

Returns:

  • (Integer)

    Process exit status



164
165
166
167
168
169
170
171
172
# File 'lib/prescient/cli.rb', line 164

def self.run(arguments, input: $stdin, output: $stdout, errors: $stderr)
  new(arguments, input:, output:, errors:).run
rescue UsageError, OptionParser::ParseError => e
  errors.puts "prescient: #{e.message}"
  2
rescue Prescient::Error => e
  errors.puts "prescient: #{e.message}"
  1
end

Instance Method Details

#runInteger

Execute the CLI command and return its process status.

Returns:

  • (Integer)

    Process exit status



189
190
191
192
193
194
195
196
197
# File 'lib/prescient/cli.rb', line 189

def run
  config_path = extract_global_config_path
  Prescient.load_configuration(config_path) if config_path || ENV['PRESCIENT_CONFIG']

  command = @arguments.shift
  return print_help(2) unless command

  run_command(command)
end

#run_command(command) ⇒ Integer

Dispatch a parsed command to its handler.

Parameters:

  • command (String)

    Command name

Returns:

  • (Integer)

    Process exit status



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/prescient/cli.rb', line 202

def run_command(command)
  case command
  when 'providers' then providers
  when 'health' then health
  when 'generate' then generate
  when 'embed' then embed
  when 'search' then search
  when 'config' then config
  when 'help', '--help', '-h' then print_help(0)
  else
    raise UsageError, "unknown command #{command.inspect}; run 'prescient help'"
  end
end