Module: Pikuri

Defined in:
lib/pikuri.rb,
lib/pikuri/tool.rb,
lib/pikuri/agent.rb,
lib/pikuri/version.rb,
lib/pikuri/tool/bash.rb,
lib/pikuri/tool/edit.rb,
lib/pikuri/tool/glob.rb,
lib/pikuri/tool/grep.rb,
lib/pikuri/tool/read.rb,
lib/pikuri/url_cache.rb,
lib/pikuri/subprocess.rb,
lib/pikuri/tool/fetch.rb,
lib/pikuri/tool/skill.rb,
lib/pikuri/tool/write.rb,
lib/pikuri/agent/tokens.rb,
lib/pikuri/agent/message.rb,
lib/pikuri/tool/confirmer.rb,
lib/pikuri/tool/sub_agent.rb,
lib/pikuri/tool/workspace.rb,
lib/pikuri/tool/calculator.rb,
lib/pikuri/tool/parameters.rb,
lib/pikuri/tool/search/exa.rb,
lib/pikuri/tool/web_scrape.rb,
lib/pikuri/tool/web_search.rb,
lib/pikuri/tool/scraper/pdf.rb,
lib/pikuri/agent/synthesizer.rb,
lib/pikuri/tool/scraper/html.rb,
lib/pikuri/tool/search/brave.rb,
lib/pikuri/tool/search/result.rb,
lib/pikuri/tool/skill_catalog.rb,
lib/pikuri/agent/listener_list.rb,
lib/pikuri/tool/scraper/simple.rb,
lib/pikuri/tool/search/engines.rb,
lib/pikuri/agent/chat_transport.rb,
lib/pikuri/tool/search/duckduckgo.rb,
lib/pikuri/agent/listener/terminal.rb,
lib/pikuri/agent/listener/token_log.rb,
lib/pikuri/tool/scraper/fetch_error.rb,
lib/pikuri/tool/search/rate_limiter.rb,
lib/pikuri/agent/listener/step_limit.rb,
lib/pikuri/agent/context_window_detector.rb,
lib/pikuri/agent/listener/message_listener.rb,
lib/pikuri/agent/listener/in_memory_message_list.rb

Overview

Boot file: configures the Zeitwerk autoloader for every file under lib/pikuri/ and eager-loads them all. After require ‘pikuri’, every constant pikuri ships (Pikuri::Agent, Pikuri::Tool, Pikuri::Tool::Calculator, Pikuri::Tool::Scraper::HTML, …) is defined and resolvable.

Beyond loading, the Pikuri module owns the logging surface — see Pikuri.logger_for, Pikuri.log_io=, and the PIKURI_LOG / PIKURI_LOG_<NAME> env vars. Each subsystem holds its own memoized Logger, all writing through a shared IO that Pikuri.log_io= can swap in one shot (handy in tests, daemons, or anywhere stderr isn’t the right sink).

Why eager-load

Tool implementations (Pikuri::Tool::CALCULATOR, Pikuri::Tool::WEB_SEARCH, Pikuri::Tool::WEB_SCRAPE, Pikuri::Tool::FETCH) are ALL_CAPS value constants rather than classes/modules, and Zeitwerk only auto-loads constants that match its filename-↔-CamelCase convention. Eager-loading at boot guarantees the files defining those values run, so the bin script can splat them straight into Pikuri::Agent.new(tools: […]) without per-file require ceremony. The cost is a few milliseconds of startup —negligible compared to a single LLM round-trip.

Defined Under Namespace

Classes: Agent, Subprocess, Tool, UrlCache

Constant Summary collapse

PROMPTS_DIR =

Absolute path to the directory holding pikuri’s bundled system prompts (pikuri-chat.txt, coding-system-prompt.txt). Resolves correctly both in a source checkout and in an installed gem because __dir__ tracks the file’s actual location either way; the gem ships prompts/ as a sibling of lib/ so the same relative jump works.

Exposed publicly so a downstream library user can read pikuri’s prompts as a starting point for their own system prompt (or use them verbatim). Prefer prompt for the common case of loading one by name; reach for this constant only if you need the directory itself (e.g. to list available prompts).

Returns:

  • (String)
File.expand_path('../prompts', __dir__)
LOG_LEVELS =

Mapping from PIKURI_LOG env-var values (lowercased) to Logger level constants. Anything else falls back to INFO.

Returns:

  • (Hash{String=>Integer})
{
  'debug' => Logger::DEBUG,
  'info'  => Logger::INFO,
  'warn'  => Logger::WARN,
  'error' => Logger::ERROR,
  'fatal' => Logger::FATAL
}.freeze
Loader =
Zeitwerk::Loader.new
VERSION =

Gem version, advertised in pikuri.gemspec. Bump on every release following semver: patch for bug fixes, minor for backward-compatible additions to the public surface (Pikuri::Tool / Pikuri::Agent / listeners / bundled tools), major for breaking changes to that surface or to the bin/pikuri-* CLIs.

'0.0.1'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.log_ioIO

Returns shared sink every logger_for writes through.

Returns:

  • (IO)

    shared sink every logger_for writes through



73
74
75
# File 'lib/pikuri.rb', line 73

def log_io
  @log_io
end

Class Method Details

.logger_for(name) ⇒ Logger

Memoized Logger tagged with name in its progname, so each subsystem’s lines stand out in the shared sink. Level resolves in this order: PIKURI_LOG_<NAME> (e.g. PIKURI_LOG_ENGINES=debug), then PIKURI_LOG, then INFO.

Repeated calls with the same name return the same instance so there is one logger per subsystem and log_io= can rewire them all in one shot.

Parameters:

  • name (String)

    subsystem tag (rendered as progname)

Returns:

  • (Logger)


110
111
112
113
114
115
116
117
# File 'lib/pikuri.rb', line 110

def logger_for(name)
  @log_loggers[name] ||= begin
    lg = Logger.new(@log_io, progname: name)
    override = ENV["PIKURI_LOG_#{name.upcase}"].to_s.downcase
    lg.level = LOG_LEVELS.fetch(override, @log_default)
    lg
  end
end

.prompt(name) ⇒ String

Read a bundled prompt by basename. name is matched against the files in PROMPTS_DIR; a .txt extension is auto-appended if absent. Symbols are accepted as a convenience (:pikuri-chat / ‘pikuri-chat’ / ‘pikuri-chat.txt’ all resolve to the same file).

Intended for downstream library users who want to bootstrap their own Pikuri::Agent wiring from pikuri’s defaults — read the prompt, customize the bits they care about, hand the result to Agent.new(system_prompt: …).

Examples:

chat_prompt = Pikuri.prompt(:'pikuri-chat')
agent = Pikuri::Agent.new(system_prompt: chat_prompt, ...)

Parameters:

  • name (String, Symbol)

    basename of the prompt file

Returns:

  • (String)

    file contents

Raises:

  • (ArgumentError)

    if no matching file exists in PROMPTS_DIR



136
137
138
139
140
141
142
143
144
145
# File 'lib/pikuri.rb', line 136

def prompt(name)
  basename = name.to_s
  basename += '.txt' unless basename.end_with?('.txt')
  path = File.join(PROMPTS_DIR, basename)
  unless File.exist?(path)
    available = Dir.children(PROMPTS_DIR).sort.join(', ')
    raise ArgumentError, "Unknown pikuri prompt #{name.inspect}; available: #{available}"
  end
  File.read(path)
end