agency-rules 0.0.0-stage → 0.3.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,77 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.2](https://github.com/deuriib/agency-rules/compare/v0.3.1...v0.3.2) (2026-10-31)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **ci:** completely remove NODE_AUTH_TOKEN to enforce OIDC ([59b966d](https://github.com/deuriib/agency-rules/commit/59b966dbdf29502b127f5b271de42f9ca56687f9))
14
+ * **ci:** completely remove NODE_AUTH_TOKEN to enforce OIDC ([a8229a7](https://github.com/deuriib/agency-rules/commit/a8229a7fc912f26b34dec9a2735a659ce63d12a9))
15
+
16
+ ## [0.3.1](https://github.com/deuriib/agency-rules/compare/v0.3.0...v0.3.1) (2026-10-31)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **ci:** resolve npm E404 by enforcing OIDC trusted publishing ([4266f4a](https://github.com/deuriib/agency-rules/commit/4266f4afd6a933c1835973e52ce09b4e620221a4))
22
+
23
+ ## [0.3.0](https://github.com/deuriib/agency-rules/compare/v0.2.0...v0.3.0) (2026-10-31)
24
+
25
+
26
+ ### Features
27
+
28
+ * **skills:** add writing-rules skill for rule authoring ([41b99d7](https://github.com/deuriib/agency-rules/commit/41b99d7416fa52463303fc150e237fbc26ba90e1))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **ci:** correct labels config file extension ([51617c6](https://github.com/deuriib/agency-rules/commit/51617c68abcf95b13040b1cfeadcbf52013ff9d4))
34
+ * **ci:** use node 24 for npm trusted publishing ([cf49055](https://github.com/deuriib/agency-rules/commit/cf4905539693f5a1d38ed902abc5a5c7c32d1635))
35
+
36
+ ## [0.2.0](https://github.com/deuriib/agency-rules/compare/v0.1.0...v0.2.0) (2026-10-31)
37
+
38
+
39
+ ### Features
40
+
41
+ * bootstrap agency-rules with mise and four harness renderers ([c1bf3c9](https://github.com/deuriib/agency-rules/commit/c1bf3c9f078d84f578f2e37633dcc87fc98f3831))
42
+ * **install:** add interactive mode and harness/scope parameters ([e1996be](https://github.com/deuriib/agency-rules/commit/e1996bec3da5a34db23c06181dcb0071b35f5eff))
43
+ * **rules:** enforce conventional commits standard ([53f78b9](https://github.com/deuriib/agency-rules/commit/53f78b97a8669db60b7ac2110f10d9ad5658df39))
44
+
45
+
46
+ ### Bug Fixes
47
+
48
+ * **ci:** add missing mise sidecar for biome ([ded656e](https://github.com/deuriib/agency-rules/commit/ded656e16acc862ef975069459bc8dd324adc051))
49
+ * **install:** resolve biome linting errors and formatting ([04f4fee](https://github.com/deuriib/agency-rules/commit/04f4feeabcd2667d66b95865d0b7dd687cdd4798))
50
+
51
+ ## [Unreleased]
52
+
53
+ ### Added
54
+
55
+ - Initial scaffold.
56
+ - Conversion engine driven by `tools.json`.
57
+ - Renderers for **Google Antigravity**, **pi-agent**, **oh-my-pi (omp)**,
58
+ and **OpenCode** (via `opencode-rules`).
59
+ - `mise.toml` with toolchain (`node@22`), tasks (`validate`, `convert`,
60
+ `install`, `test`, `ci`), and post-install hook.
61
+ - Test suites for frontmatter parsing and every renderer.
62
+ - Documentation: architecture, rule spec, harness extension guide.
63
+
64
+ ### Changed
65
+
66
+ - N/A
67
+
68
+ ### Fixed
69
+
70
+ - N/A
71
+
72
+ ---
73
+
74
+ ## [0.1.0] — TBD
75
+
76
+ First tagged release. Reserved for the v0.1.0 cut once the harness set is
77
+ stable.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deuri Vasquez
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 CHANGED
@@ -1,3 +1,145 @@
1
- # Temporary Holding Version
1
+ # agency-rules
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Write rules once. Run them everywhere.**
4
+
5
+ `agency-rules` compiles a single set of generic Markdown rules into the
6
+ native formats expected by **Google Antigravity**, **pi-agent**,
7
+ **oh-my-pi (omp)**, and **OpenCode** (via the `opencode-rules` plugin).
8
+
9
+ The whole pipeline — toolchain, environment, and tasks — is managed by
10
+ [mise](https://mise.jdx.dev), so every collaborator and CI runner uses the
11
+ exact same Node version and the exact same commands.
12
+
13
+ ---
14
+
15
+ ## Why
16
+
17
+ Every coding agent has its own rule format:
18
+
19
+ | Harness | Format | Location |
20
+ | --- | --- | --- |
21
+ | Google Antigravity | YAML frontmatter with `trigger` | `.agents/rules/*.md` |
22
+ | pi-agent | Per-rule directory with `RULES.md` | `.pi/rules/<slug>/RULES.md` |
23
+ | oh-my-pi (omp) | Markdown with frontmatter | `.omp/rules/*.{md,mdc}` |
24
+ | OpenCode | Markdown via `opencode-rules` plugin | `.opencode/rules/*.md` |
25
+
26
+ Writing the same rule four times is a maintenance nightmare. `agency-rules`
27
+ lets you write it **once** in a neutral format and compile it to all four
28
+ with a single command, driven by a declarative contract (`tools.json`).
29
+
30
+ ---
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ # 1. Install toolchain + dependencies (one time)
36
+ git clone https://github.com/<your-org>/agency-rules.git
37
+ cd agency-rules
38
+ mise install
39
+
40
+ # 2. Compile rules to every harness
41
+ mise run convert
42
+
43
+ # 3. Install into harnesses detected on your machine
44
+ mise run install --scope project
45
+ ```
46
+
47
+ That's it. Your rules are now live in `.agents/`, `.pi/`, `.omp/`, and
48
+ `.opencode/` (and the `opencode-rules` plugin is registered in
49
+ `opencode.json` automatically).
50
+
51
+ ---
52
+
53
+ ## Repository layout
54
+
55
+ ```
56
+ agency-rules/
57
+ ├── rules/ ← source of truth (Markdown + YAML frontmatter)
58
+ ├── integrations/ ← compiled output (generated, gitignored)
59
+ ├── scripts/ ← convert, install, validate + renderers
60
+ ├── tests/ ← node:test suites for renderers and parsing
61
+ ├── docs/ ← architecture, spec, extension guide
62
+ ├── tools.json ← the conversion contract
63
+ └── mise.toml ← toolchain + tasks
64
+ ```
65
+
66
+ ---
67
+
68
+ 1. **Validate**: `mise run validate`.
69
+ 2. **Convert**: `mise run convert` and inspect `integrations/`.
70
+ 3. **Test**: `mise run test`.
71
+
72
+ ## Commands
73
+
74
+ All commands go through mise:
75
+
76
+ | Command | What it does |
77
+ | --- | --- |
78
+ | `mise run setup` | Install Node dependencies (`npm ci`) |
79
+ | `mise run validate` | Validate every rule's frontmatter against `rules/_schema.json` |
80
+ | `mise run check` | Format + lint + organize imports with Biome (write mode) |
81
+ | `mise run convert` | Compile `rules/` → `integrations/<harness>/` |
82
+ | `mise run convert:dry-run` | Preview which files `convert` would write (no writes) |
83
+ | `mise run install` | Copy compiled output into harness directories |
84
+ | `mise run install:dry-run` | Preview which files `install` would copy (no writes) |
85
+ | `mise run test` | Run the Node test suite |
86
+ | `mise run ci` | Full pipeline (`validate` → `check` → `convert` → `test`) |
87
+
88
+ See `mise tasks` for the up-to-date list.
89
+
90
+ ### Dry-run workflow
91
+
92
+ The typical inner loop uses dry-runs first to inspect what will change
93
+ before anything hits disk:
94
+
95
+ ```bash
96
+ mise run convert:dry-run # see generated file paths + sizes
97
+ mise run convert # actually write to integrations/
98
+ mise run install:dry-run -- --all # see copy destinations
99
+ mise run install -- --all # actually copy into harness dirs
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Adding a rule
105
+
106
+ 1. Create `rules/<category>/<slug>.md` following
107
+ [`docs/RULE-SPEC.md`](docs/RULE-SPEC.md).
108
+ 2. Run `mise run validate` — it must pass.
109
+ 3. Run `mise run convert` — inspect `integrations/` to sanity-check output.
110
+ 4. Open a PR.
111
+
112
+ ---
113
+
114
+ ## Adding a harness
115
+
116
+ See [`docs/ADDING-A-HARNESS.md`](docs/ADDING-A-HARNESS.md). It's roughly:
117
+
118
+ 1. Add an entry in `tools.json`.
119
+ 2. Add a renderer in `scripts/lib/renderers/`.
120
+ 3. Register the renderer in `scripts/lib/renderers/index.mjs`.
121
+ 4. Add tests in `tests/convert.test.mjs`.
122
+
123
+ The conversion engine itself never changes.
124
+
125
+ ---
126
+
127
+ ## Documentation
128
+
129
+ - [Architecture](docs/ARCHITECTURE.md) — data flow, contracts, caching.
130
+ - [Rule spec](docs/RULE-SPEC.md) — the neutral frontmatter format.
131
+ - [Adding a harness](docs/ADDING-A-HARNESS.md) — extension guide.
132
+
133
+ ---
134
+
135
+ ## Contributing
136
+
137
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Pull requests welcome.
138
+
139
+ ## Security
140
+
141
+ See [SECURITY.md](SECURITY.md) for responsible disclosure.
142
+
143
+ ## License
144
+
145
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,170 @@
1
+ # Adding a Harness
2
+
3
+ This guide walks through adding support for a new coding agent. The example
4
+ uses a hypothetical harness called **`example-agent`**.
5
+
6
+ ---
7
+
8
+ ## Prerequisites
9
+
10
+ Read [`ARCHITECTURE.md`](ARCHITECTURE.md) first. You need to understand:
11
+
12
+ - Why `tools.json` is a contract (not a list).
13
+ - What renderers do (pure functions, no I/O).
14
+ - What the installer does (copies per manifest).
15
+
16
+ ---
17
+
18
+ ## Step 1 — Add an entry to `tools.json`
19
+
20
+ Add a new key under `harnesses`. Every field matters:
21
+
22
+ ```json
23
+ "example-agent": {
24
+ "id": "example-agent",
25
+ "label": "Example Agent",
26
+ "format": "md-example",
27
+ "installKind": "per-rule",
28
+ "integrationPath": "{{slug}}.md",
29
+ "detect": { "dirs": [".example", "~/.example"] },
30
+ "dest": {
31
+ "project": ".example/rules/{{slug}}.md",
32
+ "user": "~/.example/rules/{{slug}}.md"
33
+ },
34
+ "frontmatter": {
35
+ "required": ["description"],
36
+ "map": {
37
+ "description": "string",
38
+ "globs": "array<string>|string"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Field checklist:
45
+
46
+ - [ ] `id` — matches the key, kebab-case.
47
+ - [ ] `label` — human-readable name.
48
+ - [ ] `format` — you will use this string as a key in `renderers/index.mjs`.
49
+ - [ ] `installKind` — currently only `per-rule` is implemented.
50
+ - [ ] `integrationPath` — path under `integrations/<id>/`. Use `{{slug}}`.
51
+ - [ ] `detect.dirs` / `detect.files` — where the harness lives.
52
+ - [ ] `dest.project` / `dest.user` — install destinations.
53
+ - [ ] `frontmatter.required` — fields the harness cannot run without.
54
+ - [ ] `frontmatter.map` — documentation of supported fields.
55
+ - [ ] `plugin` — only if the harness needs a plugin registered.
56
+
57
+ ---
58
+
59
+ ## Step 2 — Create the renderer
60
+
61
+ Create `scripts/lib/renderers/example-agent.mjs`:
62
+
63
+ ```js
64
+ import { wrap } from './_util.mjs';
65
+
66
+ export function renderExampleAgent(rule, _harness) {
67
+ const d = rule.data;
68
+ const fm = { description: d.description };
69
+ if (d.globs !== undefined) fm.globs = d.globs;
70
+ return wrap(fm, rule.body);
71
+ }
72
+ ```
73
+
74
+ Rules for renderers:
75
+
76
+ - Pure. No `fs`, no `process`, no globals.
77
+ - Signature is `(rule, harness) => string`.
78
+ - Read only the fields your harness understands. Drop the rest silently —
79
+ other harnesses may need them.
80
+ - Use `wrap()` from `_util.mjs` for consistent frontmatter formatting.
81
+
82
+ ---
83
+
84
+ ## Step 3 — Register the renderer
85
+
86
+ Edit `scripts/lib/renderers/index.mjs`:
87
+
88
+ ```js
89
+ import { renderExampleAgent } from './example-agent.mjs';
90
+
91
+ export const renderers = {
92
+ // ...
93
+ 'md-example': renderExampleAgent,
94
+ };
95
+ ```
96
+
97
+ The key must be the exact string from `tools.json`'s `format` field.
98
+
99
+ ---
100
+
101
+ ## Step 4 — Add tests
102
+
103
+ Edit `tests/convert.test.mjs` and add a block:
104
+
105
+ ```js
106
+ import { renderExampleAgent } from '../scripts/lib/renderers/example-agent.mjs';
107
+
108
+ test('example-agent: emits description', () => {
109
+ const fm = matter(renderExampleAgent(baseRule)).data;
110
+ assert.equal(fm.description, 'Rules for TS.');
111
+ });
112
+
113
+ test('example-agent: preserves globs as-is', () => {
114
+ const fm = matter(renderExampleAgent(baseRule)).data;
115
+ assert.deepEqual(fm.globs, ['**/*.ts', '**/*.tsx']);
116
+ });
117
+ ```
118
+
119
+ Cover:
120
+
121
+ - A rule with only the required fields.
122
+ - A rule with every optional field your harness supports.
123
+ - The "body preserved verbatim" test in the shared block.
124
+
125
+ ---
126
+
127
+ ## Step 5 — Verify end-to-end
128
+
129
+ ```bash
130
+ mise run validate # schema must still pass
131
+ mise run convert # your harness should appear in the summary
132
+ ls integrations/example-agent/
133
+ mise run test # all renderer tests, including yours
134
+ mise run install --harness example-agent --scope project
135
+ ```
136
+
137
+ Inspect `integrations/example-agent/<slug>.md` and confirm the output matches
138
+ your harness's expected format.
139
+
140
+ ---
141
+
142
+ ## Step 6 — Update docs
143
+
144
+ - Add a row to the compatibility matrix in `docs/RULE-SPEC.md`.
145
+ - Add a row to the harness table in `README.md`.
146
+ - Update the "Adding a rule — checklist" in `CONTRIBUTING.md` if the new
147
+ harness changes requirements.
148
+
149
+ ---
150
+
151
+ ## Step 7 — Open a PR
152
+
153
+ Title: `feat(harness): support example-agent`
154
+
155
+ Include in the description:
156
+
157
+ - Link to the harness's documentation.
158
+ - A sample compiled output.
159
+ - Screenshot or log from `mise run install` (optional but helpful).
160
+
161
+ ---
162
+
163
+ ## Supported harnesses (current)
164
+
165
+ | Harness | `format` | `installKind` | Path |
166
+ | --- | --- | --- | --- |
167
+ | Google Antigravity | `md-yaml-trigger` | `per-rule` | `.agents/rules/*.md` |
168
+ | pi-agent | `md-rules-dir` | `per-rule` | `.pi/rules/<slug>/RULES.md` |
169
+ | oh-my-pi (omp) | `md-or-mdc` | `per-rule` | `.omp/rules/*.md` |
170
+ | OpenCode | `md-plugin` | `per-rule` | `.opencode/rules/*.md` |
@@ -0,0 +1,130 @@
1
+ # Architecture
2
+
3
+ This document explains how `agency-rules` turns one set of generic rules into
4
+ four native harness formats, and why the pipeline is shaped the way it is.
5
+
6
+ ---
7
+
8
+ ## Data flow
9
+
10
+ ```
11
+ rules/*.md tools.json
12
+ (source of truth) (contract)
13
+ │ │
14
+ │ │
15
+ ▼ ▼
16
+ ┌────────────────────────────────────────────┐
17
+ │ scripts/validate.mjs (schema + uniqueness)│
18
+ └────────────────────────────────────────────┘
19
+ │
20
+ ▼
21
+ ┌────────────────────────────────────────────┐
22
+ │ scripts/convert.mjs │
23
+ │ for each harness: │
24
+ │ render = renderers[harness.format] │
25
+ │ for each rule: │
26
+ │ write render(rule, harness) │
27
+ │ write _manifest.json │
28
+ └────────────────────────────────────────────┘
29
+ │
30
+ ▼
31
+ integrations/<harness>/...
32
+ (compiled, gitignored)
33
+ │
34
+ ▼
35
+ ┌────────────────────────────────────────────┐
36
+ │ scripts/install.mjs │
37
+ │ detect harnesses │
38
+ │ copy files to harness.dest[scope] │
39
+ │ register plugins (opencode) │
40
+ └────────────────────────────────────────────┘
41
+ │
42
+ ▼
43
+ user's harness dirs
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Layers
49
+
50
+ | Layer | File(s) | Responsibility |
51
+ | --- | --- | --- |
52
+ | **Source** | `rules/**/*.md` | Generic Markdown + YAML frontmatter. |
53
+ | **Schema** | `rules/_schema.json` | JSON Schema enforcing the neutral format. |
54
+ | **Contract** | `tools.json` | Declares each harness: format, integration path, install destinations, frontmatter mapping, plugin metadata. |
55
+ | **Engine** | `scripts/convert.mjs` | Iterates over rules × harnesses, calls the right renderer, writes output + manifest. |
56
+ | **Renderers** | `scripts/lib/renderers/*.mjs` | Pure functions: `(rule, harness) → markdown string`. No I/O. |
57
+ | **Installer** | `scripts/install.mjs` | Detects harnesses, copies compiled files to real destinations, registers plugins. |
58
+ | **Validator** | `scripts/validate.mjs` | Runs the schema and checks slug uniqueness before anything else runs. |
59
+ | **Orchestration** | `mise.toml` | Pins Node version, defines tasks, composes `ci`, wires the `postinstall` hook. |
60
+
61
+ Each layer has exactly one job. Renderers never touch the filesystem; the
62
+ engine never hardcodes a harness; the installer never renders.
63
+
64
+ ---
65
+
66
+ ## Why `tools.json` is the single source of truth
67
+
68
+ Every harness-specific decision lives there:
69
+
70
+ - **Which renderer** to use (`format`).
71
+ - **What path** the output takes inside `integrations/<harness>/` (`integrationPath`).
72
+ - **Where** the file ultimately lands for `project` and `user` scopes (`dest`).
73
+ - **Which frontmatter fields** are required and how they map (`frontmatter`).
74
+ - **Whether** plugin registration is needed (`plugin`).
75
+
76
+ Adding a harness is additive: one `tools.json` entry + one renderer +
77
+ one test. The engine, installer, and validator never change.
78
+
79
+ ---
80
+
81
+ ## Determinism guarantees
82
+
83
+ - **Pure renderers.** Same input → same output byte for byte. No timestamps,
84
+ no random ordering. Manifests are sorted by slug.
85
+ - **Full reset on convert.** `integrations/<harness>/` is deleted before
86
+ being rewritten. No stale files survive a rename.
87
+ - **Manifest as index.** `integrations/<harness>/_manifest.json` records
88
+ every file the installer should copy. The installer trusts the manifest,
89
+ not a directory walk.
90
+ - **Locked toolchain.** `mise.lock` pins exact versions of Node and any
91
+ installed CLIs. CI uses `mise install --locked` to fail on drift.
92
+
93
+ ---
94
+
95
+ ## Caching
96
+
97
+ `mise run convert` declares:
98
+
99
+ ```toml
100
+ sources = ["rules/**/*.md", "tools.json", "scripts/lib/**/*.mjs"]
101
+ outputs = ["integrations/**/*"]
102
+ ```
103
+
104
+ If no source has changed since the last run, mise skips the task entirely.
105
+ This keeps the inner loop fast as the rules tree grows.
106
+
107
+ ---
108
+
109
+ ## OpenCode plugin registration
110
+
111
+ OpenCode needs `opencode-rules` registered in `opencode.json`. The installer:
112
+
113
+ 1. Reads (or creates) `opencode.json`.
114
+ 2. Ensures `config.plugin` is an array.
115
+ 3. Appends `"opencode-rules@latest"` if it isn't already present.
116
+ 4. Writes the config back with 2-space indentation.
117
+
118
+ If the file exists but is invalid JSON, the installer warns and skips rather
119
+ than overwriting — a corrupted config is never silently clobbered.
120
+
121
+ ---
122
+
123
+ ## Security considerations
124
+
125
+ - Rule slugs are validated against `^[a-z][a-z0-9-]*$` before they reach the
126
+ filesystem, preventing path traversal via `{{slug}}` substitution.
127
+ - `install.mjs` only reads from `integrations/` and writes to paths derived
128
+ from `tools.json` — it never reads arbitrary user input paths.
129
+ - Renderers do not execute rule content; they only stringify it. No `eval`,
130
+ no shell, no dynamic imports.
@@ -0,0 +1,135 @@
1
+ # Rule Specification
2
+
3
+ The neutral format for a rule: **Markdown with YAML frontmatter**.
4
+ Every rule compiles to every harness, so the frontmatter must carry enough
5
+ information to derive each harness's expectations.
6
+
7
+ ---
8
+
9
+ ## File layout
10
+
11
+ ```
12
+ rules/
13
+ ├── _schema.json
14
+ ├── <category>/
15
+ │ └── <slug>.md
16
+ ```
17
+
18
+ - `_schema.json` is the JSON Schema. It is not a rule and is skipped.
19
+ - Any file starting with `_` is skipped.
20
+ - `<slug>` should equal the frontmatter `name`.
21
+
22
+ ---
23
+
24
+ ## Frontmatter fields
25
+
26
+ | Field | Required | Type | Description |
27
+ | --- | --- | --- | --- |
28
+ | `name` | ✅ | string, `^[a-z][a-z0-9-]*$`, ≤64 | Unique identifier. Used as the slug. |
29
+ | `description` | ✅ | string, 1–1024 | **When** the rule applies (not what it says). Harnesses use this for matching. |
30
+ | `globs` | ❌ | string \| string[] | File patterns that trigger the rule. |
31
+ | `alwaysApply` | ❌ | boolean | If `true`, ignore `globs` and always apply. |
32
+ | `trigger` | ❌ | `always_on` \| `glob` \| `model_decision` \| `manual` | Explicit trigger for Antigravity. Overrides auto-derivation. |
33
+ | `match` | ❌ | `any` \| `all` | OpenCode: match any glob or all globs. |
34
+ | `tags` | ❌ | string[] | Freeform metadata. Ignored by all harnesses. |
35
+ | `tools` | ❌ | string[] | pi-agent: tools the rule provides. Preserved verbatim. |
36
+
37
+ ---
38
+
39
+ ## Body
40
+
41
+ The body is Markdown. It is copied verbatim into every compiled output.
42
+ Don't put harness-specific syntax here — anything that must differ goes in
43
+ the frontmatter and is handled by the renderer.
44
+
45
+ ---
46
+
47
+ ## Compatibility matrix
48
+
49
+ | Field | Antigravity | pi-agent | oh-my-pi | OpenCode |
50
+ | --- | --- | --- | --- | --- |
51
+ | `name` | (derived) | ✅ used | ✅ used | — |
52
+ | `description` | ✅ emitted | ✅ emitted | ✅ emitted | ✅ used for matching |
53
+ | `globs` | ✅ joined as string under `trigger: glob` | ✗ dropped | ✅ preserved | ✅ preserved |
54
+ | `alwaysApply` | ✅ → `trigger: always_on` | ✗ dropped | ✅ preserved | ✗ dropped |
55
+ | `trigger` | ✅ emitted | ✗ dropped | ✗ dropped | ✗ dropped |
56
+ | `match` | ✗ dropped | ✗ dropped | ✗ dropped | ✅ preserved |
57
+ | `tags` | ✗ dropped | ✗ dropped | ✗ dropped | ✗ dropped |
58
+ | `tools` | ✗ dropped | ✅ preserved | ✗ dropped | ✗ dropped |
59
+
60
+ "✗ dropped" means the field is not emitted in that harness's output. It does
61
+ not mean it's an error to set it — other harnesses may need it. The renderer
62
+ picks what its harness understands.
63
+
64
+ ---
65
+
66
+ ## Antigravity trigger precedence
67
+
68
+ Antigravity requires a `trigger` field. The renderer resolves it in this
69
+ order:
70
+
71
+ 1. Explicit `trigger` in the rule.
72
+ 2. `alwaysApply: true` → `always_on`.
73
+ 3. `globs` present → `glob`.
74
+ 4. Only `description` → `model_decision`.
75
+ 5. Nothing → `manual`.
76
+
77
+ When `trigger: glob`, the renderer joins the `globs` array with commas into a
78
+ single string, which is the format Antigravity's YAML parser expects.
79
+
80
+ ---
81
+
82
+ ## Examples
83
+
84
+ ### Always-on baseline
85
+
86
+ ```markdown
87
+ ---
88
+ name: coding-standards
89
+ description: Baseline rules applied to every edit regardless of file type.
90
+ alwaysApply: true
91
+ tags: [base]
92
+ ---
93
+
94
+ # Coding Standards
95
+ ...
96
+ ```
97
+
98
+ Compiles to Antigravity `trigger: always_on`, oh-my-pi `alwaysApply: true`,
99
+ and pi-agent / OpenCode with just `name` + `description`.
100
+
101
+ ### Scoped to file globs
102
+
103
+ ```markdown
104
+ ---
105
+ name: typescript
106
+ description: Mandatory rules for editing TypeScript and TSX files.
107
+ globs:
108
+ - "**/*.ts"
109
+ - "**/*.tsx"
110
+ tags: [language, typescript]
111
+ ---
112
+
113
+ # TypeScript Rules
114
+ ...
115
+ ```
116
+
117
+ Compiles to Antigravity `trigger: glob` + `globs: "**/*.ts,**/*.tsx"`,
118
+ oh-my-pi and OpenCode with the original array, pi-agent without `globs`.
119
+
120
+ ### Description-only (model decision)
121
+
122
+ ```markdown
123
+ ---
124
+ name: api-review
125
+ description: Use this rule when reviewing REST API endpoint definitions.
126
+ ---
127
+
128
+ # API Review
129
+ ...
130
+ ```
131
+
132
+ Compiles to Antigravity `trigger: model_decision`. Other harnesses get just
133
+ `name` + `description`.
134
+
135
+ ```