Class: Pikuri::Os::SystemInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/pikuri/os/system_info.rb

Overview

Boot-time machine facts for the OS helper: a small block folded into the system prompt so the model doesn't guess the distro, init system, or paths. Narrative in book/os-assistant.md.

The core facts (distro, kernel, init, packaging, desktop, CPU/mem, home, shell, dmesg access, date) come from always-present primitivesEtc, /etc/os-release, /proc, ENV, a $PATH probe — so the block is deterministic, needs no external binary, and works headless or in CI. The dmesg line is read from kernel.dmesg_restrict (not by spawning dmesg) so the model knows up front whether the kernel log is readable. When inxi is present, inxi -b output is appended as extra hardware/desktop detail; it is enrichment, never a dependency — a missing or failing inxi is never fatal.

A read-once-at-boot snapshot (the date is "at startup"). The host doesn't change distro mid-session, and #prompt_section is memoized so the inxi subprocess runs once no matter how often the prompt is re-assembled. Unlike MachineMemory, this deliberately does not refresh on Agent#clear_conversation.

Constant Summary collapse

INXI_MAX_BYTES =

Returns cap on the appended inxi -b block, so a chatty inxi can't bloat the prompt.

Returns:

  • (Integer)

    cap on the appended inxi -b block, so a chatty inxi can't bloat the prompt.

4 * 1024
NATIVE_PACKAGE_MANAGERS =

Native package managers, in detection order: +[command, distro hint]+. The first on $PATH wins and is reported as this host's primary manager. flatpak/snap are detected separately (cross-distro, can coexist) — see #packaging.

[
  ['apt',          'Debian/Ubuntu, dpkg'],
  ['dnf',          'Fedora/RHEL, rpm'],
  ['yum',          'RHEL/CentOS, rpm'],
  ['zypper',       'openSUSE, rpm'],
  ['pacman',       'Arch'],
  ['apk',          'Alpine'],
  ['xbps-install', 'Void'],
  ['emerge',       'Gentoo, portage'],
  ['eopkg',        'Solus']
].freeze
FACT_ORDER =

Ordered (fact-key, label) pairs for render. A nil value is skipped, so a fact we couldn't read just doesn't appear.

[
  [:os,        'OS'],
  [:kernel,    'Kernel'],
  [:init,      'Init'],
  [:packaging, 'Packaging'],
  [:desktop,   'Desktop'],
  [:hostname,  'Hostname'],
  [:home,      'Home'],
  [:shell,     'Shell'],
  [:cpus,      'CPUs'],
  [:memory,    'Memory'],
  [:dmesg,     'Kernel log (dmesg)'],
  [:date,      'Date (at startup)']
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.render(facts) ⇒ String

Render a facts hash as "- Label: value" lines in FACT_ORDER, skipping keys with no value. Pure — the testable core.

Parameters:

  • facts (Hash{Symbol=>Object})

Returns:

  • (String)


87
88
89
90
91
92
# File 'lib/pikuri/os/system_info.rb', line 87

def self.render(facts)
  FACT_ORDER.filter_map do |key, label|
    value = facts[key]
    "- #{label}: #{value}" unless value.nil? || value.to_s.strip.empty?
  end.join("\n")
end

Instance Method Details

#factsHash{Symbol=>Object}

The core facts, each gathered best-effort (a reader that fails yields nil and the line is dropped).

Returns:

  • (Hash{Symbol=>Object})


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/pikuri/os/system_info.rb', line 98

def facts
  {
    os:        os_pretty_name,
    kernel:    kernel_string,
    init:      init_system,
    packaging: packaging,
    desktop:   presence(ENV['XDG_CURRENT_DESKTOP']),
    hostname:  uname[:nodename],
    home:      home_dir,
    shell:     presence(ENV['SHELL']),
    cpus:      cpu_count,
    memory:    total_memory,
    dmesg:     dmesg_access,
    date:      Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
  }
end

#prompt_sectionString

The system-prompt grounding section: a # This machine heading, a one-line "trust these over your guesses" nudge, the core facts, and (when inxi is present) an extra-detail block. Memoized (see class doc).

Returns:

  • (String)


71
72
73
74
75
76
77
78
79
80
# File 'lib/pikuri/os/system_info.rb', line 71

def prompt_section
  @prompt_section ||= begin
    parts = ['# This machine', '',
             'Rely on these facts rather than guessing the distro, paths, or init system.',
             '', self.class.render(facts)]
    detail = inxi_detail
    parts += ['', 'Further hardware/desktop detail (from `inxi -b`):', detail] if detail
    parts.join("\n")
  end
end