Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
* Personal agent configuration This repository is checked out at =~/.agents=. It contains personally curated agent configuration and is the source of truth. Harness-owned directories are mutable realizations of that configuration. Bootstrap scripts apply curated decisions outward. Runtime state never synchronizes back automatically. ** Layout #+begin_src text ~/.agents/ ├── flake.nix Pi, OpenCode, and Hermes packages ├── flake.lock ├── skills/ shared skills maintained here, consumed in place ├── skills.json selected third-party Git skills ├── skills.lock exact resolved revisions ├── vendor/ │ └── skills/ generated third-party skills, ignored by Git ├── prompts/ shared user-invoked Markdown templates ├── lib/ shared bootstrap plumbing and skill resolver ├── harnesses/ │ ├── pi/ curated Pi configuration and bootstrap │ ├── opencode/ curated OpenCode configuration and bootstrap │ └── hermes/ curated Hermes configuration and bootstrap └── tests/ #+end_src Home Manager owns installation, activation, persistence, secrets, services, and machine integration in =/etc/nixos=. Pi owns =~/.pi/agent=. OpenCode owns =~/.config/opencode=. Hermes owns =~/.hermes=. These application directories stay writable and do not point back into this repository. Pi sessions stay under working-directory subfolders in =~/.local/state/pi/sessions=. The Pi wrapper sets =PI_CODING_AGENT_SESSION_DIR= so replacing or remounting =~/.pi/agent= cannot invalidate an active session path. Inbox drop-zone prompts live in =harnesses/pi/task-drop-prompts/=. Systemd watches and Herdr launch behavior stay in the NixOS repository, while prompt edits take effect without a rebuild. Shared skills live in =~/.agents/skills/= and =~/.agents/vendor/skills/=. Pi uses its native =skills= paths, OpenCode uses =skills.paths=, and Hermes uses =skills.external_dirs=. No shared skill tree is copied into a harness home. Maintained skills are listed first; the resolver and bootstraps refuse maintained/vendor name collisions instead of silently shadowing a maintained skill. Hermes keeps its own =skills/= for bundled, installed, and generated skills. Its list prefers local entries, but bare-name loading refuses collisions across local and configured directories. Keep those names unique too. Shared skills are not write-protected; changing maintained skills requires approval. Skills that name Pi-only tools need adaptation before Hermes can use those tools. ** Selected third-party skills Use =lib/skills= from any working directory. Adding or removing a skill updates =skills.json=, =skills.lock=, and the generated tree together, after fetch and layout checks succeed. Other selected skills stay at their locked commits. No upstream installers or build scripts run. #+begin_src sh # Show selected skills, refs, exact commits, and source paths without fetching: ~/.agents/lib/skills list # Add one skill. The name must match its SKILL.md frontmatter: ~/.agents/lib/skills add pdf https://github.com/anthropics/skills.git --path skills/pdf --ref main # Remove a selected vendor skill, leaving maintained skills alone: ~/.agents/lib/skills remove pdf # Show command-specific options: ~/.agents/lib/skills add --help #+end_src =--path= defaults to =.= and =--ref= defaults to =HEAD=. Duplicate names, maintained-skill collisions, invalid layouts, and failed fetches leave the manifest, lock, and generated tree unchanged. Add/remove require the existing manifest and lock to agree. They rebuild the remaining selected tree, so they need network access when it contains remote skills. You can still edit =skills.json= by hand and run =lib/skills update=. Each entry selects one directory; repeat =git= to select several skills from one repository. The current selection is: #+begin_src json { "skills": [ { "name": "impeccable", "git": "https://github.com/pbakaus/impeccable.git", "path": ".pi/skills/impeccable", "ref": "main" } ] } #+end_src =name= and =git= are required. =git= accepts a Git URL, SSH source, or absolute local repository path. =path= defaults to =.= for a repository-root skill. =ref= defaults to =HEAD= for the upstream default branch; a branch, tag, or commit can be selected explicitly. Paths must stay inside the upstream repository. Names must be lowercase letters/digits separated by single hyphens, at most 64 characters, and match =SKILL.md= frontmatter. Unknown fields and duplicate JSON keys or skill names fail. =skills.lock= contains the normalized declarations plus a full =revision= commit hash for each skill. Commit the manifest and lock, never downloaded contents. Python 3 and Git are the only resolver dependencies. #+begin_src sh # Fresh checkout or rebuild from the existing lock, without advancing revisions: ~/.agents/lib/skills materialize # After editing the manifest, or to advance ALL selected upstream refs: ~/.agents/lib/skills update # Check declarations, lock agreement, layout, and maintained/vendor collisions: ~/.agents/lib/skills check #+end_src Materialization replaces only =vendor/skills/= and copies only selected directories. Updates fetch the requested refs, materialize them, and rewrite the lock after all fetches and layout checks succeed. Normal materialization fetches exact locked commits and leaves the lock unchanged. Missing locks or changed declarations require an explicit update. Git sources must retain the locked commits for later reproduction. Each selected directory must contain =SKILL.md= with name and description frontmatter. Nested skills, symlinks, special files, and submodules inside selected directories are rejected rather than copied incompletely. Select nested skills separately. No dependency installation or upstream build scripts run. Review third-party instructions before using them. Generated files are not manually maintained; rerun materialization to discard edits or remove deselected skills. Temporary fetches live under ignored =.scratch/= and are removed after each run. Bootstraps check the shared skill declarations and tree without fetching or updating. Run materialization before bootstrap when the manifest is nonempty. Pi packages in =harnesses/pi/settings.json= remain Pi packages. Run =python3 -B tests/test_skills.py= for local Git fixture tests. The bootstrap fixtures also need =jq= and Mike Farah's =yq= v4. *** Impeccable [[https://impeccable.style][Impeccable]] is installed at =vendor/skills/impeccable/= from the upstream Pi distribution. The shared =prompts/impeccable.md= adds =/impeccable= in Pi and OpenCode. Run their bootstraps after a fresh checkout to install the prompt. Run =/reload= in Pi or start a new OpenCode session after installing or updating it. #+begin_src text /impeccable Show the context-aware command menu /impeccable init Capture this project's product context /impeccable document Describe its existing design system /impeccable audit src/app Review a specific interface /impeccable polish src/app Refine a specific interface #+end_src Pi also supports =/skill:impeccable audit src/app= without the prompt alias. Hermes discovers the same shared skill but does not use shared slash-command prompts. Relative references and helper scripts resolve from the installed skill directory, while the working directory stays at the target project. The upstream launcher may download its versioned engine on first use into =~/.impeccable/=. It checks the release's SHA256 sidecar, but =skills.lock= pins the Git skill only, not the downloaded engine's checksum. No engine has been provisioned or tested on NixOS here. An existing engine can be selected with =IMPECCABLE_BIN=. If the helper fails, follow the skill's documented fallback and read existing =PRODUCT.md= and =DESIGN.md= directly. Project hooks remain opt-in. Do not use the upstream installer or updater to replace the repo-managed skill; use =lib/skills update= and reload instead. ** Shared prompts =skills/= holds capabilities and knowledge. =prompts/= holds user-invoked templates such as =/commit= and =/commitall=. Pi and OpenCode install the same files through their bootstraps. Hermes continues to use shared =skills/=, not =prompts/=. The shared contract is intentionally small: - Use flat, regular Markdown files named with lowercase letters, digits, and hyphens, such as =review-changes.md=. The filename determines the command name. - Start with exactly three frontmatter lines: =---=, =description: Review changes=, =---=. Use a single plain description, starting with a letter and containing only letters, digits, spaces, commas, periods, slashes, parentheses, underscores, or hyphens, with no trailing spaces. Do not use YAML boolean/null values as descriptions. No other metadata is shared. - Use =$ARGUMENTS= for optional input. Pi removes argument quoting and joins words with spaces; OpenCode retains the raw input. Do not depend on exact quoting or whitespace. Invoke templates without placeholders with no arguments, since OpenCode appends unused arguments while Pi ignores them. - Do not use positional placeholders, Pi defaults/slices, =$@=, OpenCode shell injection, or =@file= references. OpenCode's last numbered placeholder consumes remaining arguments, unlike Pi's single-argument substitution. - Keep instructions tool-neutral. Agent/model selection, delegation, and harness-only metadata or behavior belong in =harnesses/pi/prompts/= or =harnesses/opencode/commands/=, not in shared files. Both bootstraps validate shared filenames, frontmatter, and substitution syntax before writing runtime files. They reject a shared filename also present in the corresponding harness-specific source directory. Rename one rather than relying on copy order. Shared files replace runtime files with the same name, including the former commit commands; unrelated runtime files survive. Project, package, extension, and JSON-defined command precedence remains harness-owned, so avoid reusing these names there. Pi copies shared files into =$PI_CODING_AGENT_DIR/prompts/=, normally =~/.pi/agent/prompts/=. OpenCode copies them into =$OPENCODE_CONFIG_DIR/commands/=, normally =~/.config/opencode/commands/=. Its bootstrap also accepts =OPENCODE_DIR= or the XDG config home; use =OPENCODE_CONFIG_DIR= when launching OpenCode against a custom destination. Both use their existing atomic writable-copy logic and skip unchanged files. Run =/reload= in Pi or start a new OpenCode session after changes. Shared commands use the current agent; Luna is OpenCode's configured default. =harnesses/pi/prompts/= and =harnesses/opencode/commands/= currently have no templates, but remain supported for harness-specific ones. =harnesses/pi/task-drop-prompts/= stays separate because its Org prompts drive the Pi/Herdr inbox service, not user slash commands. Run =tests/test-prompts= to check both bootstraps, shared syntax, collisions, runtime preservation, and repeat runs without touching live homes. ** Opt-in project supervision =skills/sheepdog/= pairs a visible Pi supervisor with a visible Pi worker in Herdr. The worker writes project code and publishes reviewed commits; the supervisor reviews progress, requests corrections, and approves publication only when the project's mandate grants it. Both roles use separate interactive Pi sessions, panes, and terminals. Hermes is not required. This does not replace normal hands-on work or enable supervision globally. Read =skills/sheepdog/sheepdog.org= before starting. Supply the exact mandate, canonical Org task path/heading, and verified supervisor model/session evidence. Bind the existing Pi worker and prepared Pi supervisor to distinct, exact sessions in one workspace, then run the Python watcher in a visible shell pane. Its timer requests reviews during active work, including streaming output and long tools, as well as at completion. A private worker-ownership record prevents competing runs on the same Herdr instance. Fresh reviews warn once at a 900-second soft acknowledgement threshold and stop at a separate 1800-second hard timeout, capped by absolute watch expiry. =--review-seconds= sets the soft threshold; =--review-hard-seconds= sets the hard timeout. Soft overdue keeps the same review active without reminders or duplicate prompts. Legacy clocks keep their original hard deadline, with no migration or revival. Old Hermes bindings cannot continue; explicitly stop the old watch before binding a fresh Pi-only run. A hard timeout stops scheduling without replay, restart, or renewal. Runtime state, the mandate, and the generated =handoff.org= belong outside the worker repository. The handoff preserves accepted work, verification, publication, remaining work, and the stopping reason without relying on the launcher to update Org. The helper uses Python's standard library and the local Herdr CLI. Snapshots read the visible viewport without scrolling an active worker; earlier output may require separate inspection of its pinned session. It pauses on identity changes or uncertain delivery instead of retrying input. Steering and normal Pi abort are separate opt-in permissions. After abort, the supervisor must verify cancellation and an empty editor before submitting a correction; the helper cannot prove those conditions or stop every daemon-backed build. These role rules are not an OS sandbox. Stop or budget expiry leaves the worker and workspace available. Expiry records the latest observed worker state, including work that continues unsupervised, and never silently renews the budget. Reported worker cwd/foreground cwd are checked against the repository on each inspection/control path, but they may be stale or miss child-tool directories and do not constrain command targets. No service, global permission, model override, or additional agent manager is installed. Run =python3 -B skills/sheepdog/scripts/test_sheepdog.py= for credential-free Pi-pair binding, scheduling, correction/recovery, and safety checks. Earlier live trials used Hermes supervisors, not Pi. The initial =guessit= trial used =gpt-6-luna= as supervisor, deliberately planted omissions and a wrong-target marker, predefined slices, and a local bare remote, not GitHub. A second trial verified =gpt-6.1-sol= in the live supervisor session. Given an outcome rather than a slice list, Hermes selected one bounded opt-in score-storage slice, independently verified all 23 tests and reviewed file blobs, confirmed branch publication, and stopped. Worker transcripts, supervisor checks, and watcher events remain separate evidence. This establishes a small outcome-driven slice, not open-ended decomposition or unattended reliability across larger projects. The second trial required explicit recovery from an active-terminal history capture error; its measurements separate that downtime from review time. These historical Hermes trials do not validate live Pi-supervisor behavior; a controlled Pi-only trial is still needed. Future launches verify their actual selected model; the skill does not select a global default. A compact mandate is in =skills/sheepdog/mandate-example.org=. ** Packages The flake re-exports Pi, OpenCode, and Hermes from [[https://github.com/numtide/llm-agents.nix][Numtide's llm-agents flake]]. Pi is the default package. It uses Numtide's supported Node build because the Bun binary omits codemode's worker. Its wrapper sets its own package directory so nested launches cannot inherit an older Pi's assets. #+begin_src sh nix build .#pi .#opencode .#hermes-agent nix run .#pi nix run .#opencode nix run .#hermes-agent #+end_src GitHub Actions updates =flake.lock= daily at 02:00 MST (09:00 UTC, 03:00 MDT). The workflow checks all package outputs, builds the three Linux packages, and runs their version commands and a credential-free Pi codemode worker check before committing a changed lock. It also supports manual runs. These commits do not install packages on hosts; NixOS must update its =agents= input and rebuild to use them. ** Bootstrap Home Manager clones this repository into =~/.agents= when needed, then runs enabled harness bootstrap scripts during activation. Existing checkouts stay under user control. Run the scripts by hand when testing: #+begin_src sh ~/.agents/harnesses/pi/bootstrap ~/.agents/harnesses/opencode/bootstrap ~/.agents/harnesses/hermes/bootstrap #+end_src Set =PI_CODING_AGENT_DIR=, =OPENCODE_CONFIG_DIR=, =OPENCODE_DIR=, or =HERMES_HOME= to test against another destination. Pi's bootstrap deep-merges the declared =settings.json= into the current runtime file. Declared values win, while unknown application-owned keys survive. The retired =subagents= key is removed. Other named files are refreshed as normal writable copies. Named entries under =harnesses/pi/extensions/=, =harnesses/pi/prompts/=, and =harnesses/pi/themes/= are copied along with shared =prompts/=, without deleting unrelated entries. Run =harnesses/pi/test-bootstrap= for isolated preservation checks. The NixOS Pi wrapper runs =harnesses/pi/fix-dcp-package= before startup. It moves pi-dcp's incorrectly published TypeBox dependency to =peerDependencies= with a =*= range. Corrected releases are left unchanged; extension code and shared npm dependencies are not touched. Pi uses native =mcp.json= for Chrome DevTools, Context7, gh_grep, and MiniMax. Chrome tools are exposed directly; the other servers use native codemode discovery. =defaultTools: ["+codemode"]= keeps ordinary tools available and adds sandboxed scripts for parallel calls and filtering. The old MCP adapter is no longer loaded. Bootstrap removes its retired =mcp-adapter.json=, =mcp-cache.json=, and =mcp-npx-cache.json= files; migration backups remain untouched. Home Manager generates =themes/stylix.json= after Pi's bootstrap from its Stylix palette and the curated theme's existing color roles. Pi selects that fixed theme instead of the terminal-derived system theme. The generated file is a writable runtime copy, not a repository file or store symlink, and bootstrap preserves it. OpenCode's bootstrap refreshes its named config files and named entries under =harnesses/opencode/agents/=, =harnesses/opencode/commands/=, and =harnesses/opencode/themes/=, plus shared =prompts/= as runtime commands. It leaves all other runtime files alone. During migration from the old configuration checkout, it moves =.git= to =~/.local/state/opencode/legacy-config.git= before writing the runtime copy. Run =harnesses/opencode/test-bootstrap= for isolated preservation checks. Hermes deep-merges =config.yaml= into the runtime YAML using Mike Farah's =yq= v4, provided by Nix's =yq-go= package. Repo-declared values win, including whole lists; unrelated keys survive at every nesting level. Runtime-only setup, config, and dashboard settings therefore persist. Bootstrap still refreshes =SOUL.md= as a repo-owned writable copy. Both writes are atomic and skip unchanged writable regular files. Empty runtime configs initialize as mappings. Malformed, scalar, list, or multi-document YAML fails without overwriting it. Credentials, auth, sessions, jobs, memories, databases, profiles, and local skills stay untouched. Run =harnesses/hermes/test-bootstrap= for isolated preservation and repeat-run checks. Hermes defaults to =gpt-6-luna= with high reasoning through the named =custom:codex-lb= provider. Its =providers.codex-lb= entry selects =codex_responses= and reads =CODEX_LB_API_KEY= from the environment. Hermes's Responses transport sends =strict: false= for function tools so optional arguments stay optional. Bare =provider: custom= ignores a Responses override on non-OpenAI URLs, so changing only =model.api_mode= is insufficient. Legacy =model.base_url=, =model.api_key=, and =model.api_mode= fields may remain in the mutable config; the named provider entry controls this route. Auxiliary calls use native routing. Its instructions send substantial task execution and explicit Pi requests to Herdr/Pi with Pi's stronger configured model. Inside Herdr, Hermes uses its inherited session. From an ordinary terminal or dashboard, it selects the running local =default= session explicitly and creates an unfocused workspace. Missing =HERDR_ENV= is not a blocker for this authorized launch path; Hermes never fakes pane context or uses the UI-focused pane. It waits for Pi and reads the result before reporting completion. No automatic jobs, fleet gateway, dashboard, or Desktop service is enabled by this configuration. NixOS retains the former =~/.local/share/hermes= and Desktop state for rollback without importing them into the fresh home. Hermes selects its native Camofox browser backend with managed persistence. On Kit, Home Manager supplies =CAMOFOX_URL= for one =hermes= browser server and persists its browser data. Hermes uses a browser identity derived from =~/.hermes=; retired June/Pax history is not imported. The service uses a virtual display rather than the desktop. Release metadata lives in =~/src/suderman/pins=; NixOS owns the package and compatibility test. Server and engine updates are report-only until the server lockfile and engine pass navigation, click, and storage-reopen checks together. This is a tested baseline, not a permanent version freeze. The package does not refresh npm or fetch the latest engine at runtime. The package isolates the client's browser cache from =~/.cache/camoufox=; persistent browser profiles remain in the service data directory. A successful =/health= request alone does not prove browsing works. Open =https://hermes.camofox.kit/vnc.html?autoconnect=1= to view that display or log in manually after Hermes opens a page. Do not use =/wake= to select a profile for Hermes; the wake helper creates its own viewer identity. Pi/OpenCode keep their Chromium/DevTools setup. Cog has no browser endpoint configured. Hermes uses native =web_search= and =web_extract= with the existing =TAVILY_API_KEY=. Native =vision_analyze= resolves its auxiliary model to =gpt-6-luna= through codex-lb. These tools need no MiniMax MCP or separate vision provider. Search, page extraction, and image analysis have been tested. Image crop bounds are original-image pixel coordinates; omitting =region= reads the whole image. The named Responses route was tested with whole-image region omission and deliberate pixel crops. The former Chat-Completions route omitted the strict-mode flag; its upstream Responses default made the optional crop mandatory and led to zero or 1-pixel bounds. Do not work around that mismatch by inventing image dimensions. On the first run, Pi and OpenCode's bootstraps remove their old harness-local =skills/= entry from active use. Existing files move to =~/.local/state/pi/legacy-skills= or =~/.local/state/opencode/legacy-skills=. Shared skills then come from =~/.agents/skills/= and =~/.agents/vendor/skills/=. All scripts are safe to run repeatedly. They do not delete unknown packages, plugins, extensions, or application state. ** Secrets and managed integrations Secrets stay in agenix and reach each mutable home through Home Manager-generated =.env= files. Do not add those files here. Herdr owns its Pi and OpenCode integration files. Those files belong in the mutable application homes and are deliberately absent from this repository. Reinstalling or updating a Herdr integration must not change =~/.agents=.