docs: add first-run implementation plan

This commit is contained in:
2026-06-06 22:50:47 -04:00
parent 0959d82018
commit b03ec287b2
@@ -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.