Share your Vibe coding tips

I know vibe coding is a somewhat sensitive topic, but for small home stuff it is just so much fun! Just from promts, and passing it back and forth between ChatGpt and Gemini,I got a good looking script (the colors are made for Kitty) that checks updates from the official repo, the aur, and flatpaks. It checks if you have yay or paru installed and tells if missing, and displays any unread Arch news in the list of 3 latest in pink. No matter any concerns, for small stuff home use I just love vibe coding. Took me about an hour!

The Bash sript:

Summary
#!/usr/bin/env bash

checkall - Check Arch Linux, AUR, Flatpak updates and latest news

Colors (RGB truecolor for Kitty)

GREEN=$'\033[38;2;120;210;140m'
YELLOW=$'\033[38;2;255;220;0m'
BLUE=$'\033[38;2;80;160;255m'
RED=$'\033[38;2;255;100;100m'
CYAN=$'\033[38;2;0;220;220m'
PINK=$'\033[38;2;255;105;180m'
RESET=$'\033[0m'

Arch news read-state cache

NEWS_CACHE="$HOME/.cache/arch-news-read"

Track check results and failure reasons

updates_ok=false
aur_ok=skip
flatpak_ok=skip
news_ok=false

updates_reason=""
aur_reason=""
flatpak_reason=""
news_reason=""

Print a failure and its details consistently

print_failure() {
local label=$1
local reason=$2
local line

printf '%s[FAIL]%s %s\n' "$RED" "$RESET" "$label"

if [[ -n "$reason" ]]; then
    while IFS= read -r line; do
        printf '%s       %s%s\n' "$YELLOW" "$line" "$RESET"
    done <<< "$reason"
fi

}

Print non-empty output lines in blue

print_updates() {
local output=$1
local line

while IFS= read -r line; do
    [[ -n "$line" ]] &&
        printf '%s%s%s\n' "$BLUE" "$line" "$RESET"
done <<< "$output"

}

Header

printf '%s========================================\n' "$GREEN"
printf '         ARCH LINUX UPDATE CHECK\n'
printf '========================================%s\n' "$RESET"

--------------------------------------------------

Official Arch repository updates

--------------------------------------------------

printf '\n%s== System Updates ==%s\n\n' "$YELLOW" "$RESET"

if ! command -v checkupdates >/dev/null 2>&1; then
updates_reason="checkupdates is not installed.
Install with: sudo pacman -S pacman-contrib"

printf '%scheckupdates is not installed.%s\n' "$RED" "$RESET"
printf '%sInstall with: sudo pacman -S pacman-contrib%s\n' \
    "$CYAN" "$RESET"

else
updates=$(checkupdates 2>&1)
status=$?

case "$status" in
    0)
        updates_ok=true

        if [[ -n "$updates" ]]; then
            print_updates "$updates"
        else
            printf '%sNo updates available.%s\n' "$GREEN" "$RESET"
        fi
        ;;
    2)
        updates_ok=true
        printf '%sNo updates available.%s\n' "$GREEN" "$RESET"
        ;;
    *)
        updates_reason="checkupdates exited with code $status."

        if [[ -n "$updates" ]]; then
            updates_reason+=$'\n'"$updates"
        fi

        printf '%sUpdate check failed (exit code %s).%s\n' \
            "$RED" "$status" "$RESET"

        [[ -n "$updates" ]] && printf '%s\n' "$updates"
        ;;
esac

fi

--------------------------------------------------

AUR updates: prefer Yay, otherwise use Paru

--------------------------------------------------

printf '\n%s== AUR Updates ==%s\n\n' "$YELLOW" "$RESET"

aur_helper=""
aur_display=""

if command -v yay >/dev/null 2>&1; then
aur_helper="yay"
aur_display="Yay"
elif command -v paru >/dev/null 2>&1; then
aur_helper="paru"
aur_display="Paru"
fi

if [[ -z "$aur_helper" ]]; then
aur_ok=skip

printf '%sAUR updates not checked: neither Yay nor Paru is installed.%s\n' \
    "$CYAN" "$RESET"
printf '%sInstall Yay or Paru to enable AUR update checks.%s\n' \
    "$CYAN" "$RESET"

else
# Check AUR updates without displaying the helper name.
aur_updates=$("$aur_helper" -Qua 2>&1)
status=$?

if [[ $status -eq 0 ]]; then
    aur_ok=true

    if [[ -n "$aur_updates" ]]; then
        print_updates "$aur_updates"
    else
        printf '%sNo AUR updates available.%s\n' "$GREEN" "$RESET"
    fi
elif [[ $status -eq 1 ]] && [[ -z "$aur_updates" ]]; then
    # Empty output with exit code 1.
    aur_ok=true
    printf '%sNo AUR updates available.%s\n' "$GREEN" "$RESET"
else
    aur_ok=false
    aur_reason="$aur_display exited with code $status."

    if [[ -n "$aur_updates" ]]; then
        aur_reason+=$'\n'"$aur_updates"
    fi

    printf '%sAUR check failed (exit code %s).%s\n' \
        "$RED" "$status" "$RESET"

    [[ -n "$aur_updates" ]] && printf '%s\n' "$aur_updates"
fi

fi

--------------------------------------------------

Flatpak updates

--------------------------------------------------

printf '\n%s== Flatpak Updates ==%s\n\n' "$YELLOW" "$RESET"

if ! command -v flatpak >/dev/null 2>&1; then
flatpak_ok=skip

printf '%sFlatpak is not installed; update check unavailable.%s\n' \
    "$CYAN" "$RESET"
printf '%sInstall with: sudo pacman -S flatpak%s\n' \
    "$CYAN" "$RESET"

else
# Check for updates without installing them.
# Automatically decline the transaction.
flatpak_updates=$(printf 'n\n' | flatpak update 2>&1)
status=$?

if grep -Eqi 'Nothing to do|No updates' <<< "$flatpak_updates"; then
    flatpak_ok=true
    printf '%sNo Flatpak updates available.%s\n' "$GREEN" "$RESET"

elif grep -Eq '^[[:space:]]*[0-9]+\.[[:space:]]' \
    <<< "$flatpak_updates" &&
     grep -Fq 'Proceed with these changes' <<< "$flatpak_updates"; then

    # Updates were found and the transaction was declined.
    flatpak_ok=true

    # Hide progress and confirmation messages.
    # Display only the update entries in blue.
    flatpak_display=$(sed \
        -e '/Looking for updates/d' \
        -e '/Proceed with these changes/d' \
        <<< "$flatpak_updates")

    print_updates "$flatpak_display"

elif [[ $status -eq 0 ]]; then
    flatpak_ok=true

    # Suppress progress and confirmation messages in fallback output.
    flatpak_display=$(sed \
        -e '/Looking for updates/d' \
        -e '/Proceed with these changes/d' \
        <<< "$flatpak_updates")

    if [[ -n "${flatpak_display//[[:space:]]/}" ]]; then
        print_updates "$flatpak_display"
    else
        printf '%sNo Flatpak updates available.%s\n' "$GREEN" "$RESET"
    fi

else
    flatpak_ok=false
    flatpak_reason="flatpak update exited with code $status."

    if [[ -n "$flatpak_updates" ]]; then
        flatpak_reason+=$'\n'"$flatpak_updates"
    fi

    printf '%sFlatpak check failed (exit code %s).%s\n' \
        "$RED" "$status" "$RESET"

    [[ -n "$flatpak_updates" ]] && printf '%s\n' "$flatpak_updates"
fi

fi

--------------------------------------------------

Latest Arch Linux news

--------------------------------------------------

printf '\n%s== Latest Arch Linux News ==%s\n\n' "$YELLOW" "$RESET"

if ! command -v curl >/dev/null 2>&1; then
news_reason="curl is not installed.
Install with: sudo pacman -S curl"

printf '%scurl is not installed.%s\n' "$RED" "$RESET"
printf '%sInstall with: sudo pacman -S curl%s\n' \
    "$CYAN" "$RESET"

elif ! command -v python3 >/dev/null 2>&1; then
news_reason="Python 3 is not installed.
Install with: sudo pacman -S python"

printf '%sPython 3 is not installed.%s\n' "$RED" "$RESET"
printf '%sInstall with: sudo pacman -S python%s\n' \
    "$CYAN" "$RESET"

else
news=$(curl -fsSL 
--connect-timeout 5 
--max-time 15 
https://archlinux.org/feeds/news/ 2>&1)
status=$?

if [[ $status -ne 0 ]]; then
    news_reason="Failed to download Arch Linux news (curl exit code $status)."

    if [[ -n "$news" ]]; then
        news_reason+=$'\n'"Details:"
        news_reason+=$'\n'"$news"
    fi

    printf '%sCould not download Arch Linux news.%s\n' \
        "$RED" "$RESET"

    [[ -n "$news" ]] && printf '%s\n' "$news"

elif ! mkdir -p "$HOME/.cache" ||
     ! touch "$NEWS_CACHE"; then

    news_reason="Could not create or access news cache: $NEWS_CACHE"

    printf '%sCould not access the Arch news cache.%s\n' \
        "$RED" "$RESET"

else
    # Pass RSS through an environment variable so the Python
    # heredoc remains available for the Python source code.
    parsed_news=$(NEWS_XML="$news" python3 - "$NEWS_CACHE" <<'PY'

import os
import re
import sys
import tempfile
import xml.etree.ElementTree as ET
from urllib.parse import urlparse

cache_path = sys.argv[1]

GREEN = "\033[38;2;120;210;140m"
BLUE = "\033[38;2;80;160;255m"
PINK = "\033[38;2;255;105;180m"
RESET = "\033[0m"

def clean_text(value):
return re.sub(r"[\x00-\x1f\x7f-\x9f]", "", value or "").strip()

def hyperlink(url, text):
return "\033]8;;" + url + "\033\" + text + "\033]8;;\033\"

def trusted_url(url):
try:
parsed = urlparse(url)
return (
parsed.scheme == "https"
and parsed.hostname == "archlinux.org"
and parsed.username is None
and parsed.password is None
and parsed.port in (None, 443)
)
except ValueError:
return False

try:
# Read previously displayed article URLs.
with open(cache_path, "r", encoding="utf-8") as f:
seen = {line.strip() for line in f if line.strip()}

# Parse the downloaded RSS XML.
root = ET.fromstring(os.environ["NEWS_XML"])
items = root.findall("./channel/item")[:3]

if not items:
    print(GREEN + "No news items found." + RESET)
    sys.exit(0)

articles = []

for item in items:
    title = clean_text(item.findtext("title")) or "Untitled"
    date = clean_text(item.findtext("pubDate"))
    link = clean_text(item.findtext("link"))

    valid_link = link if trusted_url(link) else ""
    is_new = bool(valid_link) and valid_link not in seen

    articles.append((title, date, valid_link, is_new))

# Display the latest three articles.
for i, (title, date, link, is_new) in enumerate(articles, 1):
    color = PINK if is_new else BLUE
    label = " [UNREAD]" if is_new else ""

    print(color + "{}. {}{}".format(i, title, label) + RESET)

    if date:
        print("    Date: " + date)

    if link:
        print(GREEN + "    Link: " + hyperlink(link, link) + RESET)
    else:
        print("    Link omitted (URL is not a trusted Arch Linux HTTPS link).")

    print()

# Remember the displayed articles for the next run.
new_urls = {
    link for _, _, link, is_new in articles
    if link and is_new
}

if new_urls:
    updated = seen | new_urls
    cache_dir = os.path.dirname(os.path.abspath(cache_path))

    # Write to a temporary file before replacing the cache.
    fd, temp_path = tempfile.mkstemp(
        prefix=".arch-news-read.",
        dir=cache_dir,
        text=True
    )

    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            for url in sorted(updated):
                f.write(url + "\n")

        os.replace(temp_path, cache_path)

    except BaseException:
        try:
            os.unlink(temp_path)
        except OSError:
            pass
        raise

except Exception as error:
print("RSS parsing or cache error: " + str(error), file=sys.stderr)
sys.exit(1)
PY
)
status=$?

    if [[ $status -eq 0 ]]; then
        news_ok=true
        printf '%s\n' "$parsed_news"
    else
        news_reason="Failed to parse Arch Linux news or update its cache."

        if [[ -n "$parsed_news" ]]; then
            news_reason+=$'\n'"Details:"
            news_reason+=$'\n'"$parsed_news"
        fi

        printf '%sUnable to process Arch Linux news.%s\n' \
            "$RED" "$RESET"
        printf '%s\n' "$parsed_news"
    fi
fi

fi

--------------------------------------------------

Final summary

--------------------------------------------------

printf '\n%s========================================\n' "$GREEN"
printf '             CHECK SUMMARY\n'
printf '========================================%s\n' "$RESET"

failed_count=0
skipped_count=0

Official repository result

if [[ "$updates_ok" == true ]]; then
printf '%s[OK]%s System updates\n' "$GREEN" "$RESET"
else
print_failure "System updates" "$updates_reason"
((failed_count += 1))
fi

AUR result

if [[ "$aur_ok" == true ]]; then
printf '%s[OK]%s AUR updates (%s)\n' 
"$GREEN" "$RESET" "$aur_display"
elif [[ "$aur_ok" == skip ]]; then
printf '%s[SKIP]%s AUR updates (Yay or Paru unavailable).\n' 
"$CYAN" "$RESET"
((skipped_count += 1))
else
print_failure "AUR updates" "$aur_reason"
((failed_count += 1))
fi

Flatpak result

if [[ "$flatpak_ok" == true ]]; then
printf '%s[OK]%s Flatpak updates\n' "$GREEN" "$RESET"
elif [[ "$flatpak_ok" == skip ]]; then
printf '%s[SKIP]%s Flatpak updates (Flatpak unavailable).\n' 
"$CYAN" "$RESET"
((skipped_count += 1))
else
print_failure "Flatpak updates" "$flatpak_reason"
((failed_count += 1))
fi

News result

if [[ "$news_ok" == true ]]; then
printf '%s[OK]%s Arch Linux news\n' "$GREEN" "$RESET"
else
print_failure "Arch Linux news" "$news_reason"
((failed_count += 1))
fi

Overall result and exit code

printf '\n'

if (( failed_count > 0 && skipped_count > 0 )); then
printf '%s%d checks failed; %d checks skipped.%s\n' 
"$RED" "$failed_count" "$skipped_count" "$RESET"
exit 2
elif (( failed_count > 0 )); then
if (( failed_count == 1 )); then
printf '%s1 check failed.%s\n' "$RED" "$RESET"
else
printf '%s%d checks failed.%s\n' "$RED" "$failed_count" "$RESET"
fi
exit 1
elif (( skipped_count > 0 )); then
printf '%sChecks completed with %d skipped; no failures detected.%s\n' 
"$YELLOW" "$skipped_count" "$RESET"
exit 0
else
printf '%sAll checks completed successfully.%s\n' "$GREEN" "$RESET"
exit 0
fi

Say what you want about vibe coding, but if it helps you build useful tools, improve your workflow, and learn something along the way, where’s the downside?

I’ve improved my workflow and learned a lot by vibe coding tools for my own system.

The tools below were built entirely through vibe coding, and I’ve also improved several of my old Bash scripts for backups and other automation tasks.

  • MP3 Splitter: A desktop app that splits long MP3s into individual tracks using timestamps, adds track tags, and packages them into a ZIP—without re-encoding.
  • Media Downloader: A desktop app for downloading videos at maximum quality or up to 1080p, plus extracting MP3 audio, with progress tracking and cancellation.
  • Arch Maintenance: An interactive script for system updates, cache cleanup, orphaned-package removal, and system checks, featuring a cyberpunk-style terminal dashboard.
  • Timeshift Restore: A recovery helper tailored to my EndeavourOS setup that restores snapshots and repairs boot/kernel files, with preflight checks and live-USB support.
  • Obsidian Quick Note: A keyboard-triggered popup for capturing timestamped notes directly into my vault without opening Obsidian, with folder selection and automatic index linking.

I don’t open-source these tools because they’re tailored to my specific needs. Besides, I’m not a programmer, and I don’t have the time to deal with bug reports, feature requests, or the occasional vibe coding critic.

At the end of the day, they work for me, I’ve learned a lot, and that’s what matters.

Thanks, that’s some great inspiration!

We can think what we want about vibe coding, but it is here to stay and people will take advantage. For small scale home stuff it seems harmless to me.

As someone who was a dev for a long time and know a few programming languages, my tip would be to have fun with vibecode if it’s for yourself like Biotico said. Don’t go hey, I did that tool anywhere unless you have the upper hand and understand the language you are doing.

Disclose that you are doing AI Assisted coding or vibecoding. Don’t try to hide it if you go public because you will get called for it. AI is useful to learn coding and can get you some small tools up and running fast. That’s the important point here. Learn the language you are using ! I recently started to dabble with Rust. I use my HyperQwen on my old 3090 to understand the language and help me startup reading my power supply informations (some MSI psu have a USB connection to get the differents values of the power supply Voltage, Wattage rails etc. I want to be able to add that to CoolerControl. That said I have a strong understanding of C# and long time ago C, C++ so the notions in rust are not that complicated for me.

AI is a tool and will do what you ask. It will not imply or guess what’s in your brain. Be clear and concise and don’t be scared of stating some obvious stuff you want or don’t want.

Extra tips for vibecoding for linux :

The Linux community does not seems to like vibecoded application at all. Most of them are greeted with resistance at minimum. Don’t go trying to develop gui stuff for linux because you saw something was missing because most of the time, linux users don’t care about gui and prefer console.

I agree with you on almost everything, except this part:

That may be true for many Arch or NixOS users, but Linux users aren’t all terminal enthusiasts. Plenty of newcomers, including those using Debian- or Fedora-based distros, prefer GUI tools.

Sometimes people just want to click a button without first becoming a part-time system administrator. :joy:

Yeah it’s really a hit and miss in my opinion. I see a lot of hate coming from reddit people but that’s reddit community lolll :rofl: Personally I won’t comment on something I don’t care about but some people have fun bashing or bursting the bubble of some people. Anyway I really tend to go back to console when something is off (as bugging) most of the time. Yeah some people want gui that’s for sure.

Great tips!

As I read them I realize I just took the small scale home user context for granted when I made the thread, as in showing off small DIY stuff for others to get inspiration.

A rather bold, blanket statement. :thinking:

Yeah maybe i’m seeing to much of that on reddit… and my view is biased

In my experience, plenty of Linux users on Reddit see themselves as 1337 hackers. In reality, they’re average users with a few commands memorised and an impressive confidence-to-skills ratio. :joy:

@LamnaNasus

My project for today was to simplify something I often have to do because my written English is far from perfect. To help with this, I often go to ChatGPT (I have a subscription paid for by my boss) and ask it to improve my text without losing my style or making it too colloquial.

So I created a simple popup like the Obsidian Quick Note that takes my text and sends it to ChatGPT using my credentials (not an API key), with a choice of predefined styles. I can also write in my native language, and it will translate the text into English or another foreign language.

I’m still in the testing phase, but it’s looking good.



This is a simple project, but it would take me months, if not years, to build a tool like this on my own. With Opencode, it took me less than 2 hours.

AI might mean the end of the world, but it can also do some pretty cool things for those of us whose programming skills could use a little help :joy:

Edit: These tools are being built using Python because it’s a language I can more or less read and understand.

“Share your vibe coding tips”? The short answer is: “Don’t”

You need to understand that the definition of “vibe coding” is letting AI generate code that you have no idea how to validate it yourself (i.e. zero knowledge/skills/abilities). This is a very dangerous position to put yourself in because you have no idea what it’s doing.

Now, I agree that LLM’s have a place in coding as a tool, like any other code validation/preflight tool, penetration testing, or pre-compiling tests. But to let AI slap something together and just let it run with no “sanity-checks”…is just insanity.

In a previous life, I used to generate code by hand for a project paid for by a California company. I wrote a tool for them which modified windows based ISOs for bare-metal deployments (offshoot of the DriverPacks project I admin). I took one of my project files (maybe 100 lines of code) and let Gemini AI scan it for any improvements in the code and refactor for efficiency. To be fair, it DID find a misplaced semi-colon (which did not affect code operation)…but it added a few hundred lines of code to “enhance and boost operation efficiency” (this ended up being pure slop). Because I intimately knew what my code did and how it did it, I was able to go through the AI-generated code and determine that while it did do what my original project did, it did it by ADDING a 200+% volume of worthless code and reduced efficiency at the same time.

Bottom line: Be very careful of any code generated by AI and validate everything.

Well, we can say the risk is the same when running stuff from some user’s GitHub. If we don’t understand the source code or the programming language a project is built in, I’d say there’s a pretty good chance that sooner or later there will be trouble.
But yes, I personally will not share the code I get from these sessions for the reasons I mentioned earlier.

Just wanted to mention that scripting within userspace isn’t the same than vibe coding executable binaries.

Therefore I only partly agree with @MrSmartepants, a simple helper script as used by OP could be functionally realized as a concatenated sequence of commands, that could be invoked by the use of an alias. Without the strict requirement of a custom dialogue or (functional) script at all. You’ll need that script mostly only due to the output formatting.

In short, such a helper script in userspace isn’t that problematic in my eyes.

In that regard I only can admit that I tinkered around with claude code quite a bit. And just let me state that it is quite involved to even maintain and setup a customized configuration for claude code itself. Out of the box, it’s quite chatty - which could be addressed by the use of certain skills (e.g. caveman, simple technical english), hooks (rtk) and the actual MCPs that are being connected.

Expect that setup to break sooner rather than later, as claude code is by far the most updated package installed on my system (and they actually even superseeded git with their own version control system scion, as git is simply not up to the task to manage 30000 merge requests a day).

I will say that using AI to generate code DOES have benefits because it can be very beneficial in a learning environment where you need working code examples. AI does generate code very very quickly. It’s not great, but it can help illustrate how something works. It’s a tool, just like a screwdriver. You CAN use a screwdriver to hammer in a nail, but it’s not meant for it.

The reason I made the original post, was that I became very exited about the possibilities that just opened up, when I realized I could use AI to make simple scripts to do things I want. I have too many hobbies and too little time, I don’t see myself learning scripting like this anytime soon. I’m happy to do it with AI.

It dramatically opens up possibilities for me. I don’t have to hunt for apps that suits my specific workflow, and maybe not find any, or find one that does just almost what I want it to.

Now, I can just tell AI what I want, and then even make desktop entries to the scripts if I want something to click on.

Just now, I spent 30 minutes promting AI to make a nice, clickable NordVPN menu that lets me choose between options and do what I want when I’m too lazy to use the terminal.

Next up is making a scipt that lets me extract .srt subtitles from .mkv files in a folder.

The possibilities are endless! This seems like only a positive thing for me, and I wanted to tip other users like me, and get some tips in return.

Just be careful. AI is eager to please and like i’ve said it will do as you ask sometimes without your permission. I often see my hermes agent go kawabunga on task I’ve asked it without too much planning and suggestion and frequently assuming stuff. I have to give it a directive to suggest before act and ask or search for more information before acting. It’s bad at implying. Otherwise have fun if it’s just for you but don’t be surprised if it break something every now and then.

Good advice.

I always ask it to explain all the steps and functions, during the fine-tuning and testing phase. Sometimes I pass the script back and forth between models.

The Nordvpn script, for example, made all nordvpn terminal commands non functional, and I learned it was because a naming mistake I did when I wanted to give the script file another name.

The most important thing is to establish clear backup policies and explicitly instruct the agent to always create a backup or provide a rollback option. As mentioned earlier, keep your tools in user space.

All my tools are in my private Git repository, and I only push changes after they have passed all tests and all functionality is explained and documented in README.md.