Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,50 @@ jobs:
node: ["22", "24"]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Node ${{ matrix.node }}
uses: actions/setup-node@v6
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
cache: npm

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2

- name: Install dependencies
run: npm ci
run: bun install --frozen-lockfile

- name: Check (tsgo + biome)
run: npm run check
run: bun run check

- name: Unit tests
run: npm test
run: bun run test

- name: Integration tests
run: npm run test:integration
run: bun run test:integration

- name: Package smoke
run: npm pack --dry-run

npm-consumer:
name: npm-consumer
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node 22
uses: actions/setup-node@v7
with:
node-version: "22"
cache: npm

- name: Install with npm
run: npm ci

- name: Test
run: npm test
22 changes: 12 additions & 10 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,31 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Node 22
uses: actions/setup-node@v6
uses: actions/setup-node@v7
with:
node-version: "22"
registry-url: https://registry.npmjs.org
cache: npm

- name: Install dependencies
run: npm ci
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2

- name: Type check
run: npm run typecheck
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Lint
run: npm run lint
- name: Check (tsgo + biome)
run: bun run check

- name: Unit tests
run: npm test
run: bun run test

- name: Integration tests
run: npm run test:integration
run: bun run test:integration

- name: Package smoke
run: npm pack --dry-run
Expand Down
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ Per-rule body cap: 12,000 chars. Total injected per tool result: 40,000 chars. C
## Release flow

1. Update `CHANGELOG.md` `[Unreleased]` section.
2. `npm version patch|minor|major`
2. Bump `package.json` version (`patch` for deps-only, `minor` for features).
3. `git push origin main --follow-tags`
4. Create GitHub Release.
4. Create GitHub Release (`gh release create v<version>`).
5. CI auto-publishes to npm via OIDC trusted publishing.

Dev toolchain is Bun 1.4.2 (`bun install`, `bun run check`, `bun run test`). Keep `package-lock.json` so npm consumers can `npm ci`.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.0] - 2026-09-24

### Added

- Native `.pi/rules/` and `~/.pi/rules/` sources, matching the existing project/user-home directory pattern (fixes #11).
- Monorepo / Cargo-workspace support for dynamic rule discovery: the per-target project
root is widened to the enclosing git repository root (`widenToRepositoryRoot`). Target
files inside workspace members (nested `Cargo.toml`/`package.json` markers) now discover
`.github/instructions/` rule directories from the workspace and repository levels as
well; each rule's glob semantics stay keyed to the directory owning the rule file via the
scopeRelative path base. Static (always-on) discovery is unchanged.
scopeRelative path base. Static (always-on) discovery is unchanged. Thanks @tradem (#31).
- Native context dedup in the `tool_result` path: single-file rules (AGENTS.md/CLAUDE.md)
that pi already loaded natively into the system prompt are no longer re-injected per
matching file read when dynamic discovery walks to the repository root.

- Matcher cache reset and stats helpers for deterministic cache verification.

### Changed

- Dev and CI toolchain is Bun 1.4.2 (`bun install --frozen-lockfile`, `bun run check`, `bun run test`) with an `npm-consumer` job (`npm ci && npm test`). Actions use `actions/checkout@v7`, `actions/setup-node@v7`, and `oven-sh/setup-bun@v2`.
- Peer dependencies are `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` at `*` (never `^0.87`). Tests pin those packages at `0.87.1`. `typebox` is a devDependency, not a peer.
- Exact devDependency pins: `@biomejs/biome` 2.5.14, `vitest` 5.0.1, `typescript` 7.0.2, `@types/node` 26.6.2, `@typescript/native-preview` 7.0.0-dev.20260707.2.
- `picomatch` 4.0.7 (from 4.0.5 resolved).
- Glob matching now reuses a bounded compiled matcher cache instead of recompiling picomatch patterns for every file.
- Dynamic rule loading now deduplicates repeated target paths and rule-file parsing work.

### Fixed

- Documented `PI_RULES_DISABLED`, `PI_RULES_MAX_RULE_CHARS`, and `PI_RULES_MAX_RESULT_CHARS` environment variables are now read at extension registration; previously nothing in `src/` consulted `process.env`, so setting them had no effect.
- `findProjectRoot` no longer loops forever when the target path is on a different Windows drive than the process cwd (or on a UNC share): the walk now stops when `dirname()` stops progressing instead of only at `resolve("/")`. The infinite synchronous loop froze the host agent's event loop at 100% CPU on every `read`/`edit`/`write` tool result for such paths.
- `findProjectRoot` no longer loops forever when the target path is on a different Windows drive than the process cwd (or on a UNC share): the walk now stops when `dirname()` stops progressing instead of only at `resolve("/")`. Thanks @lavi-kj (#19).
- Dynamic rule injection now dedupes by rule across the session instead of per tool call, preventing repeated nested `AGENTS.md`/`CLAUDE.md` instruction blocks on subsequent reads.
- Dynamic injection now skips rules already injected statically or already loaded by pi's native context loader.
- Dynamic rule loading now preserves each target file's project root so nested projects load their nearest rules correctly.
Expand Down
12 changes: 7 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ Keep changes small, targeted, and tested.
Before opening a PR:

```bash
npm install
npm run check # tsc --noEmit && biome check
npm test # 229 unit tests
npm run test:integration # 43 integration tests
bun install
bun run check # tsgo --noEmit && biome check
bun run test # unit tests
bun run test:integration # integration tests
npm pack --dry-run # release sanity
```

`npm ci && npm test` remains the consumer smoke (extension hosts install with npm).

If you change rule discovery, precedence, injection format, or TUI behavior, also update `README.md` and add tests.

Tests follow `#given X #when Y #then Z` naming with `// given / // when / // then` body comments.

NO `any` in production code. Use `unknown` and narrowing.

This package is a pi coding-agent extension. Behavior that belongs in pi core (`@mariozechner/pi-coding-agent`) should be proposed there instead.
This package is a pi coding-agent extension. Behavior that belongs in pi core (`@earendil-works/pi-coding-agent`) should be proposed there instead.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![ci](https://github.com/code-yeongyu/pi-rules/actions/workflows/ci.yml/badge.svg)](https://github.com/code-yeongyu/pi-rules/actions/workflows/ci.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![npm](https://img.shields.io/badge/npm-%40code--yeongyu%2Fpi--rules-red)](https://www.npmjs.com/package/@code-yeongyu/pi-rules)

Rule context loader for the [pi coding agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent). Discovers rule files from `.omo/rules/`, `.claude/rules/`, `.cursor/rules/`, `.github/instructions/`, `AGENTS.md`, `CLAUDE.md`, and injects them into the agent context.
Rule context loader for the [pi coding agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent). Discovers rule files from `.pi/rules/`, `.omo/rules/`, `.claude/rules/`, `.cursor/rules/`, `.github/instructions/`, `AGENTS.md`, `CLAUDE.md`, and injects them into the agent context.

## Origin

Expand Down Expand Up @@ -56,6 +56,7 @@ After installation, restart pi (or run `/reload` inside an interactive session).

| Directory | Style |
|-----------|-------|
| `.pi/rules/` | pi-native |
| `.omo/rules/` | omo style |
| `.claude/rules/` | Claude Code style |
| `.cursor/rules/` | Cursor style |
Expand Down Expand Up @@ -89,6 +90,7 @@ These use **first-match-wins** at the project root: `AGENTS.md` takes priority o

| Path | Type |
|------|------|
| `~/.pi/rules/` | directory |
| `~/.omo/rules/` | directory |
| `~/.opencode/rules/` | directory |
| `~/.claude/rules/` | directory |
Expand Down Expand Up @@ -128,7 +130,7 @@ Rules are ordered deterministically before injection:

1. **Local before global** — project rules outrank user-home rules.
2. **Closest distance first** — rules from directories nearer to the target file take priority.
3. **Source priority** — `.omo/rules` > `.claude/rules` > `.cursor/rules` > `.github/instructions` > `AGENTS.md` > `CLAUDE.md` > `CONTEXT.md` > user-home variants.
3. **Source priority** — `.pi/rules` > `.omo/rules` > `.claude/rules` > `.cursor/rules` > `.github/instructions` > `AGENTS.md` > `CLAUDE.md` > `CONTEXT.md` > user-home variants.
4. **Lexicographic `relativePath`** — final tiebreaker for same-source, same-distance rules.

Deduplication is in-memory per session by `realPath + content hash`. No filesystem persistence.
Expand Down Expand Up @@ -178,7 +180,7 @@ Rule files are prompt and context input. Do NOT load untrusted repositories. All

| Symptom | Fix |
|---------|-----|
| No rules loaded | Verify `.omo/rules/`, `AGENTS.md`, etc. exist in the project root or ancestors. Run `/rules` to inspect. |
| No rules loaded | Verify `.pi/rules/`, `.omo/rules/`, `AGENTS.md`, etc. exist in the project root or ancestors. Run `/rules` to inspect. |
| Rule not matching | Check frontmatter `globs` / `paths` / `applyTo`. Confirm the target file path matches the glob. |
| Duplicate injection | Automatically deduplicated per session. Try `/reload-rules` to reset. |
| Extension not loaded | Confirm `pi.extensions` in your `package.json` or use `pi -e ./src/index.ts` for one-shot. |
Expand All @@ -190,11 +192,12 @@ Rule files are prompt and context input. Do NOT load untrusted repositories. All
```bash
git clone https://github.com/code-yeongyu/pi-rules
cd pi-rules
npm install # install dev + peer deps
npm test # 229 unit tests
npm run test:integration # 43 integration tests
npm run typecheck # tsc --noEmit
npm run check # tsc + biome
bun install # install dev + peer deps (Bun 1.4.2)
bun run test # unit tests
bun run test:integration
bun run check # tsgo + biome
# npm consumers:
npm ci && npm test
pi -e ./src/index.ts # smoke-test inside a real pi session
```

Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"preset": "recommended",
"style": {
"noNonNullAssertion": "error",
"useImportType": "error",
Expand Down
Loading
Loading