pikuri-core 0.0.7 → 0.1.0
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/pikuri/agent/chat_transport.rb +73 -93
- data/lib/pikuri/agent/configurator.rb +46 -106
- data/lib/pikuri/agent/context_window_detector.rb +44 -85
- data/lib/pikuri/agent/control/cancellable.rb +87 -66
- data/lib/pikuri/agent/control/interloper.rb +127 -105
- data/lib/pikuri/agent/control/step_limit.rb +25 -41
- data/lib/pikuri/agent/control.rb +14 -34
- data/lib/pikuri/agent/event.rb +123 -188
- data/lib/pikuri/agent/extension.rb +118 -94
- data/lib/pikuri/agent/extension_context.rb +50 -77
- data/lib/pikuri/agent/history.rb +653 -0
- data/lib/pikuri/agent/listener/rate_limited.rb +40 -66
- data/lib/pikuri/agent/listener/terminal.rb +143 -117
- data/lib/pikuri/agent/listener/token_log.rb +101 -140
- data/lib/pikuri/agent/listener.rb +23 -43
- data/lib/pikuri/agent/listener_list.rb +26 -47
- data/lib/pikuri/agent/synthesizer.rb +45 -87
- data/lib/pikuri/agent.rb +816 -474
- data/lib/pikuri/bundler_env.rb +68 -0
- data/lib/pikuri/extractor/html.rb +63 -110
- data/lib/pikuri/extractor/passthrough.rb +20 -30
- data/lib/pikuri/extractor.rb +93 -154
- data/lib/pikuri/file_type.rb +63 -135
- data/lib/pikuri/finalizers.rb +32 -47
- data/lib/pikuri/paths.rb +104 -13
- data/lib/pikuri/ruby_llm_patches.rb +106 -0
- data/lib/pikuri/sanitizer.rb +45 -67
- data/lib/pikuri/subprocess.rb +75 -119
- data/lib/pikuri/testing.rb +296 -0
- data/lib/pikuri/tool/calculator.rb +56 -66
- data/lib/pikuri/tool/execute_context.rb +42 -0
- data/lib/pikuri/tool/fetch.rb +51 -77
- data/lib/pikuri/tool/parameters.rb +21 -29
- data/lib/pikuri/tool/scraper.rb +55 -97
- data/lib/pikuri/tool/search/brave.rb +52 -80
- data/lib/pikuri/tool/search/duckduckgo.rb +59 -91
- data/lib/pikuri/tool/search/engines.rb +230 -97
- data/lib/pikuri/tool/search/exa.rb +56 -90
- data/lib/pikuri/tool/search/rate_limiter.rb +61 -38
- data/lib/pikuri/tool/search/result.rb +10 -15
- data/lib/pikuri/tool/trifecta_legs.rb +217 -0
- data/lib/pikuri/tool/web_scrape.rb +38 -54
- data/lib/pikuri/tool/web_search.rb +100 -24
- data/lib/pikuri/tool.rb +140 -65
- data/lib/pikuri/trifecta/contribution.rb +43 -0
- data/lib/pikuri/trifecta/node.rb +47 -0
- data/lib/pikuri/trifecta/report.rb +230 -0
- data/lib/pikuri/trifecta.rb +127 -0
- data/lib/pikuri/url_cache.rb +33 -49
- data/lib/pikuri/version.rb +1 -1
- data/lib/pikuri-core.rb +72 -88
- data/prompts/agent-loop.txt +5 -0
- data/prompts/pikuri-chat.txt +3 -12
- metadata +14 -3
data/lib/pikuri-core.rb
CHANGED
|
@@ -3,55 +3,34 @@
|
|
|
3
3
|
require 'logger'
|
|
4
4
|
require 'zeitwerk'
|
|
5
5
|
|
|
6
|
-
# +Pikuri::VERSION+ lives in its own tiny file so the gemspec can
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# without this line, Zeitwerk's ignore-rule below would leave
|
|
11
|
-
# +Pikuri::VERSION+ undefined for anyone using the installed gem.
|
|
6
|
+
# +Pikuri::VERSION+ lives in its own tiny file so the gemspec can +require_relative+
|
|
7
|
+
# it at build time without loading Zeitwerk and every runtime dep. Requiring it
|
|
8
|
+
# here also defines the constant for ordinary +require 'pikuri-core'+ callers —
|
|
9
|
+
# without this line the Zeitwerk ignore-rule below would leave it undefined.
|
|
12
10
|
require_relative 'pikuri/version'
|
|
13
11
|
|
|
14
|
-
# Boot file: configures the Zeitwerk autoloader for
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# is defined and resolvable. Sibling gems (+pikuri-skills+,
|
|
19
|
-
# +pikuri-mcp+, ...) set up their own Zeitwerk loaders rooted at
|
|
20
|
-
# their own +lib/+ and contribute to the same +Pikuri::+ namespace.
|
|
12
|
+
# Boot file: configures the Zeitwerk autoloader for +pikuri-core/lib/pikuri/+
|
|
13
|
+
# and eager-loads it, so after +require 'pikuri-core'+ every constant
|
|
14
|
+
# pikuri-core ships is defined. Sibling gems set up their own loaders into the
|
|
15
|
+
# same +Pikuri::+ namespace.
|
|
21
16
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# +
|
|
25
|
-
# memoized +Logger+, all writing through a shared IO that
|
|
26
|
-
# {.log_io=} can swap in one shot (handy in tests, daemons, or
|
|
27
|
-
# anywhere stderr isn't the right sink).
|
|
17
|
+
# The +Pikuri+ module also owns the logging surface ({.logger_for}, {.log_io=},
|
|
18
|
+
# the +PIKURI_LOG+ / +PIKURI_LOG_<NAME>+ env vars): each subsystem holds a
|
|
19
|
+
# memoized +Logger+ writing through a shared IO that {.log_io=} swaps in one shot.
|
|
28
20
|
#
|
|
29
21
|
# == Why eager-load
|
|
30
22
|
#
|
|
31
|
-
# The stateless bundled tools (+
|
|
32
|
-
# +
|
|
33
|
-
# constants
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# +c.add_tool+ without per-file +require+ ceremony. (web_search is instead
|
|
38
|
-
# host-configured — built per-wiring via {Pikuri::Tool::WebSearch.build}
|
|
39
|
-
# with the host's provider keys — so it is not a value constant.) The cost
|
|
40
|
-
# is a few milliseconds of startup — negligible compared to a single LLM
|
|
41
|
-
# round-trip.
|
|
23
|
+
# The stateless bundled tools (+Tool::CALCULATOR+, +Tool::WEB_SCRAPE+,
|
|
24
|
+
# +Tool::FETCH+) are +ALL_CAPS+ value constants, and Zeitwerk only auto-loads
|
|
25
|
+
# constants matching its filename↔CamelCase convention. Eager-loading runs the
|
|
26
|
+
# files defining those values so a bin script can +c.add_tool+ them without
|
|
27
|
+
# per-file +require+. (web_search is host-configured via
|
|
28
|
+
# {Tool::WebSearch.build}, so it's not a value constant.) Cost: a few ms.
|
|
42
29
|
module Pikuri
|
|
43
|
-
# Search path for bundled system prompts. Mutable
|
|
44
|
-
#
|
|
45
|
-
# +
|
|
46
|
-
#
|
|
47
|
-
# +pikuri-code/prompts/+. The core gem registers its own
|
|
48
|
-
# +pikuri-core/prompts/+ below; sibling gems do the equivalent in
|
|
49
|
-
# their own entry files.
|
|
50
|
-
#
|
|
51
|
-
# Exposed publicly so a downstream library user can read pikuri's
|
|
52
|
-
# prompts as a starting point for their own system prompt (or
|
|
53
|
-
# use them verbatim). Prefer {.prompt} for the common case of
|
|
54
|
-
# loading one by name.
|
|
30
|
+
# Search path for bundled system prompts. Mutable: each pikuri gem appends its
|
|
31
|
+
# own +prompts/+ at boot, so +Pikuri.prompt(name)+ from a host requiring
|
|
32
|
+
# +pikuri-code+ finds +coding-system-prompt.txt+ there. Public so a downstream
|
|
33
|
+
# user can read pikuri's prompts as a starting point; prefer {.prompt}.
|
|
55
34
|
#
|
|
56
35
|
# @return [Array<String>]
|
|
57
36
|
PROMPT_DIRS = [File.expand_path('../prompts', __dir__)]
|
|
@@ -76,19 +55,15 @@ module Pikuri
|
|
|
76
55
|
# @return [IO] shared sink every {.logger_for} writes through
|
|
77
56
|
attr_reader :log_io
|
|
78
57
|
|
|
79
|
-
# Swap the shared sink (
|
|
80
|
-
#
|
|
81
|
-
# {.logger_for} so callers don't have to re-fetch them.
|
|
58
|
+
# Swap the shared sink (a +StringIO+ in tests, a file in a daemon),
|
|
59
|
+
# re-pointing every logger already handed out by {.logger_for}.
|
|
82
60
|
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# caller can hand the same +IO+ back in later. This uses a documented
|
|
90
|
-
# but internal Logger ivar; if a future Ruby renames it the
|
|
91
|
-
# +Engines+ logging specs will fail loudly.
|
|
61
|
+
# Not +Logger#reopen+: it closes the previous sink before installing the new
|
|
62
|
+
# one, which makes "swap to a capture StringIO, then swap back" impossible —
|
|
63
|
+
# the original handle would be dead. Instead we swap each logger's +@logdev+
|
|
64
|
+
# to a fresh +Logger::LogDevice+ over +io+, leaving the previous device
|
|
65
|
+
# open. Uses an internal Logger ivar; if a future Ruby renames it the
|
|
66
|
+
# +Engines+ logging specs fail loudly.
|
|
92
67
|
#
|
|
93
68
|
# @param io [IO] new sink for every Pikuri logger
|
|
94
69
|
# @return [IO]
|
|
@@ -100,16 +75,11 @@ module Pikuri
|
|
|
100
75
|
io
|
|
101
76
|
end
|
|
102
77
|
|
|
103
|
-
# Memoized {Logger} tagged with +name+
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# then +PIKURI_LOG+, then +INFO+.
|
|
107
|
-
#
|
|
108
|
-
# Repeated calls with the same +name+ return the same instance so
|
|
109
|
-
# there is one logger per subsystem and {.log_io=} can rewire them
|
|
110
|
-
# all in one shot.
|
|
78
|
+
# Memoized {Logger} tagged with +name+ as its +progname+. Level resolves
|
|
79
|
+
# +PIKURI_LOG_<NAME>+ → +PIKURI_LOG+ → +INFO+. Repeated calls with the same
|
|
80
|
+
# +name+ return the same instance, so {.log_io=} can rewire them all.
|
|
111
81
|
#
|
|
112
|
-
# @param name [String] subsystem tag (
|
|
82
|
+
# @param name [String] subsystem tag (the +progname+)
|
|
113
83
|
# @return [Logger]
|
|
114
84
|
def logger_for(name)
|
|
115
85
|
@log_loggers[name] ||= begin
|
|
@@ -120,25 +90,16 @@ module Pikuri
|
|
|
120
90
|
end
|
|
121
91
|
end
|
|
122
92
|
|
|
123
|
-
# Read a bundled prompt by basename
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# (+:pikuri-chat+ / +'pikuri-chat'+ / +'pikuri-chat.txt'+ all
|
|
127
|
-
# resolve to the same file).
|
|
128
|
-
#
|
|
129
|
-
# Intended for downstream library users who want to bootstrap their
|
|
130
|
-
# own +Pikuri::Agent+ wiring from pikuri's defaults — read the
|
|
131
|
-
# prompt, customize the bits they care about, hand the result to
|
|
132
|
-
# +Agent.new(system_prompt: ...)+.
|
|
93
|
+
# Read a bundled prompt by basename, searching {PROMPT_DIRS} in order
|
|
94
|
+
# (+.txt+ auto-appended; Symbols accepted). For downstream users
|
|
95
|
+
# bootstrapping their own +Agent+ wiring from pikuri's defaults.
|
|
133
96
|
#
|
|
134
97
|
# @example
|
|
135
|
-
#
|
|
136
|
-
# agent = Pikuri::Agent.new(system_prompt: chat_prompt, ...)
|
|
98
|
+
# agent = Pikuri::Agent.new(system_prompt: Pikuri.prompt(:'pikuri-chat'), ...)
|
|
137
99
|
#
|
|
138
100
|
# @param name [String, Symbol] basename of the prompt file
|
|
139
101
|
# @return [String] file contents
|
|
140
|
-
# @raise [ArgumentError] if no matching file exists in any
|
|
141
|
-
# directory in {PROMPT_DIRS}
|
|
102
|
+
# @raise [ArgumentError] if no matching file exists in any {PROMPT_DIRS}
|
|
142
103
|
def prompt(name)
|
|
143
104
|
basename = name.to_s
|
|
144
105
|
basename += '.txt' unless basename.end_with?('.txt')
|
|
@@ -149,30 +110,53 @@ module Pikuri
|
|
|
149
110
|
available = PROMPT_DIRS.flat_map { |dir| Dir.exist?(dir) ? Dir.children(dir) : [] }.sort.uniq
|
|
150
111
|
raise ArgumentError, "Unknown pikuri prompt #{name.inspect}; available: #{available.join(', ')}"
|
|
151
112
|
end
|
|
113
|
+
|
|
114
|
+
# Read several bundled prompts and join them into one system prompt —
|
|
115
|
+
# the way a binary assembles a base prompt plus shared fragments (e.g.
|
|
116
|
+
# the tool-loop hygiene in +agent-loop.txt+).
|
|
117
|
+
#
|
|
118
|
+
# system_prompt = Pikuri.prompts(:'pikuri-chat', :'agent-loop')
|
|
119
|
+
# system_prompt += "\n\nCurrent date: #{...}" # dynamic tail, still one blank line
|
|
120
|
+
#
|
|
121
|
+
# Each fragment is right-stripped before joining with a single blank
|
|
122
|
+
# line, and the result carries no trailing newline — so neither the
|
|
123
|
+
# fragment seams nor a later +"\n\n"+-prefixed append double-blanks,
|
|
124
|
+
# regardless of the +.txt+ files' own trailing newlines.
|
|
125
|
+
#
|
|
126
|
+
# @param names [Array<String, Symbol>] prompt basenames, in order
|
|
127
|
+
# @return [String] the fragments joined by a blank line
|
|
128
|
+
# @raise [ArgumentError] if any name has no matching file
|
|
129
|
+
def prompts(*names)
|
|
130
|
+
names.map { |name| prompt(name).rstrip }.join("\n\n")
|
|
131
|
+
end
|
|
152
132
|
end
|
|
153
133
|
|
|
154
|
-
# Zeitwerk loader
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
# rules can reach it without monkey-patching. Sibling gems
|
|
158
|
-
# (+pikuri-skills+, +pikuri-mcp+, ...) set up their own loaders
|
|
159
|
-
# rooted at their own +lib/+ dirs — see each gem's entry file.
|
|
134
|
+
# Zeitwerk loader for every constant under +pikuri-core/lib/pikuri/+. A
|
|
135
|
+
# constant (not block-scoped) so a downstream host can add ignore rules
|
|
136
|
+
# without monkey-patching. Sibling gems set up their own loaders.
|
|
160
137
|
#
|
|
161
138
|
# @return [Zeitwerk::Loader]
|
|
162
139
|
Loader = Zeitwerk::Loader.new
|
|
163
140
|
Loader.tag = 'pikuri-core'
|
|
164
141
|
Loader.push_dir(File.expand_path('.', __dir__))
|
|
165
142
|
Loader.ignore(__FILE__)
|
|
166
|
-
# +pikuri/version.rb+ defines +Pikuri::VERSION
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
# at the top of this file (and also by the gemspec at build time), so
|
|
170
|
-
# the constant is defined before Zeitwerk runs.
|
|
143
|
+
# +pikuri/version.rb+ defines the ALL_CAPS +Pikuri::VERSION+, not
|
|
144
|
+
# +Pikuri::Version+ as Zeitwerk expects — so skip it (it's +require_relative+'d
|
|
145
|
+
# above and by the gemspec, so the constant is already defined).
|
|
171
146
|
Loader.ignore(File.expand_path('pikuri/version.rb', __dir__))
|
|
147
|
+
# +pikuri/testing.rb+ is opt-in test scaffolding (+require 'pikuri/testing'+
|
|
148
|
+
# from a spec), kept off the production load path — so don't autoload or
|
|
149
|
+
# eager-load it. See {Pikuri::Testing}.
|
|
150
|
+
Loader.ignore(File.expand_path('pikuri/testing.rb', __dir__))
|
|
172
151
|
Loader.inflector.inflect(
|
|
173
152
|
'html' => 'HTML',
|
|
174
153
|
'duckduckgo' => 'DuckDuckGo'
|
|
175
154
|
)
|
|
176
155
|
Loader.setup
|
|
177
156
|
Loader.eager_load
|
|
157
|
+
|
|
158
|
+
# Reap +~/.cache/pikuri/temp-*+ scratch left by processes killed before their
|
|
159
|
+
# {Pikuri::Finalizers} reap could fire. Best-effort, once per process at the
|
|
160
|
+
# earliest load point (every gem requires pikuri-core). See {Paths.new_temp}.
|
|
161
|
+
Paths.sweep_stale_temps!
|
|
178
162
|
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Working with tools:
|
|
2
|
+
- If several next steps are independent (e.g. two unrelated lookups), emit them as parallel tool calls in one turn rather than one at a time.
|
|
3
|
+
- Don't repeat a tool call with identical arguments — re-read the previous observation instead.
|
|
4
|
+
- Treat a tool error (an observation starting with `Error:`) as feedback, not a dead-end: it often tells you how to proceed — retry with corrected or narrowed arguments. Only when the error genuinely leaves no way forward, use what you already have, or tell the user plainly what failed and why (a timeout, an unreachable resource, a rate limit, a missing tool). Don't retry the identical call.
|
|
5
|
+
- When you're done, reply in plain text with no tool call — that is how you end the turn.
|
data/prompts/pikuri-chat.txt
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
You are an expert assistant who solves tasks by calling tools when needed.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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".
|
|
3
|
+
How to work:
|
|
4
|
+
- When 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 — or for anything time-sensitive or obscure, search the web rather than answering from memory. Your training will *feel* certain on these and is often wrong; check rather than guess. One well-chosen search beats three exploratory ones.
|
|
5
|
+
- For any arithmetic beyond simple mental math, compute it with a tool rather than in your head — and don't search the web for "what is X * Y".
|
|
10
6
|
- 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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pikuri-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Martin Vysny
|
|
@@ -85,14 +85,14 @@ dependencies:
|
|
|
85
85
|
requirements:
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '1.
|
|
88
|
+
version: '1.16'
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '1.
|
|
95
|
+
version: '1.16'
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
97
|
name: zeitwerk
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -136,6 +136,7 @@ files:
|
|
|
136
136
|
- lib/pikuri/agent/event.rb
|
|
137
137
|
- lib/pikuri/agent/extension.rb
|
|
138
138
|
- lib/pikuri/agent/extension_context.rb
|
|
139
|
+
- lib/pikuri/agent/history.rb
|
|
139
140
|
- lib/pikuri/agent/listener.rb
|
|
140
141
|
- lib/pikuri/agent/listener/in_memory_event_list.rb
|
|
141
142
|
- lib/pikuri/agent/listener/rate_limited.rb
|
|
@@ -143,16 +144,20 @@ files:
|
|
|
143
144
|
- lib/pikuri/agent/listener/token_log.rb
|
|
144
145
|
- lib/pikuri/agent/listener_list.rb
|
|
145
146
|
- lib/pikuri/agent/synthesizer.rb
|
|
147
|
+
- lib/pikuri/bundler_env.rb
|
|
146
148
|
- lib/pikuri/extractor.rb
|
|
147
149
|
- lib/pikuri/extractor/html.rb
|
|
148
150
|
- lib/pikuri/extractor/passthrough.rb
|
|
149
151
|
- lib/pikuri/file_type.rb
|
|
150
152
|
- lib/pikuri/finalizers.rb
|
|
151
153
|
- lib/pikuri/paths.rb
|
|
154
|
+
- lib/pikuri/ruby_llm_patches.rb
|
|
152
155
|
- lib/pikuri/sanitizer.rb
|
|
153
156
|
- lib/pikuri/subprocess.rb
|
|
157
|
+
- lib/pikuri/testing.rb
|
|
154
158
|
- lib/pikuri/tool.rb
|
|
155
159
|
- lib/pikuri/tool/calculator.rb
|
|
160
|
+
- lib/pikuri/tool/execute_context.rb
|
|
156
161
|
- lib/pikuri/tool/fetch.rb
|
|
157
162
|
- lib/pikuri/tool/parameters.rb
|
|
158
163
|
- lib/pikuri/tool/scraper.rb
|
|
@@ -162,10 +167,16 @@ files:
|
|
|
162
167
|
- lib/pikuri/tool/search/exa.rb
|
|
163
168
|
- lib/pikuri/tool/search/rate_limiter.rb
|
|
164
169
|
- lib/pikuri/tool/search/result.rb
|
|
170
|
+
- lib/pikuri/tool/trifecta_legs.rb
|
|
165
171
|
- lib/pikuri/tool/web_scrape.rb
|
|
166
172
|
- lib/pikuri/tool/web_search.rb
|
|
173
|
+
- lib/pikuri/trifecta.rb
|
|
174
|
+
- lib/pikuri/trifecta/contribution.rb
|
|
175
|
+
- lib/pikuri/trifecta/node.rb
|
|
176
|
+
- lib/pikuri/trifecta/report.rb
|
|
167
177
|
- lib/pikuri/url_cache.rb
|
|
168
178
|
- lib/pikuri/version.rb
|
|
179
|
+
- prompts/agent-loop.txt
|
|
169
180
|
- prompts/pikuri-chat.txt
|
|
170
181
|
homepage: https://codeberg.org/mvysny/pikuri
|
|
171
182
|
licenses:
|