Module: Pikuri

Defined in:
lib/pikuri-core.rb,
lib/pikuri/tool.rb,
lib/pikuri/agent.rb,
lib/pikuri/version.rb,
lib/pikuri/url_cache.rb,
lib/pikuri/subprocess.rb,
lib/pikuri/tool/fetch.rb,
lib/pikuri/agent/event.rb,
lib/pikuri/agent/control.rb,
lib/pikuri/agent/listener.rb,
lib/pikuri/tool/sub_agent.rb,
lib/pikuri/agent/extension.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/agent/configurator.rb,
lib/pikuri/tool/search/result.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/control/interloper.rb,
lib/pikuri/agent/control/step_limit.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/control/cancellable.rb,
lib/pikuri/agent/listener/rate_limited.rb,
lib/pikuri/agent/context_window_detector.rb,
lib/pikuri/agent/listener/in_memory_event_list.rb

Overview

Boot file: configures the Zeitwerk autoloader for every file under pikuri-core/lib/pikuri/ and eager-loads them all. After require ‘pikuri-core’, every constant pikuri-core ships (Pikuri::Agent, Pikuri::Tool, Pikuri::Tool::Calculator, …) is defined and resolvable. Sibling gems (pikuri-skills, pikuri-mcp, …) set up their own Zeitwerk loaders rooted at their own lib/ and contribute to the same Pikuri:: namespace.

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 drop them straight into the Agent.new block via c.add_tool 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

PROMPT_DIRS =

Search path for bundled system prompts. Mutable list: each pikuri gem appends its own prompts/ directory when it boots, so a Pikuri.prompt(name) call from a host that requires pikuri-code (for example) finds coding-system-prompt.txt in pikuri-code/prompts/. The core gem registers its own pikuri-core/prompts/ below; sibling gems do the equivalent in their own entry files.

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.

Returns:

  • (Array<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 managing every constant under pikuri-core/lib/pikuri/. Exposed as a constant (rather than scoped to the boot block) so a downstream host that wants to add ignore rules can reach it without monkey-patching. Sibling gems (pikuri-skills, pikuri-mcp, …) set up their own loaders rooted at their own lib/ dirs — see each gem’s entry file.

Returns:

  • (Zeitwerk::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.3'

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



75
76
77
# File 'lib/pikuri-core.rb', line 75

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)


112
113
114
115
116
117
118
119
# File 'lib/pikuri-core.rb', line 112

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. Searches every directory in PROMPT_DIRS in order, returning the first match. .txt 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 any directory in PROMPT_DIRS



140
141
142
143
144
145
146
147
148
149
# File 'lib/pikuri-core.rb', line 140

def prompt(name)
  basename = name.to_s
  basename += '.txt' unless basename.end_with?('.txt')
  PROMPT_DIRS.each do |dir|
    path = File.join(dir, basename)
    return File.read(path) if File.exist?(path)
  end
  available = PROMPT_DIRS.flat_map { |dir| Dir.exist?(dir) ? Dir.children(dir) : [] }.sort.uniq
  raise ArgumentError, "Unknown pikuri prompt #{name.inspect}; available: #{available.join(', ')}"
end