Installable oh-my-pi (omp) plugin bundle. It extends an omp agent with context-cost controls, durable memory, dangerous-command guards, and a project skill — all loadable into any omp profile.
| Piece | Path | Loaded by omp via |
|---|---|---|
| Integration extension — rtk/lean-ctx bash rewrite, engram memory auto-save + turn-start retrieval, receipt carriage, GPG/SSH hard-stop guards | plugins/oh-my-pi-integration/extensions/index.ts (+ guards/, receipt/) |
package.json → omp.extensions |
| Universal project rules — harness behavior, tool-routing discipline, strict review standards, docs-and-planning audit, parallel-safe tests, config merge precedence, safe-command guards | plugins/oh-my-pi-integration/rules/ |
~/.omp/agent/rules/ and ~/.omp/rules/ (both; root-level is picked up directly by omp) |
omp-specific universal agent rules (lean-ctx tool-call corrections, receipt contract) — one canonical document; each profile's AGENTS.md is an installer-managed symlink |
AGENTS.md |
agent/AGENTS.md + per-profile symlinks |
| Agent config scaffold (no credentials) | agent/config.yml |
agent/config.yml |
The bundle also ships a .omp-plugin/marketplace.json catalog so the contained
plugin can be installed directly with omp plugin install.
- Bash tool-call rewrite — rewrites
bashtool calls to usertk(output trimming) orlean-ctx -c(compression) so agent tool output costs less context. Simple commands route throughrtkwhen the installed binary exposes a curated safe subcommand; anything else — compounds, pipelines, argv0 wrappers — is compressed vialean-ctx -c(whole command as one argv). Commands already routing through lean-ctx are never re-wrapped (single-wrap invariant), and PTY or async calls are passed through untouched. rtk subcommands are discovered at runtime from the installed binary (rtk --help) — version drift or a missing rtk falls through to lean-ctx. - Engram memory persistence — buffers notable mutations each turn and saves them to engram at turn end and session shutdown, so later sessions can reuse recorded solutions.
- Turn-start retrieval — when enabled, distills the user prompt into a keyword query and injects prior project memories back into the agent loop (best-effort, bounded, never blocking).
- Receipt carriage — when
<project>/.omp/receipt.tomlexists, each turn carries the small TOML job ledger ([[job]]/[[issue]]entries withstatefields) into the agent loop as an invisible footer, and applies chores: bumps the[carriage] ncounter, stamps finished jobs withdone_at, prunes finished jobs after 3 receipts, and drops empty entries — line-oriented, so comments and unknown keys survive. Fail-open throughout; opt out withPI_RECEIPT_DISABLE=1. - GPG & SSH hard-stop guards — when a commit signing or ssh-agent/socket
failure needs a human (locked GPG key, stale SSH agent), substitutes an
imperative hard-stop directive and blocks the agent's usual self-recovery
commands (
gpgconf,gpg-agent,ssh-agentlifecycle, socket reassignment, passphrase bypass). - Destructive-git guard — notifies (never blocks) when a
gitcommand would destroy work (reset --hard,clean -f,push -f,branch -D,stash,checkout --, …) so the agent sees the risk before committing. - Post-edit lint feedback — after a successful
edit/writeof a TS file, runs the project's biome on that file and surfaces diagnostics in the tool result (best-effort, bounded; resolves the repo-local biome fromnode_modules/.bin, falling back to PATH). - Compaction preservation — on
session.compacting, injects the in-flight mutation buffer into the compaction summary so no uncommitted work is lost across a compaction (harness-evasion-guardpre-hook additionally blocks agent attempts to bypass the read-only harness tools in the shell).
All guards live as pure, unit-tested logic in extensions/guards/: they take
command/output strings and return decisions, so behavior is auditable without a
harness.
Rules live in plugins/oh-my-pi-integration/rules/ and are installed to both
<target>.omp/agent/rules/ (agent-scoped, backward-compat) and
<target>.omp/rules/ (root-level, picked up directly by omp). Each rule has YAML
frontmatter:
---
name: <filename-stem>
description: "<one-line purpose>"
condition: ["<regex-with-lookahead-AND-facets>"]
scope: ["text", "thinking"]
---
<imperative steering content>The ttsr engine compiles each condition: array element into an independent
RegExp and triggers when any one matches (OR). To express AND (all facets
must co-appear in the assistant's stream), each rule's condition is a single
regex using lookahead chains:
^(?=[\s\S]*facet1)(?=[\s\S]*facet2)(?=[\s\S]*facet3)...
This fires only when the assistant's streamed text/thinking contains all facets. A rule with a single facet is unchanged.
Always anchor the chain with ^. Without it the zero-width lookahead
regex has no anchor, so on non-matching input .test() re-runs the greedy
[\s\S]* scan at every stream position — O(n²) ReDoS (measured ~2.3 s across
75 rules on an 8 KB non-match vs ~1.2 ms anchored). Anchoring is semantically
identical: each facet's [\s\S]* already scans the whole stream from
position 0. scripts/check-regex-safety.ts (wired into bun run verify)
errors on any unanchored lookahead chain and on unbounded .*.
| Scope | Fires on | Use for |
|---|---|---|
text |
assistant prose | reminders about approach, quality, conventions |
thinking |
assistant internal reasoning | same as text, for thinking blocks |
tool:bash |
bash tool-call composition | preventing specific commands or tool misuse |
tool:edit / tool:write |
edit/write tool calls | file-content rules |
Rules about preventing specific commands (e.g. git status during
implementation) should scope to tool:bash only — scoping to text causes
them to fire during review/discussion where the commands are legitimate.
# install into an isolated omp profile root (default target)
bun scripts/install.ts
# explicit target (PREFIX or --target)
PREFIX=/tmp/omp-test bun scripts/install.ts
bun scripts/install.ts --target /tmp/omp-test
# overwrite an existing install; or preview without writing
bun scripts/install.ts --force
bun scripts/install.ts --dry-run
# sweep installer-parked .bak backups (owned paths only; strays survive)
bun scripts/install.ts --target ~/.omp --live --clean-bak
# update your live profile directly (e.g. AGENTS.md, rules, extensions)
bun scripts/install.ts --target "$HOME/.omp" --liveThe installer lays the payloads into:
TARGET/.omp/agent/—AGENTS.md,config.yml,extensions/,hooks/pre/TARGET/.omp/agent/rules/— universal project rules (agent-scoped, backward-compat)TARGET/.omp/rules/— universal project rules (root-level, picked up directly by omp)TARGET/.omp/profiles/<name>/agent/— per-profile config fromprofiles/<name>/agent/in the repo:config.fragment.ymlis deep-merged overagent/config.yml(the base) into a completeconfig.yml; a shippedconfig.ymlis installed verbatim instead (full override).AGENTS.mdis a symlink to the canonicalagent/AGENTS.md— the universal document ships once with zero per-profile driftTARGET/.omp/plugins/— plugin registry
When TARGET is itself a profile root (e.g. ~/.omp), the bundle is laid down
directly under it without nesting a second .omp. It writes only bundle-owned
files and never touches databases, sessions, caches, or memories. It refuses to
run against your live home profile unless you pass --live (use an isolated
target such as /tmp/omp-test by default), then point a scratch profile at it
with omp --profile test.
Re-runs are safe and manifest-driven: an ownership ledger records every file
the installer wrote. On re-run, installer-owned files are updated in place
(rules added, changed, or renamed synchronize without --force), files that no
longer ship are removed, and files you modified locally (or never installed)
are kept with a notice — never clobbered without --force, which overwrites
them while keeping the previous copy as <dst>.bak. Symlinked destinations are
never followed; every touched path is checked to stay inside TARGET.
| Environment variable | Effect |
|---|---|
PI_INTEGRATION_DISABLE=1 |
Disable the whole integration extension |
PI_INTEGRATION_RETRIEVE=1 |
At turn start, retrieve prior engram memories for the project and inject a bounded context block |
PI_RETRIEVE_EVERY_TURN=1 |
Re-retrieve every turn (default: once per session) |
Requires bun — the omp runtime runs on bun and
@oh-my-pi/pi-coding-agent is consumed as raw TypeScript via its exports map.
bun install # install toolchain (@oh-my-pi/pi-coding-agent, biome, typescript)
bun run verify # lint + typecheck + test| Script | What it runs |
|---|---|
verify |
lint → typecheck → test → check:rules → check:ship |
lint / lint:fix |
Biome check, then the console-log gate (scripts/check-no-console.ts) — lint:fix also applies safe fixes + import sorting |
typecheck |
tsc --noEmit over plugins/**/*.ts and scripts/**/*.ts |
test |
bun test — guard/hook tests in plugins/**/__tests__/ and installer/checker tests in scripts/__tests__/ |
check:rules |
scripts/check-rules-sync.ts — validates every rule's frontmatter (name == filename, description/condition non-empty, valid scope) and syncs against the installer laydown |
check:ship |
scripts/check-shipment.ts — installs into a temp target and asserts no __tests__/ dirs ship, no .bak/.original files, and only index.ts at top of agent/extensions/ |
install:test |
bun scripts/install.ts --target /tmp/omp-test |
check:coverage |
scripts/check-coverage.ts — runs the suite under lcov and fails when global line coverage drops below the pinned ratchet (93% at landing; target 100%) |
check:rules |
scripts/check-rules-sync.ts — validates every rule's frontmatter (name == filename, description/condition non-empty, valid scope) and syncs against the installer laydown |
| Linting, types, tests, and the rules bundle are all exercised together by | |
bun run verify, which is the standard pre-commit / CI gate for this |
|
| repository. |
Note on Biome and regex guards.
noUselessStringRawis disabled inbiome.jsonbecause the guard modules build regexes fromString.rawliterals that contain regex escapes (\s,\d). Biome's rule only checks JavaScript escape sequences (\n,\t,\\, …) and would propose removingString.rawfrom literals whose backslash-escapes are meaningful to the regex — silently changing\sintos. Keeping the rule on would invite a corrupting fix on security-critical enforcement code.
├── .omp-plugin/marketplace.json catalog for `omp plugin install`
├── AGENTS.md universal agent rules (installed to <target>.omp/agent/AGENTS.md;
│ profiles get installer-managed symlinks to it)
├── agent/config.yml agent config scaffold (credential-free)
├── plugins/oh-my-pi-integration/ the plugin package (extensions/hooks/rules)
│ └── rules/ universal project rules (installed to both
│ <target>.omp/agent/rules/ and <target>.omp/rules/)
├── profiles/ per-profile scaffolds (installed to <target>.omp/profiles/<name>/agent/)
│ ├── glm/agent/config.fragment.yml zai GLM-5.3 profile fragment (deep-merged over the base)
│ └── minimax/agent/
│ └── config.fragment.yml MiniMax profile fragment (deep-merged over the base)
├── scripts/install.ts installer for an isolated omp profile
├── biome.json lint/format config
├── tsconfig.json TS config (moduleResolution: bundler)
├── package.json / bun.lock bun dev tooling (typecheck, lint, test)
└── LICENSE
OMP supports multiple named profiles under ~/.omp/profiles/. Each profile has its own
agent/config.yml (model, provider, memory backend); agent/AGENTS.md is universal —
the installer symlinks every profile to the canonical ~/.omp/agent/AGENTS.md, so the
same document loads once under any profile. Profile settings override the base
~/.omp/agent/ defaults.
The bundle ships a profiles/ directory in the repo; the installer scaffolds any
profile it finds there (currently minimax and glm). Profile configs are
developed as fragments: profiles/<name>/agent/config.fragment.yml holds
only the per-profile deltas (model roles, theme, provider quirks, compaction),
and the installer deep-merges each fragment over agent/config.yml (the base)
to serve a complete config.yml into the live profile. Map keys merge
recursively; lists replace wholesale; scalars override. Shipping a
config.yml in the profile dir instead bypasses assembly entirely (full
override). To add a new profile:
- Create
profiles/<name>/agent/config.fragment.ymlin the repo — model roles and any per-profile overrides (or a fullconfig.ymlto opt out of the base merge). - Optionally add
profiles/<name>/agent/config.ymlfor a full config override (agent rules are universal — the installer links the profile to the canonicalAGENTS.mdautomatically). - Bootstrap it with
omp --profile <name> -p ""(omp creates~/.omp/profiles/<name>/agent/on first invocation), then runbun scripts/install.ts --target ~/.omp --live— the profile is scaffolded automatically.
Switch profiles at runtime:
omp --profile glm # zai GLM-5.3
omp --profile minimax # MiniMax-M3
omp --profile default # base profileAGPL-3.0 — see LICENSE for the full text. (The plugin manifest
in plugins/oh-my-pi-integration/package.json declares AGPL-3.0-or-later.)