@tigorhutasuhut/pi-rules 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tigor Hutasuhut
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# pi-rules
|
|
2
|
+
|
|
3
|
+
Claude-compatible, file-scoped project rules for [Pi](https://pi.dev/) agents and subagents.
|
|
4
|
+
|
|
5
|
+
Use Markdown files to give Pi durable instructions for an entire repository or only for matching files. Existing `.claude/rules/` and `.agents/rules/` directories can work without duplication.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pi install npm:@tigorhutasuhut/pi-rules
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For one project only:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pi install -l npm:@tigorhutasuhut/pi-rules
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Already running Pi? Use `/reload` after installation.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
Create `.pi/rules/project.md` in your repository:
|
|
24
|
+
|
|
25
|
+
```md
|
|
26
|
+
Run focused tests after changing production code.
|
|
27
|
+
Never commit secrets or generated build output.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Start or reload Pi. Rules without `paths` frontmatter load before the model's first call:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
Loaded rules:
|
|
34
|
+
↳ .pi/rules/project.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Scope a rule to files
|
|
38
|
+
|
|
39
|
+
Add `paths` frontmatter when guidance only applies to part of the repository:
|
|
40
|
+
|
|
41
|
+
```md
|
|
42
|
+
---
|
|
43
|
+
paths:
|
|
44
|
+
- "src/**/*.ts"
|
|
45
|
+
- "test/**/*.ts"
|
|
46
|
+
---
|
|
47
|
+
Use strict TypeScript.
|
|
48
|
+
Keep tests close to the behavior they verify.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Path-scoped rules activate after Pi successfully reads, edits, or writes a matching project file. Globs are repository-relative, use `/` separators, and match dotfiles.
|
|
52
|
+
|
|
53
|
+
`paths` accepts one glob string or a list:
|
|
54
|
+
|
|
55
|
+
```md
|
|
56
|
+
---
|
|
57
|
+
paths: "docs/**/*.md"
|
|
58
|
+
---
|
|
59
|
+
Use concise headings and relative links.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Only `paths` frontmatter is supported. Markdown without frontmatter is unconditional.
|
|
63
|
+
|
|
64
|
+
## Rule discovery
|
|
65
|
+
|
|
66
|
+
Rules are read recursively from the **first non-empty directory** in this order:
|
|
67
|
+
|
|
68
|
+
1. `<repo>/.pi/rules/`
|
|
69
|
+
2. `<repo>/.agents/rules/`
|
|
70
|
+
3. `<repo>/.claude/rules/`
|
|
71
|
+
4. `${PI_CODING_AGENT_DIR}/rules/`, or `${PI_CONFIG_DIR}/agent/rules/` when unset
|
|
72
|
+
5. `~/.agents/rules/`
|
|
73
|
+
6. `${CLAUDE_CONFIG_DIR}/rules/`, or `~/.claude/rules/` when unset
|
|
74
|
+
|
|
75
|
+
Default Pi user rules live in `~/.pi/agent/rules/`.
|
|
76
|
+
|
|
77
|
+
Sources are fallbacks, not merged layers. For example, when `.pi/rules/` contains any Markdown file, `.agents/rules/`, `.claude/rules/`, and user sources are ignored. Files inside the selected directory are sorted and loaded together. Symlinks are ignored.
|
|
78
|
+
|
|
79
|
+
`PI_CODING_AGENT_DIR` points directly to Pi's agent directory. When unset, the directory resolves to `${PI_CONFIG_DIR:-~/.pi}/agent`.
|
|
80
|
+
|
|
81
|
+
## Configure source priority
|
|
82
|
+
|
|
83
|
+
Configuration can live at either level:
|
|
84
|
+
|
|
85
|
+
| Level | Path |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Repository | `<repo>/.pi/rules.json` |
|
|
88
|
+
| User | `${PI_CODING_AGENT_DIR}/rules.json`, or `${PI_CONFIG_DIR:-~/.pi}/agent/rules.json` when unset |
|
|
89
|
+
|
|
90
|
+
Repository configuration wins whenever `<repo>/.pi/rules.json` exists. Configurations are not merged, and an invalid repository configuration does not fall back to user configuration.
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"enabled": true,
|
|
95
|
+
"sources": [
|
|
96
|
+
{ "scope": "repo", "kind": "pi" },
|
|
97
|
+
{ "scope": "repo", "kind": "agents" },
|
|
98
|
+
{ "scope": "repo", "kind": "claude" },
|
|
99
|
+
{ "scope": "user", "kind": "pi" },
|
|
100
|
+
{ "scope": "user", "kind": "agents" },
|
|
101
|
+
{ "scope": "user", "kind": "claude" }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Array order controls precedence. Remove a source to disable that fallback. Use an empty array to disable all rule discovery:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{ "sources": [] }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Supported values:
|
|
113
|
+
|
|
114
|
+
| Field | Values |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| `scope` | `repo`, `user` |
|
|
117
|
+
| `kind` | `pi`, `agents`, `claude` |
|
|
118
|
+
|
|
119
|
+
`enabled` defaults to `true`. When `false`, rule and nudge injections are skipped. Unknown configuration fields are allowed. Invalid JSON or invalid configuration values emit a warning, inject no rules, and do not crash Pi. File configuration reloads at session start and after compaction.
|
|
120
|
+
|
|
121
|
+
## Commit nudges
|
|
122
|
+
|
|
123
|
+
Commit nudges are opt-in. Enable them in project or user `rules.json`:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"sources": [
|
|
128
|
+
{ "scope": "repo", "kind": "pi" },
|
|
129
|
+
{ "scope": "repo", "kind": "agents" },
|
|
130
|
+
{ "scope": "repo", "kind": "claude" },
|
|
131
|
+
{ "scope": "user", "kind": "pi" },
|
|
132
|
+
{ "scope": "user", "kind": "agents" },
|
|
133
|
+
{ "scope": "user", "kind": "claude" }
|
|
134
|
+
],
|
|
135
|
+
"nudges": {
|
|
136
|
+
"afterCommit": true
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
After a successful direct `git commit` or `rtk git commit` command settles, Pi reviews the committed work for a durable repository-specific convention. It creates or updates the smallest appropriate rule only when one is warranted. Nudge-created changes are never committed or pushed automatically.
|
|
142
|
+
|
|
143
|
+
Nudges are disabled by default. `pi-rules` does not infer session roles.
|
|
144
|
+
|
|
145
|
+
## Runtime configuration
|
|
146
|
+
|
|
147
|
+
Other extensions can shallow-merge any configuration field into the current runtime through `pi-rules:config`:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { emitPiRulesConfig } from "@tigorhutasuhut/pi-rules";
|
|
151
|
+
|
|
152
|
+
pi.on("session_start", () => {
|
|
153
|
+
emitPiRulesConfig(pi, { enabled: false });
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Partial patches are accepted. Top-level fields use shallow-copy semantics, so a supplied `nudges` object replaces the previous `nudges` object:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
emitPiRulesConfig(pi, {
|
|
161
|
+
sources: [{ scope: "repo", kind: "claude" }],
|
|
162
|
+
nudges: { afterCommit: false }
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The event bus is already scoped to the current Pi runtime, so no `ExtensionContext` needs to cross extension boundaries. Raw emitters may call `pi.events.emit("pi-rules:config", patch)`. Updates that fail the TypeBox configuration schema are rejected and shown through Pi's warning notification, or stderr when no UI exists. Patches apply immediately; emitters own event timing.
|
|
167
|
+
|
|
168
|
+
## Runtime behavior
|
|
169
|
+
|
|
170
|
+
- Unconditional rules inject once per compaction epoch before the first model call.
|
|
171
|
+
- Path-scoped rules inject once per compaction epoch after a successful matching `read`, `edit`, or `write`.
|
|
172
|
+
- Rule files are rediscovered at injection boundaries and turn end, so new rules and edits to not-yet-loaded rules reach the next eligible injection. Already-loaded rules refresh after compaction.
|
|
173
|
+
- Parallel path matches are combined into one message.
|
|
174
|
+
- Tool results are never modified.
|
|
175
|
+
- Collapsed messages list loaded rule sources; expanded messages show matched targets and full rule bodies.
|
|
176
|
+
- Compaction resets activation and immediately restores unconditional rules.
|
|
177
|
+
- Each loaded session maintains independent rule activation and nudge state.
|
|
178
|
+
- Opt-in commit nudges wait for `agent_settled`, avoiding interruption of remaining tools or queued follow-ups.
|
|
179
|
+
|
|
180
|
+
Package inheritance and extension allowlists determine which Pi sessions load these rules.
|
|
181
|
+
|
|
182
|
+
## Choosing between rules, AGENTS.md, and skills
|
|
183
|
+
|
|
184
|
+
- Use **rules** for always-on or file-scoped repository constraints.
|
|
185
|
+
- Use **`AGENTS.md`** for broad project and directory context Pi should discover natively.
|
|
186
|
+
- Use **skills** for guidance loaded by task intent rather than file access.
|
|
187
|
+
|
|
188
|
+
## Troubleshooting
|
|
189
|
+
|
|
190
|
+
### Rules do not load
|
|
191
|
+
|
|
192
|
+
1. Confirm package is enabled with `pi list` or `pi config`.
|
|
193
|
+
2. Run `/reload` after installation or configuration changes.
|
|
194
|
+
3. Check whether an earlier non-empty source directory is winning.
|
|
195
|
+
4. Read stderr for `[pi-rules] invalid config` or `[pi-rules] skipped` warnings.
|
|
196
|
+
|
|
197
|
+
### Path rule does not activate
|
|
198
|
+
|
|
199
|
+
- Make glob repository-relative: `src/**/*.ts`, not an absolute path.
|
|
200
|
+
- Confirm Pi successfully used `read`, `edit`, or `write` on matching file.
|
|
201
|
+
- Files outside current repository do not activate project rules.
|
|
202
|
+
- Rule already loaded in current compaction epoch will not load again until compaction.
|
|
203
|
+
|
|
204
|
+
### Claude rules are ignored
|
|
205
|
+
|
|
206
|
+
An earlier `.pi/rules/` or `.agents/rules/` directory contains Markdown. Remove it or change source priority in `.pi/rules.json`.
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
Requires Node.js 20.6 or newer.
|
|
211
|
+
|
|
212
|
+
```sh
|
|
213
|
+
npm install
|
|
214
|
+
npm run check
|
|
215
|
+
npm pack --dry-run
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Tests use Node's built-in `node:test` runner.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
[MIT](LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tigorhutasuhut/pi-rules",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Claude-compatible project rules for Pi agents and subagents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
21
|
+
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"test": "npm run build && node --test test/*.test.js",
|
|
24
|
+
"check": "npm run typecheck && npm test",
|
|
25
|
+
"prepack": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"picomatch": "4.0.5",
|
|
29
|
+
"yaml": "2.9.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@earendil-works/pi-coding-agent": "0.81.1",
|
|
33
|
+
"@earendil-works/pi-tui": "0.81.1",
|
|
34
|
+
"@types/node": "24.3.0",
|
|
35
|
+
"@types/picomatch": "4.0.2",
|
|
36
|
+
"typebox": "1.1.38",
|
|
37
|
+
"typescript": "5.7.3"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
41
|
+
"@earendil-works/pi-tui": "*",
|
|
42
|
+
"typebox": "*"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.6.0"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"pi-package",
|
|
49
|
+
"pi",
|
|
50
|
+
"extension",
|
|
51
|
+
"rules",
|
|
52
|
+
"claude-code"
|
|
53
|
+
],
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/tigorlazuardi/pi-rules.git"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/tigorlazuardi/pi-rules#readme",
|
|
60
|
+
"bugs": "https://github.com/tigorlazuardi/pi-rules/issues",
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public",
|
|
63
|
+
"registry": "https://registry.npmjs.org"
|
|
64
|
+
},
|
|
65
|
+
"pi": {
|
|
66
|
+
"extensions": [
|
|
67
|
+
"./dist/index.js"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|