diff --git a/README.md b/README.md index c6eee83..58a2310 100644 --- a/README.md +++ b/README.md @@ -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. --- diff --git a/conf.d/first_run.fish b/conf.d/first_run.fish new file mode 100644 index 0000000..801976d --- /dev/null +++ b/conf.d/first_run.fish @@ -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 diff --git a/config.fish b/config.fish index 34bfff9..01a9b87 100644 --- a/config.fish +++ b/config.fish @@ -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) diff --git a/docs/fish-config.md b/docs/fish-config.md index b93c14c..1170a03 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -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 diff --git a/docs/superpowers/plans/2026-06-07-first-run.md b/docs/superpowers/plans/2026-06-07-first-run.md new file mode 100644 index 0000000..d04cb3a --- /dev/null +++ b/docs/superpowers/plans/2026-06-07-first-run.md @@ -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. diff --git a/docs/superpowers/specs/2026-06-07-first-run-design.md b/docs/superpowers/specs/2026-06-07-first-run-design.md new file mode 100644 index 0000000..2f5da81 --- /dev/null +++ b/docs/superpowers/specs/2026-06-07-first-run-design.md @@ -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.