pikuri-os 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 +7 -0
- data/README.md +53 -0
- data/lib/pikuri/os/extension.rb +148 -0
- data/lib/pikuri/os/fileindex_read.rb +129 -0
- data/lib/pikuri/os/fileindex_search.rb +156 -0
- data/lib/pikuri/os/local_search.rb +210 -0
- data/lib/pikuri/os/machine_memory.rb +109 -0
- data/lib/pikuri/os/recoll.rb +307 -0
- data/lib/pikuri/os/system_info.rb +247 -0
- data/lib/pikuri-os.rb +29 -0
- data/prompts/os-assistant.txt +13 -0
- data/prompts/os-network-online.txt +7 -0
- metadata +141 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'etc'
|
|
4
|
+
|
|
5
|
+
module Pikuri
|
|
6
|
+
module Os
|
|
7
|
+
# Boot-time machine facts for the OS helper: a small block folded into
|
|
8
|
+
# the system prompt so the model doesn't guess the distro, init system,
|
|
9
|
+
# or paths. Narrative in book/os-assistant.md.
|
|
10
|
+
#
|
|
11
|
+
# The core facts (distro, kernel, init, packaging, desktop, CPU/mem,
|
|
12
|
+
# home, shell, dmesg access, date) come from **always-present
|
|
13
|
+
# primitives** — +Etc+, +/etc/os-release+, +/proc+, +ENV+, a +$PATH+
|
|
14
|
+
# probe — so the block is deterministic, needs no external binary, and
|
|
15
|
+
# works headless or in CI. The dmesg line is read from
|
|
16
|
+
# +kernel.dmesg_restrict+ (not by spawning +dmesg+) so the model knows
|
|
17
|
+
# up front whether the kernel log is readable. When +inxi+ is present,
|
|
18
|
+
# +inxi -b+ output is appended as extra hardware/desktop detail; it is
|
|
19
|
+
# enrichment, never a dependency — a missing or failing +inxi+ is never
|
|
20
|
+
# fatal.
|
|
21
|
+
#
|
|
22
|
+
# A read-once-at-boot snapshot (the date is "at startup"). The host
|
|
23
|
+
# doesn't change distro mid-session, and {#prompt_section} is
|
|
24
|
+
# **memoized** so the +inxi+ subprocess runs once no matter how often
|
|
25
|
+
# the prompt is re-assembled. Unlike {MachineMemory}, this deliberately
|
|
26
|
+
# does *not* refresh on {Pikuri::Agent#clear_conversation}.
|
|
27
|
+
class SystemInfo
|
|
28
|
+
# @return [Integer] cap on the appended +inxi -b+ block, so a chatty
|
|
29
|
+
# inxi can't bloat the prompt.
|
|
30
|
+
INXI_MAX_BYTES = 4 * 1024
|
|
31
|
+
|
|
32
|
+
# Native package managers, in detection order: +[command, distro
|
|
33
|
+
# hint]+. The first on +$PATH+ wins and is reported as this host's
|
|
34
|
+
# primary manager. flatpak/snap are detected separately (cross-distro,
|
|
35
|
+
# can coexist) — see {#packaging}.
|
|
36
|
+
NATIVE_PACKAGE_MANAGERS = [
|
|
37
|
+
['apt', 'Debian/Ubuntu, dpkg'],
|
|
38
|
+
['dnf', 'Fedora/RHEL, rpm'],
|
|
39
|
+
['yum', 'RHEL/CentOS, rpm'],
|
|
40
|
+
['zypper', 'openSUSE, rpm'],
|
|
41
|
+
['pacman', 'Arch'],
|
|
42
|
+
['apk', 'Alpine'],
|
|
43
|
+
['xbps-install', 'Void'],
|
|
44
|
+
['emerge', 'Gentoo, portage'],
|
|
45
|
+
['eopkg', 'Solus']
|
|
46
|
+
].freeze
|
|
47
|
+
|
|
48
|
+
# Ordered (fact-key, label) pairs for {.render}. A +nil+ value is
|
|
49
|
+
# skipped, so a fact we couldn't read just doesn't appear.
|
|
50
|
+
FACT_ORDER = [
|
|
51
|
+
[:os, 'OS'],
|
|
52
|
+
[:kernel, 'Kernel'],
|
|
53
|
+
[:init, 'Init'],
|
|
54
|
+
[:packaging, 'Packaging'],
|
|
55
|
+
[:desktop, 'Desktop'],
|
|
56
|
+
[:hostname, 'Hostname'],
|
|
57
|
+
[:home, 'Home'],
|
|
58
|
+
[:shell, 'Shell'],
|
|
59
|
+
[:cpus, 'CPUs'],
|
|
60
|
+
[:memory, 'Memory'],
|
|
61
|
+
[:dmesg, 'Kernel log (dmesg)'],
|
|
62
|
+
[:date, 'Date (at startup)']
|
|
63
|
+
].freeze
|
|
64
|
+
|
|
65
|
+
# The system-prompt grounding section: a +# This machine+ heading,
|
|
66
|
+
# a one-line "trust these over your guesses" nudge, the core facts,
|
|
67
|
+
# and (when +inxi+ is present) an extra-detail block.
|
|
68
|
+
# Memoized (see class doc).
|
|
69
|
+
#
|
|
70
|
+
# @return [String]
|
|
71
|
+
def prompt_section
|
|
72
|
+
@prompt_section ||= begin
|
|
73
|
+
parts = ['# This machine', '',
|
|
74
|
+
'Rely on these facts rather than guessing the distro, paths, or init system.',
|
|
75
|
+
'', self.class.render(facts)]
|
|
76
|
+
detail = inxi_detail
|
|
77
|
+
parts += ['', 'Further hardware/desktop detail (from `inxi -b`):', detail] if detail
|
|
78
|
+
parts.join("\n")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Render a facts hash as +"- Label: value"+ lines in {FACT_ORDER},
|
|
83
|
+
# skipping keys with no value. Pure — the testable core.
|
|
84
|
+
#
|
|
85
|
+
# @param facts [Hash{Symbol=>Object}]
|
|
86
|
+
# @return [String]
|
|
87
|
+
def self.render(facts)
|
|
88
|
+
FACT_ORDER.filter_map do |key, label|
|
|
89
|
+
value = facts[key]
|
|
90
|
+
"- #{label}: #{value}" unless value.nil? || value.to_s.strip.empty?
|
|
91
|
+
end.join("\n")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The core facts, each gathered best-effort (a reader that fails
|
|
95
|
+
# yields +nil+ and the line is dropped).
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash{Symbol=>Object}]
|
|
98
|
+
def facts
|
|
99
|
+
{
|
|
100
|
+
os: os_pretty_name,
|
|
101
|
+
kernel: kernel_string,
|
|
102
|
+
init: init_system,
|
|
103
|
+
packaging: packaging,
|
|
104
|
+
desktop: presence(ENV['XDG_CURRENT_DESKTOP']),
|
|
105
|
+
hostname: uname[:nodename],
|
|
106
|
+
home: home_dir,
|
|
107
|
+
shell: presence(ENV['SHELL']),
|
|
108
|
+
cpus: cpu_count,
|
|
109
|
+
memory: total_memory,
|
|
110
|
+
dmesg: dmesg_access,
|
|
111
|
+
date: Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# The host's package-management surface: the primary native manager
|
|
118
|
+
# (first {NATIVE_PACKAGE_MANAGERS} entry on +$PATH+, with its distro
|
|
119
|
+
# hint) followed by whichever of flatpak / snap are installed, e.g.
|
|
120
|
+
# +"apt (Debian/Ubuntu, dpkg); flatpak"+. +nil+ when none are found
|
|
121
|
+
# (so the line is dropped rather than asserting a bare box has no way
|
|
122
|
+
# to install software).
|
|
123
|
+
#
|
|
124
|
+
# @return [String, nil]
|
|
125
|
+
def packaging
|
|
126
|
+
native = NATIVE_PACKAGE_MANAGERS.find { |cmd, _| on_path?(cmd) }
|
|
127
|
+
parts = []
|
|
128
|
+
parts << "#{native[0]} (#{native[1]})" if native
|
|
129
|
+
parts << 'flatpak' if on_path?('flatpak')
|
|
130
|
+
parts << 'snap' if on_path?('snap')
|
|
131
|
+
parts.empty? ? nil : parts.join('; ')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Whether this user can read the kernel ring buffer (+dmesg+), decided
|
|
135
|
+
# from +kernel.dmesg_restrict+ rather than by spawning +dmesg+ — so
|
|
136
|
+
# the model knows in advance whether a +dmesg+ call returns logs or
|
|
137
|
+
# fails with "read kernel buffer failed: Operation not permitted".
|
|
138
|
+
# +nil+ (line dropped) when +dmesg+ isn't installed.
|
|
139
|
+
#
|
|
140
|
+
# @return [String, nil]
|
|
141
|
+
def dmesg_access
|
|
142
|
+
return nil unless on_path?('dmesg')
|
|
143
|
+
return 'readable (running as root)' if Process.uid.zero?
|
|
144
|
+
|
|
145
|
+
case File.read('/proc/sys/kernel/dmesg_restrict').strip
|
|
146
|
+
when '0' then 'readable'
|
|
147
|
+
when '1' then 'restricted as this user — needs sudo (kernel.dmesg_restrict=1)'
|
|
148
|
+
end
|
|
149
|
+
rescue SystemCallError
|
|
150
|
+
# dmesg_restrict absent on this kernel: unprivileged reads are the
|
|
151
|
+
# historical default, so treat it as readable rather than guessing.
|
|
152
|
+
'likely readable (kernel.dmesg_restrict unset)'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @param name [String] a bare command name, e.g. "flatpak"
|
|
156
|
+
# @return [Boolean] true if an executable by that name is on +$PATH+
|
|
157
|
+
def on_path?(name)
|
|
158
|
+
ENV['PATH'].to_s.split(File::PATH_SEPARATOR).any? do |dir|
|
|
159
|
+
next false if dir.empty?
|
|
160
|
+
|
|
161
|
+
path = File.join(dir, name)
|
|
162
|
+
File.file?(path) && File.executable?(path)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# +inxi -b -c 0+ output (color stripped) when inxi is on PATH, capped
|
|
167
|
+
# to {INXI_MAX_BYTES}; +nil+ when inxi is absent, errors, or is
|
|
168
|
+
# empty. Never raises out — enrichment only.
|
|
169
|
+
#
|
|
170
|
+
# @return [String, nil]
|
|
171
|
+
def inxi_detail
|
|
172
|
+
result = Pikuri::Subprocess.spawn('inxi', '-b', '-c', '0', chdir: '/').wait
|
|
173
|
+
return nil unless result.status.success?
|
|
174
|
+
|
|
175
|
+
out = result.output.strip
|
|
176
|
+
return nil if out.empty?
|
|
177
|
+
|
|
178
|
+
out.bytesize > INXI_MAX_BYTES ? "#{out.byteslice(0, INXI_MAX_BYTES)}\n…" : out
|
|
179
|
+
rescue Errno::ENOENT
|
|
180
|
+
nil
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @return [String, nil] +PRETTY_NAME+ from +/etc/os-release+
|
|
184
|
+
def os_pretty_name
|
|
185
|
+
line = File.foreach('/etc/os-release').find { |l| l.start_with?('PRETTY_NAME=') }
|
|
186
|
+
return nil unless line
|
|
187
|
+
|
|
188
|
+
line.split('=', 2).last.strip.delete_prefix('"').delete_suffix('"')
|
|
189
|
+
rescue SystemCallError
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @return [String, nil] +"<release> <machine>"+, e.g. "7.0.0-27-generic x86_64"
|
|
194
|
+
def kernel_string
|
|
195
|
+
u = uname
|
|
196
|
+
return nil unless u[:release]
|
|
197
|
+
|
|
198
|
+
[u[:release], u[:machine]].compact.join(' ')
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# @return [Hash] memoized +Etc.uname+ (sysname/nodename/release/…),
|
|
202
|
+
# or +{}+ if unavailable.
|
|
203
|
+
def uname
|
|
204
|
+
@uname ||= (Etc.uname rescue {})
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @return [String, nil] PID 1's command (e.g. "systemd")
|
|
208
|
+
def init_system
|
|
209
|
+
presence(File.read('/proc/1/comm').strip)
|
|
210
|
+
rescue SystemCallError
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @return [String, nil]
|
|
215
|
+
def home_dir
|
|
216
|
+
Dir.home
|
|
217
|
+
rescue ArgumentError
|
|
218
|
+
presence(ENV['HOME'])
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @return [Integer, nil]
|
|
222
|
+
def cpu_count
|
|
223
|
+
Etc.nprocessors
|
|
224
|
+
rescue StandardError
|
|
225
|
+
nil
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @return [String, nil] total RAM, e.g. "23.5 GiB", from /proc/meminfo
|
|
229
|
+
def total_memory
|
|
230
|
+
line = File.foreach('/proc/meminfo').find { |l| l.start_with?('MemTotal:') }
|
|
231
|
+
return nil unless line
|
|
232
|
+
|
|
233
|
+
kb = line[/(\d+)/, 1]&.to_i
|
|
234
|
+
return nil unless kb
|
|
235
|
+
|
|
236
|
+
format('%.1f GiB', kb / 1024.0 / 1024.0)
|
|
237
|
+
rescue SystemCallError
|
|
238
|
+
nil
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# @return [String, nil] +value+ unless nil/blank
|
|
242
|
+
def presence(value)
|
|
243
|
+
value && !value.to_s.strip.empty? ? value : nil
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
data/lib/pikuri-os.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pikuri-core'
|
|
4
|
+
require 'pikuri-workspace'
|
|
5
|
+
require 'pikuri-code'
|
|
6
|
+
|
|
7
|
+
# Entry file for the pikuri-os gem: the OS-integration layer and home of
|
|
8
|
+
# the +bin/pikuri-os+ demo — a single, local, network-severed
|
|
9
|
+
# agent for understanding and operating the host OS ({Pikuri::Os::Extension}
|
|
10
|
+
# is the one-call wiring). The load-bearing posture: egress severed at the
|
|
11
|
+
# kernel (a net-isolated Bash sandbox), every mutation confirmed, durable
|
|
12
|
+
# memory a user-owned +MACHINE.md+ rather than mem0 — so this gem depends
|
|
13
|
+
# on pikuri-code but NOT pikuri-mcp/-memory/-vectordb. The two cross-cutting
|
|
14
|
+
# pieces (+FullFsNoNet+ sandbox, passive-command detector) live in
|
|
15
|
+
# pikuri-code with the other OS-touching primitives. Design notes:
|
|
16
|
+
# +pikuri-os/DESIGN.md+ (the file-index engine survey) and
|
|
17
|
+
# book/os-assistant.md (the security narrative).
|
|
18
|
+
Pikuri::PROMPT_DIRS << File.expand_path('../prompts', __dir__)
|
|
19
|
+
|
|
20
|
+
module Pikuri
|
|
21
|
+
module Os
|
|
22
|
+
LOADER = Zeitwerk::Loader.new
|
|
23
|
+
LOADER.tag = 'pikuri-os'
|
|
24
|
+
LOADER.push_dir(__dir__)
|
|
25
|
+
LOADER.ignore(File.expand_path('pikuri-os.rb', __dir__))
|
|
26
|
+
LOADER.setup
|
|
27
|
+
LOADER.eager_load
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
You are a local assistant for this Linux machine. You help the user understand and operate the computer they are sitting at: answer questions about the system, find and open their files, read logs and explain what went wrong, and advise on configuration. This is a standard Linux system whose filesystem is organized according to the FHS (Filesystem Hierarchy Standard), so system files live in their conventional locations.
|
|
2
|
+
|
|
3
|
+
How you operate:
|
|
4
|
+
- You act on the *real* host, and you run as the ordinary (non-root) user.
|
|
5
|
+
- You can't elevate privileges (no working sudo/su). A command that fails with `Permission denied`, `Operation not permitted`, or `are you root?` means the real answer needs root: hand the user the exact `sudo …` command and explain what it does — the same hand-off you use for anything needing the network. If that command *reads* something, ask them to paste the output back. If it *changes* the machine (installs a package, edits a system file, restarts a service), ask them only to say when it's done, then confirm the result yourself with the commands you can already run — don't make them paste output you don't need.
|
|
6
|
+
- Network: the shell and the programs you run cannot reach the internet — you can't fetch a URL, install or update packages, or run a connectivity test. That's a property of your tools, not of the machine: it's a normal, networked computer and the user's own connectivity is fine, so never tell them their machine is offline, and don't read a failed fetch of your own as an outage on their end. When something genuinely needs the network — a download, an install, a reachability check — hand the user the exact command to run themselves (the same hand-off as anything needing root). Don't claim to have looked something up online unless you actually did.
|
|
7
|
+
- Look, don't guess. When you're unsure of a path, a flag, or the current state of something, inspect the machine — read the file, list the directory, check the log — rather than assuming. The installed documentation is authoritative and beats a half-remembered flag: run `man <cmd>` or `<cmd> --help` for a command's exact options — especially for an unfamiliar, vendor, or locally-installed tool, or when behavior may differ by version — and `apropos <keyword>` (or `man -k`) to find which utility does something when you don't know its name. A package's own `/usr/share/doc/<pkg>/` often holds a README or examples.
|
|
8
|
+
- To find a file (or which file mentions something) anywhere on this machine, search the desktop file index first — it reads inside PDFs and office documents a raw scan can't, and it's cheap to query. Reach for a content/filename scan of a directory tree only once you've narrowed to a specific place to look.
|
|
9
|
+
|
|
10
|
+
Keeping notes: you keep durable notes about this machine (where the user keeps their music, work, and documents; how things are synced; quirks of this setup). When you learn a lasting fact like that, record it by editing the notes file named below, so a future conversation starts already knowing it. Keep the notes accurate and trim what's no longer true.
|
|
11
|
+
|
|
12
|
+
Other guidelines:
|
|
13
|
+
- When you have enough to answer, answer. Show the commands you'd run or did run, and cite file paths so the user can follow along. Give one clear recommendation with its reason rather than listing every option.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
You have one exception to the no-network rule: for a genuine external lookup
|
|
2
|
+
(current documentation, a library version, the meaning of an error seen online)
|
|
3
|
+
you may delegate a self-contained research task to the web-capable research
|
|
4
|
+
agent. It runs in isolation with no access to this machine's files, and the
|
|
5
|
+
user approves the exact task before it leaves the machine — so write each task
|
|
6
|
+
to stand on its own, put nothing private in it, and treat it as a way to *read*
|
|
7
|
+
the web, never to act on this machine.
|
metadata
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pikuri-os
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Martin Vysny
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: pikuri-code
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.1.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: pikuri-core
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - '='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.1.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - '='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.1.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: pikuri-pdf
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - '='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.1.0
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - '='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.1.0
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: pikuri-subagents
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - '='
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 0.1.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - '='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 0.1.0
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: pikuri-workspace
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - '='
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 0.1.0
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - '='
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 0.1.0
|
|
82
|
+
description: |
|
|
83
|
+
pikuri-os is the OS-integration layer for pikuri and the home of
|
|
84
|
+
the +bin/pikuri-os+ demo: a single, local, network-severed
|
|
85
|
+
agent that understands and operates the host OS (answer questions
|
|
86
|
+
about the machine, find and open files, read logs and explain
|
|
87
|
+
errors, advise on configuration). It is the federation's privacy-
|
|
88
|
+
first +@os+ member shipped standalone — no egress, no MCP, no cloud.
|
|
89
|
+
|
|
90
|
+
The gem holds the OS-specific surface (a localsearch-backed
|
|
91
|
+
content-search tool, a single-file +MACHINE.md+ resident memory,
|
|
92
|
+
and boot-time machine grounding) on top of pikuri-core's Agent +
|
|
93
|
+
Tool framework, pikuri-workspace's file tools, and pikuri-code's
|
|
94
|
+
Bash + sandbox. It deliberately does NOT depend on
|
|
95
|
+
pikuri-mcp/-memory/-vectordb: severing those keeps the no-egress,
|
|
96
|
+
"small enough to audit" posture honest.
|
|
97
|
+
|
|
98
|
+
See +ideas/pikuri-os.md+ in the repo for the full design.
|
|
99
|
+
email:
|
|
100
|
+
- martin@vysny.me
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- README.md
|
|
106
|
+
- lib/pikuri-os.rb
|
|
107
|
+
- lib/pikuri/os/extension.rb
|
|
108
|
+
- lib/pikuri/os/fileindex_read.rb
|
|
109
|
+
- lib/pikuri/os/fileindex_search.rb
|
|
110
|
+
- lib/pikuri/os/local_search.rb
|
|
111
|
+
- lib/pikuri/os/machine_memory.rb
|
|
112
|
+
- lib/pikuri/os/recoll.rb
|
|
113
|
+
- lib/pikuri/os/system_info.rb
|
|
114
|
+
- prompts/os-assistant.txt
|
|
115
|
+
- prompts/os-network-online.txt
|
|
116
|
+
homepage: https://codeberg.org/mvysny/pikuri
|
|
117
|
+
licenses:
|
|
118
|
+
- MIT
|
|
119
|
+
metadata:
|
|
120
|
+
source_code_uri: https://codeberg.org/mvysny/pikuri/src/branch/master
|
|
121
|
+
changelog_uri: https://codeberg.org/mvysny/pikuri/src/branch/master/CHANGELOG.md
|
|
122
|
+
bug_tracker_uri: https://codeberg.org/mvysny/pikuri/issues
|
|
123
|
+
rubygems_mfa_required: 'true'
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '3.3'
|
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
137
|
+
requirements: []
|
|
138
|
+
rubygems_version: 3.6.7
|
|
139
|
+
specification_version: 4
|
|
140
|
+
summary: Offline Linux/OS helper agent + tools for pikuri.
|
|
141
|
+
test_files: []
|