Merge pull request 'feat: first-run initialization routine' (#34) from feat/first-run-init into main
Reviewed-on: #34
This commit was merged in pull request #34.
This commit is contained in:
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 117–130 | 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 117–130 (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 1–6 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 ~118–130) 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 118–130) |
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user