From 19286e98603bba701aca9d124e33b0487a999527 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Tue, 19 May 2026 08:24:06 -0700 Subject: [PATCH] feat(patterns): full Linux-to-Linux distro matrix + M2 LLM chat wiring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pattern files: 12 cross-family migration pairs covering debian, fedora, arch, opensuse — each tuned to the user's prior tooling (apt, dnf, pacman, zypper). Includes the custom linux-to-arch file for experienced distro-hoppers and the macos-to-arch / windows-to-debian expansions from the prior session. Code changes: - patterns::load() accepts source_distro_family: Option<&str> — tries specific debian-to-arch.toml before falling back to linux-to-arch.toml - MigrationConfig adds source_distro_family: Option with serde default - complete_onboarding() accepts optional source_distro arg and derives family via distro_family() for Linux-to-Linux migrations - llm.rs: Ollama streaming client with Vec buffer for UTF-8 safety, emit errors logged not silenced - commands::chat: spawns stream task, returns immediately so frontend isn't blocked waiting for full LLM response - lib.rs: registers mod llm and commands::chat in invoke_handler --- src-tauri/patterns/arch-to-debian.toml | 146 ++++++++++++++++ src-tauri/patterns/arch-to-fedora.toml | 136 +++++++++++++++ src-tauri/patterns/arch-to-opensuse.toml | 130 ++++++++++++++ src-tauri/patterns/debian-to-arch.toml | 186 +++++++++++++++++++++ src-tauri/patterns/debian-to-fedora.toml | 144 ++++++++++++++++ src-tauri/patterns/debian-to-opensuse.toml | 120 +++++++++++++ src-tauri/patterns/fedora-to-arch.toml | 172 +++++++++++++++++++ src-tauri/patterns/fedora-to-debian.toml | 138 +++++++++++++++ src-tauri/patterns/fedora-to-opensuse.toml | 130 ++++++++++++++ src-tauri/patterns/opensuse-to-arch.toml | 172 +++++++++++++++++++ src-tauri/patterns/opensuse-to-debian.toml | 138 +++++++++++++++ src-tauri/patterns/opensuse-to-fedora.toml | 146 ++++++++++++++++ src-tauri/src/commands.rs | 56 ++++++- src-tauri/src/config.rs | 6 + src-tauri/src/lib.rs | 4 +- src-tauri/src/llm.rs | 157 +++++++++++++++++ src-tauri/src/patterns.rs | 31 ++-- 17 files changed, 1999 insertions(+), 13 deletions(-) create mode 100644 src-tauri/patterns/arch-to-debian.toml create mode 100644 src-tauri/patterns/arch-to-fedora.toml create mode 100644 src-tauri/patterns/arch-to-opensuse.toml create mode 100644 src-tauri/patterns/debian-to-arch.toml create mode 100644 src-tauri/patterns/debian-to-fedora.toml create mode 100644 src-tauri/patterns/debian-to-opensuse.toml create mode 100644 src-tauri/patterns/fedora-to-arch.toml create mode 100644 src-tauri/patterns/fedora-to-debian.toml create mode 100644 src-tauri/patterns/fedora-to-opensuse.toml create mode 100644 src-tauri/patterns/opensuse-to-arch.toml create mode 100644 src-tauri/patterns/opensuse-to-debian.toml create mode 100644 src-tauri/patterns/opensuse-to-fedora.toml create mode 100644 src-tauri/src/llm.rs diff --git a/src-tauri/patterns/arch-to-debian.toml b/src-tauri/patterns/arch-to-debian.toml new file mode 100644 index 0000000..051df29 --- /dev/null +++ b/src-tauri/patterns/arch-to-debian.toml @@ -0,0 +1,146 @@ +[meta] +source_os = "linux" +target_distro_family = "debian" + +# Arch/Manjaro/EndeavourOS user moving to Debian/Ubuntu/Mint. +# Body text assumes pacman, AUR, and rolling release familiarity. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── apt / dpkg ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apt-lock" +sources = ["journald"] +match_text = "Could not get lock /var/lib/dpkg/lock" +severity = "warn" +title = "Package manager is locked" +body = "Another apt process is running — often unattended-upgrades (automatic background updates, no Arch equivalent). Wait a minute. If stuck: sudo rm /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock && sudo dpkg --configure -a" + +[[patterns]] +id = "dpkg-interrupted" +sources = ["journald"] +match_text = "dpkg was interrupted" +severity = "warn" +title = "Package install was interrupted" +body = "Like a pacman transaction that got killed, but dpkg needs manual recovery. Fix: sudo dpkg --configure -a" + +[[patterns]] +id = "apt-unmet-dependency" +sources = ["journald"] +match_text = "Unmet dependencies" +severity = "warn" +title = "Package dependency conflict" +body = "Unlike pacman which puts the conflict choice on you, apt tries to auto-resolve. Let it: sudo apt --fix-broken install — if it can't, read the message for which packages conflict." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "On Debian: sudo apt install firmware-linux firmware-linux-nonfree (enable non-free sources first). On Ubuntu: sudo apt install linux-firmware. Unlike Arch's single linux-firmware package, Debian splits firmware by license." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "Debian stable has conservative defaults — Ubuntu enables zswap, Debian doesn't. If you used zram-generator on Arch, set up a swapfile here: fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo apt install smartmontools" + +[[patterns]] +id = "locale-error" +sources = ["journald"] +match_text = "failed to set locale" +severity = "info" +title = "Locale configuration error" +body = "Debian generates locales via dpkg-reconfigure: sudo dpkg-reconfigure locales — select your locale in the curses UI. Unlike Arch where you edit /etc/locale.gen directly, Debian abstracts this." + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "Debian/Ubuntu ships AppArmor — Arch doesn't use MAC by default. An app is blocked by a security profile. Check: sudo aa-status — audit: sudo aa-logprof — or put the profile in complain mode: sudo aa-complain /etc/apparmor.d/" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Ubuntu 22.04+/Debian 12+ ship PipeWire like Arch. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "pulseaudio-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to pulseaudio" +severity = "warn" +title = "PulseAudio not responding" +body = "Older Debian systems still use PulseAudio. Restart: pulseaudio --kill && pulseaudio --start — you can migrate to PipeWire: sudo apt install pipewire-audio" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as Arch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on Ubuntu: sudo apt install nvidia-driver- or ubuntu-drivers autoinstall. Debian requires non-free sources: apt install nvidia-driver" + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Debian minimal installs may use ifupdown instead of NetworkManager. Check: systemctl status NetworkManager — install if missing: sudo apt install network-manager" + +# ── Printing ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "cups-server-error" +sources = ["journald"] +match_text = "Unable to connect to CUPS server" +severity = "info" +title = "Printer service not running" +body = "sudo systemctl start cups && sudo systemctl enable cups — Debian/Ubuntu handle printing through CUPS; Arch also uses CUPS but it's not always enabled by default." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." diff --git a/src-tauri/patterns/arch-to-fedora.toml b/src-tauri/patterns/arch-to-fedora.toml new file mode 100644 index 0000000..0e82a53 --- /dev/null +++ b/src-tauri/patterns/arch-to-fedora.toml @@ -0,0 +1,136 @@ +[meta] +source_os = "linux" +target_distro_family = "fedora" + +# Arch/Manjaro/EndeavourOS user moving to Fedora. +# Body text assumes pacman, AUR, and rolling release familiarity. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── DNF / RPM ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "dnf-lock" +sources = ["journald"] +match_text = "Another app is currently holding the dnf lock" +severity = "warn" +title = "DNF package manager is locked" +body = "dnf-automatic (Fedora's equivalent of Arch's unattended auto-updates, though Arch doesn't do auto-updates) is probably running. Wait it out or: sudo ps aux | grep dnf" + +[[patterns]] +id = "dnf-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "Unlike pacman where you resolve conflicts manually, dnf tries to auto-resolve. It usually succeeds. If not: sudo dnf distro-sync — the Fedora equivalent of pacman -Syu for bringing the system fully in sync." + +[[patterns]] +id = "dnf-gpg-key" +sources = ["journald"] +match_text = "GPG key retrieval failed" +severity = "warn" +title = "Repository GPG key missing" +body = "Import the key: sudo rpm --import /path/to/key.gpg — or re-run with: sudo dnf install --nogpgcheck (only if you trust the source). RPM Fusion keys are imported automatically when you enable the repo." + +# ── SELinux (new concept for Arch users) ───────────────────────────────────── + +[[patterns]] +id = "selinux-denial" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "SELinux access denied" +body = "Fedora ships SELinux enforcing by default — there's nothing like this on Arch by default. A security policy is blocking an action. Check what's blocked: ausearch -m AVC -ts recent — get a fix suggestion: sealert -a /var/log/audit/audit.log — don't just set SELinux to permissive; that defeats the security model." + +[[patterns]] +id = "selinux-context-wrong" +sources = ["journald"] +match_text = "restorecon" +severity = "info" +title = "SELinux file context mismatch" +body = "A file has the wrong security label — common when copying files from an Arch system or an external drive. Fix: sudo restorecon -Rv /path/to/file" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo dnf install linux-firmware — same coverage as Arch's linux-firmware package. Some chips may need RPM Fusion nonfree: sudo dnf install rpmfusion-nonfree-release-$(rpm -E %fedora)" + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Fedora enables zswap by default on modern releases. If you used zram on Arch, install zram-generator: sudo dnf install zram-generator" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo dnf install smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Both Arch and Fedora ship PipeWire. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as Arch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on Fedora, RPM Fusion is the right source: sudo dnf install akmod-nvidia — unlike Arch's nvidia-dkms from the official repos." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "X11 apps dead until session restart. Fedora GNOME defaults to Wayland like Arch KDE/GNOME can." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — same NetworkManager as Arch. Firmware issues: sudo dmesg | grep firmware" + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. Steam on Fedora: sudo dnf install steam (from RPM Fusion free)." diff --git a/src-tauri/patterns/arch-to-opensuse.toml b/src-tauri/patterns/arch-to-opensuse.toml new file mode 100644 index 0000000..520629d --- /dev/null +++ b/src-tauri/patterns/arch-to-opensuse.toml @@ -0,0 +1,130 @@ +[meta] +source_os = "linux" +target_distro_family = "opensuse" + +# Arch/Manjaro/EndeavourOS user moving to openSUSE Tumbleweed or Leap. +# Body text assumes pacman, AUR, and rolling release familiarity. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── zypper / RPM ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "zypper-lock" +sources = ["journald"] +match_text = "System management is locked" +severity = "warn" +title = "zypper package manager is locked" +body = "Another zypper or PackageKit (GNOME Software) process is running. Wait it out or check: sudo ps aux | grep zypper" + +[[patterns]] +id = "zypper-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "Unlike pacman which puts the conflict on you immediately, zypper presents resolution options. zypper dup (distribution upgrade) is more aggressive than zypper up and usually resolves what zypper up won't." + +[[patterns]] +id = "zypper-gpg-key" +sources = ["journald"] +match_text = "does not verify" +severity = "warn" +title = "Repository signature not trusted" +body = "Auto-import keys: sudo zypper --gpg-auto-import-keys ref — unlike pacman-key, zypper manages repo keys through RPM's keyring." + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "openSUSE ships AppArmor — Arch doesn't use MAC by default. An app is blocked by a profile. Check: sudo aa-status — audit and fix: sudo aa-logprof — or set the profile to complain mode: sudo aa-complain " + +# ── YaST ────────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "yast-backend-fail" +sources = ["journald"] +match_text = "YaST got signal" +severity = "warn" +title = "YaST configuration tool crashed" +body = "YaST is openSUSE's graphical admin tool — no Arch equivalent. If it crashed mid-operation: sudo yast2 in a terminal to run the text-mode version, which is more stable for recovery." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo zypper install kernel-firmware — similar to Arch's linux-firmware in scope." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. If you used zram-generator on Arch, openSUSE supports it too: sudo zypper install zram-generator — or set up swap via YaST -> System -> Partitioner." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo zypper install smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Tumbleweed ships PipeWire like Arch. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as Arch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on openSUSE: use the NVIDIA OBS repo (similar to Arch's nvidia-dkms from official repos). AMD users: mesa is in the default repos." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — openSUSE may use Wicked on server installs instead of NetworkManager. Check: systemctl status NetworkManager wicked" + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. Steam on openSUSE: sudo zypper install steam (from the games repo on OBS)." diff --git a/src-tauri/patterns/debian-to-arch.toml b/src-tauri/patterns/debian-to-arch.toml new file mode 100644 index 0000000..abba8f8 --- /dev/null +++ b/src-tauri/patterns/debian-to-arch.toml @@ -0,0 +1,186 @@ +[meta] +source_os = "linux" +target_distro_family = "arch" + +# Debian/Ubuntu/Mint user on their first Arch install. +# Body text assumes comfort with apt and systemd but not AUR or rolling release. + +[log_paths] +pacman = "/var/log/pacman.log" +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" +lutris = "~/.cache/lutris/logs/lutris.log" + +# ── pacman / AUR ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "pacman-db-lock" +sources = ["journald", "applog:pacman"] +match_text = "could not lock database: File exists" +severity = "warn" +title = "pacman database locked" +body = "Lock file left from a crashed pacman run — like dpkg getting killed mid-install. If nothing is running: sudo rm /var/lib/pacman/db.lck — verify first: fuser /var/lib/pacman/db.lck" + +[[patterns]] +id = "pacman-dep-conflict" +sources = ["journald", "applog:pacman"] +match_text = "conflicting dependencies" +severity = "warn" +title = "Dependency conflict" +body = "Unlike apt, pacman won't auto-resolve conflicts — you decide. Usually one package replaces another (e.g. pipewire-pulse replaces pulseaudio). Remove the conflicting package first, then retry." + +[[patterns]] +id = "pacman-conflicting-files" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to commit transaction (conflicting files)" +severity = "warn" +title = "Conflicting files on install" +body = "A file already exists on disk that the package wants to own. Check which package owns it: pacman -Qo /path/to/file — then remove the stale file or use --overwrite if you're sure it's safe." + +[[patterns]] +id = "partial-upgrade-warning" +sources = ["applog:pacman"] +match_text = "warning: database file for" +severity = "info" +title = "Package database out of sync" +body = "On Arch, pacman -Sy (sync without upgrade) is dangerous — partial upgrades break the system. Unlike apt where partial syncs are fine, on Arch always use pacman -Syu. This is the biggest rule to learn coming from Debian." + +[[patterns]] +id = "aur-build-failure" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to build" +severity = "warn" +title = "AUR package build failed" +body = "The AUR is source-based — no binary packages, makepkg compiles from a PKGBUILD. Read the full output for the cause: missing makedepend, broken upstream URL, or bad patch. Check the package's AUR comments page for known fixes." + +[[patterns]] +id = "aur-pgp-key" +sources = ["journald", "applog:pacman"] +match_text = "unknown public key" +severity = "warn" +title = "PGP key not in keyring" +body = "AUR packages verify signatures — different from apt's keyring model. Import the key: gpg --recv-keys — or if the PKGBUILD lists validpgpkeys, import exactly those." + +[[patterns]] +id = "makepkg-missing-deps" +sources = ["journald", "applog:pacman"] +match_text = "Missing dependencies" +severity = "warn" +title = "AUR build dependencies missing" +body = "AUR helpers like paru/yay resolve makedepends automatically. If building manually: sudo pacman -S first. This is like build-dep in apt but you have to do it manually with plain makepkg." + +[[patterns]] +id = "chaotic-aur-sig-fail" +sources = ["journald", "applog:pacman"] +match_text = "signature from" +severity = "warn" +title = "Package signature verification failed" +body = "Chaotic-AUR key not trusted. Import it: sudo pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com && sudo pacman-key --lsign-key 3056513887B78AEB — then retry." + +# ── Kernel / DKMS ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "dkms-build-fail" +sources = ["journald"] +match_text = "Error! Build of" +severity = "warn" +title = "DKMS module failed to build" +body = "A kernel module didn't compile after a kernel update. On Arch's rolling release this happens more than on Debian stable. Check: dkms status — then reinstall the dkms package or wait for an AUR update. Debian had linux-headers; here it's linux-headers (or linux-cachyos-headers on CachyOS)." + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo pacman -S linux-firmware — similar to firmware-linux-nonfree on Debian but one package covers most hardware. Specific chips (Realtek wifi) may need AUR packages." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "locale-not-set" +sources = ["journald"] +match_text = "Cannot set LC_ALL to default locale" +severity = "info" +title = "Locale not generated" +body = "Unlike Debian, Arch doesn't pre-generate locales. Edit /etc/locale.gen (uncomment your locale, e.g. en_US.UTF-8), then: sudo locale-gen — and set LANG=en_US.UTF-8 in /etc/locale.conf." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Arch doesn't enable zswap by default like Ubuntu does. Add zram: sudo pacman -S zram-generator — or add a swapfile." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Storage error on a block device. Check SMART: sudo smartctl -a /dev/sdX — or for NVMe: sudo nvme smart-log /dev/nvme0." + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "systemctl --user restart pipewire pipewire-pulse wireplumber — Arch defaults to PipeWire; no PulseAudio fallback like Ubuntu has." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — if hard-blocked, check BIOS or a physical switch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. On Arch with AMD: check mesa version vs kernel version — both roll together. On NVIDIA: check nvidia-dkms matches the running kernel." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "X11 apps will be dead until session restart. If reproducible with a specific app, check for a Wayland-native version or force X11 with WAYLAND_DISPLAY= unset." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — if a wifi adapter is missing, check dmesg for firmware errors. Arch ships most firmware in linux-firmware; some Realtek chips need AUR packages." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." + +[[patterns]] +id = "lutris-wine-fail" +sources = ["applog:lutris"] +match_text = "Wine is not installed" +severity = "warn" +title = "Lutris: Wine not found" +body = "Lutris needs a Wine runner. In Lutris: Preferences -> Runners -> Wine -> Install — or: paru -S wine-staging" diff --git a/src-tauri/patterns/debian-to-fedora.toml b/src-tauri/patterns/debian-to-fedora.toml new file mode 100644 index 0000000..0bf995b --- /dev/null +++ b/src-tauri/patterns/debian-to-fedora.toml @@ -0,0 +1,144 @@ +[meta] +source_os = "linux" +target_distro_family = "fedora" + +# Debian/Ubuntu/Mint user on their first Fedora/RHEL/CentOS install. +# Body text assumes apt/dpkg familiarity, no RPM experience. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── DNF / RPM ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "dnf-lock" +sources = ["journald"] +match_text = "Another app is currently holding the dnf lock" +severity = "warn" +title = "DNF package manager is locked" +body = "Another dnf process is running — like apt being held by unattended-upgrades. Wait for it to finish or check: sudo ps aux | grep dnf — then kill if stuck." + +[[patterns]] +id = "rpm-db-corrupt" +sources = ["journald"] +match_text = "rpmdb" +severity = "warn" +title = "RPM database issue" +body = "Like a corrupted dpkg database. Rebuild it: sudo rpm --rebuilddb — then retry your dnf command." + +[[patterns]] +id = "dnf-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "DNF can auto-resolve some conflicts but not all. Read the conflict message — usually one package provides what another needs. Try: sudo dnf distro-sync to bring everything in sync." + +[[patterns]] +id = "dnf-gpg-key" +sources = ["journald"] +match_text = "GPG key retrieval failed" +severity = "warn" +title = "Repository GPG key missing" +body = "A repo's signing key isn't trusted. Import it: sudo rpm --import /path/to/key.gpg — or re-enable the repo's key: sudo dnf repoinfo " + +# ── SELinux ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "selinux-denial" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "SELinux access denied" +body = "Fedora ships SELinux enforcing by default — there's nothing like this on Debian/Ubuntu. This is a security policy denial, not a bug. Check: ausearch -m AVC -ts recent — then run: sealert -a /var/log/audit/audit.log for a fix suggestion." + +[[patterns]] +id = "selinux-context-wrong" +sources = ["journald"] +match_text = "restorecon" +severity = "info" +title = "SELinux file context mismatch" +body = "A file has the wrong security label. Fix: sudo restorecon -Rv /path/to/file — common after copying files from Debian or moving data between filesystems." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "On Fedora: sudo dnf install linux-firmware — or for specific hardware check RPM Fusion's nonfree repo. Enable it: sudo dnf install https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm" + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Fedora enables zswap by default on modern releases, but adding a swapfile helps on low-RAM systems: sudo systemctl enable --now systemd-swap" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Storage error. Check SMART: sudo smartctl -a /dev/sdX — smartmontools is in the default Fedora repos." + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Fedora pioneered PipeWire adoption — it's the default. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — if hard-blocked, check BIOS or a physical switch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. Check: dnf list installed | grep -i nvidia (or mesa) — RPM Fusion is the right source for proprietary NVIDIA drivers on Fedora." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "Fedora ships GNOME on Wayland by default; XWayland handles X11 apps. Restart your session to recover X11 apps." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Fedora ships NetworkManager by default, same as Ubuntu. If a wifi adapter is missing, check: dmesg | grep firmware" + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. Steam on Fedora may also need: sudo dnf install steam (from RPM Fusion free repo)." diff --git a/src-tauri/patterns/debian-to-opensuse.toml b/src-tauri/patterns/debian-to-opensuse.toml new file mode 100644 index 0000000..c5b4d18 --- /dev/null +++ b/src-tauri/patterns/debian-to-opensuse.toml @@ -0,0 +1,120 @@ +[meta] +source_os = "linux" +target_distro_family = "opensuse" + +# Debian/Ubuntu/Mint user on their first openSUSE Tumbleweed or Leap install. +# Body text assumes apt/dpkg familiarity; explains zypper and YaST concepts. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── zypper / RPM ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "zypper-lock" +sources = ["journald"] +match_text = "System management is locked" +severity = "warn" +title = "zypper package manager is locked" +body = "Another zypper or PackageKit process is running — like apt being held by unattended-upgrades. Wait it out or check: sudo ps aux | grep zypper — the lock file is at /var/run/zypp.pid" + +[[patterns]] +id = "zypper-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "zypper presents conflict resolution choices interactively. If running non-interactively, read the error — usually one package needs to be removed or a different provider selected. zypper dup (distribution upgrade) resolves more aggressively than zypper up." + +[[patterns]] +id = "zypper-gpg-key" +sources = ["journald"] +match_text = "does not verify" +severity = "warn" +title = "Repository signature not trusted" +body = "A repo key isn't trusted. Accept it: sudo zypper --gpg-auto-import-keys ref — or import manually: sudo rpm --import /path/to/key.gpg" + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "openSUSE ships AppArmor (similar to Ubuntu, not Debian default). An app is blocked by its security profile. Check: sudo aa-status — then audit the profile with: sudo aa-logprof" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo zypper install kernel-firmware — openSUSE packages firmware separately like Debian but the package is called kernel-firmware, not firmware-linux." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. openSUSE sets up swap during install; if you skipped it, add a swapfile via YaST -> System -> Partitioner or manually with dd + mkswap." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Storage error. Check SMART: sudo smartctl -a /dev/sdX — install smartmontools first: sudo zypper install smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Tumbleweed ships PipeWire by default. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — if hard-blocked, check BIOS or a physical switch." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on openSUSE, use the official NVIDIA repo: https://www.nvidia.com/object/unix.html — or the community packages.opensuse.org repo." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — openSUSE uses NetworkManager by default. For wifi firmware issues: sudo zypper install kernel-firmware-iwlwifi (Intel) or kernel-firmware-realtek (Realtek)." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. Steam on openSUSE: sudo zypper install steam (from the games repo on OBS)." diff --git a/src-tauri/patterns/fedora-to-arch.toml b/src-tauri/patterns/fedora-to-arch.toml new file mode 100644 index 0000000..54468af --- /dev/null +++ b/src-tauri/patterns/fedora-to-arch.toml @@ -0,0 +1,172 @@ +[meta] +source_os = "linux" +target_distro_family = "arch" + +# Fedora/RHEL/CentOS user on their first Arch install. +# Body text assumes DNF, SELinux, and RPM Fusion familiarity. + +[log_paths] +pacman = "/var/log/pacman.log" +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" +lutris = "~/.cache/lutris/logs/lutris.log" + +# ── pacman / AUR ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "pacman-db-lock" +sources = ["journald", "applog:pacman"] +match_text = "could not lock database: File exists" +severity = "warn" +title = "pacman database locked" +body = "Lock file left from a crashed pacman run — like a dnf transaction that got killed. Remove if nothing is running: sudo rm /var/lib/pacman/db.lck — check first: fuser /var/lib/pacman/db.lck" + +[[patterns]] +id = "partial-upgrade-warning" +sources = ["applog:pacman"] +match_text = "warning: database file for" +severity = "info" +title = "Package database out of sync" +body = "Arch rule #1 coming from Fedora: never run pacman -Sy (sync only). On Fedora, dnf check-update is safe; on Arch, syncing the database without upgrading breaks the system. Always: pacman -Syu" + +[[patterns]] +id = "pacman-dep-conflict" +sources = ["journald", "applog:pacman"] +match_text = "conflicting dependencies" +severity = "warn" +title = "Dependency conflict" +body = "Unlike dnf which auto-resolves most conflicts, pacman puts the choice on you. Read the conflict — typically one package is being replaced (e.g. pipewire-pulse replaces pulseaudio). Remove the old one first." + +[[patterns]] +id = "aur-build-failure" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to build" +severity = "warn" +title = "AUR package build failed" +body = "The AUR is source-only — no binary RPM equivalent. makepkg compiles from a PKGBUILD. Read the build output; check the package's AUR comments page. paru/yay handle makedepends automatically." + +[[patterns]] +id = "aur-pgp-key" +sources = ["journald", "applog:pacman"] +match_text = "unknown public key" +severity = "warn" +title = "PGP key not in keyring" +body = "Import the key: gpg --recv-keys — different from RPM's --import; this is GnuPG's own keyring used by makepkg." + +[[patterns]] +id = "chaotic-aur-sig-fail" +sources = ["journald", "applog:pacman"] +match_text = "signature from" +severity = "warn" +title = "Package signature verification failed" +body = "Chaotic-AUR key not trusted. Import: sudo pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com && sudo pacman-key --lsign-key 3056513887B78AEB" + +# ── SELinux → no SELinux ────────────────────────────────────────────────────── + +[[patterns]] +id = "selinux-remnant" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "SELinux audit entry (ignored on Arch)" +body = "Arch doesn't ship SELinux by default — if you see this, you may have carried over a journal from a Fedora partition, or installed selinux-utils manually. No action needed unless you intentionally set up SELinux on Arch." + +# ── Kernel / DKMS ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "dkms-build-fail" +sources = ["journald"] +match_text = "Error! Build of" +severity = "warn" +title = "DKMS module failed to build" +body = "A kernel module didn't compile after a kernel update. More frequent on Arch's rolling kernel than on Fedora's slower cadence. Check: dkms status — reinstall the failing module package." + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo pacman -S linux-firmware — covers most hardware. Some chips need AUR packages (similar to RPM Fusion nonfree on Fedora)." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "locale-not-set" +sources = ["journald"] +match_text = "Cannot set LC_ALL to default locale" +severity = "info" +title = "Locale not generated" +body = "Unlike Fedora where locale is configured in the installer, Arch requires manual setup. Edit /etc/locale.gen, uncomment your locale, run: sudo locale-gen — then set LANG in /etc/locale.conf." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Fedora enables zswap by default; Arch doesn't. Add zram: sudo pacman -S zram-generator" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install smartmontools: sudo pacman -S smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Both Fedora and Arch ship PipeWire by default. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — check: rfkill list to distinguish hard vs soft block." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. On Arch, AMD uses mesa (pacman -S mesa); NVIDIA uses nvidia-dkms. Unlike Fedora where RPM Fusion handles NVIDIA, on Arch install from the official repos." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "X11 apps dead until session restart. Fedora GNOME also defaults to Wayland; the behavior is the same." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — same NetworkManager as Fedora. If a wifi adapter is missing, check dmesg for firmware errors." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." diff --git a/src-tauri/patterns/fedora-to-debian.toml b/src-tauri/patterns/fedora-to-debian.toml new file mode 100644 index 0000000..d4388d3 --- /dev/null +++ b/src-tauri/patterns/fedora-to-debian.toml @@ -0,0 +1,138 @@ +[meta] +source_os = "linux" +target_distro_family = "debian" + +# Fedora/RHEL user moving to Debian/Ubuntu/Mint. +# Body text assumes DNF, SELinux, and systemd familiarity. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── apt / dpkg ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apt-lock" +sources = ["journald"] +match_text = "Could not get lock /var/lib/dpkg/lock" +severity = "warn" +title = "Package manager is locked" +body = "Another apt process is running — often unattended-upgrades in the background. Wait a minute. If stuck: sudo rm /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock — then: sudo dpkg --configure -a" + +[[patterns]] +id = "dpkg-interrupted" +sources = ["journald"] +match_text = "dpkg was interrupted" +severity = "warn" +title = "Package install was interrupted" +body = "A previous install didn't finish cleanly. Fix: sudo dpkg --configure -a — like an interrupted dnf transaction, but requires manual recovery." + +[[patterns]] +id = "apt-unmet-dependency" +sources = ["journald"] +match_text = "Unmet dependencies" +severity = "warn" +title = "Package dependency conflict" +body = "apt can't resolve a dependency. Try: sudo apt --fix-broken install — this is more automatic than dnf's conflict resolution." + +# ── AppArmor (replaces SELinux) ─────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "Debian/Ubuntu ships AppArmor instead of SELinux. The concepts are similar but the tooling differs. Check: sudo aa-status — for audit logs: sudo aa-logprof — profiles are in /etc/apparmor.d/" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo apt install firmware-linux firmware-linux-nonfree — unlike Fedora where firmware comes via linux-firmware, Debian splits it into free/nonfree packages. Enable non-free in /etc/apt/sources.list first." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Ubuntu enables zswap by default; Debian doesn't always. Add a swapfile or enable zswap via /sys/module/zswap/parameters/enabled" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo apt install smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Ubuntu 22.04+ and Debian 12+ ship PipeWire. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "pulseaudio-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to pulseaudio" +severity = "warn" +title = "PulseAudio not responding" +body = "Older Debian/Ubuntu systems use PulseAudio instead of PipeWire. Restart: pulseaudio --kill && pulseaudio --start" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as Fedora." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on Debian/Ubuntu: sudo apt install nvidia-driver — or use ubuntu-drivers autoinstall on Ubuntu. Similar to Fedora's RPM Fusion approach." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Debian may use ifupdown instead of NetworkManager on minimal installs. Check: systemctl status NetworkManager" + +# ── Media ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "missing-codec" +sources = ["journald"] +match_text = "GStreamer: Failed to find plugin" +severity = "info" +title = "Missing media codec" +body = "On Ubuntu/Mint: sudo apt install ubuntu-restricted-extras — on Debian: enable non-free and install libavcodec-extra. Fedora's RPM Fusion serves the same purpose." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." diff --git a/src-tauri/patterns/fedora-to-opensuse.toml b/src-tauri/patterns/fedora-to-opensuse.toml new file mode 100644 index 0000000..0432aab --- /dev/null +++ b/src-tauri/patterns/fedora-to-opensuse.toml @@ -0,0 +1,130 @@ +[meta] +source_os = "linux" +target_distro_family = "opensuse" + +# Fedora/RHEL user moving to openSUSE Tumbleweed or Leap. +# Body text assumes DNF and RPM familiarity; both use RPM so tooling overlaps. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── zypper / RPM ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "zypper-lock" +sources = ["journald"] +match_text = "System management is locked" +severity = "warn" +title = "zypper package manager is locked" +body = "Another zypper or PackageKit process is running — same situation as dnf being held by dnf-automatic. Wait it out or check: sudo ps aux | grep zypper" + +[[patterns]] +id = "zypper-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "zypper presents conflicts interactively. Both Fedora's dnf and zypper use RPM, but zypper's solver can be more conservative. Try: sudo zypper dup (distribution upgrade) for more aggressive resolution." + +[[patterns]] +id = "zypper-gpg-key" +sources = ["journald"] +match_text = "does not verify" +severity = "warn" +title = "Repository signature not trusted" +body = "Auto-import: sudo zypper --gpg-auto-import-keys ref — similar to dnf's GPG key prompts but the accept syntax differs." + +# ── AppArmor (replaces SELinux) ─────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "openSUSE ships AppArmor, not SELinux like Fedora. Similar purpose but different tooling. Check: sudo aa-status — audit: sudo aa-logprof — you'll need to rebuild your mental model from SELinux policy types to AppArmor profiles." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo zypper install kernel-firmware — openSUSE uses a single kernel-firmware package similar to Fedora's linux-firmware." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. openSUSE prompts for swap setup during install. If skipped: sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 && sudo mkswap /swapfile && sudo swapon /swapfile" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo zypper install smartmontools" + +# ── YaST (openSUSE-specific) ────────────────────────────────────────────────── + +[[patterns]] +id = "yast-backend-fail" +sources = ["journald"] +match_text = "YaST got signal" +severity = "warn" +title = "YaST configuration tool crashed" +body = "YaST is openSUSE's graphical admin tool (no Fedora equivalent). If it crashed mid-operation, check what it was doing: sudo yast2 -- the text mode version often recovers where the GUI fails." + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Tumbleweed ships PipeWire like Fedora. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as Fedora." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on openSUSE: use the NVIDIA OBS repo or packages.opensuse.org — similar to RPM Fusion on Fedora." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — openSUSE uses NetworkManager or Wicked depending on the install profile. Check which is active: systemctl status NetworkManager wicked" + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. For Steam on openSUSE: sudo zypper install steam (from the games repo on OBS)." diff --git a/src-tauri/patterns/opensuse-to-arch.toml b/src-tauri/patterns/opensuse-to-arch.toml new file mode 100644 index 0000000..0c2d834 --- /dev/null +++ b/src-tauri/patterns/opensuse-to-arch.toml @@ -0,0 +1,172 @@ +[meta] +source_os = "linux" +target_distro_family = "arch" + +# openSUSE Tumbleweed/Leap user moving to Arch. +# Body text assumes zypper, YaST, and AppArmor familiarity. + +[log_paths] +pacman = "/var/log/pacman.log" +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" +lutris = "~/.cache/lutris/logs/lutris.log" + +# ── pacman / AUR ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "pacman-db-lock" +sources = ["journald", "applog:pacman"] +match_text = "could not lock database: File exists" +severity = "warn" +title = "pacman database locked" +body = "Lock file left from a crashed pacman run — like zypper's /var/run/zypp.pid getting orphaned. Remove if nothing is running: sudo rm /var/lib/pacman/db.lck" + +[[patterns]] +id = "partial-upgrade-warning" +sources = ["applog:pacman"] +match_text = "warning: database file for" +severity = "info" +title = "Package database out of sync" +body = "On Arch, syncing without upgrading is dangerous — unlike openSUSE where zypper ref is safe to run alone. Always: pacman -Syu — never pacman -Sy without the u." + +[[patterns]] +id = "pacman-dep-conflict" +sources = ["journald", "applog:pacman"] +match_text = "conflicting dependencies" +severity = "warn" +title = "Dependency conflict" +body = "Unlike zypper which resolves conflicts interactively, pacman puts the choice directly on you. Read the conflict — usually one package replaces another. Remove the conflicting package first." + +[[patterns]] +id = "aur-build-failure" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to build" +severity = "warn" +title = "AUR package build failed" +body = "The AUR has no binary packages — makepkg compiles from source every time. There's no OBS equivalent here. Check the build log and the AUR comments page for the package." + +[[patterns]] +id = "aur-pgp-key" +sources = ["journald", "applog:pacman"] +match_text = "unknown public key" +severity = "warn" +title = "PGP key not in keyring" +body = "Import the key: gpg --recv-keys — different from zypper's --gpg-auto-import-keys; this is GnuPG's personal keyring used by makepkg." + +[[patterns]] +id = "chaotic-aur-sig-fail" +sources = ["journald", "applog:pacman"] +match_text = "signature from" +severity = "warn" +title = "Package signature verification failed" +body = "Chaotic-AUR key not trusted. Import: sudo pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com && sudo pacman-key --lsign-key 3056513887B78AEB" + +# ── AppArmor → none ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-remnant" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor log entry (no AppArmor on Arch by default)" +body = "Arch doesn't ship AppArmor by default. If you see this, you may have installed apparmor from the AUR manually. Run: sudo systemctl disable apparmor if you don't need it." + +# ── Kernel / DKMS ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "dkms-build-fail" +sources = ["journald"] +match_text = "Error! Build of" +severity = "warn" +title = "DKMS module failed to build" +body = "A kernel module didn't compile. Arch's rolling kernel updates more frequently than Tumbleweed's. Check: dkms status — reinstall the failing dkms package." + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo pacman -S linux-firmware — equivalent to openSUSE's kernel-firmware." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "locale-not-set" +sources = ["journald"] +match_text = "Cannot set LC_ALL to default locale" +severity = "info" +title = "Locale not generated" +body = "Unlike YaST which configured this for you, Arch requires manual locale setup. Edit /etc/locale.gen, uncomment your locale, run: sudo locale-gen — set LANG in /etc/locale.conf." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "Arch doesn't set up swap by default like openSUSE's installer does. Add zram: sudo pacman -S zram-generator — or add a swapfile." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo pacman -S smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Both openSUSE and Arch ship PipeWire. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth" + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. On Arch: AMD uses mesa (official repos), NVIDIA uses nvidia or nvidia-dkms. No OBS equivalent — everything is in pacman or the AUR." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "X11 apps dead until session restart. Same behavior as on openSUSE Wayland sessions." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Arch uses NetworkManager (not Wicked). If a wifi adapter is missing, check dmesg for firmware errors." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." diff --git a/src-tauri/patterns/opensuse-to-debian.toml b/src-tauri/patterns/opensuse-to-debian.toml new file mode 100644 index 0000000..2061cd4 --- /dev/null +++ b/src-tauri/patterns/opensuse-to-debian.toml @@ -0,0 +1,138 @@ +[meta] +source_os = "linux" +target_distro_family = "debian" + +# openSUSE Tumbleweed/Leap user moving to Debian/Ubuntu/Mint. +# Body text assumes zypper, YaST, and AppArmor familiarity. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── apt / dpkg ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apt-lock" +sources = ["journald"] +match_text = "Could not get lock /var/lib/dpkg/lock" +severity = "warn" +title = "Package manager is locked" +body = "Another apt process is running — often unattended-upgrades (no zypper equivalent, but similar to PackageKit background updates). Wait a minute. If stuck: sudo rm /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock && sudo dpkg --configure -a" + +[[patterns]] +id = "dpkg-interrupted" +sources = ["journald"] +match_text = "dpkg was interrupted" +severity = "warn" +title = "Package install was interrupted" +body = "Like a zypper transaction that got killed, but dpkg needs manual recovery. Fix: sudo dpkg --configure -a" + +[[patterns]] +id = "apt-unmet-dependency" +sources = ["journald"] +match_text = "Unmet dependencies" +severity = "warn" +title = "Package dependency conflict" +body = "apt auto-resolves most conflicts — less interactive than zypper's conflict wizard. Let it try: sudo apt --fix-broken install" + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "AppArmor access denied" +body = "Both openSUSE and Debian/Ubuntu use AppArmor — the tooling is the same. Check: sudo aa-status — audit: sudo aa-logprof — profiles: /etc/apparmor.d/" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "On Debian: sudo apt install firmware-linux firmware-linux-nonfree (enable non-free sources first). On Ubuntu: sudo apt install linux-firmware. Debian splits firmware by license unlike openSUSE's single kernel-firmware package." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. openSUSE's installer sets up swap; Debian minimal may not. Add a swapfile: sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo apt install smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Both Tumbleweed and Ubuntu 22.04+/Debian 12+ ship PipeWire. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "pulseaudio-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to pulseaudio" +severity = "warn" +title = "PulseAudio not responding" +body = "Older Debian systems still use PulseAudio. Restart: pulseaudio --kill && pulseaudio --start" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as openSUSE." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on Ubuntu: ubuntu-drivers autoinstall — on Debian: apt install nvidia-driver (requires non-free). Unlike openSUSE's OBS NVIDIA repo, Ubuntu keeps drivers in the main archive." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Debian minimal may use ifupdown instead of NetworkManager. Install if missing: sudo apt install network-manager" + +# ── Printing ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "cups-server-error" +sources = ["journald"] +match_text = "Unable to connect to CUPS server" +severity = "info" +title = "Printer service not running" +body = "sudo systemctl start cups && sudo systemctl enable cups — YaST auto-configured printing on openSUSE; Debian leaves CUPS disabled until you enable it." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity." diff --git a/src-tauri/patterns/opensuse-to-fedora.toml b/src-tauri/patterns/opensuse-to-fedora.toml new file mode 100644 index 0000000..bc1520d --- /dev/null +++ b/src-tauri/patterns/opensuse-to-fedora.toml @@ -0,0 +1,146 @@ +[meta] +source_os = "linux" +target_distro_family = "fedora" + +# openSUSE Tumbleweed/Leap user moving to Fedora. +# Body text assumes zypper, YaST, and AppArmor familiarity; both use RPM. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── DNF / RPM ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "dnf-lock" +sources = ["journald"] +match_text = "Another app is currently holding the dnf lock" +severity = "warn" +title = "DNF package manager is locked" +body = "dnf-automatic (Fedora's background updater) is probably running — similar to PackageKit on openSUSE. Wait it out or check: sudo ps aux | grep dnf" + +[[patterns]] +id = "dnf-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Package dependency conflict" +body = "Both use RPM but their solvers differ. DNF auto-resolves more aggressively than zypper. If dnf can't fix it: sudo dnf distro-sync — the equivalent of zypper dup." + +[[patterns]] +id = "dnf-gpg-key" +sources = ["journald"] +match_text = "GPG key retrieval failed" +severity = "warn" +title = "Repository GPG key missing" +body = "Import: sudo rpm --import /path/to/key.gpg — same rpm command as openSUSE. RPM Fusion keys are imported automatically when you enable the repo." + +# ── SELinux (replaces AppArmor) ─────────────────────────────────────────────── + +[[patterns]] +id = "selinux-denial" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "SELinux access denied" +body = "Fedora uses SELinux instead of openSUSE's AppArmor. Both are MAC systems but with different models — SELinux uses type enforcement, AppArmor uses path-based profiles. Check: ausearch -m AVC -ts recent — get a fix: sealert -a /var/log/audit/audit.log" + +[[patterns]] +id = "selinux-context-wrong" +sources = ["journald"] +match_text = "restorecon" +severity = "info" +title = "SELinux file context mismatch" +body = "Files copied from openSUSE or an external drive may have wrong SELinux labels. Fix: sudo restorecon -Rv /path/to/file — equivalent to aa-relabel in AppArmor terms." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Firmware file missing" +body = "sudo dnf install linux-firmware — same scope as openSUSE's kernel-firmware. Some chips need RPM Fusion nonfree: sudo dnf install rpmfusion-nonfree-release-$(rpm -E %fedora)" + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "OOM killer fired" +body = "A process was killed for RAM. Fedora enables zswap by default on modern releases. For zram: sudo dnf install zram-generator — similar setup to openSUSE." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Disk I/O error" +body = "Check SMART: sudo smartctl -a /dev/sdX — install: sudo dnf install smartmontools" + +# ── YaST → no YaST ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "yast-not-found" +sources = ["journald"] +match_text = "yast: command not found" +severity = "info" +title = "YaST not available on Fedora" +body = "Fedora has no YaST equivalent — use GNOME Settings for display/network/user config, and dnf/rpm for package management. Most things YaST handled are done via systemctl, nmcli, or the GNOME control center." + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "PipeWire not responding" +body = "Both Tumbleweed and Fedora ship PipeWire. Restart: systemctl --user restart pipewire pipewire-pulse wireplumber" + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth rfkill blocked" +body = "rfkill unblock bluetooth — same as openSUSE." + +# ── GPU / display ───────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "GPU hang" +body = "GPU stopped responding. For NVIDIA on Fedora: sudo dnf install akmod-nvidia (from RPM Fusion) — similar to openSUSE's NVIDIA OBS repo but uses akmods instead of DKMS." + +[[patterns]] +id = "xwayland-crash" +sources = ["journald"] +match_text = "XWayland server terminated unexpectedly" +severity = "warn" +title = "XWayland crashed" +body = "Fedora GNOME defaults to Wayland like openSUSE's GNOME spin. X11 apps dead until session restart." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "NetworkManager: connection failed" +body = "nmcli device status — Fedora uses NetworkManager, not Wicked. If you had Wicked-specific configs on openSUSE, recreate them in NetworkManager format." + +# ── Gaming ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "proton-runtime-missing" +sources = ["applog:proton"] +match_text = "wine: cannot find" +severity = "warn" +title = "Proton runtime issue" +body = "Right-click game in Steam -> Properties -> Local Files -> Verify integrity. Steam on Fedora: sudo dnf install steam (from RPM Fusion free)." diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index cf96e71..77f6db4 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,6 +1,6 @@ use crate::config::{MigrationConfig, NotificationLevel, RobinConfig, SourceOs}; use std::sync::Mutex; -use tauri::State; +use tauri::{Emitter, State}; pub struct AppState { pub config: Mutex, @@ -28,6 +28,7 @@ pub fn needs_onboarding(state: State<'_, AppState>) -> bool { pub fn complete_onboarding( source_os: String, distro: String, + source_distro: Option, state: State<'_, AppState>, ) -> Result<(), String> { let source = match source_os.to_lowercase().as_str() { @@ -43,10 +44,18 @@ pub fn complete_onboarding( distro }; + // For Linux-to-Linux migrations, derive source family from the reported source distro. + // If a source_distro was passed explicitly, use distro_family() on it; otherwise leave None. + let source_distro_family = source_distro.as_deref().map(|sd| { + let family = crate::distro::distro_family(sd); + if family == "unknown" { None } else { Some(family.to_string()) } + }).flatten(); + let mut config = state.config.lock().map_err(|e| e.to_string())?; config.migration = Some(MigrationConfig { source_os: source, distro: detected, + source_distro_family, fluency_level: 0, }); config.save().map_err(|e| e.to_string()) @@ -79,3 +88,48 @@ pub fn panel_opened() { pub fn panel_closed() { crate::notify::set_panel_open(false); } + +#[tauri::command] +pub async fn chat( + message: String, + state: State<'_, AppState>, + app_handle: tauri::AppHandle, +) -> Result<(), String> { + let (base_url, model, source_os, distro) = { + let cfg = state.config.lock().map_err(|e| e.to_string())?; + let (source_os, distro) = if let Some(ref m) = cfg.migration { + let os = match m.source_os { + crate::config::SourceOs::Macos => "macOS", + crate::config::SourceOs::Windows => "Windows", + crate::config::SourceOs::Linux => "Linux", + crate::config::SourceOs::Unknown => "Unknown", + }; + (os.to_string(), m.distro.clone()) + } else { + ("Unknown".to_string(), "unknown".to_string()) + }; + ( + cfg.ollama.base_url.clone(), + cfg.ollama.model.clone(), + source_os, + distro, + ) + }; + + let system_prompt = crate::llm::build_system_prompt(&source_os, &distro); + let messages = vec![ + crate::llm::ChatMessage { role: "system".into(), content: system_prompt }, + crate::llm::ChatMessage { role: "user".into(), content: message }, + ]; + + tauri::async_runtime::spawn(async move { + if let Err(e) = crate::llm::chat_stream(&base_url, &model, messages, &app_handle).await { + log::error!("chat stream error: {e}"); + if let Err(emit_err) = app_handle.emit("robin:chat-error", e.to_string()) { + log::warn!("failed to emit robin:chat-error: {emit_err}"); + } + } + }); + + Ok(()) +} diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 0197af2..68ed20f 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -34,6 +34,10 @@ pub struct MigrationConfig { pub source_os: SourceOs, /// Detected distro string, e.g. "cachyos", "linuxmint" pub distro: String, + /// Source distro family for Linux-to-Linux migrations, e.g. "debian", "fedora", "arch", "opensuse" + /// Used to load a more specific pattern file (e.g. debian-to-arch) before the generic linux-to-arch fallback. + #[serde(default)] + pub source_distro_family: Option, /// 0–5: grows as user dismisses suggestions they already know pub fluency_level: u8, } @@ -138,6 +142,7 @@ mod tests { config.migration = Some(MigrationConfig { source_os: SourceOs::Macos, distro: "cachyos".into(), + source_distro_family: None, fluency_level: 0, }); assert!(!config.needs_onboarding()); @@ -149,6 +154,7 @@ mod tests { migration: Some(MigrationConfig { source_os: SourceOs::Windows, distro: "linuxmint".into(), + source_distro_family: None, fluency_level: 2, }), ..Default::default() diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5da7576..3033771 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ mod commands; mod config; mod distro; +mod llm; mod notify; mod patterns; mod tray; @@ -41,7 +42,7 @@ pub fn run() { config::SourceOs::Linux => "linux", config::SourceOs::Unknown => "unknown", }; - patterns::load(source, family).ok() + patterns::load(source, migration.source_distro_family.as_deref(), family).ok() } else { None }; @@ -76,6 +77,7 @@ pub fn run() { commands::get_pending_events, commands::panel_opened, commands::panel_closed, + commands::chat, ]) .run(tauri::generate_context!()) .expect("error while running Robin"); diff --git a/src-tauri/src/llm.rs b/src-tauri/src/llm.rs new file mode 100644 index 0000000..a4b3eed --- /dev/null +++ b/src-tauri/src/llm.rs @@ -0,0 +1,157 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use tauri::Emitter; + +#[derive(Debug, Clone, Serialize)] +pub struct ChatMessage { + pub role: String, + pub content: String, +} + +#[derive(Debug, Deserialize)] +struct OllamaChunk { + message: Option, + #[serde(default)] + done: bool, +} + +#[derive(Debug, Deserialize)] +struct OllamaChunkMessage { + content: String, +} + +/// Build a migration-aware system prompt from the user's source OS and current distro. +pub fn build_system_prompt(source_os: &str, distro: &str) -> String { + format!( + "You are Robin, a friendly Linux migration assistant running locally on the user's machine. \ + The user is migrating from {source_os} to {distro}. \ + Help them with Linux questions, explain differences from their previous OS, \ + and provide practical command-line solutions. \ + Be concise, accurate, and use examples when helpful. \ + Never suggest Windows or macOS solutions — focus on Linux." + ) +} + +/// Stream a chat response from Ollama, emitting tokens as Tauri events. +/// +/// Emits: `robin:chat-token` (String), `robin:chat-done` (unit), `robin:chat-error` (String). +pub async fn chat_stream( + base_url: &str, + model: &str, + messages: Vec, + app: &tauri::AppHandle, +) -> Result<()> { + let client = reqwest::Client::new(); + let url = format!("{base_url}/api/chat"); + + let body = serde_json::json!({ + "model": model, + "messages": messages, + "stream": true, + }); + + let response = client + .post(&url) + .json(&body) + .send() + .await + .with_context(|| format!("failed to connect to Ollama at {url}"))?; + + if !response.status().is_success() { + let status = response.status(); + let text = response.text().await.unwrap_or_default(); + anyhow::bail!("Ollama returned {status}: {text}"); + } + + // Buffer bytes across chunks to handle UTF-8 sequences split at chunk boundaries. + let mut buf: Vec = Vec::new(); + let mut response = response; + + while let Some(chunk) = response.chunk().await.context("stream read error")? { + buf.extend_from_slice(&chunk); + + // Extract complete newline-delimited JSON lines from the buffer. + while let Some(pos) = buf.iter().position(|&b| b == b'\n') { + let line_bytes: Vec = buf.drain(..=pos).collect(); + let line = String::from_utf8_lossy(&line_bytes); + let line = line.trim(); + if line.is_empty() { + continue; + } + match serde_json::from_str::(line) { + Ok(chunk) => { + if let Some(msg) = chunk.message { + if !msg.content.is_empty() { + if let Err(e) = app.emit("robin:chat-token", msg.content) { + log::warn!("failed to emit robin:chat-token: {e}"); + } + } + } + if chunk.done { + if let Err(e) = app.emit("robin:chat-done", ()) { + log::warn!("failed to emit robin:chat-done: {e}"); + } + return Ok(()); + } + } + Err(e) => { + log::warn!("failed to parse Ollama chunk '{}': {e}", line); + } + } + } + } + + // Stream ended without a done=true chunk — emit done anyway so frontend unblocks. + if let Err(e) = app.emit("robin:chat-done", ()) { + log::warn!("failed to emit robin:chat-done at stream end: {e}"); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn system_prompt_contains_source_and_distro() { + let prompt = build_system_prompt("Windows", "cachyos"); + assert!(prompt.contains("Windows")); + assert!(prompt.contains("cachyos")); + } + + #[test] + fn system_prompt_contains_robin() { + let prompt = build_system_prompt("macOS", "arch"); + assert!(prompt.to_lowercase().contains("robin")); + } + + #[test] + fn ollama_chunk_parses_token() { + let json = r#"{"model":"llama3.2","message":{"role":"assistant","content":"Hello"},"done":false}"#; + let chunk: OllamaChunk = serde_json::from_str(json).unwrap(); + assert_eq!(chunk.message.unwrap().content, "Hello"); + assert!(!chunk.done); + } + + #[test] + fn ollama_chunk_parses_done() { + let json = r#"{"model":"llama3.2","message":{"role":"assistant","content":""},"done":true}"#; + let chunk: OllamaChunk = serde_json::from_str(json).unwrap(); + assert!(chunk.done); + } + + #[test] + fn ollama_chunk_parses_no_message() { + let json = r#"{"model":"llama3.2","done":true}"#; + let chunk: OllamaChunk = serde_json::from_str(json).unwrap(); + assert!(chunk.message.is_none()); + assert!(chunk.done); + } + + #[test] + fn malformed_json_fails_to_parse() { + let json = r#"{"not_valid": }"#; + let result = serde_json::from_str::(json); + assert!(result.is_err()); + } +} diff --git a/src-tauri/src/patterns.rs b/src-tauri/src/patterns.rs index 4821264..61fd741 100644 --- a/src-tauri/src/patterns.rs +++ b/src-tauri/src/patterns.rs @@ -39,16 +39,25 @@ pub struct MatchedEvent { /// Load the pattern file for a source OS and distro family. /// -/// Tries candidates in order: dev-relative path, src-tauri-relative path, -/// system path. The Tauri resource directory is the authoritative location -/// at runtime; passing a base path is handled in Task 10's caller via candidate list. -pub fn load(source_os: &str, distro_family: &str) -> Result { - let filename = format!("{source_os}-to-{distro_family}.toml"); - let candidates = [ - format!("patterns/{filename}"), - format!("src-tauri/patterns/{filename}"), - format!("/usr/share/robin/patterns/{filename}"), - ]; +/// For Linux-to-Linux migrations, `source_distro_family` (e.g. "debian", "fedora") is +/// tried first: `debian-to-arch.toml` before the generic `linux-to-arch.toml` fallback. +/// Tries each candidate at three path depths: dev-relative, src-tauri-relative, system. +pub fn load( + source_os: &str, + source_distro_family: Option<&str>, + distro_family: &str, +) -> Result { + let mut candidates: Vec = Vec::new(); + if let Some(src_distro) = source_distro_family { + let specific = format!("{src_distro}-to-{distro_family}.toml"); + candidates.push(format!("patterns/{specific}")); + candidates.push(format!("src-tauri/patterns/{specific}")); + candidates.push(format!("/usr/share/robin/patterns/{specific}")); + } + let generic = format!("{source_os}-to-{distro_family}.toml"); + candidates.push(format!("patterns/{generic}")); + candidates.push(format!("src-tauri/patterns/{generic}")); + candidates.push(format!("/usr/share/robin/patterns/{generic}")); for path in &candidates { let content = match std::fs::read_to_string(path) { Ok(c) => c, @@ -75,7 +84,7 @@ pub fn load(source_os: &str, distro_family: &str) -> Result { } return Ok(pf); } - anyhow::bail!("pattern file not found: {filename}") + anyhow::bail!("pattern file not found for {source_os}-to-{distro_family}.toml") } #[must_use]