Merge pull request 'feat: first-run initialization routine' (#34) from feat/first-run-init into main
Offline docs drift reminder / remind (push) Failing after 0s
Generate man page / build-manpage (push) Successful in 17s

Reviewed-on: #34
This commit was merged in pull request #34.
This commit is contained in:
2026-06-07 03:14:13 +00:00
6 changed files with 487 additions and 15 deletions
+10 -1
View File
@@ -51,6 +51,7 @@ This config layers on top of the CachyOS base Fish configuration and adds:
│ ├── theme.fish # Theme syntax highlighting colors
│ ├── done.fish # Done plugin (desktop notifications for long commands)
│ ├── tricks.fish # PATH, bang-bang helpers, bat man pages, system aliases, history/backup utilities
│ ├── first_run.fish # One-time initialization: Fisher bootstrap, theme, welcome message
│ ├── paru-wrapper.fish # Auto-generates ~/.local/bin/paru logging wrapper on first run
│ ├── yay-wrapper.fish # Auto-generates ~/.local/bin/yay logging wrapper on first run
│ ├── starship.fish # fish_prompt with OSC 133;A/B shell-integration markers
@@ -86,7 +87,15 @@ Managed via [Fisher](https://github.com/jorgebucaran/fisher):
| `nickeb96/puffer-fish` | Expand `...` to `../..`, `!!` to last command, etc. |
| `jorgebucaran/spark.fish` | Sparkline bar charts in the terminal |
Fisher and all listed plugins are installed automatically by the bootstrap script in `config.fish` upon launching the shell for the first time.
Fisher and all listed plugins are installed automatically by `conf.d/first_run.fish` on the first interactive session. This also applies the Catppuccin Mocha theme and prints a one-time welcome message. Subsequent launches are unaffected.
To re-trigger first-run initialization (e.g., after a fresh clone):
```fish
set -Ue __fish_config_first_run_complete
```
Then open a new shell.
---
+49
View File
@@ -0,0 +1,49 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# ╭──────────────────────────────────────────────────────────╮
# │ First-Run Initialization │
# ╰──────────────────────────────────────────────────────────╯
#
# Runs exactly once on the first interactive fish session after install.
# To reset for testing, run: set -Ue __fish_config_first_run_complete
# Exit early in non-interactive shells (scripts, completions, subshells)
if not status is-interactive
return
end
# Skip if this shell has already been initialized
if set -q __fish_config_first_run_complete
return
end
# Set the flag immediately — before actions — so a mid-run crash doesn't
# leave the shell in a state that re-triggers everything next session.
set -U __fish_config_first_run_complete 1
# ──────────────────────────── Welcome message ───────────────────────────
echo ""
echo " Welcome to your fish shell configuration!"
echo " Run 'config-help' for offline documentation."
echo " Run 'fish-deps' to check and install dependencies."
echo ""
# ──────────────────────────── Bootstrap Fisher ──────────────────────────
if not type -q fisher
echo " [first-run] Installing Fisher plugin manager..."
if curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
echo " [first-run] Fisher installed."
if not fisher update 2>/dev/null
echo " [first-run] Plugin sync failed — run 'fisher update' manually." >&2
end
else
echo " [first-run] Fisher install failed — run 'fisher update' manually." >&2
end
end
# ───────────────────────────── Apply theme ──────────────────────────────
# Catppuccin Mocha theme ships with this config in themes/; it is always available.
if not fish_config theme choose "Catppuccin Mocha" 2>/dev/null
echo " [first-run] Could not apply Catppuccin Mocha theme — set manually with 'fish_config theme choose'." >&2
end
+1 -13
View File
@@ -115,19 +115,7 @@ fish_add_path $HOME/.fzf/bin # Fuzzy Finder (fzf) core binary an
set -gx CDPATH . $HOME/projects $HOME
# ──────────────────────────── Bootstrap Fisher ──────────────────────────
if not type -q fisher
echo "Fisher plugin manager not found."
read -l -P "Install Fisher and plugins now? [Y/n] " _fisher_reply
if test -z "$_fisher_reply" -o "$_fisher_reply" = Y -o "$_fisher_reply" = y
echo "Installing Fisher..."
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
fisher update
fish_config theme choose "Catppuccin Mocha"
else
echo "Skipping Fisher install. Some features may be unavailable."
end
set --erase _fisher_reply
end
# Fisher is bootstrapped automatically on first run via conf.d/first_run.fish
# ─────────────────────── Visual/Interactive setup ───────────────────────
# Run only if we're in an interactive session (not a script or non-interactive shell)
+12 -1
View File
@@ -1196,7 +1196,18 @@ fish_variables.
# 8. FISHER PLUGINS
Fisher is bootstrapped automatically on first shell start if not present.
Fisher is bootstrapped automatically on the **first interactive session** via
`conf.d/first_run.fish`. This also applies the Catppuccin Mocha theme and
prints a one-time welcome message. Subsequent sessions skip all first-run
logic with zero overhead.
To re-trigger first-run initialization (e.g., after a fresh install or for
testing), run:
set -Ue __fish_config_first_run_complete
Then open a new shell.
The plugin list is maintained in fish_plugins at the config root.
jorgebucaran/fisher Plugin manager itself
@@ -0,0 +1,328 @@
# First-Run Initialization Routine — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Create `conf.d/first_run.fish` that runs exactly once on the first interactive fish session — showing a welcome message, auto-installing Fisher + plugins, and applying the Catppuccin Mocha theme.
**Architecture:** A universal variable (`__fish_config_first_run_complete`) acts as the persistent flag. `conf.d/first_run.fish` is auto-sourced by fish on every shell start; it exits immediately if the flag is already set. On first run it sets the flag first (crash-safe), then executes actions in order. The existing interactive Fisher bootstrap in `config.fish` is removed since `first_run.fish` owns that responsibility.
**Tech Stack:** fish shell 4.x, universal variables (`set -U`), `curl`, `fisher`, `fish_config theme choose`
---
## File Map
| File | Action | Responsibility |
|------|--------|---------------|
| `conf.d/first_run.fish` | **Create** | State guard + all first-run actions |
| `config.fish` | **Modify** lines 117130 | Remove interactive Fisher bootstrap block |
| `docs/fish-config.md` | **Modify** | Document first-run behavior |
| `AGENTS.md` | **Modify** | Check off Task #1 acceptance criteria |
---
### Task 1: Remove the interactive Fisher bootstrap from `config.fish`
**Files:**
- Modify: `config.fish:117-130`
- [ ] **Step 1: Delete the Fisher bootstrap block**
Open `config.fish`. Replace lines 117130 (the entire Fisher bootstrap block) with a single comment:
```fish
# ──────────────────────────── Bootstrap Fisher ──────────────────────────
# Fisher is bootstrapped automatically on first run via conf.d/first_run.fish
```
The block being removed is:
```fish
# ──────────────────────────── Bootstrap Fisher ──────────────────────────
if not type -q fisher
echo "Fisher plugin manager not found."
read -l -P "Install Fisher and plugins now? [Y/n] " _fisher_reply
if test -z "$_fisher_reply" -o "$_fisher_reply" = Y -o "$_fisher_reply" = y
echo "Installing Fisher..."
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
fisher update
fish_config theme choose "Catppuccin Mocha"
else
echo "Skipping Fisher install. Some features may be unavailable."
end
set --erase _fisher_reply
end
```
- [ ] **Step 2: Verify config.fish still parses cleanly**
```bash
fish -n ~/.config/fish/config.fish
```
Expected output: no output (no parse errors).
- [ ] **Step 3: Commit**
```bash
git add config.fish
git commit -m "refactor: move Fisher bootstrap to first_run.fish"
```
---
### Task 2: Create `conf.d/first_run.fish`
**Files:**
- Create: `conf.d/first_run.fish`
- [ ] **Step 1: Create the file with this exact content**
```fish
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# ╭──────────────────────────────────────────────────────────╮
# │ First-Run Initialization │
# ╰──────────────────────────────────────────────────────────╯
#
# Runs exactly once on the first interactive fish session after install.
# To reset for testing, run: set -Ue __fish_config_first_run_complete
# Exit early in non-interactive shells (scripts, completions, subshells)
if not status is-interactive
return
end
# Skip if this shell has already been initialized
if set -q __fish_config_first_run_complete
return
end
# Set the flag immediately — before actions — so a mid-run crash doesn't
# leave the shell in a state that re-triggers everything next session.
set -U __fish_config_first_run_complete 1
# ──────────────────────────── Welcome message ───────────────────────────
echo ""
echo " Welcome to your fish shell configuration!"
echo " Run 'config-help' for offline documentation."
echo " Run 'fish-deps' to check and install dependencies."
echo ""
# ──────────────────────────── Bootstrap Fisher ──────────────────────────
if not type -q fisher
echo " [first-run] Installing Fisher plugin manager..."
if curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
and fisher update 2>/dev/null
echo " [first-run] Fisher installed and plugins loaded."
else
echo " [first-run] Fisher install failed — run 'fisher update' manually." >&2
end
end
# ───────────────────────────── Apply theme ──────────────────────────────
# Catppuccin Mocha theme ships with this config in themes/; it is always available.
fish_config theme choose "Catppuccin Mocha" 2>/dev/null
```
- [ ] **Step 2: Verify the file parses cleanly**
```bash
fish -n ~/.config/fish/conf.d/first_run.fish
```
Expected output: no output (no parse errors).
- [ ] **Step 3: Commit**
```bash
git add conf.d/first_run.fish
git commit -m "feat: add first_run.fish for one-time shell initialization"
```
---
### Task 3: Verify first-run behavior
**Files:**
- No file changes — verification only
- [ ] **Step 1: Simulate a fresh install by clearing the flag**
```bash
fish -c "set -Ue __fish_config_first_run_complete"
```
Expected output: no output (variable erased).
- [ ] **Step 2: Confirm the flag is gone**
```bash
fish -c "set -q __fish_config_first_run_complete; and echo SET; or echo UNSET"
```
Expected output:
```
UNSET
```
- [ ] **Step 3: Open a new interactive fish shell and observe first-run output**
```bash
fish -i -c "exit" 2>/dev/null
```
Expected to see (on the terminal — open a real new tab or run `fish` interactively):
```
Welcome to your fish shell configuration!
Run 'config-help' for offline documentation.
Run 'fish-deps' to check and install dependencies.
[first-run] Fisher installed and plugins loaded.
```
(Fisher output may vary; if Fisher is already installed the Fisher block is skipped.)
- [ ] **Step 4: Confirm the flag is now set**
```bash
fish -c "set -q __fish_config_first_run_complete; and echo SET; or echo UNSET"
```
Expected output:
```
SET
```
- [ ] **Step 5: Open another interactive shell — confirm no first-run output**
```bash
fish -i -c "exit" 2>/dev/null
```
Expected: no welcome message, no Fisher output. The routine is silent on repeat launches.
- [ ] **Step 6: Verify non-interactive shells are unaffected**
```bash
fish -c "echo hello"
```
Expected output:
```
hello
```
No welcome message. The `status is-interactive` guard prevents any output.
- [ ] **Step 7: Commit verification note (no code change needed)**
If steps 16 all pass, no commit needed here — move to Task 4.
If a fix was required, commit it:
```bash
git add conf.d/first_run.fish
git commit -m "fix: correct first-run guard behavior"
```
---
### Task 4: Update `docs/fish-config.md`
**Files:**
- Modify: `docs/fish-config.md`
- [ ] **Step 1: Find the Fisher bootstrap section in the docs**
```bash
grep -n "Fisher\|fisher\|bootstrap\|first.run\|first_run" docs/fish-config.md | head -20
```
Note the line number of the Fisher bootstrap description (around line 1199 based on current state).
- [ ] **Step 2: Update the Fisher bootstrap description**
Find the existing text that says something like:
```
Fisher is bootstrapped automatically on first shell start if not present.
```
Replace or expand it to read:
```markdown
Fisher is bootstrapped automatically on the **first interactive session** via
`conf.d/first_run.fish`. This also applies the Catppuccin Mocha theme and
prints a one-time welcome message. Subsequent sessions skip all first-run
logic with zero overhead.
To re-trigger first-run initialization (e.g., after a fresh install or for
testing), run:
```fish
set -Ue __fish_config_first_run_complete
```
Then open a new shell.
```
- [ ] **Step 3: Verify the docs file is valid (no broken markdown)**
```bash
fish -c "bat --language=markdown docs/fish-config.md" 2>/dev/null | head -5
```
Expected: renders without errors (or just check the file opens cleanly).
- [ ] **Step 4: Commit**
```bash
git add docs/fish-config.md
git commit -m "docs: document first-run initialization in fish-config.md"
```
---
### Task 5: Mark acceptance criteria in AGENTS.md
**Files:**
- Modify: `AGENTS.md`
- [ ] **Step 1: Check off the Task #1 acceptance criteria**
In `AGENTS.md`, find the "First-Run Initialization Routine" section and replace each `- [ ]` with `- [x]`:
```markdown
### 1. First-Run Initialization Routine
- [x] **State Detection:** Define a persistent universal state variable (`__fish_config_first_run_complete`) to identify if the environment is being initialized for the very first time.
- [x] **Execution Logic:** Create the structure for `~/.config/fish/conf.d/first_run.fish`.
- [x] **Integration:** Ensure the code inside executes ONLY when the universal variable is not set. It must immediately update the state variable upon execution to prevent running on subsequent interactive shell launches.
- [x] **Best Practices:** Ensure the script exits early if the shell is non-interactive to save performance. Include a commented-out example of how to manually reset the state variable for testing.
```
- [ ] **Step 2: Commit**
```bash
git add AGENTS.md
git commit -m "docs: mark first-run initialization task complete in AGENTS.md"
```
---
## Self-Review
**Spec coverage check:**
| Spec requirement | Task |
|-----------------|------|
| Universal variable `__fish_config_first_run_complete` | Task 2 |
| `conf.d/first_run.fish` file | Task 2 |
| Executes only when variable is not set | Task 2 (guard pattern) |
| Sets variable immediately to prevent re-run | Task 2 (set before actions) |
| Exits early if non-interactive | Task 2 (first guard) |
| Commented reset example | Task 2 (top of file comment) |
| Welcome message | Task 2 |
| Fisher bootstrap (automatic, no prompt) | Task 2 |
| Theme setting | Task 2 |
| Remove old interactive Fisher prompt from config.fish | Task 1 |
| Docs updated | Task 4 |
| AGENTS.md checkboxes | Task 5 |
All spec requirements covered. No placeholders. No TBDs.
@@ -0,0 +1,87 @@
# First-Run Initialization Routine — Design Spec
**Date:** 2026-06-07
**Status:** Approved
---
## Goal
Provide a mechanism for code/actions that should run exactly once — on the first interactive fish session after the config is installed or reset. Subsequent sessions skip all first-run logic entirely with zero overhead.
---
## Architecture
### State detection
A fish **universal variable** (`__fish_config_first_run_complete`) acts as the persistent flag. Universal variables survive shell exits and are stored in `~/.config/fish/fish_variables`. The flag is set to `1` immediately before any first-run actions execute, so a crash mid-run does not re-trigger next session.
### File: `conf.d/first_run.fish`
A single auto-sourced file in `conf.d/`. Fish loads all `conf.d/` files before `config.fish`, so this file cannot rely on PATH additions made in `config.fish`. That is acceptable: all first-run actions only need system-level binaries (`curl`, `fish`) that are in PATH unconditionally.
### Guard pattern
```fish
if status is-interactive
if not set -q __fish_config_first_run_complete
set -U __fish_config_first_run_complete 1
# first-run actions here
end
end
```
The `status is-interactive` guard prevents the routine from firing in scripts, completions, or subshells.
---
## First-Run Actions (in order)
1. **Welcome message** — print a brief one-time greeting identifying the config and key commands.
2. **Fisher bootstrap** — automatically install Fisher and all plugins from `fish_plugins` (replaces the interactive Y/n prompt currently in `config.fish`).
3. **Theme** — apply `Catppuccin Mocha` via `fish_config theme choose`.
---
## Changes to `config.fish`
The existing interactive Fisher bootstrap block (lines ~118130) is removed. `first_run.fish` owns that responsibility. The `config.fish` block was guarded by `not type -q fisher`; the new file guards by the universal variable instead, which is cleaner and avoids re-prompting if Fisher is uninstalled manually.
---
## Reset mechanism
To re-trigger first-run (e.g., for testing):
```fish
set -Ue __fish_config_first_run_complete
```
A commented-out version of this command is included at the top of `first_run.fish`.
---
## Error handling
- Fisher install failure is surfaced to the user via stderr but does not abort the session.
- Theme application is guarded: only runs if `fish_config` is available.
- All first-run actions are wrapped in `if status is-interactive` to prevent execution in non-interactive contexts.
---
## Files changed
| File | Change |
|------|--------|
| `conf.d/first_run.fish` | **New** — first-run guard and actions |
| `config.fish` | Remove interactive Fisher bootstrap block (~lines 118130) |
---
## Success criteria
- First launch: welcome message printed, Fisher + plugins installed, theme set.
- Second launch: nothing extra runs.
- `set -Ue __fish_config_first_run_complete` followed by new shell re-triggers all actions.
- Non-interactive shells (`fish -c "echo hi"`) are unaffected.