harness-tools
Personal Pi extensions, skills, global agent instructions, sandbox profiles, and supporting services.
What this repository installs
config/pi-links.json is the source of truth for the normal installation. The link manager exposes:
- global agent instructions for Pi, Codex, and Claude;
- 34 shared skills under
~/.agents/skills/; - the
manage-pi-linksandnono-profilesproject-only skills under.pi/skills/; - five global Pi extensions under
~/.pi/agent/extensions/:- dictate: local Linux voice dictation;
- effort-controls:
/effortreasoning selection and/fastpriority mode; - pi-workflow: persisted, gated feature workflows;
- prompt-snippets: per-message prompt rule toggles;
- update:
/updateand--updatesupport.
The repository also contains copy-all, file-search, and subagents extensions, but the normal manifest does not install them globally. The pptx, stop-slop, subagents, and xlsx skills are also intentionally excluded. Archived resources under __archived/ are never installed. Update config/pi-links.json deliberately if that policy changes.
Fresh-machine setup
The commands below reproduce the normal setup. They assume Linux, a systemd user session, and the current canonical checkout path. Pi and the shared skills work on other platforms, but the nono launcher and Dictate sections are Linux-specific.
1. Install the base tools
Required:
- Git with access to
git.jb.codes; - Node.js 22.19 or newer with npm;
- Pi Coding Agent;
- a configured model provider.
Install Pi using its official npm package:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
pi --version
A Bun global install also works, but record its executable path later when configuring pi-sbx.
Start Pi once and authenticate with a subscription or API-key provider:
pi
Run /login inside Pi. Pi stores its user state under ${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}. When Pi runs through nono, prefer nono credential routes so real API keys do not enter the sandbox; see Sandboxed Pi credentials.
2. Clone the repository
Several current profiles and instruction imports contain /srv/data/code/harness-tools, so using that path requires no repository edits:
if [ ! -e /srv/data/code ]; then
sudo install -d -o "$USER" -g "$(id -gn)" /srv/data/code
fi
test -d /srv/data/code && test -w /srv/data/code || {
echo '/srv/data/code already exists but is not a writable directory' >&2
exit 1
}
git clone ssh://git@git.jb.codes:7722/jb/harness-tools.git \
/srv/data/code/harness-tools
cd /srv/data/code/harness-tools
The setup creates /srv/data/code only when absent. If it already exists, keep its current ownership and obtain access through the machine's normal administration policy.
If the checkout lives elsewhere, replace the machine-specific paths before syncing:
docs/global.CLAUDE.md;profiles/nono/pi-local.json;profiles/nono/pi-orca-local.json;profiles/nono/pi-work.json;- the copied
~/.config/pi-sbx/config.jsondescribed below.
Dictate currently targets /run/user/1000/pi-dictate.sock in both the extension and profile. It therefore expects UID 1000. Change and review the socket path before installation if id -u reports another UID.
3. Install repository dependencies
The linked TypeScript extensions resolve runtime dependencies from this checkout, so node_modules is required even though the extensions have no build step:
cd /srv/data/code/harness-tools
npm ci
npm run typecheck
Use npm ci after pulling a lockfile change. Node.js must satisfy the version in package.json.
4. Install instructions, skills, and extensions
Preview the exact changes first, then apply them:
cd /srv/data/code/harness-tools
./scripts/pi-links.mjs sync --dry-run
./scripts/pi-links.mjs sync
./scripts/pi-links.mjs check
A successful check currently reports 44 links and zero local package entries. The manager:
- preserves unrelated global resources;
- refuses to overwrite regular files, directories, or links owned outside this repository;
- uses absolute links for global resources and relative links for project-only skills.
Resolve any reported collision manually instead of deleting it blindly. Run /reload in an active Pi session, or restart Pi, after the sync.
Do not also run pi install /path/to/harness-tools for the normal setup. That installs the root as a Pi package and applies the broader package.json resource globs, which is different from the curated config/pi-links.json policy and can duplicate resources. It remains useful for a temporary package-development test in an isolated Pi agent directory.
5. Verify Pi
pi
Inside Pi:
- Run
/reload. - Confirm
/effort,/snippets,/update, and/workfloware available. - Ask Pi to list its available skills and confirm a shared skill such as
tddappears. - From this repository, confirm the project-only
manage-pi-linksandnono-profilesskills appear.
Additional Pi packages from Gitea
Two extensions are maintained in separate Gitea repositories rather than harness-tools. Install them as Pi-managed Git packages after completing the canonical link sync above. Pi clones each package into its agent directory and installs its package dependencies.
Run these commands from a normal host terminal. A restrictive nono session may intentionally lack the SSH key or agent socket needed to clone private repositories.
Verify Gitea SSH access
Both repositories use SSH on port 7722. Confirm access without changing either checkout:
GIT_TERMINAL_PROMPT=0 git ls-remote \
ssh://git@git.jb.codes:7722/jb/pi-interactive-subagents.git HEAD
GIT_TERMINAL_PROMPT=0 git ls-remote \
ssh://git@git.jb.codes:7722/jb/pi-todo-herdr.git HEAD
Each command should print a commit hash and HEAD. Configure the machine's SSH key and git.jb.codes host key before continuing if either command fails.
Install both packages
pi install ssh://git@git.jb.codes:7722/jb/pi-interactive-subagents.git
pi install ssh://git@git.jb.codes:7722/jb/pi-todo-herdr.git
pi list
Confirm both Gitea URLs appear under User packages, then restart Pi or run /reload. These package entries are independent of config/pi-links.json, so ./scripts/pi-links.mjs check still reports zero local package entries.
Do not globally enable harness-tools/extensions/subagents at the same time as pi-interactive-subagents. They provide overlapping subagent tools. The curated harness-tools manifest leaves its in-repository implementation disabled for this reason.
Configure interactive subagents
pi-interactive-subagents creates asynchronous child Pi sessions in Herdr tabs or tmux panes. It requires one terminal backend:
- Herdr: install its Pi integration, then launch Pi inside a Herdr-managed pane;
- tmux: launch Pi inside tmux when Herdr is unavailable.
For Herdr:
herdr integration install pi
herdr integration status
herdr
Start Pi in a Herdr pane. The extension detects HERDR_ENV=1 and opens each subagent in an unfocused tab. The pi-local nono profile already declares the reviewed Herdr socket used on the original machine; update that machine-specific path if necessary.
For tmux:
tmux new -A -s pi 'pi'
The package bundles scout, researcher, and worker agent definitions. Their configured provider, model, thinking level, and tool allowlist live in the package's agents/ directory. Authenticate the configured provider and confirm the named model is available. To override a bundled definition without editing Pi's managed Git clone, create a same-named file in ${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/agents/ or a trusted project's .pi/agents/; project definitions take precedence over global definitions, which take precedence over package definitions.
Inside Pi, verify and try the extension:
- Run
/reload. - Ask Pi to call
subagents_list. - Confirm
scout,researcher, andworkerare listed. - Run
/subagent scout Summarize this repositoryfrom a disposable test checkout. - Confirm a Herdr tab or tmux pane opens and the completed result returns to the parent session.
The status widget is enabled by the package's example configuration when no local config.json exists. Copy and edit config.json.example beside the installed package only when a status override is needed. Treat Pi's managed Git checkout as disposable because package updates may reset it; prefer global or project agent overrides for durable customization.
Configure Pi Todo Herdr
pi-todo-herdr provides the set_tasks, update_task, rm_task, list_task, and get_task tools plus a session-local Pi task widget. Herdr is optional for the tools and widget.
Inside Pi after /reload:
- Confirm the five task tools are available.
- Run
/todoto toggle the widget. - Ask Pi to create a small three-step test plan, update one task, and list the tree.
For Herdr sidebar task metadata, back up and edit the existing configuration:
cp -a ~/.config/herdr/config.toml \
~/.config/herdr/config.toml.before-pi-todo-herdr
"${EDITOR:-vi}" ~/.config/herdr/config.toml
If [ui.sidebar.agents] already exists, edit its existing rows value and add "$task_progress", "$task", and "$task_count" where desired. Do not add a duplicate TOML table header. If the table does not exist, append the complete block:
[ui.sidebar.agents]
rows = [["state_icon", "workspace", "tab"], ["$ask", "$ask_count"], ["$task_progress", "$task", "$task_count"]]
Apply and verify the change:
herdr server reload-config
herdr status client
Update or remove the Gitea packages
Use the same source strings stored by Pi:
pi update ssh://git@git.jb.codes:7722/jb/pi-interactive-subagents.git
pi update ssh://git@git.jb.codes:7722/jb/pi-todo-herdr.git
Remove them with:
pi remove ssh://git@git.jb.codes:7722/jb/pi-interactive-subagents.git
pi remove ssh://git@git.jb.codes:7722/jb/pi-todo-herdr.git
Run /reload after an update or removal.
Optional development checkouts
A Pi-managed package clone is enough for normal use. For source development, use separate durable checkouts so Pi package updates cannot discard work:
mkdir -p /srv/data/repo-store
git clone ssh://git@git.jb.codes:7722/jb/pi-interactive-subagents.git \
/srv/data/repo-store/pi-interactive-subagents
git clone ssh://git@git.jb.codes:7722/jb/pi-todo-herdr.git \
/srv/data/repo-store/pi-todo-herdr
npm ci --prefix /srv/data/repo-store/pi-interactive-subagents
npm test --prefix /srv/data/repo-store/pi-interactive-subagents
npm ci --prefix /srv/data/repo-store/pi-todo-herdr
npm run typecheck --prefix /srv/data/repo-store/pi-todo-herdr
npm test --prefix /srv/data/repo-store/pi-todo-herdr
Both repositories are separate projects. Keep their changes and releases out of the harness-tools checkout.
Companion tools used by installed skills
The link sync installs skill instructions, not every external program those skills may call. Install the companion tools you intend to use.
OpenSrc for dependency research
code-research and package-source require the opensrc CLI and intentionally use /srv/data/opensrc as the single source cache:
npm install -g --prefix "$HOME/.local" opensrc
if [ ! -e /srv/data/opensrc ]; then
sudo install -d -o "$USER" -g "$(id -gn)" /srv/data/opensrc
fi
test -d /srv/data/opensrc && test -w /srv/data/opensrc || {
echo '/srv/data/opensrc already exists but is not a writable directory' >&2
exit 1
}
if [ ! -e "$HOME/.opensrc" ] && [ ! -L "$HOME/.opensrc" ]; then
ln -s /srv/data/opensrc "$HOME/.opensrc"
fi
command -v opensrc
readlink -f "$HOME/.opensrc"
The setup preserves existing directory ownership and any existing ~/.opensrc. If that path already exists, inspect it instead of replacing it. Completion requires it to resolve to /srv/data/opensrc.
If ~/.opensrc already exists, inspect it instead of replacing it. Completion requires it to resolve to /srv/data/opensrc.
Browser Harness
The browser-harness skill requires uv, Chrome or Chromium, and the Browser Harness CLI:
uv tool install --python 3.12 --upgrade --force browser-harness
browser-harness --doctor
browser-harness <<'PY'
print(page_info())
PY
For a Browser Use cloud session, authenticate separately with browser-harness auth login. Local browser control does not require a cloud key.
Artifact presentation
The artifact-presenter skill uses Herdr and mcat. Install both and verify:
command -v herdr
command -v mcat
herdr --version
mcat --version
On Arch-based systems, mcat is available as the mcat-bin AUR package. Follow Herdr's installation guide for its CLI. The skill falls back to reporting the artifact path when either program is unavailable.
PDF work
The pdf skill calls tools as needed rather than maintaining a repository environment. For its common workflows, install the relevant subset of:
- Python packages:
pypdf,pdfplumber,reportlab,pymupdf,pytesseract, andpdf2image; - system tools: Poppler (
pdftotext,pdfimages, andpdftoppm),qpdf, and Tesseract OCR.
The intentionally excluded pptx and xlsx skills have additional office and rendering dependencies and are not part of the normal setup.
Optional Linux sandbox setup
The repository-managed nono profiles and pi-sbx launcher are optional, but they reproduce the normal restricted Pi runtime.
Install nono and its Pi package
Install the nono CLI using its upstream instructions, then pull the parent Pi profile:
nono pull nolabs-ai/pi
nono profile show nolabs-ai/pi
Link the repository profiles
cd /srv/data/code/harness-tools
mkdir -p ~/.config/nono/profiles
for profile in pi-local pi-orca-local pi-harness-tools pi-work; do
source="$PWD/profiles/nono/$profile.json"
destination="$HOME/.config/nono/profiles/$profile.json"
if [ -L "$destination" ] && \
[ "$(readlink -f "$destination")" = "$(readlink -f "$source")" ]; then
continue
fi
if [ -e "$destination" ] || [ -L "$destination" ]; then
echo "Refusing to replace existing profile: $destination" >&2
exit 1
fi
ln -s "$source" "$destination"
done
for profile in pi-local pi-orca-local pi-harness-tools pi-work; do
nono profile validate "$profile"
done
The loop is rerunnable for links already targeting this checkout and refuses every other collision.
These profiles inherit from one another, so install and validate all four even if only pi-local is used initially. Review their filesystem paths, Orca integration paths, socket grants, network allowlist, and work-specific Git path on every new machine.
Install the routed launcher
pi-sbx is a Fish script. Install Fish, then link the launch commands and systemd slice:
cd /srv/data/code/harness-tools
mkdir -p ~/.local/bin ~/.config/pi-sbx ~/.config/systemd/user
safe_link() {
source="$1"
destination="$2"
if [ -L "$destination" ] && \
[ "$(readlink -f "$destination")" = "$(readlink -f "$source")" ]; then
return 0
fi
if [ -e "$destination" ] || [ -L "$destination" ]; then
echo "Refusing to replace existing path: $destination" >&2
return 1
fi
ln -s "$source" "$destination"
}
safe_link "$PWD/scripts/pi-sbx.fish" "$HOME/.local/bin/pi-sbx"
safe_link "$PWD/scripts/pi-sbx-profile.py" "$HOME/.local/bin/pi-sbx-profile"
safe_link "$PWD/scripts/pi-orca.fish" "$HOME/.local/bin/pi-orca"
safe_link "$PWD/scripts/orca-nono-profile.py" "$HOME/.local/bin/orca-nono-profile"
safe_link "$PWD/systemd/user/pi-agents.slice" \
"$HOME/.config/systemd/user/pi-agents.slice"
if [ ! -e "$HOME/.config/pi-sbx/config.json" ]; then
cp "$PWD/config/pi-sbx.example.json" "$HOME/.config/pi-sbx/config.json"
chmod 600 "$HOME/.config/pi-sbx/config.json"
else
echo 'Keeping existing ~/.config/pi-sbx/config.json'
fi
systemctl --user daemon-reload
The setup preserves an existing launcher, systemd unit, or routing configuration unless it already links to this checkout. Resolve reported collisions manually after inspecting both paths.
Edit ~/.config/pi-sbx/config.json before launching. Set:
nodeExecutabletoreadlink -f "$(command -v node)";piExecutableto the real Pi executable path;piAgentDirectoryto the matching Pi state directory;- directory routes to paths that exist on this machine;
- profile-specific runtimes only when a different Node ABI or Pi state directory is intentional.
The checked-in example contains paths from the original machine and is a template, not a portable final configuration. Separate Pi agent directories are required when profiles use incompatible Node ABIs with native dependencies.
Verify and launch:
pi-sbx-profile list
pi-sbx-profile show
pi-sbx-profile set pi-local /path/to/a/project
pi-sbx-profile resolve /path/to/a/project
pi-sbx
See docs/nono.md for routing behavior, multi-repository grants, Internet tiers, profile editing, and troubleshooting denials.
Sandboxed Pi credentials
The base nolabs-ai/pi package supports mediated credential routes. Store secrets in the OS keychain or another nono-supported secret source, then enable only the required route names in a reviewed child profile. For example, supported built-in route names include openai, anthropic, gemini, github, and gitlab.
Do not place real provider keys in files readable by sandboxed Pi if the goal is credential mediation. Consult the installed package guide after nono pull:
~/.config/nono/packages/nolabs-ai/pi/README.md
Optional Dictate service
Dictate is Linux-only and requires:
- PipeWire with
pw-record; - systemd user services;
- an NVIDIA GPU with a working CUDA runtime;
uv;- network access for the initial locked dependency and model download;
- UID 1000 unless the hardcoded socket path is changed first.
Install the host service from the repository root:
cd /srv/data/code/harness-tools
./services/dictate/install.sh
systemctl --user status pi-dictate.socket pi-dictate.service
The installer provisions the locked Python environment, verifies the pinned model, installs host-owned systemd units, and enables socket activation. Initial storage is several gigabytes. To reuse an existing model archive or preview the operation:
./services/dictate/install.sh --model-source /path/to/parakeet-tdt-0.6b-v3.nemo
./services/dictate/install.sh --dry-run --model-source /path/to/model.nemo
Restart a sandboxed Pi after profile or socket changes. In Pi, press Alt+M to start or stop dictation and Alt+N to cancel. See extensions/dictate/README.md for privacy, dictionary, model, and troubleshooting details.
Maintenance
After pulling repository changes:
cd /srv/data/code/harness-tools
git pull --rebase
npm ci
./scripts/pi-links.mjs sync --dry-run
./scripts/pi-links.mjs sync
./scripts/pi-links.mjs check
npm run typecheck
Run /reload after skill, extension, instruction, or separately installed Gitea package changes. Re-run ./services/dictate/install.sh when Dictate's host service or protocol changes.
Before removing repository-managed exposure, preview exactly what will be deleted:
./scripts/pi-links.mjs prune --dry-run
Only run ./scripts/pi-links.mjs prune after reviewing every removal. The manager prunes repository-owned exposure and leaves unrelated global resources alone.
Development and checks
Extensions are TypeScript modules loaded directly by Pi; no build step is required.
npm ci
npm run typecheck
npm test
For a one-off extension development run:
pi -e ./extensions/effort-controls
pi -e ./extensions/update
Repository layout
config/ Installation and launcher manifests
docs/ Usage guides, plans, and ADRs
extensions/ Pi extensions
profiles/nono/ Repository-managed nono profiles
scripts/ Link manager and sandbox launch helpers
services/ Host-side services used by extensions
skills/ Pi skills, one SKILL.md per skill
systemd/user/ Repository-managed systemd user units
License
MIT