This repository (julsemaan/profile) is a personal developer environment profile that bootstraps
and manages shell, editor, tmux, and AI coding-agent tooling across machines. It is designed to be
installed on fresh systems (servers, workstations, containers) via a one-liner curl bootstrap.
The profile consists of several layers:
- Bootstrap & install —
setupcurls and runsinstall, which is the main provisioning script. - Shell configuration —
.bashrc_append,.inputrc,.blerc,.fzfsourced into the user's~/.bashrc. Provides a rich prompt with git status, history preservation, tmux integration, and shell completion improvements (ble.sh, fzf, complete_alias). - Editor configuration —
.vimrc(vim with Vundle plugins), Neovim profile (cloned fromjulsemaan/nvim-profile), and common.gitignore. - Tmux configuration —
.tmux.confwith TPM plugins (catppuccin theme, vim-tmux-navigator, tmux-yank, tmux-sensible), custom keybindings, and tmuxifier for window/session layouts. - AI coding agents (sandboxed) — Shell wrappers (
pi-unleashed-safely,opencode-unleashed-safely,codex-unleashed-safely) that run each agent inside a Docker container based on a shared base image (julsemaan/code-sandbox-img). - Pi coding agent configuration —
.pi/directory containing agent definitions, extensions, prompts, modes, MCP servers, and model settings. - Tmuxifier session layouts —
tmuxifiers/directory with predefined tmux session/window layouts for common workflows (coding, kpp, kubex). - Utilities —
utils/with kinto config for macOS key remapping, Ghostty config, port-forwarding scripts, and more. - Self-updating — A system cron job (
cron) and daily cron script (cron.daily) that periodically re-runs the install to stay up to date.
curl -L -s https://semaan.ca/jsemaan-profile | bash
This downloads the latest install script from the repository's master branch and runs it.
The install script:
- Clones or uses the local copy of the repository.
- Copies config files to
/usr/local/etc/(system-wide):.bashrc_append→/usr/local/etc/.bashrc_append.inputrc→/usr/local/etc/.inputrc.tmux.conf→/usr/local/etc/.tmux.conf.vimrc→/usr/local/etc/.vimrc.gitignore→/usr/local/etc/.gitignore.blerc→/usr/local/etc/.blerc
- Installs sandbox wrappers to
/usr/local/bin/:codex-unleashed-safelyopencode-unleashed-safelypi-unleashed-safely
- Installs opencode system config from
.opencode/to/usr/local/etc/opencode/. - Installs tmuxifier layouts from
tmuxifiers/to/usr/local/etc/tmuxifiers/. - Installs Neovim from upstream tarball to
/opt/nvim-linux64. - Configures Vim by sourcing
/usr/local/etc/.vimrcfrom the system vimrc and installing Vundle plugins. - Sets up git with a system-level core.excludesfile pointing at the installed
.gitignore. - Iterates over all user home directories and:
- Symlinks/copies config files (
.tmux.conf,.inputrc,.blerc) - Creates config dirs for Ghostty, kinto, direnv
- Sources
.bashrc_appendinto each user's~/.bashrc - Installs TPM (Tmux Plugin Manager)
- Clones/updates the Neovim profile
- Installs the pi agent configuration (
install_pi_global_config): copies.pi/.*files into~/.pi/agent/
- Symlinks/copies config files (
- Installs supporting tools:
- Tmuxifier (session/window manager for tmux)
- ble.sh (bash-line-editor, syntax highlighting and completion)
- fzf (fuzzy finder)
- complete_alias (bash completion for aliases)
- Installs cron jobs:
/etc/cron.d/jprofile— hourly cleanup of ble.sh cache/etc/cron.daily/jprofile— daily re-install of the profile
All three AI coding agents (pi, opencode, codex) are wrapped in a Docker-based sandbox pattern:
- Each wrapper script builds (or reuses) a Docker image on top of
julsemaan/code-sandbox-img:latest. - The container runs the AI agent with read/write access only to the mounted working directory.
- The container has no access to the host system beyond the mounted paths — no ability to modify system config or install packages globally. Sensitive host-file access is blocked except for one pi-specific read-only SSH exception described below.
- The agent's home directory is a tmpfs (ephemeral), so no state persists on the host.
- Selected host paths are explicitly bind-mounted for persistence (e.g., pi's
~/.pi, opencode's config/cache/data).pi-unleashed-safelycan opt into read-only SSH file mounts only whenPI_SSH_KEY_PATHis set to a host private-key file. In that case it also mounts~/.ssh/known_hostsand~/.ssh/configread-only when present. It does not forward an SSH agent or socket. - Clipboard integration is achieved by forwarding terminal environment variables and mounting tmux/X11/Wayland sockets when available.
- Go module cache is mounted from the host (read-only by default) to reuse downloaded modules.
- API keys for AI providers are forwarded from the host environment.
From code-sandbox/Dockerfile:
- Based on
node:25-bookworm - Includes: Go, kubectl, Helm, ripgrep, Python 3 (with
pythonalias viapython-is-python3),shellcheck,shfmt(pinned), vim, tmux, clipboard utilities (xclip, xsel, wl-clipboard) - Published via GitHub Actions on push (see
.github/workflows/docker.yml)
| Script | Agent | Image | Entrypoint |
|---|---|---|---|
pi-unleashed-safely.sh |
@earendil-works/pi-coding-agent |
pi-unleashed-safely:latest |
pi |
opencode-unleashed-safely.sh |
opencode-ai |
opencode-unleashed-safely:latest |
opencode |
codex-unleashed-safely.sh |
@openai/codex |
codex-unleashed-safely:latest |
codex --dangerously-bypass-approvals-and-sandbox |
Each script accepts --mount, --mount2, --workdir, --rebuild, --no-tty flags and passes remaining
arguments through to the agent. --mount2 HOST CONTAINER uses Docker --mount (bind) instead
of -v, so host paths containing : (e.g. gvfs sftp mounts) work correctly.
Located at the repository root, the .pi/ directory configures the pi coding agent:
settings.json— Default provider/model (openai-codex/gpt-5.5), enabled models, compaction, andpackagesarray for managed pi packages.models.json— Provider overrides (e.g., DeepSeek context window).mcp.json— MCP server definitions (Context7 docs, Playwright, GitHub MCP, Bitbucket MCP).extensions/— Custom pi extensions (todo tracking, build-plan mode, question asking, subagent delegation, modes switcher).agents/— Custom agent definitions (pr-feedback-analyzer,pr-feedback-reviewer,pr-feedback-worker,model-test-orchestrator,model-test-worker).prompts/— Prompt templates (pr-feedback,fix-lint,model-test).modes/— Custom modes (brainstorm).
The PR feedback automation surface is now one command and three agents:
/pr-feedback <PR-URL>— one-pass orchestrationpr-feedback-analyzer— fetches PR state, CI, reviewer summary, and actionable itemspr-feedback-reviewer— skeptical per-item triagepr-feedback-worker— minimal execution, mandatory in-thread reply, optional commitTODO.md— Known issues (e.g., multiline copy/paste in pi).
When modifying pi extensions or configuration, always edit the files under .pi/ in this repository.
During install, these are copied to each user's ~/.pi/agent/. Never apply changes directly to
~/.pi/ on a live system or to node_modules.
Since the AI coding agents run inside Docker containers:
- The filesystem is not the same as the host machine.
- The agent sees only the mounted working directory and its own ephemeral home.
- System commands (apt, systemctl, etc.) are either unavailable or run inside the sandbox.
- Most tools (git, curl, vim, tmux, go, kubectl, helm, rg, python, shellcheck, shfmt) are present inside the container.
- Do not assume access to host-specific paths, services, or credentials.
cron— Installed as/etc/cron.d/jprofile. Runs hourly to clear ble.sh cache (which can leak). Content:0 * * * * root /bin/bash -c "rm -fr /run/user/*/blesh/*"cron.daily— Installed as/etc/cron.daily/jprofile. Re-runs the install script daily. Content:curl -L -s https://semaan.ca/jsemaan-profile | bash > /dev/null
| Path | Purpose |
|---|---|
profile/ |
Shell config files (.bashrc_append, .vimrc, .tmux.conf, .inputrc, .blerc, .gitignore) |
profile/pi-unleashed-safely.sh |
Pi coding agent sandbox wrapper |
profile/opencode-unleashed-safely.sh |
OpenCode coding agent sandbox wrapper |
profile/codex-unleashed-safely.sh |
Codex CLI sandbox wrapper |
code-sandbox/ |
Dockerfile + README for the shared base image |
.pi/ |
Pi agent config (agents, extensions, prompts, modes, settings, MCP, models) |
.opencode/ |
OpenCode system config (opencode.json, tui.json, agent dir, plugins) |
tmuxifiers/ |
Tmuxifier session/window layouts |
utils/ |
Utility scripts (Ghostty config, kinto, port-forwarding, etc.) |
cron |
Hourly cron job (ble.sh cache cleanup) |
cron.daily |
Daily re-install bootstrap |
install |
Main provisioning script |
setup |
One-liner bootstrap that curls and runs install |
To extend the profile:
- Edit files in this repository.
- Commit and push (the daily cron will pick up changes on target systems).
- For immediate testing, run
sudo ./installlocally. - For pi agent changes, edit
.pi/files (never modify~/.pi/on a live system directly). - For sandbox changes, modify the relevant
*-unleashed-safely.shwrapper orcode-sandbox/Dockerfile. - For bashrc changes, edit fragments in
profile/bashrc.d/(never edit~/.bashrcor~/.bashrc_appendon a live system directly).
.bashrc_append is a thin loader that sources fragments from bashrc.d/ via BASH_SOURCE[0].
Fragments are deployed to /usr/local/etc/bashrc.d/ by install.
| Fragment | Scope |
|---|---|
00-core.bash |
Prompt, completions, ble.sh, fzf, base aliases, PATH, bashrc reload |
10-tmux.bash |
joined-tmux, jointmuxifier, tmux-new-coding, fix-tmux-ssh |
20-git.bash |
Git aliases, gch, gchjira, gtagpush, gom, gacp |
30-ai.bash |
genCommitMsg, gcoto, agent sandbox aliases |
40-kube-and-ux.bash |
kubectl completion, k, klogs_deploy |
Constraints: Never edit fragments on a live system — edit profile/bashrc.d/, let install deploy.
PROMPT_COMMAND must stay idempotent. Guard optional tools with command -v/file checks.
Run bash tests/bashrc-smoke.sh to validate.
CI checks (.github/workflows/scripts.yml): bash -n → function-syntax → smoke → shellcheck → shfmt.
Run bash tests/run-ci-checks.sh before pushing bash changes.
When adding a new function, register it in Scenario 2 of tests/bashrc-smoke.sh.
Guard optional tools with command -v/file checks — smoke test must pass with no tools on PATH.
Function syntax: All bash functions must use the function name { keyword form, never the name() { POSIX form. Example: function my_func { ... } not my_func() { ... }.
You will always be running in a sandboxed environment, do not assume that the filesystem is the same as the local machine.