From omo (oh-my-openagent) — ported to pi-mono's coding-agent extension API.
A pi-mono coding-agent extension that automatically injects nearby AGENTS.md files into the agent's context whenever it reads a file in a nested directory. When the agent reads src/components/Button.tsx, this extension finds src/AGENTS.md and src/components/AGENTS.md, attaches their contents to the read tool result, and the agent gets the surrounding directory's rules without you having to paste them yourself.
The pi coding-agent already loads the project root's AGENTS.md (and the chain of ancestor AGENTS.md files above the project root) at session start. This extension fills the missing piece: nested AGENTS.md files inside subdirectories of your project. That mirrors how omo's directory-agents-injector injects nested context into opencode.
Real codebases are layered. Your repo's root AGENTS.md describes house rules. src/AGENTS.md describes architectural patterns for application code. src/components/AGENTS.md describes component-author conventions. src/api/AGENTS.md describes route-handler conventions. The agent should pick up the closest rules to the file it is touching, automatically — exactly how a human team member learns the local conventions of the folder they're editing.
- Walk-up discovery — when the agent reads a file, this extension walks from the file's directory up toward the project root, finding
AGENTS.mdfiles at each ancestor. - Skips the root — pi-mono already loads the root
AGENTS.mdnatively; this extension never duplicates it. - Outermost-first ordering — when multiple ancestors have an
AGENTS.md, they are appended in the order from highest ancestor down to the file's parent, matching how a reader would naturally encounter them. - Per-session deduplication — the same
AGENTS.mdis not appended twice in a session. - Cache reset on
session_compact/session_shutdown— after compaction or session restart, files become eligible for re-injection. - Realpath-based root containment — symlinks that escape the project root are rejected; sibling roots that share a name prefix (
repovsrepo-evil) are correctly distinguished. - Code-point-safe truncation — large
AGENTS.mdfiles are truncated at a UTF-8 byte boundary that never splits a code point. Defaults: 32 KiB per file, 128 KiB total per read. - Tool-result safety — never mutates non-
readresults; never touchesisErrorresults; never touches non-text content; preserves all original content blocks as a prefix of the patched content. - TUI status line, optional widget, slash command — see TUI features.
--no-nested-agentsflag — fully bypass the extension when you want pi's default behavior only.
This extension follows pi-mono's standard discovery and packaging conventions documented in pi-mono/packages/coding-agent/docs/extensions.md.
Clone (or git submodule add) into pi's global extensions directory:
mkdir -p ~/.pi/agent/extensions
git clone https://github.com/code-yeongyu/pi-nested-agents-md ~/.pi/agent/extensions/pi-nested-agents-md
cd ~/.pi/agent/extensions/pi-nested-agents-md
npm install --omit=devpi will auto-discover the extension via the pi.extensions field in package.json and load it for every session in every project.
Drop it inside your project's .pi/extensions/:
mkdir -p .pi/extensions
git clone https://github.com/code-yeongyu/pi-nested-agents-md .pi/extensions/pi-nested-agents-md
cd .pi/extensions/pi-nested-agents-md
npm install --omit=devProject-local extensions only activate when pi is launched from this project's working directory.
For trying it out without installing globally:
pi -e /absolute/path/to/pi-nested-agents-md/src/index.tsIf you prefer pi's package manager:
pi install git:github.com/code-yeongyu/pi-nested-agents-mdThis adds the extension to pi's settings.json packages list, and pi update keeps it current.
Once installed, the extension activates automatically. There is no configuration step.
# Just run pi normally:
piNow whenever pi's read tool fires inside a directory whose ancestors contain an AGENTS.md, the file's contents will be appended to the read result for the agent to see. Pi-mono already loads the root AGENTS.md at session start — this extension only adds the nested ones above the file being read.
/nested-agents
Toggles the optional widget that lists every AGENTS.md injected so far this session, with truncation marks. The command also writes a debug entry containing the live cache state via pi.appendEntry, so it survives session export.
pi --no-nested-agentsDisables the extension for the session. No injection, no UI elements. Useful when you want strict default-pi behavior, or for A/B comparing prompt sizes.
Walk-up discovery (per read tool result):
/Users/you/repo/ <-- ctx.cwd (root AGENTS.md handled by pi natively, SKIPPED here)
├── AGENTS.md (skipped)
├── src/
│ ├── AGENTS.md <-- ✓ injected (1st)
│ ├── components/
│ │ ├── AGENTS.md <-- ✓ injected (2nd)
│ │ └── Button.tsx <-- agent calls read
│ └── ...
└── ...
For each ancestor directory between the read file's parent and the project root (exclusive), the extension looks for AGENTS.md. Each unique directory is injected at most once per session. After a session_compact or session_shutdown, the cache is cleared so subsequent reads pick the file up again.
The injected text uses the same omo-compatible block format:
[Directory Context: /abs/path/AGENTS.md]
<file contents>
When a file exceeds the byte cap, a truncation notice is appended:
[Note: Content was truncated to save context window space. For full context, please read the file directly: /abs/path/AGENTS.md]
| Element | Where | When | Format |
|---|---|---|---|
| Status line | Footer (key ext:nested-agents:status) |
Updated after every injection | 🤖 N (dim), with a trailing ⚠️ (warning) when an AGENTS.md failed to read |
| Widget | Above editor (key ext:nested-agents:widget) |
Hidden by default; shown after /nested-agents toggle |
Nested Context: header (accent), one line per file (dim), truncated files highlighted (warning) |
| Notification | Standard | After /nested-agents toggle |
"Nested AGENTS.md context widget shown/hidden" |
| Debug entry | Session log | After /nested-agents runs |
Custom entry of type nested-agents-md:debug with absolute paths, cache size, truncation flags |
In headless / RPC / pi -p print mode (ctx.hasUI === false), all UI calls are silently skipped. Injection still occurs — the agent gets its nested context regardless of UI presence.
| Knob | Default | Override |
|---|---|---|
| File names searched | AGENTS.md only (strict omo parity) |
Edit DEFAULT_FILE_NAMES in src/core/types.ts to add CLAUDE.md |
Max bytes per AGENTS.md file |
32 KiB | Pass config.maxBytesPerFile when invoking the orchestrator directly |
| Max bytes per read tool result | 128 KiB total budget across all injected files | Pass config.maxBytesPerRead |
| Status line key | ext:nested-agents:status |
Edit STATUS_KEY in src/ui/reporter.ts |
| Widget key | ext:nested-agents:widget |
Edit WIDGET_KEY in src/ui/reporter.ts |
| Slash command | /nested-agents |
Edit COMMAND_TOGGLE in src/index.ts |
src/
├── index.ts # pi adapter — registers hooks, command, flag
├── core/ # pure modules, no pi imports
│ ├── types.ts # shared types + defaults
│ ├── find-agents-md-up.ts # outermost-first walk-up discovery
│ ├── containment.ts # realpath-based root containment
│ ├── injection-cache.ts # per-session dedup
│ ├── truncate.ts # code-point-safe UTF-8 byte truncation
│ ├── format.ts # omo-exact directory-context block
│ ├── session-key.ts # session identity for cache scoping
│ └── inject-directory-context.ts # orchestrator
└── ui/
└── reporter.ts # setStatus, setWidget, theme, hasUI guards
The pi adapter (src/index.ts) only wires events and pushes results to the UI reporter. All algorithmic decisions (walk, dedup, truncate, format) are pure functions in src/core/* testable without the pi runtime.
npm install # installs vitest, typescript, pi types
npm test # runs vitest --run (62 tests)
npm run check # tsc --noEmit
npm run test:watch # vitest watch modeThe test suite includes:
- Unit tests for each pure module (finder, containment, cache, truncate, format).
- Orchestrator tests for
injectDirectoryContextcovering happy path, symlink escape, sibling-root prefix bug, dedup, EACCES, truncation, deep nesting. - Tool-result middleware tests that simulate pi-mono's chained
tool_resulthandler model and prove our handler appends to the latest content (not the original event content). - Lifecycle integration tests that drive the full extension via a fake
piAPI harness — proving cache reset onsession_compact/session_shutdown, multi-session isolation, and the original-content-as-prefix invariant. - TUI integration tests asserting status/widget calls under
hasUI=true/false, the--no-nested-agentsinert mode, slash-command toggle on/off, themed line rendering, the truncation warning line, and the debug entry shape.
Tests use given/when/then comments per the project AGENTS.md convention.
This extension is a faithful port of omo's directory-agents-injector hook from the opencode runtime to the pi-mono coding-agent runtime. The key design choices preserved from omo:
- Hook on the
readtool's result event (not on session start), so the agent only spends context on directories it actually visits. - Walk UP from the read file's directory to the project root.
- Skip the root file because the host runtime already loads it natively (opencode in omo, pi in this port).
- Per-session in-memory cache, cleared on compaction.
[Directory Context: <path>]\n<content>block format, omo-exact.
The pi-mono port adds:
- A pi-native TUI surface (status line, optional widget, slash command, flag) using
ctx.ui.setStatus,ctx.ui.setWidget,ctx.ui.theme,pi.registerCommand,pi.registerFlag,pi.appendEntry. - Realpath-based root containment that defends against symlink escapes and prefix-overlap roots (e.g.
repovsrepo-evil) — issues that omo's lexicalstartsWithcheck did not address. - A pure orchestrator decoupled from the pi runtime, so the algorithm is testable without booting pi.
MIT © YeonGyu Kim
Heavily inspired by, and grateful to, omo (oh-my-openagent). The pi-mono coding-agent and its extension API are by Mario Zechner — see pi-mono.