@h-rig/product-ad-website 0.0.6-alpha.283

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.
Files changed (38) hide show
  1. package/README.md +3 -0
  2. package/package.json +52 -0
  3. package/site/assets/favicon.svg +1 -0
  4. package/site/assets/og-card.png +0 -0
  5. package/site/assets/rig.css +199 -0
  6. package/site/assets/rig.js +66 -0
  7. package/site/assets/search-index.json +1 -0
  8. package/site/assets/site.css +46 -0
  9. package/site/assets/site.js +510 -0
  10. package/site/cli/index.html +88 -0
  11. package/site/config/index.html +256 -0
  12. package/site/docs/agent-tools/index.html +129 -0
  13. package/site/docs/architecture/index.html +42 -0
  14. package/site/docs/capabilities/index.html +84 -0
  15. package/site/docs/entities/index.html +68 -0
  16. package/site/docs/environment/index.html +62 -0
  17. package/site/docs/faq/index.html +17 -0
  18. package/site/docs/fleet-irc/index.html +44 -0
  19. package/site/docs/gate/index.html +128 -0
  20. package/site/docs/getting-started/index.html +50 -0
  21. package/site/docs/glossary/index.html +31 -0
  22. package/site/docs/index.html +68 -0
  23. package/site/docs/operator/index.html +49 -0
  24. package/site/docs/packages/index.html +36 -0
  25. package/site/docs/pi-extensions/index.html +15 -0
  26. package/site/docs/recipes/index.html +28 -0
  27. package/site/docs/runs/index.html +35 -0
  28. package/site/docs/security/index.html +29 -0
  29. package/site/docs/server/index.html +27 -0
  30. package/site/docs/swarm-commander/index.html +41 -0
  31. package/site/docs/task-sources/index.html +129 -0
  32. package/site/docs/troubleshooting/index.html +23 -0
  33. package/site/docs/validators/index.html +177 -0
  34. package/site/index.html +1653 -0
  35. package/site/plugins/examples/index.html +419 -0
  36. package/site/plugins/index.html +193 -0
  37. package/site/skills/index.html +140 -0
  38. package/site/variants/deck/index.html +1 -0
@@ -0,0 +1,193 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Plugin authoring // RIG</title><meta property="og:image" content="https://how.rig-does.work/assets/og-card.png"><meta name="twitter:card" content="summary_large_image"><meta name="description" content="From definePlugin to a composed package: Rig's contribution types, the single-channel metadata+executable model, the capability model, and contract testing."><meta property="og:title" content="Plugin authoring // RIG"><meta property="og:description" content="From definePlugin to a composed package: Rig's contribution types, the single-channel metadata+executable model, the capability model, and contract testing."><link rel="stylesheet" href="../assets/rig.css?v=v9"><link rel="stylesheet" href="../assets/site.css?v=v9"><link rel="icon" type="image/svg+xml" href="../assets/favicon.svg"></head><body><canvas id="docfield"></canvas><nav class="nav"><div class="nav-in"><a class="brand" href="../index.html"><span class="mk"></span><span class="blk">Rig</span></a><button class="burger" onclick="document.getElementById('nl').classList.toggle('open')">menu</button><div class="nav-links" id="nl"><button class="nsearch" type="button" onclick="rigSearchOpen()">search<kbd>&#8984;K</kbd></button><a href="../index.html" class="">overview</a><a href="../docs/index.html" class="">docs</a><a href="../plugins/index.html" class="active">plugins</a><a href="../skills/index.html" class="">skills</a><a href="../cli/index.html" class="">cli</a><a href="../config/index.html" class="">config</a><a href="https://github.com/humanity-org/rig" class="ext">github</a></div></div></nav><div class="docwrap"><aside class="sidebar"><div class="grp">START</div><a href="../docs/index.html" class="">docs home</a><a href="../docs/capabilities/index.html" class="">capabilities</a><a href="../docs/getting-started/index.html" class="">getting started</a><div class="grp">RUN</div><a href="../docs/runs/index.html" class="">the run lifecycle</a><a href="../docs/operator/index.html" class="">operator guide</a><a href="../docs/swarm-commander/index.html" class="">swarm commander</a><a href="../docs/fleet-irc/index.html" class="">drone messages</a><a href="../docs/agent-tools/index.html" class="">agent tools</a><a href="../docs/gate/index.html" class="">the merge gate</a><a href="../docs/troubleshooting/index.html" class="">troubleshooting</a><div class="grp">ARCHITECTURE</div><a href="../docs/architecture/index.html" class="">how rig works</a><a href="../docs/entities/index.html" class="">entities & data model</a><a href="../docs/server/index.html" class="">OMP relay/web</a><a href="../docs/security/index.html" class="">security & trust</a><a href="../docs/environment/index.html" class="">environment variables</a><div class="grp">EXTEND</div><a href="../plugins/index.html" class="active">plugin authoring</a><a href="../plugins/examples/index.html" class="">plugin examples</a><a href="../docs/pi-extensions/index.html" class="">OMP/Pi extensions</a><a href="../skills/index.html" class="">skills</a><a href="../docs/task-sources/index.html" class="">task sources</a><a href="../docs/validators/index.html" class="">validators & hooks</a><div class="grp">OPERATE</div><a href="../cli/index.html" class="">cli reference</a><a href="../config/index.html" class="">rig.config.ts</a><a href="../docs/recipes/index.html" class="">recipes</a><a href="../docs/glossary/index.html" class="">glossary</a><a href="../docs/faq/index.html" class="">faq</a><div class="grp">ENGINE</div><a href="../docs/packages/index.html" class="">packages</a></aside><main class="doc"><h1>Plugin authoring</h1>
2
+ <p class="lede">Almost everything in Rig is a plugin &mdash; the CLI launcher and the Rig session included. The <code>apps/rig</code> application composes the first-party graph; a project adds explicit executable packages through <code>rig.config.ts</code>. Live OMP capability comes from OMP/Pi packages such as <code>runtime.pi.packages</code>. Rig never scans disk, so if a package is not composed, it does not exist.</p>
3
+
4
+ <h2 id="walkthrough">The walkthrough: definePlugin &rarr; published</h2>
5
+ <p>Six steps from empty package to a composable plugin a consumer can <code>bun add</code>. No registry, no marketplace &mdash; npm is the distribution; live OMP capability still comes from OMP/Pi packages.</p>
6
+ <table class="t"><thead><tr><th>step</th><th>what you do</th></tr></thead>
7
+ <tbody>
8
+ <tr><td>1. scaffold</td><td>A plain Bun/npm package: <code>bun init</code>, add <code>@rig/core</code> (and <code>@rig/contracts</code> types) as deps. One default export.</td></tr>
9
+ <tr><td>2. declare</td><td><code>definePlugin({ name, version, contributes })</code> &mdash; validated against the <code>RigPlugin</code> schema at call time; invalid shapes throw immediately, not at dispatch time.</td></tr>
10
+ <tr><td>3. implement</td><td>Executable contributions (validators, task sources, hooks&hellip;) carry their function on the same entry as their metadata &mdash; one object, no separate runtime channel. See <a href="#single-channel">the single-channel model</a>.</td></tr>
11
+ <tr><td>4. test</td><td>Contract tests with <code>bun:test</code>: <code>definePlugin</code> throws on bad shape, ids namespace, and you call the executable fns directly off the returned object &mdash; no session required.</td></tr>
12
+ <tr><td>5. wire</td><td>In a consuming project: <code>plugins: [myPlugin(options)]</code> in <code>rig.config.ts</code>. <code>rig plugin list</code> shows what it contributes; <code>rig plugin validate --task &lt;id&gt;</code> exercises its validators.</td></tr>
13
+ <tr><td>6. publish</td><td><code>bun publish</code> like any npm package. Consumers <code>bun add</code> it and import it in their config &mdash; no registry, no marketplace step, npm is the distribution.</td></tr>
14
+ </tbody></table>
15
+
16
+ <h2 id="worked-examples">Worked examples <span class="ns">// copyable packages</span></h2>
17
+ <p>The walkthrough is the contract. The examples are the sortie brief: three complete packages you can copy into a repo, wire through real config, and verify against the loaded plugin host. They cover the three extension seams teams usually need first:</p>
18
+ <table class="t"><thead><tr><th>example</th><th>what it proves</th><th>load path</th></tr></thead><tbody>
19
+ <tr><td><a href="examples/index.html#validator-plugin">validator plugin</a></td><td>A custom check enters the same validation gate as <code>std:typecheck</code>.</td><td><code>.rig/rig.config.ts</code> &mdash; executable plugin code.</td></tr>
20
+ <tr><td><a href="examples/index.html#task-source-plugin">task-source plugin</a></td><td>A custom <code>taskSource.kind</code> resolves through the same factory path as the standard adapters.</td><td><code>.rig/rig.config.ts</code> &mdash; executable factory.</td></tr>
21
+ <tr><td><a href="examples/index.html#pi-extension-package">OMP/Pi package</a></td><td>A live-session tool is pinned with <code>runtime.pi.packages</code> and materialized into <code>.pi/settings.json</code>.</td><td><code>rig.config.ts</code> or declarative <code>rigfig.toml</code> &mdash; package data, not a Rig plugin.</td></tr>
22
+ </tbody></table>
23
+ <p>Go to <a href="examples/index.html">plugin examples &rarr;</a> for the full file listings, config, verification steps, and the exact source seams each one relies on.</p>
24
+
25
+
26
+ <h2 id="entry">The entry point</h2>
27
+ <pre class="code"><span class="k">import</span> { definePlugin } <span class="k">from</span> <span class="s">"@rig/core"</span>;
28
+
29
+ <span class="k">export default</span> <span class="f">definePlugin</span>({
30
+ name: <span class="s">"my-plugin"</span>,
31
+ version: <span class="s">"0.1.0"</span>,
32
+ contributes: { <span class="c">/* any subset of the eight types below */</span> },
33
+ });</pre>
34
+ <p><code>definePlugin</code> validates the shape against the <code>RigPlugin</code> schema in <code>@rig/contracts</code> and throws on invalid input. The return value is what you pass to <code>plugins: [...]</code>.</p>
35
+ <div class="note"><b>Effect Schema</b> is the runtime schema/validation library Rig&rsquo;s contracts are written in &mdash; the same one used across <code>@rig/contracts</code>. Every <code>RigPlugin</code> field (<code>name</code>, <code>version</code>, each contribution type) is an Effect <code>Schema.Struct</code>, so <code>definePlugin</code> decodes your object against it and rejects the bad shape <i>before</i> anything boots. The whole plugin contract lives in one file: <code>packages/contracts/src/plugin.ts</code>.</div>
36
+
37
+ <h3 id="where-contributions-land">Where each contribution lands <span class="ns">// metadata in, registry out</span></h3>
38
+ <p>Every contribution is two things: metadata at config-load time, a registry entry at boot. The plugin host (<code>@rig/core</code>) flattens all composed plugins, dedupes the ids, then routes each contribution type to its own registry:</p>
39
+ <pre class="code"><span class="c">// appPluginGraph + rig.config.ts project extras</span>
40
+ <span class="p">&darr;</span>
41
+ <span class="f">createPluginHost</span>(plugins) <span class="c">// flatten + dedupe ids, enforce namespacing</span>
42
+ <span class="p">&darr;</span>
43
+ &#9500;&#9472; validators <span class="p">&rarr;</span> <span class="f">createValidatorRegistry</span> <span class="c">// in-process run(ctx)</span>
44
+ &#9500;&#9472; hooks <span class="p">&rarr;</span> harness settings file <span class="c">// managed files</span>
45
+ &#9500;&#9472; skills <span class="p">&rarr;</span> <span class="s">.pi/skills/&lt;id&gt;/</span> <span class="c">// materialized SKILL.md</span>
46
+ &#9500;&#9472; repoSources <span class="p">&rarr;</span> <span class="f">createRepoRegistry</span>
47
+ &#9500;&#9472; agentRoles <span class="p">&rarr;</span> <span class="f">createAgentRoleRegistry</span> <span class="c">// role:&lt;id&gt; &rarr; model</span>
48
+ &#9500;&#9472; taskFieldSchemas <span class="p">&rarr;</span> <span class="f">createTaskFieldRegistry</span>
49
+ &#9500;&#9472; taskSources <span class="p">&rarr;</span> <span class="f">buildTaskSourceRegistry</span> <span class="c">// resolved by kind</span>
50
+ &#9500;&#9472; panels <span class="p">&rarr;</span> read-model presentation cargo <span class="c">// display metadata for session surfaces</span>
51
+ &#9500;&#9472; capabilities <span class="p">&rarr;</span> capability resolver <span class="c">// provides / requires / replaces</span>
52
+ &#9492;&#9472; cliCommands <span class="p">&rarr;</span> the <span class="s">rig</span> command surface</pre>
53
+ <p>Every registry above lives in <code>@rig/core</code> (kernel capabilities resolve through <code>@rig/kernel-seed</code>). Contributions carry their executable fn on the <em>same</em> entry as their metadata &mdash; see <a href="#single-channel">the single-channel model</a>.</p>
54
+
55
+ <h2 id="namespacing">Namespacing</h2>
56
+ <p>Every registration id is <code>&lt;plugin-name&gt;:&lt;local-id&gt;</code> (e.g. <code>my-plugin:typecheck</code>). The plugin host (<code>@rig/core</code>) throws at config-load time on duplicate plugin names, duplicate contribution ids, duplicate executable task-source kinds, and metadata/runtime drift.</p>
57
+
58
+ <h2 id="types">The contribution types</h2>
59
+ <table class="t"><thead><tr><th>type</th><th>what it adds</th></tr></thead>
60
+ <tbody>
61
+ <tr><td><code>validators</code></td><td>Pass/fail gates that run at task completion (carry an executable <code>run(ctx)</code>).</td></tr>
62
+ <tr><td><code>hooks</code></td><td><code>PreToolUse</code> / <code>PostToolUse</code> / <code>UserPromptSubmit</code> / <code>Stop</code> / <code>SessionStart</code> / <code>SessionEnd</code> hooks, written into the active harness settings file.</td></tr>
63
+ <tr><td><code>skills</code></td><td>SKILL.md prompt context, materialized into <code>.pi/skills/&lt;id&gt;/</code>.</td></tr>
64
+ <tr><td><code>repoSources</code></td><td>External repositories the runtime may clone/reference.</td></tr>
65
+ <tr><td><code>agentRoles</code></td><td><code>role:&lt;id&gt;</code> &rarr; model resolution.</td></tr>
66
+ <tr><td><code>taskFieldSchemas</code></td><td>Structured fields parsed from issue-body blocks.</td></tr>
67
+ <tr><td><code>taskSources</code></td><td><code>kind</code>-keyed task adapters (carry an executable <code>factory</code>; even first-party ones resolve this way).</td></tr>
68
+ <tr><td><code>cliCommands</code></td><td>Commands callable directly as <code>rig &lt;command&gt;</code> (or <code>rig plugin run &lt;id&gt;</code>).</td></tr>
69
+ <tr><td><code>stages</code> / <code>stageMutations</code></td><td>Run-lifecycle stages and mutations (validate / verify / commit / push / PR / merge&hellip;).</td></tr>
70
+ <tr><td><code>capabilities</code></td><td>Named product capabilities other plugins resolve through the capability model.</td></tr>
71
+ <tr><td><code>panels</code></td><td>Read-model presentation cargo &mdash; display metadata consumed by session surfaces. Reach for <code>sessionExtensions</code> when you need live in-session tools.</td></tr>
72
+ <tr><td><code>blockerClassifiers</code></td><td>Classifiers that label why a run is blocked.</td></tr>
73
+ <tr><td><code>sessionExtensions</code></td><td>Executable installers wired into the OMP/Pi session at creation time.</td></tr>
74
+ <tr><td><code>seedEntrypoints</code></td><td>Bootstrap entrypoints resolved through the <code>@rig/kernel-seed</code> plugin ABI.</td></tr>
75
+ </tbody></table>
76
+
77
+ <h2 id="single-channel">The single-channel model: metadata + executable, one object</h2>
78
+ <p>A plugin is <b>one object</b>. Each <code>contributes</code> entry carries its schema-validated metadata <em>and</em> its executable fn together &mdash; there is no second <code>definePlugin</code> argument and no separate runtime channel. Effect Schema can&rsquo;t represent functions, so <code>definePlugin</code> decodes the object against the metadata manifest and simply ignores the fns (Schema strips excess), returning the original object unchanged with its fns intact:</p>
79
+ <pre class="code"><span class="k">export default</span> <span class="f">definePlugin</span>({
80
+ name: <span class="s">"my-plugin"</span>, version: <span class="s">"0.1.0"</span>,
81
+ contributes: {
82
+ validators: [{
83
+ id: <span class="s">"my-plugin:typecheck"</span>, category: <span class="s">"integration"</span>, <span class="c">// metadata…</span>
84
+ <span class="k">async</span> <span class="f">run</span>(ctx) { <span class="c">// …and the executable fn, same entry</span>
85
+ <span class="c">// ctx: { taskId, workspaceRoot, scope, monorepoRoot?, artifactsDir?, taskConfig? }</span>
86
+ <span class="k">return</span> { id: <span class="s">"my-plugin:typecheck"</span>, passed: <span class="k">true</span>, summary: <span class="s">"ok"</span> };
87
+ },
88
+ }],
89
+ },
90
+ });</pre>
91
+ <p>The plugin host auto-registers each executable into the <code>ValidatorRegistry</code> at boot; <code>taskValidate</code> dispatches <b>in-process</b>, no subprocess. Because the fn rides on the returned object, tests reach it directly (<code>plugin.contributes?.validators?.[0].run(ctx)</code>) without booting a session. Kernel capability implementations ride the same way, in a <code>capabilityProviders</code> map keyed by the tag the plugin <code>provides</code>.</p>
92
+
93
+ <h2 id="validators">Validators</h2>
94
+ <p>Categories: <code>boundary</code> &middot; <code>contract</code> &middot; <code>integration</code> &middot; <code>regression</code> &middot; <code>external</code> &middot; <code>custom</code>. Result shape: <code>{ id, passed, summary, details? }</code>. Wire one to a task with a <code>validator:&lt;id&gt;</code> label, or exercise a plugin&rsquo;s validators against a task with <code>rig plugin validate --task &lt;id&gt;</code>. For how validators and hooks behave at run time &mdash; categories, the gate, hook events &mdash; see <a href="../docs/validators/index.html">validators &amp; hooks &rarr;</a>.</p>
95
+
96
+ <h2 id="hooks">Hooks</h2>
97
+ <pre class="code">hooks: [{
98
+ id: <span class="s">"my-org:stop-check"</span>,
99
+ event: <span class="s">"Stop"</span>, <span class="c">// one of the six HookEvent literals</span>
100
+ matcher: { kind: <span class="s">"all"</span> }, <span class="c">// "all" | { kind: "tool", name } | { kind: "glob", pattern }</span>
101
+ command: <span class="s">"/repo/scripts/my-stop-check.sh"</span>,
102
+ }]</pre>
103
+ <p>Written into the active harness settings file when Rig prepares the workspace. The materializer is idempotent &mdash; operator-authored entries (no <code>_rigPlugin</code> marker) are preserved; plugin-owned entries are replaced and tagged <code>_rigPlugin: "&lt;plugin&gt;"</code>. A hook may ship a shell <code>command</code> or a typed implementation (<code>{ hooks: { [hookId]: fn } }</code> on the same plugin object); the runtime materializes a shim command for typed hooks automatically.</p>
104
+
105
+ <h2 id="skills">Skills</h2>
106
+ <pre class="code">skills: [{
107
+ id: <span class="s">"my-plugin:boundary-analysis"</span>,
108
+ path: <span class="k">new</span> <span class="f">URL</span>(<span class="s">"skills/boundary-analysis/SKILL.md"</span>, import.meta.url).pathname,
109
+ description: <span class="s">"Boundary analysis prompt"</span>,
110
+ }]</pre>
111
+ <p>On boot the runtime validates each skill and materializes it into <code>.pi/skills/&lt;id&gt;/SKILL.md</code> &mdash; OMP/Pi&rsquo;s project-scope directory, scanned natively at session start (so plugin skills reach the live session too). Each materialized dir carries a <code>.rig-plugin</code> marker; re-materialization replaces only plugin-owned skills and never touches operator-authored ones. See <a href="../skills/index.html">skills &rarr;</a>.</p>
112
+
113
+ <h2 id="misc">repoSources, agentRoles, taskFieldSchemas</h2>
114
+ <pre class="code">repoSources: [{ id: <span class="s">"my-plugin:main-repo"</span>, url: <span class="s">"https://github.com/org/repo"</span>, defaultPath: <span class="s">"repos/main-repo/"</span> }]
115
+ agentRoles: [{ id: <span class="s">"my-plugin:architect"</span>, defaultModel: <span class="s">"team-default"</span> }]
116
+ taskFieldSchemas: [{ id: <span class="s">"my-plugin:browser-field"</span>, fieldName: <span class="s">"Browser"</span>, schemaJson: <span class="s">"{...}"</span> }]</pre>
117
+ <p>A <code>Browser:</code> block in an issue body is parsed as JSON and validated against <code>schemaJson</code> by <code>createTaskFieldRegistry</code>, then passed to the agent under the field name. Agent roles resolve <code>role:&lt;id&gt;</code> task labels to models; repo sources can optionally carry managed-mirror fields (<code>defaultBranch</code>, <code>remoteEnvVar</code>, <code>checkoutEnvVar</code>) when Rig should sync the repo on the plugin&rsquo;s behalf.</p>
118
+
119
+ <h3 id="schemajson-format">The <code>schemaJson</code> format <span class="ns">// JSON in, presence out</span></h3>
120
+ <p><code>schemaJson</code> is a <b>JSON string</b> &mdash; a JSON-serialized schema-like payload, not an object. Use <code>JSON.stringify(&hellip;)</code> when you author it:</p>
121
+ <pre class="code">taskFieldSchemas: [{
122
+ id: <span class="s">"my-plugin:browser-field"</span>,
123
+ fieldName: <span class="s">"Browser"</span>,
124
+ schemaJson: <span class="f">JSON.stringify</span>({
125
+ type: <span class="s">"object"</span>,
126
+ properties: { url: { type: <span class="s">"string"</span> }, viewport: { type: <span class="s">"string"</span> } },
127
+ required: <span class="k">true</span>, <span class="c">// top-level boolean — the presence signal Rig reads</span>
128
+ }),
129
+ }]</pre>
130
+ <p>The payload is <b>opaque to the engine</b>: Rig does not deep-validate properties or JSON-Schema constraints. It reads exactly one thing &mdash; a <b>top-level <code>"required": true</code></b> &mdash; as the canonical &ldquo;this field must be present on the task&rdquo; marker. With that set, a task whose <code>fieldName</code> value is missing, <code>null</code>, or empty fails <code>validateTaskFields</code> with a collected error. Without it, the extension is optional and the schema is yours to interpret however your plugin or agent prompt wants. Note this is a <i>top-level</i> boolean <code>required: true</code>, distinct from JSON Schema&rsquo;s per-property <code>required: ["url"]</code> array (which Rig ignores).</p>
131
+
132
+ <h2 id="tasksources">taskSources</h2>
133
+ <pre class="code">taskSources: [{ id: <span class="s">"my-plugin:linear"</span>, kind: <span class="s">"linear"</span>, description: <span class="s">"Linear issues adapter"</span> }]</pre>
134
+ <p><code>kind</code> is an open string &mdash; name yours whatever you like (<code>"linear"</code>, <code>"jira"</code>, <code>"my-org:queue"</code>). <code>buildTaskSourceRegistry(config, pluginHost)</code> reads <code>config.taskSource.kind</code>, finds a matching executable factory on the plugin host, and instantiates the source; uniqueness is enforced at resolution. There are <b>no hard-coded kinds</b>: the application graph contributes GitHub Issues, and project plugin extras contribute Linear or any custom source through the same resolver. Source-specific config rides in the <code>taskSource.options</code> bag. The full first-party source shapes, label conventions, and write-back behaviour live on the <a href="../docs/task-sources/index.html">task sources page &rarr;</a></p>
135
+
136
+ <h2 id="clicommands">cliCommands</h2>
137
+ <pre class="code">cliCommands: [{ id: <span class="s">"my-plugin:report"</span>, command: <span class="s">"report"</span>, description: <span class="s">"Generate the weekly report"</span> }]</pre>
138
+ <p>Callable as <code>rig plugin run report</code> &mdash; or directly as <code>rig report</code>; plugin-contributed commands join the top-level CLI surface.</p>
139
+
140
+ <h2 id="testing">Contract testing</h2>
141
+ <p>Don&rsquo;t ship a plugin you haven&rsquo;t run through a contract test. There is no separate testkit &mdash; <code>definePlugin</code> <em>is</em> the contract check: it decodes your object against the <code>RigPlugin</code> schema in <code>@rig/contracts</code> and throws on a bad shape (missing/duplicate ids, bad name/version, under-declared effects). Since the executable fns ride on the same object, you call them directly &mdash; no session required:</p>
142
+ <pre class="code"><span class="c">// my-plugin.contract.test.ts</span>
143
+ <span class="k">import</span> { describe, it, expect } <span class="k">from</span> <span class="s">"bun:test"</span>;
144
+ <span class="k">import</span> myPlugin <span class="k">from</span> <span class="s">"./src/index"</span>;
145
+
146
+ <span class="k">const</span> plugin = <span class="f">myPlugin</span>();
147
+
148
+ <span class="f">describe</span>(<span class="s">"my-plugin contract"</span>, () =&gt; {
149
+ <span class="c">// 1) construction itself is the metadata contract — definePlugin threw if the shape was bad</span>
150
+ <span class="f">it</span>(<span class="s">"declares namespaced ids"</span>, () =&gt; {
151
+ <span class="k">const</span> v = plugin.contributes?.validators?.[<span class="p">0</span>];
152
+ <span class="f">expect</span>(v?.id).<span class="f">toMatch</span>(/^my-plugin:/);
153
+ });
154
+
155
+ <span class="c">// 2) call the executable validator directly, off the same object</span>
156
+ <span class="f">it</span>(<span class="s">"validator runs and passes"</span>, <span class="k">async</span> () =&gt; {
157
+ <span class="k">const</span> v = plugin.contributes?.validators?.[<span class="p">0</span>];
158
+ <span class="k">const</span> result = <span class="k">await</span> v!.<span class="f">run</span>({ taskId: <span class="s">"t-1"</span>, workspaceRoot: <span class="s">"/tmp/x"</span>, scope: [<span class="s">"src/a.ts"</span>] });
159
+ <span class="f">expect</span>(result.passed).<span class="f">toBe</span>(<span class="k">true</span>);
160
+ });
161
+
162
+ <span class="c">// 3) exercise a custom task-source factory by kind</span>
163
+ <span class="f">it</span>(<span class="s">"task source resolves its kind"</span>, () =&gt; {
164
+ <span class="k">const</span> ts = plugin.contributes?.taskSources?.[<span class="p">0</span>];
165
+ <span class="k">const</span> src = ts!.<span class="f">factory</span>({ kind: ts!.kind });
166
+ <span class="f">expect</span>(src.kind).<span class="f">toBe</span>(ts!.kind);
167
+ });
168
+ });</pre>
169
+ <p>Run it with <code>bun test</code> &mdash; the same runner the engine uses. Metadata is verified by construction; hook and skill <i>dispatch</i> are the runtime&rsquo;s job, so cover end-to-end hook behaviour with your own integration test.</p>
170
+
171
+ <h2 id="working-examples">Living references in the repo</h2>
172
+ <p>The first-party plugins under <code>packages/</code> are the reference implementations &mdash; real, composed, contract-tested code rather than toy snippets:</p>
173
+ <ul>
174
+ <li><b><code>packages/tasks-plugin/</code></b> &mdash; the single tasks capability: source access, source adapters, and read-only derived state. The canonical <code>taskSources</code> + read-model shape.</li>
175
+ <li><b><code>packages/github-provider-plugin/</code></b> &mdash; a swappable SCM provider (auth, credentials, Projects, issue analysis) contributed as a capability. Good for seeing <code>provides</code>/<code>requires</code> and <code>capabilityProviders</code> in practice.</li>
176
+ <li><b><code>packages/config-plugin/</code></b> &mdash; a compact plugin that contributes both a CLI command (<code>rig config get/set</code>) and read-model presentation cargo via a panel. A clean template for a command + display contribution.</li>
177
+ </ul>
178
+ <p>Copy the closest match, rename the <code>&lt;plugin-name&gt;:</code> id prefix everywhere, and fill in your own <code>factory(config)</code> and <code>run(ctx)</code> logic.</p>
179
+
180
+ <h2 id="see-also">See also</h2>
181
+ <ul>
182
+ <li><a href="examples/index.html">Plugin examples</a> &mdash; complete validator, task-source, and OMP/Pi extension packages with load and verification steps.</li>
183
+ <li><a href="../docs/validators/index.html">Validators &amp; hooks</a> &mdash; run-time behaviour of the two channels you contribute most.</li>
184
+ <li><a href="../docs/task-sources/index.html">Task sources</a> &mdash; the first-party <code>github-issues</code> / <code>linear</code> adapters and their conventions.</li>
185
+ <li><a href="../skills/index.html">Skills</a> &mdash; SKILL.md authoring and how plugin skills reach the OMP/Pi session.</li>
186
+ <li><a href="../docs/pi-extensions/index.html">OMP/Pi extensions</a> &mdash; the other half: extend the live OMP session, not the workflow automation Rig runs around it.</li>
187
+ <li><a href="../config/index.html">rig.config.ts</a> &mdash; where <code>plugins: [...]</code> and <code>taskSource</code> are declared.</li>
188
+ </ul>
189
+
190
+ <div class="note"><b>Plugins extend the workflow automation Rig runs around a session; OMP/Pi extensions extend the live session.</b> If what you want is a new tool or slash command <i>inside OMP</i>, you want an <a href="../docs/pi-extensions/index.html">OMP/Pi extension</a>, not a Rig plugin. The two compose: a Rig plugin declares workflow metadata; an OMP/Pi package acts at session start.</div>
191
+
192
+ <div class="nextnav"><a href="../docs/environment/index.html"><span class="dir">&larr; Back</span><span class="ttl">Environment variables</span></a><a href="../plugins/examples/index.html"><span class="dir">Next &rarr;</span><span class="ttl">Plugin examples</span></a></div>
193
+ </main></div><footer><div class="wrap"><div class="foot"><div class="col"><h5>orchestrate</h5><a href="../docs/getting-started/index.html">getting started</a><a href="../docs/runs/index.html">the run lifecycle</a><a href="../docs/operator/index.html">operator guide</a><a href="../docs/gate/index.html">the merge gate</a></div><div class="col"><h5>extend</h5><a href="../plugins/index.html">plugin authoring</a><a href="../skills/index.html">skills guide</a><a href="../docs/pi-extensions/index.html">OMP/Pi extensions</a><a href="../docs/packages/index.html">packages</a></div><div class="col"><h5>live</h5><a href="../index.html">the home rig</a><a href="../cli/index.html">cli reference</a><a href="https://where.rig-does.work/install">install script</a></div><div class="sig">Rig is the OMP session extension.<br><span class="blk">You are the operator.</span><br>release: rig --version</div></div></div></footer><script src="../assets/rig.js"></script><script src="../assets/site.js?v=v9"></script></body></html>
@@ -0,0 +1,140 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Skills // RIG</title><meta property="og:image" content="https://how.rig-does.work/assets/og-card.png"><meta name="twitter:card" content="summary_large_image"><meta name="description" content="Author SKILL.md prompt context, ship it in a plugin, and understand how Rig materializes skills for OMP/Pi sessions without scanning arbitrary packages."><meta property="og:title" content="Skills // RIG"><meta property="og:description" content="Author SKILL.md prompt context, ship it in a plugin, and understand how Rig materializes skills for OMP/Pi sessions without scanning arbitrary packages."><link rel="stylesheet" href="../assets/rig.css?v=v9"><link rel="stylesheet" href="../assets/site.css?v=v9"><link rel="icon" type="image/svg+xml" href="../assets/favicon.svg"></head><body><canvas id="docfield"></canvas><nav class="nav"><div class="nav-in"><a class="brand" href="../index.html"><span class="mk"></span><span class="blk">Rig</span></a><button class="burger" onclick="document.getElementById('nl').classList.toggle('open')">menu</button><div class="nav-links" id="nl"><button class="nsearch" type="button" onclick="rigSearchOpen()">search<kbd>&#8984;K</kbd></button><a href="../index.html" class="">overview</a><a href="../docs/index.html" class="">docs</a><a href="../plugins/index.html" class="">plugins</a><a href="../skills/index.html" class="active">skills</a><a href="../cli/index.html" class="">cli</a><a href="../config/index.html" class="">config</a><a href="https://github.com/humanity-org/rig" class="ext">github</a></div></div></nav><div class="docwrap"><aside class="sidebar"><div class="grp">START</div><a href="../docs/index.html" class="">docs home</a><a href="../docs/capabilities/index.html" class="">capabilities</a><a href="../docs/getting-started/index.html" class="">getting started</a><div class="grp">RUN</div><a href="../docs/runs/index.html" class="">the run lifecycle</a><a href="../docs/operator/index.html" class="">operator guide</a><a href="../docs/swarm-commander/index.html" class="">swarm commander</a><a href="../docs/fleet-irc/index.html" class="">drone messages</a><a href="../docs/agent-tools/index.html" class="">agent tools</a><a href="../docs/gate/index.html" class="">the merge gate</a><a href="../docs/troubleshooting/index.html" class="">troubleshooting</a><div class="grp">ARCHITECTURE</div><a href="../docs/architecture/index.html" class="">how rig works</a><a href="../docs/entities/index.html" class="">entities & data model</a><a href="../docs/server/index.html" class="">OMP relay/web</a><a href="../docs/security/index.html" class="">security & trust</a><a href="../docs/environment/index.html" class="">environment variables</a><div class="grp">EXTEND</div><a href="../plugins/index.html" class="">plugin authoring</a><a href="../plugins/examples/index.html" class="">plugin examples</a><a href="../docs/pi-extensions/index.html" class="">OMP/Pi extensions</a><a href="../skills/index.html" class="active">skills</a><a href="../docs/task-sources/index.html" class="">task sources</a><a href="../docs/validators/index.html" class="">validators & hooks</a><div class="grp">OPERATE</div><a href="../cli/index.html" class="">cli reference</a><a href="../config/index.html" class="">rig.config.ts</a><a href="../docs/recipes/index.html" class="">recipes</a><a href="../docs/glossary/index.html" class="">glossary</a><a href="../docs/faq/index.html" class="">faq</a><div class="grp">ENGINE</div><a href="../docs/packages/index.html" class="">packages</a></aside><main class="doc"><h1>Skills</h1>
2
+ <p class="lede">A skill is standing prompt context for an OMP/Pi session &mdash; a <code>SKILL.md</code> file, YAML frontmatter plus a markdown body, that a plugin ships and Rig materializes verbatim into <code>.pi/skills/</code> where OMP/Pi reads it.</p>
3
+
4
+ <h2 id="what-a-skill-is">What a skill is <span class="ns">// reusable prompt context</span></h2>
5
+ <p>An OMP/Pi session starts from a base prompt plus whatever context the work carries. A skill injects <em>standing</em> context on top of that &mdash; a procedure, a checklist, a house rule &mdash; so you brief the session once instead of pasting it into every issue. The format is deliberately boring: a markdown file with a small YAML header. No build step, no runtime, no code path. The frontmatter names the skill; the body <em>is</em> the prompt.</p>
6
+ <p>Skills reach OMP/Pi by being copied into the directory it scans. Rig&rsquo;s job is to take the <code>SKILL.md</code> files a plugin declares, validate them, and place them where OMP/Pi looks. Discovery is native from there.</p>
7
+
8
+ <div class="note">A skill is content, not behavior. If you need to <em>add a tool, a slash command, or a hook</em>, that&rsquo;s a <a href="../docs/pi-extensions/index.html">Pi extension</a> or a <a href="../docs/validators/index.html">hook</a>, not a skill. See <a href="#skills-vs-extensions-vs-hooks">skills vs. extensions vs. hooks</a> below for the dividing line.</div>
9
+
10
+ <h2 id="skill-md-format">The SKILL.md format <span class="ns">// frontmatter + body</span></h2>
11
+ <p>One file. The frontmatter block is delimited by <code>---</code> lines; everything after the closing delimiter is the body. Here is a real, non-trivial skill &mdash; a boundary-analysis procedure an OMP/Pi session can follow when asked to touch module edges:</p>
12
+
13
+ <pre class="code"><span class="c">---</span>
14
+ name: boundary-analysis
15
+ description: How to reason about module boundaries before editing across them
16
+ owner: platform-team
17
+ priority: high
18
+ <span class="c">---</span>
19
+
20
+ <span class="c"># Boundary analysis</span>
21
+
22
+ Before editing code that crosses a package or module boundary, do this:
23
+
24
+ 1. Identify the boundary. Name the two sides and the contract between them
25
+ (the exported types, the function signatures, the event shapes).
26
+ 2. Decide which side owns the change. A change is &quot;upstream&quot; if it alters
27
+ the contract, &quot;downstream&quot; if it only consumes it. Prefer downstream.
28
+ 3. If the change is upstream, list every downstream consumer first. Do not
29
+ edit the contract until you have the full blast radius written down.
30
+ 4. Keep the diff on one side of the boundary per commit where possible.
31
+ A commit that edits both the producer and three consumers is hard to
32
+ review and hard to revert.
33
+
34
+ When in doubt, widen the type on the producer and narrow on the consumer,
35
+ never the reverse &mdash; that keeps old callers working.</pre>
36
+
37
+ <p>The body can be any length and any markdown. It is copied verbatim, so write it for the OMP/Pi session the way you would brief a new engineer: concrete steps, not vibes.</p>
38
+
39
+ <h3 id="frontmatter-fields">Frontmatter fields</h3>
40
+ <p>The loader parses the header line-by-line into key/value pairs. <code>name</code> is the only required field. Everything else is optional and free-form.</p>
41
+ <table class="t"><thead><tr><th>field</th><th>required</th><th>meaning</th></tr></thead>
42
+ <tbody>
43
+ <tr><td>name</td><td>yes</td><td>The skill&rsquo;s human name. Must be a string, or the skill is skipped with a warning at materialization.</td></tr>
44
+ <tr><td>description</td><td>no</td><td>One-line summary. Carried on the plugin&rsquo;s skill registration, not required in the frontmatter.</td></tr>
45
+ <tr><td><i>any other key</i></td><td>no</td><td>Arbitrary extra keys are tolerated (e.g. <code>owner</code>, <code>priority</code>). The parser coerces <code>true</code>/<code>false</code>, integers, and decimals and unquotes quoted strings while validating, then copies the file through unchanged.</td></tr>
46
+ </tbody></table>
47
+ <div class="note">The frontmatter parser is intentionally small &mdash; it is line-based, not a full YAML engine. Keep each field on its own <code>key: value</code> line. Lines starting with <code>#</code> and blank lines inside the header are ignored. Nested YAML structures are not parsed. <b>Keep the header flat.</b></div>
48
+
49
+ <h2 id="contributing-a-skill">How a plugin contributes a skill <span class="ns">// the registration</span></h2>
50
+ <p>A plugin-contributed skill reaches OMP/Pi through explicit Rig materialization &mdash; nothing is scanned from arbitrary packages. A plugin declares each one in its <code>contributes.skills</code> array with a namespaced id and a path to the <code>SKILL.md</code> on disk:</p>
51
+ <pre class="code">contributes: {
52
+ skills: [{
53
+ id: <span class="s">"my-plugin:boundary-analysis"</span>,
54
+ path: <span class="k">new</span> <span class="f">URL</span>(<span class="s">"skills/boundary-analysis/SKILL.md"</span>, import.meta.url).pathname,
55
+ description: <span class="s">"Boundary analysis prompt"</span>,
56
+ }]
57
+ }</pre>
58
+ <p>The <code>id</code> follows the standard <code>&lt;plugin-name&gt;:&lt;local-id&gt;</code> namespacing convention and must be unique across loaded plugins. For the <code>path</code>: use an absolute path (via <code>import.meta.url</code>) for npm-installed plugins, since the package can be unpacked anywhere; a relative path is resolved against the project root. The full plugin shape lives in <a href="../plugins/index.html">plugin authoring &rarr;</a>.</p>
59
+
60
+ <h2 id="the-loader">Validation <span class="ns">// what the materializer checks</span></h2>
61
+ <p>The skill materializer inside Rig&rsquo;s provider plugin turns each declared <code>SKILL.md</code> into a materialized directory. Before it copies a file it does one check: split the frontmatter from the body, parse the header, and enforce that a string <code>name</code> exists. If that passes, the <em>whole</em> file is copied through byte for byte &mdash; frontmatter and body together, exactly as the author wrote it.</p>
62
+
63
+ <h3 id="loader-behavior">What validation guarantees</h3>
64
+ <ul>
65
+ <li><b>name is mandatory.</b> If the parsed frontmatter has no string <code>name</code>, the materializer throws <code>SKILL.md missing required field "name"</code> for that file, skips it with a warning, and keeps going. A file with no frontmatter at all fails the same way &mdash; no header means no <code>name</code>.</li>
66
+ <li><b>the file is copied verbatim.</b> The materialized <code>SKILL.md</code> is the source file unchanged &mdash; frontmatter, body, whitespace and all &mdash; so the session sees exactly what the author wrote.</li>
67
+ <li><b>the header parser is lenient.</b> Extra keys beyond <code>name</code> and <code>description</code> are tolerated, not rejected. The parser is line-based, not a full YAML engine.</li>
68
+ <li><b>scalars are coerced while parsing.</b> <code>true</code>/<code>false</code> become booleans; integer and decimal strings become numbers; single- or double-quoted strings are unquoted &mdash; this feeds the <code>name</code> check, and does not alter the copied file.</li>
69
+ </ul>
70
+ <p>This is the same validation Rig runs on every boot. There is no second, hidden validator.</p>
71
+
72
+ <h2 id="materialization">Materialization <span class="ns">// placing skills where OMP/Pi looks</span></h2>
73
+ <p>This is where the skill becomes a file the OMP/Pi session can read. During explicit Rig materialization, the plugin host flattens every configured plugin&rsquo;s <code>contributes.skills</code> into one list and writes it into the workspace. Each skill becomes a directory under <code>.pi/skills/</code> holding the verbatim <code>SKILL.md</code> plus a marker file. The directory name is the skill id with every run of non-directory-safe characters replaced by <code>-</code> &mdash; so <code>my-plugin:boundary-analysis</code> lands at <code>.pi/skills/my-plugin-boundary-analysis/SKILL.md</code>.</p>
74
+
75
+ <pre class="code"><span class="c">// project root</span>
76
+ .pi/
77
+ <span class="p">&#9492;&#9472;</span> skills/
78
+ <span class="p">&#9492;&#9472;</span> my-plugin-boundary-analysis/ <span class="c">// &lt;sanitized-id&gt;</span>
79
+ <span class="p">&#9500;&#9472;</span> SKILL.md <span class="c">// copied verbatim from the plugin</span>
80
+ <span class="p">&#9492;&#9472;</span> .rig-plugin <span class="c">// { "plugin": "my-plugin", "skillId": "my-plugin:boundary-analysis" }</span></pre>
81
+
82
+ <p>This is the same directory OMP/Pi scans natively. OMP/Pi looks in <code>&lt;cwd&gt;/.pi/skills/</code> (project scope) and <code>~/.pi/agent/skills/</code> (user scope). Rig only ever writes the project-scope copy; the user-scope directory is yours.</p>
83
+
84
+ <div class="note"><b>Correction worth stating plainly:</b> the project-scope skill directory is <code>.pi/skills/</code> &mdash; not <code>.agents/skills/</code>. The materialized location and OMP/Pi&rsquo;s scan path are the same <code>.pi/skills/</code> tree.</div>
85
+ <p>Current boot path: Rig resolves the project plugin host, asks the provider-owned session asset materializer to run, and that materializer handles both sides of the OMP/Pi handoff. Plugin skills are copied into <code>.pi/skills/</code>; config-declared Pi packages from <code>runtime.pi.packages</code> are merged into <code>.pi/settings.json</code> with Rig-managed package state tracked separately in <code>.rig/state/pi-managed-packages.json</code>. The settings file stays Pi-native &mdash; no Rig marker fields are added to it.</p>
86
+
87
+ <h3 id="idempotency-and-the-marker">Idempotency &amp; the .rig-plugin marker</h3>
88
+ <p>Materialization is idempotent and safe to run on every boot. Before writing the current set, Rig scans <code>.pi/skills/</code> and deletes <em>only</em> directories that carry a <code>.rig-plugin</code> marker, then re-inserts the current plugin skills. The marker is a small JSON file recording the owning plugin and the skill id:</p>
89
+ <pre class="code"><span class="p">{</span>
90
+ <span class="s">"plugin"</span>: <span class="s">"my-plugin"</span>,
91
+ <span class="s">"skillId"</span>: <span class="s">"my-plugin:boundary-analysis"</span>
92
+ <span class="p">}</span></pre>
93
+ <p>Because the delete is gated on the marker, the cycle converges: a plugin you removed from <code>rig.config.ts</code> loses its materialized directory on the next boot, and a plugin you kept gets exactly one fresh copy.</p>
94
+
95
+ <h3 id="operator-sovereignty">Operator sovereignty</h3>
96
+ <p>Any skill directory <em>without</em> a <code>.rig-plugin</code> marker is treated as operator-authored and is never deleted, never overwritten. You can drop your own <code>SKILL.md</code> into <code>.pi/skills/</code> by hand and it will sit alongside plugin skills untouched across every re-materialization. <b>Plugin-owned is replaceable; operator-owned is sovereign.</b></p>
97
+
98
+ <div class="note"><b>The same discipline applies to hooks.</b> Plugin hooks are written into the active harness settings file with their own plugin marker; operator-authored hook entries are preserved on re-materialization. One rule across skills and hooks: marked = managed by Rig, unmarked = yours. See <a href="../docs/validators/index.html">validators &amp; hooks &rarr;</a>.</div>
99
+
100
+ <h3 id="boot-safety">Boot-safety &mdash; a bad skill never blocks a run</h3>
101
+ <p>Materialization is non-fatal by design. If a declared <code>SKILL.md</code> is missing on disk, or fails frontmatter validation (no string <code>name</code>), Rig skips that one skill with a warning and continues &mdash; it does not abort the OMP/Pi session boot. The validated file is then copied verbatim, so the session sees exactly what the author wrote, byte for byte.</p>
102
+
103
+ <h2 id="reproducibility">Reproducibility, honestly <span class="ns">// what &ldquo;pinned&rdquo; does and doesn&rsquo;t mean</span></h2>
104
+ <p>Skills are reproducible in the plain sense: the <code>SKILL.md</code> is a file checked into the plugin (or the project), and materialization copies it verbatim. Two operators on the same plugin version get the same skill body. That is the whole story.</p>
105
+ <div class="note">There is <b>no separate version-lock or pin mechanism</b> for an individual skill beyond the plugin&rsquo;s own version. Reproducibility comes from the plugin being pinned in <a href="../config/index.html">rig.config.ts</a> and the file being copied unchanged &mdash; not from any per-skill pin. If you want a skill frozen, freeze the plugin version that ships it.</div>
106
+
107
+ <h2 id="loading-a-skill-in-tests">Testing a skill</h2>
108
+ <p>Verify a skill the same way Rig does &mdash; materialize it into a scratch project root and assert the file landed. A valid skill writes <code>.pi/skills/&lt;sanitized-id&gt;/SKILL.md</code> and is reported in the returned list; an invalid one is skipped and the list comes back empty:</p>
109
+ <pre class="code"><span class="c">// materialize into a temp root, then assert on the result</span>
110
+ <span class="k">const</span> written = <span class="k">await</span> <span class="f">materializeSkills</span>(root, [
111
+ { pluginName: <span class="s">"my-plugin"</span>, skill: { id: <span class="s">"my-plugin:boundary-analysis"</span>, path: <span class="s">"SKILL.md"</span> } },
112
+ ]);
113
+
114
+ <span class="f">expect</span>(written).<span class="f">toHaveLength</span>(<span class="s">1</span>);
115
+ <span class="f">expect</span>(existsSync(resolve(root, <span class="s">".pi"</span>, <span class="s">"skills"</span>, <span class="s">"my-plugin-boundary-analysis"</span>, <span class="s">"SKILL.md"</span>))).<span class="f">toBe</span>(<span class="k">true</span>);</pre>
116
+
117
+ <h2 id="session-skills-in-the-palette">Session skills in the native palette <span class="ns">// what the Rig session reports</span></h2>
118
+ <p>Bare <code>rig</code> and every detached OMP drone report only the skills their own runtime loaded. Foreground-only capabilities are not projected as drone capabilities, regardless of local or remote placement. Fleet work materializes through inline <code>/fleet</code>, <code>/tasks</code>, and <code>/drone</code> cards; skills remain prompt context. Full live UX in <a href="../docs/pi-extensions/index.html">OMP/Pi extensions &rarr;</a>.</p>
119
+
120
+ <h2 id="skills-vs-extensions-vs-hooks">Skills vs. extensions vs. hooks <span class="ns">// pick the right tool</span></h2>
121
+ <p>Three mechanisms extend what an OMP/Pi-backed workflow can do. They are not interchangeable:</p>
122
+ <table class="t"><thead><tr><th>mechanism</th><th>what it adds</th><th>where it lives</th></tr></thead>
123
+ <tbody>
124
+ <tr><td>skill</td><td>Standing prompt context &mdash; procedures, checklists, house rules. Content only.</td><td><code>.pi/skills/&lt;sanitized-id&gt;/SKILL.md</code></td></tr>
125
+ <tr><td>OMP/Pi extension</td><td>Installable package: tools, slash commands, behaviors. Code.</td><td><code>.pi/settings.json</code> <code>packages</code>, auto-installed at session start</td></tr>
126
+ <tr><td>hook</td><td>A guard that fires on a lifecycle event (e.g. block edits outside scope).</td><td>Active harness settings file, with a plugin marker</td></tr>
127
+ </tbody></table>
128
+ <p><b>OMP/Pi extensions</b> are discovered and added with <code>rig pi search</code> / <code>rig pi add</code>, or declared team-wide via <code>runtime.pi.packages</code> in <a href="../config/index.html">rig.config.ts</a> &mdash; Rig materializes the list into <code>.pi/settings.json</code> and OMP/Pi auto-installs at session start on every host. Reach for a skill when you need to <em>tell</em> the session something; reach for an extension when you need to <em>give</em> it a new capability; reach for a hook when you need to <em>stop</em> it doing something.</p>
129
+
130
+ <h2 id="see-also">See also</h2>
131
+ <ul>
132
+ <li><a href="../plugins/index.html">Plugin authoring</a> &mdash; the full <code>contributes</code> shape and how to ship a skill in a package.</li>
133
+ <li><a href="../plugins/examples/index.html#pi-extension-package">Plugin examples: OMP/Pi package</a> &mdash; a small live-session tool pinned through <code>runtime.pi.packages</code> and materialized beside skills.</li>
134
+ <li><a href="../docs/pi-extensions/index.html">OMP/Pi extensions</a> &mdash; installable tools and slash commands, and the session/collab UX.</li>
135
+ <li><a href="../docs/validators/index.html">Validators &amp; hooks</a> &mdash; the other side of the materialization discipline.</li>
136
+ <li><a href="../config/index.html">rig.config.ts</a> &mdash; where plugins and <code>runtime.pi.packages</code> are pinned.</li>
137
+ </ul>
138
+
139
+ <div class="nextnav"><a href="../docs/pi-extensions/index.html"><span class="dir">&larr; Back</span><span class="ttl">OMP/Pi extensions</span></a><a href="../docs/task-sources/index.html"><span class="dir">Next &rarr;</span><span class="ttl">Task sources</span></a></div>
140
+ </main></div><footer><div class="wrap"><div class="foot"><div class="col"><h5>orchestrate</h5><a href="../docs/getting-started/index.html">getting started</a><a href="../docs/runs/index.html">the run lifecycle</a><a href="../docs/operator/index.html">operator guide</a><a href="../docs/gate/index.html">the merge gate</a></div><div class="col"><h5>extend</h5><a href="../plugins/index.html">plugin authoring</a><a href="../skills/index.html">skills guide</a><a href="../docs/pi-extensions/index.html">OMP/Pi extensions</a><a href="../docs/packages/index.html">packages</a></div><div class="col"><h5>live</h5><a href="../index.html">the home rig</a><a href="../cli/index.html">cli reference</a><a href="https://where.rig-does.work/install">install script</a></div><div class="sig">Rig is the OMP session extension.<br><span class="blk">You are the operator.</span><br>release: rig --version</div></div></div></footer><script>document.querySelectorAll('[data-tab]').forEach(function(t){t.onclick=function(){document.querySelectorAll('[data-tab]').forEach(function(x){x.classList.remove('on')});document.querySelectorAll('[data-cmd]').forEach(function(x){x.style.display='none'});t.classList.add('on');var c=document.querySelector('[data-cmd="'+t.dataset.tab+'"]');if(c)c.style.display='flex';};});document.querySelectorAll('.cp').forEach(function(b){b.onclick=function(){var code=b.parentElement.querySelector('code');if(navigator.clipboard)navigator.clipboard.writeText(code.innerText);var o=b.innerText;b.innerText='copied';setTimeout(function(){b.innerText=o;},1200);};});</script><script src="../assets/rig.js"></script><script src="../assets/site.js?v=v9"></script></body></html>
@@ -0,0 +1 @@
1
+ <!doctype html><meta http-equiv="refresh" content="0; url=../../index.html"><link rel="canonical" href="../../index.html"><title>Redirecting…</title><a href="../../index.html">the live deck now lives on the home page →</a>