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 requirespikuri-code(for example) findscoding-system-prompt.txtinpikuri-code/prompts/. The core gem registers its ownpikuri-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.
[File.('../prompts', __dir__)]
- LOG_LEVELS =
Mapping from
PIKURI_LOGenv-var values (lowercased) to Logger level constants. Anything else falls back toINFO. { '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 ownlib/dirs — see each gem’s entry file. 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 thebin/pikuri-*CLIs. '0.0.3'
Class Attribute Summary collapse
-
.log_io ⇒ IO
Shared sink every Pikuri.logger_for writes through.
Class Method Summary collapse
-
.logger_for(name) ⇒ Logger
Memoized Logger tagged with
namein itsprogname, so each subsystem’s lines stand out in the shared sink. -
.prompt(name) ⇒ String
Read a bundled prompt by basename.
Class Attribute Details
.log_io ⇒ IO
Returns 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.
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: …).
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 |