SLOPSHOPPER

skill-toast

Toast when a skill loads

newtoastprompt
A shopper browsing a rack in a slop shop
README

dotfiles

Configuration files for Bash, Starship, Homebrew, Ghostty, Herdr, Neovim, OpenCode, Claude Code, Codex, and Pi. GNU Stow manages symlinks from stow/* into $HOME. Ghostty, Herdr, Neovim, Pi, OpenCode, Claude Code, and Codex use Tokyo Night with MonoLisaCode 14 pt.

Theme and font status

  • Ghostty, Herdr, Neovim, Pi, OpenCode, and Codex use Tokyo Night.
  • Neovim uses the Tokyo Night night style with transparent editor, sidebar, float, statusline, and tab-fill surfaces.
  • Ghostty uses MonoLisaCode at 14 pt with explicit regular, italic, bold, and bold-italic styles.
  • Starship uses the intended Nerd Font glyphs through Ghostty's built-in Symbols Nerd Font fallback. See plans/theme-font-glyph-followups.md.
  • Starship and FZF inherit the Tokyo Night terminal palette from Ghostty.
  • Cursor and Zed are archived under old/ and are not restored or rethemed.

Requirements

ToolMinimum VersionNotes
Neovim>= 0.11.0Required for mason-lspconfig v2 and vim.lsp.config()
Git>= 2.19.0Required for lazy.nvim partial clones
GNU Stow>= 2.4.0Symlink manager for tracked dotfiles
GhosttyLatestUses macos-option-as-alt syntax
HerdrLatestAgent-aware terminal workspace manager
Node.jsLTSFor LSP servers via Mason
tree-sitter-cli>= 0.26.1Required for nvim-treesitter main branch parser compilation
lazygit>= 0.40Required for snacks.lazygit keymap (<leader>gg)
ripgrep>= 13.0Required for nvim-spectre search backend
gnu-sedLatestRecommended on macOS for nvim-spectre replace engine (brew install gnu-sed)
imagemagick>= 7.0Required for snacks.image preview support

Spotify Terminal Visualizer

spotify-visualizer is a standalone TypeScript command managed by the bin Stow package. It renders a procedural terminal dot matrix based on the website music visualizer colors, then uses Spotify only for the current track, artist, play state, and track-specific animation seed.

Setup:

# 1. Create or reuse a Spotify developer app.
# 2. Add this redirect URI to that app:
#    http://127.0.0.1:8974/callback
# 3. Export the client id before launching the visualizer:
export SPOTIFY_CLIENT_ID=your_spotify_client_id

spotify-visualizer

The command stores OAuth tokens under ~/.cache/dotfiles/spotify-visualizer/. Run it in any Herdr pane or tab when you want a dedicated visualizer screen.

Controls:

KeyAction
SpaceToggle Spotify play or pause
nSkip to the next track
pSkip to the previous track
sToggle shuffle
rCycle repeat off, context, and current track
q / Ctrl-CQuit and restore the terminal

The visualizer shows this key legend in the header. Short notices, such as pressing a playback key before Spotify has an active track, replace the legend for about 3 seconds.

Shuffle and repeat state use compact status tokens in the header. Shuffle uses [S:-] when inactive and yellow [S:*] when active. Repeat uses gray [R:-] when inactive, red [R:all] for repeat context, and red [R:1] for repeat current track.

If Spotify returns 401 after scopes change, remove the cached token and authorize again:

rm ~/.cache/dotfiles/spotify-visualizer/tokens.json
spotify-visualizer

TypeSafe Jev

The TypeSafe skill defines when and how to request a Jev judgment. Pi 1.0 uses native Codemode classifiers; it no longer installs a custom TypeSafe SDK adapter. OpenCode retains its typesafe_evaluate adapter under stow/opencode/.config/opencode/plugins/typesafe-ai/. Both send the supplied state and questions to TypeSafe and return typed judgments.

In Pi Codemode:

const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
if (!jev) throw new Error("Jev classifier is unavailable");
const result = await models.classify(jev, {
  state: { message: "A customer asks to cancel today" },
  questions: {
    urgent: {
      type: "bool",
      instructions: "Does this need a reply today?",
      criteria: { true: "Needs a reply today", false: "Can wait" },
    },
  },
});
if (result.stopReason !== "stop") throw new Error(result.errorMessage || "Jev classification failed");
return result.answers.urgent;

Native questions use choice, score, or bool; a bool answer supplies a probability, not a boolean decision. Specialized Jev MCP tools remain available for screening, verification, and gates. Do not include credentials, secrets, or unrelated private data.

OpenCode pins the TypeSafe tool in its Code Mode catalog and adds tool invocation guidance to outgoing contexts where the catalog lists it. Shared global rules and the Jev skill own judgment policy; the adapter explains only the Code Mode calling convention and result shape. Use exact search, parsing, arithmetic, and tests for deterministic facts. Inside execute, the tool returns a validated object with answers, model, and usage; no JSON.parse is needed. For example, when the catalog lists tools.typesafe_evaluate:

const result = await tools.typesafe_evaluate({
  state: "A customer asks to cancel today",
  noul_questions: [{ id: "urgent", instructions: "Does this need a reply today?" }],
});
return result.answers.urgent;

The Noul answer is { type: "noul", noul: probability }, not a boolean. Choice and Score answers include confidence and probability distributions. Missing or malformed provider answers fail explicitly rather than returning an empty success. Tool results retain readable text and model/token metadata for other consumers. Judgments advise; they do not replace permission checks or prove correctness.

Keep TYPESAFE_API_KEY as machine-local state in ~/.config/bash/local.bash:

export TYPESAFE_API_KEY="YOUR_API_KEY"

scripts/bootstrap.sh installs the OpenCode adapters' pinned runtime dependencies. Pi's native classifier needs no separate SDK installation. Apply and reload live configuration only after reviewing and approving the tracked changes.

OpenCode Configuration

The opencode Stow package owns the tracked sources under stow/opencode/.config/opencode/.

Tracked sourcePurpose
opencode.jsoncModel defaults, permissions, MCP servers, providers, skills, and global instructions
cli.jsonTokyo Night, TUI layout, permission handling, and keybindings
commands/Slash commands such as /lg
plugins/typesafe-ai/TypeSafe Jev tool
plugins/tui-conveniences//copy-all, /restart, /update, skill-load confirmations, and the Git status footer
plugins/request-logger/Opt-in private HTTP request capture

New sessions use openai/gpt-6.1-sol-fast with medium reasoning effort and low response verbosity. Its pinned limits match the running ChatGPT catalog checked on 2026-10-04: 400,000 context, 272,000 input, and 128,000 output tokens. This catalog alias sends gpt-6.1-sol with the priority service tier. The limits preserve the current compaction budget rather than assuming the public API's larger window applies to this connection. The TUI hides the session sidebar and persistent tab strip. The TUI also provides Pi-style navigation shortcuts.

OpenCode can use the current user's filesystem, processes, and network without a permission prompt. Review the tracked configuration before you apply it.

REF_API_KEY, EXA_API_KEY, and TYPESAFE_API_KEY are machine-local state. Do not put real credentials in tracked sources.

Apply only the OpenCode Stow package with:

cd ~/dotfiles
./scripts/stow.sh apply opencode

Bootstrap installs plugin dependencies automatically. Restart OpenCode after an apply.

OpenCode HTTP request logger

The last local plugin is request-logger, disabled by default with options.enabled: false. After reviewing the tracked changes and approving live activation, enable it through the plugin's options in opencode.jsonc:

{
  "package": "../../code/personal/dotfiles/stow/opencode/.config/opencode/plugins/request-logger",
  "options": {
    "enabled": true,
    "maxFiles": 100
  }
}

This is an entry in the existing plugins array, not a replacement configuration. Bootstrap installs its dependencies along with the other local plugins. The logger runs in the background service, so a flag on a new CLI process is not used to enable it. It writes to ~/.local/state/opencode/requests, or an absolute options.directory, with directory mode 0700 and file mode 0600. It refuses a symlink at the log directory and retains the newest 100 logger-owned files by default; maxFiles must be a positive integer. Retention limits file count, not total bytes, and leaves unrelated files alone.

Each JSON file records the timestamp, session, agent, catalog model, request kind, raw body string, and byte counts for instructions, input, and tools when those fields exist. The body shows the actual wire model and provider options; the catalog model can be an alias. Byte counts are not token counts, and the instruction count excludes system messages embedded in the input array. For non-UTF-8 bodies, bodyBase64 preserves the original bytes. The original request remains unchanged and readable; logging failures emit a generic server warning without error details and do not block dispatch.

Logs contain full prompts, tool schemas, and file contents, including any secrets already present in the body. The logger deliberately omits URLs and request headers, but does not redact bodies because that would hide what was sent. Keep logs outside version control, review them before sharing, and disable logging when the investigation ends. Capture covers HTTP requests for primary turns, compaction, titles, transient generation, and retries, not responses or WebSocket frames. Keep the logger after request-mutating plugins; hooks registered later can still change the request after capture. Automatic updates, MCP package updates, and automatic compaction remain unchanged.

Codex Chrome Extension Bridge

OpenCode's codex-chrome MCP server uses codex-control-chrome-mcp to control the existing Chrome profile through the Codex Chrome extension. Unlike the isolated chrome-devtools server, it can use existing signed-in tabs and capture screenshots of localhost apps. It grants access to page contents and browser actions; the community bridge does not enforce per-site permissions.

After approving live configuration changes, install the bridge and register it for Google Chrome only:

npm install -g codex-control-chrome-mcp@1.4.1
codex-control-chrome-mcp install-native-host --browser chrome
codex-control-chrome-mcp status --browser chrome

The tracked MCP entry starts codex-control-chrome-mcp from PATH; on Linux the binary is absent, so only that server fails to start. The installer backs up Chrome's existing native-host manifest and records its original host for proxy mode. Reload the Codex Chrome extension after installation, then reconnect codex-chrome through OpenCode's /mcps menu or restart OpenCode. Automatic registration repair remains enabled: after a Codex update restores its own host registration, starting the bridge re-registers it, and the extension may need another reload.

To restore the previous Chrome native-host registration, disconnect codex-chrome in OpenCode and run:

codex-control-chrome-mcp uninstall-native-host --browser chrome

Also remove the codex-chrome MCP entry if you no longer want OpenCode to start the bridge.

GPT Response Verbosity

OpenAI GPT-5 models and the verified GPT-6 Astra, GPT-6 Sol, and GPT-6.1 Sol models support low, medium, and high output verbosity through the Responses API. The tracked configs currently use low.

  • Pi sets verbosity for every GPT-5 model and the verified gpt-6-astra, gpt-6-sol, and gpt-6.1-sol IDs using openai-responses or openai-codex-responses in stow/pi/.pi/agent/extensions/gpt-verbosity.ts. Change the verbosity: "low" value, then run /reload in Pi.
  • Codex sets verbosity with model_verbosity in stow/codex/.codex/config.toml. Change the value, then restart Codex.
  • OpenCode sets textVerbosity per provider and model in stow/opencode/.config/opencode/opencode.jsonc. Update each GPT-5 model entry you use under provider.openai.models or provider.opencode.models, then restart OpenCode.

For example, an OpenCode model override uses this shape:

"providers": {
  "openai": {
    "models": {
      "gpt-5.6-sol-fast": {
        "settings": {
          "textVerbosity": "low",
        },
      },
    },
  },
}

Claude provider request logger

Run claude-log from your regular shell instead of claude to start Claude Code through an opt-in local Anthropic request logger:

claude-log

The command starts a local proxy on a temporary loopback port, launches Claude Code against it, and stops the proxy when Claude exits. Each /v1/messages request is written under ~/.claude/logs/requests/ as readable Markdown plus the raw JSON payload. The Markdown includes request sizes, ranked tool schemas, redacted request headers, the full payload, and the streamed provider response. This logger is inspired by Matt Pocock's agent proxy. It does not capture direct MCP network traffic.

The directory and files use owner-only permissions. The logs can contain sensitive source code, prompts, and connected-service data. Review them before sharing, and remove them when finished:

rm -rf ~/.claude/logs/requests

Set CLAUDE_REQUEST_LOG_DIR to store logs somewhere else. Normal claude sessions do not write request logs.

Claude Code theme and mods

Claude Code uses the Tokyo Night night theme from stow/claude/.claude/themes/tokyonight-night.json, taken from folke's tokyonight.nvim Claude Code extras. Claude Code watches ~/.claude/themes/, so theme edits apply to running sessions.

Mods live in stow/claude/.claude/mods/ and are not stowed. CLAUDE_CODE_PLUGIN_DIRS in settings.json loads them from the repository, and interactive sessions reload a mod when its files are saved.

  • optojr-slack registers optojr_slack_send, which posts as @OptoJr through the relay credentials in the macOS Keychain.
  • skill-toast shows a toast when a skill loads.
  • session-relaunch adds /update, which runs claude update and resumes the session on the new version, and /restart, which resumes the session on whatever version is installed. The relaunch comes from the claude function in stow/bash/.config/bash/functions.bash. Sessions started any other way print the claude --resume command instead of exiting.
  • jev-pipeline registers mcp__jev-pipeline__run, which fetches a list from one MCP tool, optionally enriches each item with a second MCP call, and classifies or reranks every item with Jev. The items never enter the model's context: the reply holds counts and the top items, and the full results go to $TMPDIR/jev-pipeline-<session id>-<timestamp>.json.
  • jev-coding adds a jev_decide reminder to the first Edit or Write of each turn, and records every edit the turn makes. When the agent tries to finish, Jev classifies each edit against the recent prompts as requested, scope creep, speculative, or leftover. A confidently flagged edit blocks the stop once with the list, so the agent reverts or justifies it; a Jev failure shows a toast and lets the turn finish.
  • jev-screen runs jev_screen on every Exa and Ref fetch result, in 20,000-character chunks. A review or block verdict, or a failed screen, adds a warning the model reads after the result, and block also shows a toast.
  • model-cost writes the session's cost per model to $TMPDIR/claude-model-cost-<session id>.json, and statusline.sh shows it after the context usage. The engine reports only a session total, so each response is charged the total's growth since the previous response.

Add a new mod folder to CLAUDE_CODE_PLUGIN_DIRS to load it. Check a mod with claude plugin validate <folder> and claude plugin test <folder>.

Pi provider request logger

Run pi-log from your regular shell instead of pi when you need to inspect the exact payload Pi sends to its model provider:

pi-log

The command enables the tracked request-logger.ts extension for that Pi process only. Each request is written as a readable Markdown file under ~/.pi/agent/logs/requests/, including a size audit, ranked tool schemas, the complete provider payload, the normalized assistant response, and response status metadata when the active provider exposes it. The directory and files use owner-only permissions. These logs can contain source code, prompts, tool results, Gmail, Slack, or Drive data, so do not commit or share them without reviewing the contents. Remove captured requests when finished:

rm -rf ~/.pi/agent/logs/requests

You can also run pi --request-log directly, or set PI_REQUEST_LOG_DIR to store logs somewhere else. Normal pi sessions do not write request logs.

Test Dotfiles

Quick Start (New Mac)

# 1. Clone the repo
git clone https://github.com/manifoldfrs/dotfiles.git ~/dotfiles

# 2. Run the installer
cd ~/dotfiles
./scripts/bootstrap.sh

# 3. Fully quit and reopen your terminal

# 4. Verify Node.js works
node --version

# 5. Verify OpenCode 2 (installed by bootstrap)
opencode --version

Update an Existing Mac / Work Laptop

Use the daily Stow wrapper when the repo is already on the machine and you just want the latest dotfiles applied.

# 1. Get the latest committed dotfiles
cd ~/dotfiles
git pull

# 2. Validate, then reapply all tracked shell/editor/terminal and Herdr config
./scripts/validate-dotfiles.sh
./scripts/stow.sh apply

# First time on this machine? Install Bash, Starship, and the supporting tools:
brew bundle --file=Brewfile
# or only the packaged shell stack:
# brew install bash starship zoxide fzf mise ripgrep fd gawk

# 3. Fully quit and reopen your terminal

# 4. Install/update declared Herdr plugins and reload a running server
./scripts/sync_herdr_plugins.sh

# 5. Verify the basics
node --version
herdr --version

Use ./scripts/bootstrap.sh instead when you also want to install or refresh Homebrew packages, Node.js, and Neovim plugins.

What this already handles for you:

  • stows Bash, Starship, Git, Ghostty, Herdr, Neovim, OpenCode, Claude Code, Codex, Pi settings, and local bin config
  • configures Herdr with Tokyo Night, Bash, tmux-style Ctrl-a bindings, persistence, and agent-aware workspaces
  • avoids rerunning full-machine bootstrap tasks during normal dotfile updates

What ./scripts/bootstrap.sh additionally handles for you:

  • installs Homebrew packages from Brewfile
  • runs Neovim headless plugin sync automatically
  • installs Bun and Plannotator TUI, then syncs the declared Herdr plugins

What is still separate:

  • ./mcp_setup.sh install for the optional Claude Desktop MCP config
  • Claude Code's user-scoped MCP config in ~/.claude.json, which stays local because it contains credentials and account-specific state
  • OpenCode install if you use it on that machine

Herdr and Neovim integrations

The herdr Stow package also manages ~/.config/herdr/plugins.txt and ~/.config/plannotator-tui/config.toml. The default profile includes it. Stow only applies configuration, it does not install or update plugins.

# Existing machines: install the terminal review tool, then sync plugins.
# Requires herdr >= 0.8.0, Bun, and jq on PATH.
brew tap plannotator/tap
# On Homebrew versions that support trust:
# brew trust plannotator/tap
brew install plannotator/tap/plannotator-tui
./scripts/stow.sh apply
./scripts/sync_herdr_plugins.sh

The sync command installs or updates plannotator/herdr-annotate and paulbkim-dev/vim-herdr-navigation, checks the config, and reloads a running Herdr server. Plannotator TUI opens in a full-tab overlay. The agent sidebar prioritizes agents needing attention, uses distinct status symbols, and asks before closing workspaces. Pane-history persistence remains disabled.

ShortcutAction
Ctrl-h/j/k/lNavigate Neovim splits, then adjacent Herdr panes at the edge
Ctrl-a aAnnotate selected terminal text
Ctrl-a Shift-aCopy annotations as agent context
Ctrl-a mManage annotations
Ctrl-a Shift-oReview documents in the current folder
Ctrl-a Shift-lReview the agent's last reply
Neovim visual <leader>aSend the selection to Herdr Annotate

Press Ctrl-a, release it, then press the shortcut's second key.

Launch Plannotator TUI directly from a shell:

plannotator-tui README.md       # Review a file
plannotator-tui docs/           # Browse a folder
plannotator-tui herdr open .    # Review this folder in a Herdr overlay
plannotator-tui herdr last      # Annotate the agent's last reply in Herdr
plannotator-tui last --host pi      # Review the latest Pi reply outside Herdr
plannotator-tui last --host claude  # Review the latest Claude reply outside Herdr

The plannotator-tui commands open the terminal interface. The browser-based plannotator integration is installed alongside it.

Existing Ctrl-a o pane cycling and Ctrl-a z zoom bindings are unchanged. Global Ctrl-k and Ctrl-l navigation takes precedence over shell line deletion and screen clearing inside Herdr. Neovim outside Herdr retains ordinary split navigation. The annotation handoff uses a private temporary file that the plugin consumes and deletes.

Clickable links in terminal chat

In Ghostty on macOS, hold Shift + Cmd and click a link to open it, including inside Herdr. This bypasses application mouse capture and lets Ghostty handle the link. This gesture was verified in this setup, while Herdr's documented Ctrl-click gesture did not work. Keep mouse capture enabled to preserve Herdr's mouse UI. See Herdr's mouse guide.

Pi renders Markdown links as terminal hyperlinks, but relative targets such as docs/plan.md remain unresolved relative paths. The global Pi rules request absolute file:/// URLs for local files in chat and full https:// URLs for web links. File labels can still show readable repository-relative paths and line numbers. Line numbers are informational, not editor jump targets. Links written inside repository documentation remain relative for portability.

Shift-Cmd-click uses the system opener rather than the Herdr Annotate plugin. To review Markdown in Plannotator TUI, use Ctrl-a Shift-o or plannotator-tui herdr open <file.md>. Existing messages are not rewritten by the rule change. Start a new Pi session or use /reload to refresh the global instructions in an existing session.

Neovim secret masking and TypeScript tools

  • cloak.nvim visually masks values in .env, .dev.vars, selected shell configuration files, and TOML token assignments. Use <leader>uC to toggle masking. This only affects display, not file contents, clipboard access, or agent access.
  • :TSC runs the project's TypeScript compiler with --noEmit and opens errors in quickfix. Install TypeScript in the project first.
  • ts-error-translator.nvim improves the readability of TypeScript diagnostics.
  • In TypeScript buffers, :TwoslashQueriesEnable enables inline type queries and `:T
Source 1 files
hooks/register.ts 10 lines
1import type { Register } from 'claude-code'
2
3export const register: Register = on => {
4  on('skill.prompt', ($, e, next) => {
5    $.ui.toast(`Skill ready: /${e.skill}`)
6
7    return next(e)
8  })
9}
10