pikuri 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +62 -0
  3. data/GETTING_STARTED.md +223 -0
  4. data/LICENSE +21 -0
  5. data/README.md +193 -0
  6. data/lib/pikuri/agent/chat_transport.rb +41 -0
  7. data/lib/pikuri/agent/context_window_detector.rb +101 -0
  8. data/lib/pikuri/agent/listener/in_memory_message_list.rb +33 -0
  9. data/lib/pikuri/agent/listener/message_listener.rb +93 -0
  10. data/lib/pikuri/agent/listener/step_limit.rb +97 -0
  11. data/lib/pikuri/agent/listener/terminal.rb +137 -0
  12. data/lib/pikuri/agent/listener/token_log.rb +166 -0
  13. data/lib/pikuri/agent/listener_list.rb +113 -0
  14. data/lib/pikuri/agent/message.rb +61 -0
  15. data/lib/pikuri/agent/synthesizer.rb +120 -0
  16. data/lib/pikuri/agent/tokens.rb +56 -0
  17. data/lib/pikuri/agent.rb +286 -0
  18. data/lib/pikuri/subprocess.rb +166 -0
  19. data/lib/pikuri/tool/bash.rb +272 -0
  20. data/lib/pikuri/tool/calculator.rb +82 -0
  21. data/lib/pikuri/tool/confirmer.rb +96 -0
  22. data/lib/pikuri/tool/edit.rb +196 -0
  23. data/lib/pikuri/tool/fetch.rb +167 -0
  24. data/lib/pikuri/tool/glob.rb +310 -0
  25. data/lib/pikuri/tool/grep.rb +338 -0
  26. data/lib/pikuri/tool/parameters.rb +314 -0
  27. data/lib/pikuri/tool/read.rb +254 -0
  28. data/lib/pikuri/tool/scraper/fetch_error.rb +16 -0
  29. data/lib/pikuri/tool/scraper/html.rb +285 -0
  30. data/lib/pikuri/tool/scraper/pdf.rb +54 -0
  31. data/lib/pikuri/tool/scraper/simple.rb +177 -0
  32. data/lib/pikuri/tool/search/brave.rb +184 -0
  33. data/lib/pikuri/tool/search/duckduckgo.rb +196 -0
  34. data/lib/pikuri/tool/search/engines.rb +154 -0
  35. data/lib/pikuri/tool/search/exa.rb +217 -0
  36. data/lib/pikuri/tool/search/rate_limiter.rb +92 -0
  37. data/lib/pikuri/tool/search/result.rb +29 -0
  38. data/lib/pikuri/tool/skill.rb +80 -0
  39. data/lib/pikuri/tool/skill_catalog.rb +376 -0
  40. data/lib/pikuri/tool/sub_agent.rb +102 -0
  41. data/lib/pikuri/tool/web_scrape.rb +117 -0
  42. data/lib/pikuri/tool/web_search.rb +38 -0
  43. data/lib/pikuri/tool/workspace.rb +150 -0
  44. data/lib/pikuri/tool/write.rb +170 -0
  45. data/lib/pikuri/tool.rb +118 -0
  46. data/lib/pikuri/url_cache.rb +106 -0
  47. data/lib/pikuri/version.rb +10 -0
  48. data/lib/pikuri.rb +165 -0
  49. data/prompts/coding-system-prompt.txt +28 -0
  50. data/prompts/pikuri-chat.txt +15 -0
  51. metadata +259 -0
data/lib/pikuri.rb ADDED
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'zeitwerk'
5
+
6
+ # +Pikuri::VERSION+ lives in its own tiny file so the gemspec can read
7
+ # the version constant via +require_relative+ at build time without
8
+ # loading Zeitwerk and every runtime dep. Requiring it here makes the
9
+ # constant available to ordinary +require 'pikuri'+ callers too —
10
+ # without this line, Zeitwerk's ignore-rule below would leave
11
+ # +Pikuri::VERSION+ undefined for anyone using the installed gem.
12
+ require_relative 'pikuri/version'
13
+
14
+ # Boot file: configures the Zeitwerk autoloader for every file under
15
+ # +lib/pikuri/+ and eager-loads them all. After +require 'pikuri'+,
16
+ # every constant pikuri ships (+Pikuri::Agent+, +Pikuri::Tool+,
17
+ # +Pikuri::Tool::Calculator+, +Pikuri::Tool::Scraper::HTML+, ...) is
18
+ # defined and resolvable.
19
+ #
20
+ # Beyond loading, the +Pikuri+ module owns the logging surface — see
21
+ # {.logger_for}, {.log_io=}, and the +PIKURI_LOG+ /
22
+ # +PIKURI_LOG_<NAME>+ env vars. Each subsystem holds its own
23
+ # memoized +Logger+, all writing through a shared IO that
24
+ # {.log_io=} can swap in one shot (handy in tests, daemons, or
25
+ # anywhere stderr isn't the right sink).
26
+ #
27
+ # == Why eager-load
28
+ #
29
+ # Tool implementations (+Pikuri::Tool::CALCULATOR+,
30
+ # +Pikuri::Tool::WEB_SEARCH+, +Pikuri::Tool::WEB_SCRAPE+,
31
+ # +Pikuri::Tool::FETCH+) are +ALL_CAPS+ value constants rather than
32
+ # classes/modules, and Zeitwerk only auto-loads constants that match
33
+ # its filename-↔-CamelCase convention. Eager-loading at boot guarantees
34
+ # the files defining those values run, so the bin script can splat them
35
+ # straight into +Pikuri::Agent.new(tools: [...])+ without per-file
36
+ # +require+ ceremony. The cost is a few milliseconds of startup —
37
+ # negligible compared to a single LLM round-trip.
38
+ module Pikuri
39
+ # Absolute path to the directory holding pikuri's bundled system
40
+ # prompts (+pikuri-chat.txt+, +coding-system-prompt.txt+). Resolves
41
+ # correctly both in a source checkout and in an installed gem because
42
+ # +__dir__+ tracks the file's actual location either way; the gem
43
+ # ships +prompts/+ as a sibling of +lib/+ so the same relative jump
44
+ # works.
45
+ #
46
+ # Exposed publicly so a downstream library user can read pikuri's
47
+ # prompts as a starting point for their own system prompt (or use
48
+ # them verbatim). Prefer {.prompt} for the common case of loading
49
+ # one by name; reach for this constant only if you need the
50
+ # directory itself (e.g. to list available prompts).
51
+ #
52
+ # @return [String]
53
+ PROMPTS_DIR = File.expand_path('../prompts', __dir__)
54
+
55
+ # Mapping from +PIKURI_LOG+ env-var values (lowercased) to
56
+ # {Logger} level constants. Anything else falls back to +INFO+.
57
+ #
58
+ # @return [Hash{String=>Integer}]
59
+ LOG_LEVELS = {
60
+ 'debug' => Logger::DEBUG,
61
+ 'info' => Logger::INFO,
62
+ 'warn' => Logger::WARN,
63
+ 'error' => Logger::ERROR,
64
+ 'fatal' => Logger::FATAL
65
+ }.freeze
66
+
67
+ @log_io = $stderr
68
+ @log_loggers = {} # name → Logger; lets {.log_io=} rewire all of them
69
+ @log_default = LOG_LEVELS.fetch(ENV['PIKURI_LOG'].to_s.downcase, Logger::INFO)
70
+
71
+ class << self
72
+ # @return [IO] shared sink every {.logger_for} writes through
73
+ attr_reader :log_io
74
+
75
+ # Swap the shared sink (e.g. a +StringIO+ in tests, a file in a
76
+ # daemon). Re-points every logger already handed out by
77
+ # {.logger_for} so callers don't have to re-fetch them.
78
+ #
79
+ # Why we don't use +Logger#reopen+: that method closes the previous
80
+ # sink before installing the new one, which makes "swap to a
81
+ # capture +StringIO+, then swap back to the original" impossible —
82
+ # the original handle would be dead the second time around. Instead
83
+ # we swap each logger's +@logdev+ to a fresh +Logger::LogDevice+
84
+ # over the new +io+, leaving the previous device untouched so the
85
+ # caller can hand the same +IO+ back in later. This uses a documented
86
+ # but internal Logger ivar; if a future Ruby renames it the
87
+ # +Engines+ logging specs will fail loudly.
88
+ #
89
+ # @param io [IO] new sink for every Pikuri logger
90
+ # @return [IO]
91
+ def log_io=(io)
92
+ @log_io = io
93
+ @log_loggers.each_value do |lg|
94
+ lg.instance_variable_set(:@logdev, Logger::LogDevice.new(io))
95
+ end
96
+ io
97
+ end
98
+
99
+ # Memoized {Logger} tagged with +name+ in its +progname+, so each
100
+ # subsystem's lines stand out in the shared sink. Level resolves
101
+ # in this order: +PIKURI_LOG_<NAME>+ (e.g. +PIKURI_LOG_ENGINES=debug+),
102
+ # then +PIKURI_LOG+, then +INFO+.
103
+ #
104
+ # Repeated calls with the same +name+ return the same instance so
105
+ # there is one logger per subsystem and {.log_io=} can rewire them
106
+ # all in one shot.
107
+ #
108
+ # @param name [String] subsystem tag (rendered as +progname+)
109
+ # @return [Logger]
110
+ def logger_for(name)
111
+ @log_loggers[name] ||= begin
112
+ lg = Logger.new(@log_io, progname: name)
113
+ override = ENV["PIKURI_LOG_#{name.upcase}"].to_s.downcase
114
+ lg.level = LOG_LEVELS.fetch(override, @log_default)
115
+ lg
116
+ end
117
+ end
118
+
119
+ # Read a bundled prompt by basename. +name+ is matched against the
120
+ # files in {PROMPTS_DIR}; a +.txt+ extension is auto-appended if
121
+ # absent. Symbols are accepted as a convenience (+:pikuri-chat+ /
122
+ # +'pikuri-chat'+ / +'pikuri-chat.txt'+ all resolve to the same file).
123
+ #
124
+ # Intended for downstream library users who want to bootstrap their
125
+ # own +Pikuri::Agent+ wiring from pikuri's defaults — read the
126
+ # prompt, customize the bits they care about, hand the result to
127
+ # +Agent.new(system_prompt: ...)+.
128
+ #
129
+ # @example
130
+ # chat_prompt = Pikuri.prompt(:'pikuri-chat')
131
+ # agent = Pikuri::Agent.new(system_prompt: chat_prompt, ...)
132
+ #
133
+ # @param name [String, Symbol] basename of the prompt file
134
+ # @return [String] file contents
135
+ # @raise [ArgumentError] if no matching file exists in {PROMPTS_DIR}
136
+ def prompt(name)
137
+ basename = name.to_s
138
+ basename += '.txt' unless basename.end_with?('.txt')
139
+ path = File.join(PROMPTS_DIR, basename)
140
+ unless File.exist?(path)
141
+ available = Dir.children(PROMPTS_DIR).sort.join(', ')
142
+ raise ArgumentError, "Unknown pikuri prompt #{name.inspect}; available: #{available}"
143
+ end
144
+ File.read(path)
145
+ end
146
+ end
147
+
148
+ Loader = Zeitwerk::Loader.new
149
+ Loader.tag = 'pikuri'
150
+ Loader.push_dir(File.expand_path('.', __dir__))
151
+ Loader.ignore(__FILE__)
152
+ # +lib/pikuri/version.rb+ defines +Pikuri::VERSION+ (an ALL_CAPS value
153
+ # constant), not +Pikuri::Version+ as Zeitwerk would expect from the
154
+ # filename — so tell the loader to skip it. The file is +require_relative+'d
155
+ # at the top of this file (and also by the gemspec at build time), so
156
+ # the constant is defined before Zeitwerk runs.
157
+ Loader.ignore(File.expand_path('pikuri/version.rb', __dir__))
158
+ Loader.inflector.inflect(
159
+ 'html' => 'HTML',
160
+ 'pdf' => 'PDF',
161
+ 'duckduckgo' => 'DuckDuckGo'
162
+ )
163
+ Loader.setup
164
+ Loader.eager_load
165
+ end
@@ -0,0 +1,28 @@
1
+ You are an expert coding assistant who reads, edits, and runs code via tools to solve software engineering tasks.
2
+
3
+ You operate on the local filesystem under a workspace directory. All file-touching tools resolve paths within that workspace; trying to escape returns an error. The `bash` and `write` tools may prompt the user for confirmation before running — if they decline, accept it and don't retry the same operation.
4
+
5
+ You have access to tools described in the API's tool list. To call one, use the standard tool-call mechanism — do not write tool calls as text.
6
+
7
+ If several next steps are independent (e.g. reading two unrelated files, or running unrelated checks), emit them as parallel tool calls in a single turn rather than one at a time.
8
+
9
+ Choosing a tool:
10
+ - File reading: `read` for a known path, `grep` to search file contents by pattern, `glob` to find files by name pattern. Do NOT use `bash` for these (no `cat`, `sed`, `awk`, `rg`, `find`) — the dedicated tools respect the workspace and produce cleaner output.
11
+ - Editing: `edit` for surgical changes — its `old_string` argument must match exact bytes, so always `read` the target file first. Use `write` for new files, or for wholesale rewrites of existing files (which will prompt the user for confirmation).
12
+ - Running things: `bash` for tests, builds, git, scripts, and other shell commands. Briefly explain non-trivial commands before running them.
13
+ - Research: `web_search`, `web_scrape`, `fetch` for stack traces, library docs, current API references — prefer local source via `read` / `grep` when the information is already in the workspace. `sub_agent` to delegate research that would otherwise blow up your context (full-codebase audits, multi-page reading).
14
+ - `calculator` for arithmetic beyond simple mental math.
15
+
16
+ Working on code:
17
+ - Look at neighboring files first — match the existing conventions, library choices, naming, and comment style.
18
+ - Make the smallest change that does the job. Don't refactor, rename, or add features beyond what was asked.
19
+ - If you're unsure how a piece of code is used, `grep` for its callers before editing it.
20
+ - After a substantive change, run the project's tests or build if you can locate them (look at README, package.json, Cargo.toml, Makefile, build.gradle, Gemfile, etc.).
21
+ - Don't add ceremonial comments. Match the surrounding code's commenting style.
22
+ - NEVER commit, push, or open a PR unless the user explicitly asks.
23
+
24
+ Other guidelines:
25
+ - Don't repeat a tool call with identical arguments — re-read the previous observation instead.
26
+ - On a tool error (observation starting with `Error:`): use the data you already have to continue if you can. If you can't, reply to the user that you weren't able to complete the task and briefly say why (e.g. "the test runner isn't on PATH", "the file is outside the workspace"). Do not retry the same call hoping for a different result, and do not loop on rephrased variants of the same failing call.
27
+ - Reference code with `file_path:line_number` so the user can navigate (e.g. `lib/agent.rb:42`).
28
+ - When the task is done, reply in plain text with no tool call — a short summary of what changed and any next steps the user should consider. That is how you finish.
@@ -0,0 +1,15 @@
1
+ You are an expert assistant who solves tasks by calling tools when needed.
2
+
3
+ You have access to tools described in the API's tool list. To call one, use the standard tool-call mechanism — do not write tool calls as text.
4
+
5
+ If several next steps are independent (e.g. two unrelated lookups), emit them as parallel tool calls in a single turn rather than one at a time.
6
+
7
+ Choosing a tool:
8
+ - Default to `web_search` whenever a question asks for a specific fact about a named entity — company HQs and headcounts, product versions and release dates, prices, people's bios and roles, anything tying a proper noun to a specific attribute — and for anything time-sensitive or obscure. Your training will *feel* certain on these and is often wrong; check rather than guess. One well-chosen search beats three exploratory ones.
9
+ - Use `calculator` for any arithmetic beyond simple mental math. Don't search the web for "what is X * Y".
10
+ - Reply directly, without any tool, for language and translation, definitions of common terms, and widely-known general history.
11
+
12
+ Other guidelines:
13
+ - Don't repeat a tool call with identical arguments — re-read the previous observation instead.
14
+ - On a tool error (observation starting with `Error:`): use the data you already have to answer if you can. If you can't, reply to the user that you weren't able to find the answer and briefly say why (e.g. "web search hit a rate limit", "the page wasn't reachable"). Do not retry the same call hoping for a different result, and do not loop on rephrased variants of the same failing call.
15
+ - When you have the answer, reply in plain text with no tool call. That is how you finish.
metadata ADDED
@@ -0,0 +1,259 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pikuri
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Martin Vysny
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dentaku
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.19'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.19'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pdf-reader
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.15'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.15'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rainbow
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: reverse_markdown
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: ruby-readability
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.7'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: ruby_llm
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.15'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.15'
125
+ - !ruby/object:Gem::Dependency
126
+ name: tsort
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.2'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.2'
139
+ - !ruby/object:Gem::Dependency
140
+ name: tty-markdown
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.7'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.7'
153
+ - !ruby/object:Gem::Dependency
154
+ name: zeitwerk
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '2.7'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '2.7'
167
+ description: |
168
+ A library for assembling AI assistants: Pikuri::Agent (a thin wrapper around
169
+ ruby_llm's chat loop), Pikuri::Tool with strict argument validation, a
170
+ listener surface for rendering / budgets / sub-agents, and a small set of
171
+ bundled tools (calculator, web search, web scrape, fetch, plus six coding
172
+ tools: read / write / edit / grep / glob / bash). Downstream projects
173
+ (notably the planned pikuri-tui) wire these into actual binaries. The repo
174
+ also ships `bin/pikuri-chat` and `bin/pikuri-code` as demo entry points,
175
+ but they are NOT installed by `gem install pikuri` — they exist to show
176
+ how to wire the library together and to support development.
177
+ email:
178
+ - martin@vysny.me
179
+ executables: []
180
+ extensions: []
181
+ extra_rdoc_files: []
182
+ files:
183
+ - CHANGELOG.md
184
+ - GETTING_STARTED.md
185
+ - LICENSE
186
+ - README.md
187
+ - lib/pikuri.rb
188
+ - lib/pikuri/agent.rb
189
+ - lib/pikuri/agent/chat_transport.rb
190
+ - lib/pikuri/agent/context_window_detector.rb
191
+ - lib/pikuri/agent/listener/in_memory_message_list.rb
192
+ - lib/pikuri/agent/listener/message_listener.rb
193
+ - lib/pikuri/agent/listener/step_limit.rb
194
+ - lib/pikuri/agent/listener/terminal.rb
195
+ - lib/pikuri/agent/listener/token_log.rb
196
+ - lib/pikuri/agent/listener_list.rb
197
+ - lib/pikuri/agent/message.rb
198
+ - lib/pikuri/agent/synthesizer.rb
199
+ - lib/pikuri/agent/tokens.rb
200
+ - lib/pikuri/subprocess.rb
201
+ - lib/pikuri/tool.rb
202
+ - lib/pikuri/tool/bash.rb
203
+ - lib/pikuri/tool/calculator.rb
204
+ - lib/pikuri/tool/confirmer.rb
205
+ - lib/pikuri/tool/edit.rb
206
+ - lib/pikuri/tool/fetch.rb
207
+ - lib/pikuri/tool/glob.rb
208
+ - lib/pikuri/tool/grep.rb
209
+ - lib/pikuri/tool/parameters.rb
210
+ - lib/pikuri/tool/read.rb
211
+ - lib/pikuri/tool/scraper/fetch_error.rb
212
+ - lib/pikuri/tool/scraper/html.rb
213
+ - lib/pikuri/tool/scraper/pdf.rb
214
+ - lib/pikuri/tool/scraper/simple.rb
215
+ - lib/pikuri/tool/search/brave.rb
216
+ - lib/pikuri/tool/search/duckduckgo.rb
217
+ - lib/pikuri/tool/search/engines.rb
218
+ - lib/pikuri/tool/search/exa.rb
219
+ - lib/pikuri/tool/search/rate_limiter.rb
220
+ - lib/pikuri/tool/search/result.rb
221
+ - lib/pikuri/tool/skill.rb
222
+ - lib/pikuri/tool/skill_catalog.rb
223
+ - lib/pikuri/tool/sub_agent.rb
224
+ - lib/pikuri/tool/web_scrape.rb
225
+ - lib/pikuri/tool/web_search.rb
226
+ - lib/pikuri/tool/workspace.rb
227
+ - lib/pikuri/tool/write.rb
228
+ - lib/pikuri/url_cache.rb
229
+ - lib/pikuri/version.rb
230
+ - prompts/coding-system-prompt.txt
231
+ - prompts/pikuri-chat.txt
232
+ homepage: https://codeberg.org/mvysny/pikuri
233
+ licenses:
234
+ - MIT
235
+ metadata:
236
+ source_code_uri: https://codeberg.org/mvysny/pikuri/src/branch/master
237
+ changelog_uri: https://codeberg.org/mvysny/pikuri/src/branch/master/CHANGELOG.md
238
+ bug_tracker_uri: https://codeberg.org/mvysny/pikuri/issues
239
+ rubygems_mfa_required: 'true'
240
+ post_install_message:
241
+ rdoc_options: []
242
+ require_paths:
243
+ - lib
244
+ required_ruby_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '3.3'
249
+ required_rubygems_version: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - ">="
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
254
+ requirements: []
255
+ rubygems_version: 3.5.22
256
+ signing_key:
257
+ specification_version: 4
258
+ summary: A small, privacy-first Ruby toolkit for building local AI assistants.
259
+ test_files: []