@msdavid/pi-distro 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.
Files changed (35) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +394 -0
  4. package/docs/authoring.md +238 -0
  5. package/docs/preview.png +0 -0
  6. package/docs/preview.svg +58 -0
  7. package/extensions/catalogue.ts +221 -0
  8. package/extensions/deploy.ts +141 -0
  9. package/extensions/frontmatter.ts +158 -0
  10. package/extensions/github.ts +110 -0
  11. package/extensions/index.ts +86 -0
  12. package/extensions/info.ts +133 -0
  13. package/extensions/pick.ts +132 -0
  14. package/extensions/resolve.ts +70 -0
  15. package/extensions/save.ts +217 -0
  16. package/extensions/show.ts +96 -0
  17. package/extensions/undeploy.ts +124 -0
  18. package/extensions/update.ts +109 -0
  19. package/extensions/util.ts +44 -0
  20. package/harnesses/minimal/README.md +21 -0
  21. package/harnesses/minimal/files/AGENTS.md +20 -0
  22. package/harnesses/minimal/files/settings.json +4 -0
  23. package/harnesses/minimal/harness.md +24 -0
  24. package/harnesses/pi-distro-one/README.md +50 -0
  25. package/harnesses/pi-distro-one/files/.pi/extensions/claude-statusline.ts +220 -0
  26. package/harnesses/pi-distro-one/files/AGENTS.md +166 -0
  27. package/harnesses/pi-distro-one/files/settings.json +9 -0
  28. package/harnesses/pi-distro-one/harness.md +79 -0
  29. package/harnesses/web-fullstack/README.md +25 -0
  30. package/harnesses/web-fullstack/files/.pi/prompts/review.md +12 -0
  31. package/harnesses/web-fullstack/files/AGENTS.md +37 -0
  32. package/harnesses/web-fullstack/files/settings.json +11 -0
  33. package/harnesses/web-fullstack/harness.md +40 -0
  34. package/package.json +65 -0
  35. package/skills/pi-distro/SKILL.md +359 -0
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@msdavid/pi-distro",
3
+ "version": "0.2.0",
4
+ "description": "Reusable, composable configurations for the pi coding agent — seed distros, project snapshots, and GitHub sharing with version-aware updates.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "msdavid",
8
+ "homepage": "https://github.com/msdavid/pi-distro#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/msdavid/pi-distro.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/msdavid/pi-distro/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "pi",
19
+ "coding-agent",
20
+ "configuration",
21
+ "distro",
22
+ "developer-tools"
23
+ ],
24
+ "engines": {
25
+ "node": ">=22.19.0"
26
+ },
27
+ "sideEffects": false,
28
+ "files": [
29
+ "extensions",
30
+ "skills",
31
+ "harnesses",
32
+ "README.md",
33
+ "docs",
34
+ "LICENSE",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "pi": {
41
+ "extensions": ["./extensions"],
42
+ "skills": ["./skills"],
43
+ "image": "https://raw.githubusercontent.com/msdavid/pi-distro/main/docs/preview.png"
44
+ },
45
+ "scripts": {
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "node --import tsx --test tests/*.test.ts"
48
+ },
49
+ "peerDependencies": {
50
+ "@earendil-works/pi-ai": "*",
51
+ "@earendil-works/pi-agent-core": "*",
52
+ "@earendil-works/pi-coding-agent": "*",
53
+ "@earendil-works/pi-tui": "*",
54
+ "typebox": "*"
55
+ },
56
+ "devDependencies": {
57
+ "@earendil-works/pi-ai": "^0.80.0",
58
+ "@earendil-works/pi-coding-agent": "^0.80.0",
59
+ "@earendil-works/pi-tui": "^0.80.0",
60
+ "@types/node": "^24.0.0",
61
+ "tsx": "^4.23.0",
62
+ "typebox": "^1.1.24",
63
+ "typescript": "^5.0.0"
64
+ }
65
+ }
@@ -0,0 +1,359 @@
1
+ ---
2
+ name: pi-distro
3
+ description: "Manages pi distros — reusable, composable project configurations. Use when deploying a distro (/pi-distro deploy), saving the current project config as a distro (/pi-distro save), listing/showing/removing distros, or checking distro status. Triggers on: distro, save my config, deploy a distro, set up a distro."
4
+ ---
5
+
6
+ # pi-distro Skill
7
+
8
+ This skill guides the agent through the interactive phases of the pi-distro
9
+ extension: deploying a distro (`/pi-distro deploy`) and saving a live project
10
+ configuration as a distro (`/pi-distro save`). The extension command handles
11
+ catalogue reading, UI selectors, and kickoff-message injection; this skill provides
12
+ the agent-side guidance for those non-deterministic, collaborative flows.
13
+
14
+ ## Governing principle: the user is in the loop for every decision
15
+
16
+ pi-distro is a **collaborative, agent-driven** tool. The agent never makes a
17
+ state-changing decision for the user. Before overwriting a file, installing a
18
+ package, skipping a step, replacing a tool, applying an upgrade, or resolving any
19
+ conflict (merge, redundancy, version downgrade, same-version, etc.), the agent
20
+ must:
21
+
22
+ 1. **Surface** the decision clearly — what it's about to do, and why.
23
+ 2. **Present** the available options (skip / replace / keep theirs / keep both /
24
+ cancel, as applicable).
25
+ 3. **Wait** for the user's explicit choice. Never proceed on assumption.
26
+
27
+ Never silently skip, silently overwrite, silently substitute, or silently choose.
28
+ If a decision is ambiguous or the user is unsure, explain the tradeoffs and let
29
+ them choose. **The agent proposes; the user disposes.** Every other rule in this
30
+ skill (merge-don't-clobber, package-redundancy, version-aware deploy, the GitHub
31
+ trust gate) is a specific instance of this principle.
32
+
33
+ ## `/pi-distro deploy` — Distro Deployment
34
+
35
+ When you receive a kickoff message from the `deploy` command, it contains:
36
+
37
+ - The full `harness.md` body (directives) for the selected distro.
38
+ - A manifest of bundled files (source path → target path) and the absolute path of
39
+ the distro `files/` directory on disk.
40
+ - The merge-don't-clobber rule.
41
+ - An instruction to write/update provenance.
42
+
43
+ The distro may come from the local catalogue (seed or user-saved) or from GitHub
44
+ (`/pi-distro deploy owner/repo`). For GitHub distros, the extension has already
45
+ cloned the repo, displayed a security warning + preview, and obtained the user's
46
+ explicit confirmation before sending the kickoff — so you can proceed normally.
47
+ The provenance `sourceCatalogue` will be `github:owner/repo[/subpath]`.
48
+ Bundled files for GitHub distros are located in a temp directory (`/tmp/`) — copy
49
+ them from there as usual.
50
+
51
+ ### Deployment steps
52
+
53
+ 1. **Read the directives.** The `harness.md` body tells you what to set up — bundled
54
+ files, pi packages to install, hooks/extensions to create, context to write, and
55
+ skills/prompts to configure. Follow those directives.
56
+
57
+ 2. **Deploy bundled files with merge-don't-clobber.** For every bundled file in the
58
+ manifest that maps to a target path in the project:
59
+
60
+ - If the target file does **not** exist, copy it verbatim from the distro `files/`
61
+ directory.
62
+ - If the target file **already exists**, do **NOT** overwrite it silently. Instead:
63
+ - Read both the existing file and the bundled source.
64
+ - Show the user a diff (or summary of differences).
65
+ - Ask the user whether to **overwrite**, **keep theirs**, or **merge**.
66
+ - For **JSON files** (e.g. `settings.json`): if the user chooses merge, merge
67
+ field-by-field — combine keys from both objects, with the bundled values applied
68
+ on top but respecting existing user customisations where the user prefers to keep
69
+ them. Never silently destroy a key the user added.
70
+ - For **AGENTS.md**: never replace. Append the bundled content under a clearly
71
+ delimited section, e.g.:
72
+
73
+ ```markdown
74
+ <!-- pi-distro: <distro-name> -->
75
+ ...bundled AGENTS.md content...
76
+ <!-- /pi-distro: <distro-name> -->
77
+ ```
78
+
79
+ If the section already exists (re-deploy), replace only that delimited section.
80
+
81
+ 3. **Install pi packages — with redundancy/conflict detection.** If the directives list pi packages to
82
+ install, run `pi install -l <package>` for each one — the `-l` flag installs
83
+ **project-locally** (writes to `./.pi/settings.json`, not global) — but **only after
84
+ confirming with the user** AND after evaluating tool redundancy/conflicts:
85
+
86
+ **Do NOT pre-add packages to `./.pi/settings.json` by hand.** `pi install -l` is the single
87
+ mechanism that registers a package: it installs the package AND appends the source to
88
+ `./.pi/settings.json` on success. If an install fails, nothing is added to settings — so
89
+ settings never contains a package that isn't actually installed. (This is why the bundled
90
+ `settings.json` does not list packages — they are registered by `pi install -l`, not by
91
+ merging a `packages` array.)
92
+
93
+ **Redundancy/conflict evaluation (do this BEFORE installing each package):** Some packages
94
+ may provide tools that overlap with already-active tools. This can be:
95
+ - An **exact name collision** — two tools with the same name. pi handles these by load
96
+ order (project-local tools shadow global ones; the conflict is a **non-fatal diagnostic**,
97
+ not a fatal error — pi still starts, but the shadowed tool is unavailable).
98
+ - **Semantic redundancy** — different tool names, but doing very similar things (e.g. two
99
+ web-search tools, two browser-automation tools, two todo-list tools). These both load,
100
+ leaving the user with duplicate capability and a confusing tool set.
101
+ You (the agent) must **evaluate** this — it requires judgment, not just string matching.
102
+ Before installing each package:
103
+ 1. Read the **already-active tools** and **project packages** from the kickoff's
104
+ "Current project state" section, and run `pi list` to see globally-installed packages.
105
+ 2. For each to-be-installed package, compare its stated purpose (from the directives)
106
+ against the already-active tools. Ask: *does this package do something an existing tool
107
+ already does?* Consider both exact name matches and semantic overlap.
108
+ 3. If redundancy or a conflict is detected, do **NOT** install blindly — present the user
109
+ a choice and explain the overlap:
110
+ - **(a) Skip** — the capability already exists; don't install the package. Note this
111
+ in the provenance/deployment report.
112
+ - **(b) Replace** — `pi remove -l <overlapping-package>` (or remove it globally with
113
+ `pi remove <pkg>` if that's where it lives), then `pi install -l <new-package>`.
114
+ - **(c) Keep both** — install it anyway. Use this when the user prefers the new tool,
115
+ or when the two tools serve subtly different purposes despite surface similarity.
116
+ - **(d) Cancel** — don't install anything.
117
+ 4. Only proceed with the user's chosen option.
118
+
119
+ 4. **Create hooks / extensions / prompts / skills.** If the directives instruct creating
120
+ files under `./.pi/extensions/`, `./.pi/prompts/`, or `./.pi/skills/`, create them
121
+ using the bundled source files or the directives' instructions. Apply the same
122
+ merge-don't-clobber rule for any that already exist.
123
+
124
+ 5. **Write/update provenance.** When the deployment is complete, write (or update)
125
+ `./.pi/harness.md` in the project directory. The provenance file is itself a valid
126
+ `harness.md` (the applied distro's frontmatter + directives) with a provenance
127
+ header injected at the top of the body:
128
+
129
+ ```markdown
130
+ <!-- pi-distro provenance
131
+ appliedHarness: <name>
132
+ appliedVersion: <version>
133
+ sourceCatalogue: <user|seed|none>
134
+ lastUpdated: <ISO8601>
135
+ -->
136
+ ```
137
+
138
+ - `appliedHarness`: the name of the distro that was deployed.
139
+ - `appliedVersion`: the version from the distro frontmatter.
140
+ - `sourceCatalogue`: `seed` if it came from the package seeds, `user` if from
141
+ `~/.pi/harnesses/`.
142
+ - `lastUpdated`: current timestamp in ISO 8601 format.
143
+
144
+ 6. **Report.** Summarise what was deployed, merged, skipped, and any packages installed.
145
+ Mention that the user can run `/pi-distro status` to see the current configuration.
146
+
147
+ 7. **Recommend a restart.** Tell the user to **restart pi** — newly installed packages and
148
+ extensions are loaded at startup and are not available until the next session. The
149
+ configuration changes (settings, context, skills) take effect immediately, but code
150
+ that runs at startup (extensions, package-provided tools) requires a restart.
151
+
152
+ ### Version-aware deploy
153
+
154
+ The kickoff message includes a **Version note** section that compares the incoming distro
155
+ version against the project's existing provenance (`appliedVersion` in `./.pi/harness.md`).
156
+ Follow it:
157
+ - **Upgrade** (incoming > existing): proceed normally with merge-don't-clobber. Existing
158
+ user customisations should be preserved — the new version adds/changes, it doesn't wipe.
159
+ - **Downgrade** (incoming < existing): **ask the user to confirm** before proceeding — this
160
+ may remove features or regress fixes. Only proceed if they confirm.
161
+ - **Same version** (incoming == existing): **ask the user** whether to (a) skip (no changes
162
+ — the project already has this exact distro) or (b) force re-deploy (re-run the merge,
163
+ useful if files were manually edited or the distro was updated in-place without a version
164
+ bump). Only proceed if they choose (b).
165
+ - **Different distro** (existing `appliedHarness` != incoming name): treat as a distro
166
+ switch — do not assume the new distro's files are a superset of the old. Merge with
167
+ existing config; the user may want to clean up files the old distro added.
168
+ - **First deploy** (no existing provenance): proceed normally.
169
+
170
+ ## `/pi-distro update` — Update Applied Distro
171
+
172
+ When you receive a kickoff from the `update` command, the extension has already:
173
+
174
+ 1. Read the project's provenance (`appliedHarness`, `appliedVersion`, `sourceCatalogue`).
175
+ 2. Resolved the *current* version of that distro from the catalogue (or re-cloned the
176
+ GitHub repo if `sourceCatalogue` starts with `github:`).
177
+ 3. Compared versions.
178
+ 4. If a newer version exists: displayed a preview to the user and obtained their explicit
179
+ confirmation before sending the kickoff.
180
+
181
+ So by the time you (the agent) receive the kickoff, the user has already confirmed they
182
+ want the update. Proceed exactly as a normal deploy (merge-don't-clobber, user-involvement
183
+ rule, package-redundancy check, version note — which will show as an "upgrade"). The
184
+ update is just a re-deploy of the same distro at a newer version, with the user's consent
185
+ already obtained.
186
+
187
+ If the extension did NOT send a kickoff (e.g. "already up to date" or "downgrade
188
+ warning"), there is nothing for you to do — those cases are handled entirely by the
189
+ extension with a notification.
190
+
191
+ ## `/pi-distro pick` — Partial Deploy
192
+
193
+ When you receive a kickoff from the `pick` command, the user wants to **select which
194
+ components to apply** from a distro — not the whole thing. This lets them combine pieces
195
+ from different distros to build their own configuration.
196
+
197
+ The kickoff lists the distro's components grouped by category (packages, bundled files,
198
+ and any other components described in the directives) along with the full directives for
199
+ your reference. Follow the selection procedure in the kickoff exactly:
200
+
201
+ 1. **Walk the user through each category, one at a time.** Use `ctx.ui.select`/`ctx.ui.confirm`
202
+ to let them pick which items to apply. Present each item with its one-line purpose (from
203
+ the directives). Let them select any subset — including none (skip the category).
204
+ 2. **Surface dependencies.** As the user selects, evaluate cross-component dependencies and
205
+ warn about them before applying. Example: if they pick an extension that references a
206
+ theme provided by a package they skipped, point that out and ask whether to also install
207
+ the package or skip the extension. Reason about the dependencies from the directives
208
+ prose — this is a judgment task. Never silently install a dependency the user didn't pick.
209
+ 3. **Apply only the selected components** with the same rules as a full deploy:
210
+ merge-don't-clobber, package-redundancy check, `pi install -l` for packages (after
211
+ confirming), copy/merge for files (overwrite / keep theirs / merge).
212
+ 4. **Do NOT write standard provenance.** A partial deploy is a custom config, not "this
213
+ distro was applied." Do not write `appliedHarness`/`appliedVersion` provenance. Instead,
214
+ after applying, suggest the next step: "This is a custom configuration. Run
215
+ `/pi-distro save` to snapshot it as your own reusable distro" (which writes clean
216
+ provenance for the saved distro).
217
+ 5. **Recommend a restart** if any packages or extensions were installed.
218
+
219
+ The natural loop is: `/pi-distro pick <distro-A>` → `/pi-distro pick <distro-B>` →
220
+ `/pi-distro save` (snapshot the combined result as a new distro). This is how users build
221
+ their own distro from pieces of others.
222
+
223
+ ## `/pi-distro undeploy` — Remove Applied Distro
224
+
225
+ When you receive a kickoff from the `undeploy` command, the user wants to **remove** the
226
+ applied distro from the project — the reverse of `deploy`. The extension has already read
227
+ provenance and sent you the distro's directives plus the current project state (which of
228
+ the distro's packages are still installed, whether the `AGENTS.md` delimited section
229
+ exists, what's in `.pi/settings.json`).
230
+
231
+ **Critical context:** provenance records the distro's *intentions* (the directives), not
232
+ the exact outcome of the interactive deploy. The user may have skipped packages during
233
+ deploy, customized files afterward, or removed things manually. So you must compare the
234
+ directives against the *current* project state and let the user decide what to remove.
235
+
236
+ Follow the removal procedure in the kickoff exactly:
237
+
238
+ 1. **Walk the user through each removal category, one at a time:**
239
+ - **(a) Packages** — for each distro package still installed, offer `pi remove -l <pkg>`.
240
+ Ask per package — the user may want to keep some. Warn if removing a package that other
241
+ components depend on.
242
+ - **(b) Bundled files** — for each file the distro placed, check if it exists. If it
243
+ does, **show the user the file (or a summary) before removing** — they may have
244
+ customized it. Offer: remove / keep. Never silently delete. For `settings.json`, offer
245
+ to remove specific keys the distro merged, not the whole file.
246
+ - **(c) AGENTS.md delimited section** — if the `<!-- pi-distro: <name> -->` ...
247
+ `<!-- /pi-distro: <name> -->` block exists, offer to remove it. Leave any non-distro
248
+ content.
249
+ - **(d) Extensions / skills / prompts / themes** — if described in the directives and
250
+ present, offer to remove each (show before delete).
251
+ 2. **Remove provenance last** — only after the user confirms the component removals, remove
252
+ `./.pi/harness.md`. Confirm before deleting.
253
+ 3. **Report and recommend a restart** — summarise what was removed vs. kept. Tell the user to
254
+ restart pi so removed packages/extensions fully unload.
255
+
256
+ Removal is destructive — the user-involvement rule applies doubly here. Never silently
257
+ skip, delete, or strip. The user decides; you execute their choices.
258
+
259
+ > **Note:** `undeploy` only works for full deploys (which write provenance). A partial
260
+ > deploy via `pick` doesn't write provenance — there's nothing to undeploy as a unit.
261
+
262
+ ## `/pi-distro save` — Distro Authoring
263
+
264
+ When you receive a draft request from the `save` command, it contains a live-config
265
+ snapshot of the current project: the system prompt options (tools, skills, context
266
+ files, guidelines), and the raw contents of **every project-local config file** found
267
+ under `./` (root `AGENTS.md`/`CLAUDE.md` variants), `./.pi/` (recursively — `settings.json`,
268
+ `SYSTEM.md`, `APPEND_SYSTEM.md`, `extensions/`, `skills/`, `prompts/`, `themes/`, and any
269
+ per-extension/skill config files like `.pi/<name>.json`), `./.crew/{agents,teams,workflows}/`
270
+ (pi-crew project-local authored definitions — NOT the runtime state subdirs), and
271
+ `./.agents/skills/` (project root only). Data/runtime dirs (`npm/`, `git/`, `sessions/`,
272
+ `state/`, `tmp/`, the non-config subdirs of `.crew/`, `node_modules/`) and the provenance file
273
+ `./.pi/harness.md` are excluded. Ancestor (parent-dir) `AGENTS.md` / `.agents/skills/`
274
+ appear only in the context-files list as informational — they are out of scope and must NOT
275
+ be bundled.
276
+
277
+ ### Authoring steps
278
+
279
+ 1. **Analyse the snapshot.** Review the live tools, skills, context files, installed
280
+ packages, and raw config files to understand what makes this project's configuration
281
+ unique and reproducible.
282
+
283
+ 2. **Draft a `harness.md`.** Write a distro that reproduces this configuration:
284
+ - **Frontmatter**: propose a `name` (slug: lowercase a-z/0-9/hyphens, no
285
+ leading/trailing/consecutive hyphens, matching what the distro directory will be
286
+ named), a `title` (human-readable), a `description` (one-liner, ≤300 chars), and a
287
+ `version` (semver). Optionally `author` and `tags`.
288
+ - **Directives body**: include sections for bundled files, pi packages to install,
289
+ hooks/extensions, context, and skills/prompts — mirroring the live config.
290
+
291
+ 3. **Propose & confirm.** Present the proposed `name`, `title`, `description`, and the full
292
+ draft to the user. Ask for confirmation or edits. Do not proceed until the user
293
+ confirms.
294
+
295
+ 4. **Save to the catalogue (you perform this, not the extension).** Follow the procedure in
296
+ the kickoff message exactly:
297
+ - Ask the user whether to **save as a new distro** or **update an existing distro**.
298
+ - If **save as new**: validate the slug; warn on collisions with existing user
299
+ distros or seed names and ask whether to overwrite.
300
+ - If **update existing**: let the user pick from the existing user distros listed in
301
+ the kickoff. Refuse to update names not in that list (those are read-only package
302
+ seeds). Back the old distro up to `~/.pi/harnesses/.trash/<name>-<timestamp>/` before
303
+ overwriting. **Bump the version**: read the old distro's `version` and increment it
304
+ with semver — **patch** for small tweaks/bug fixes, **minor** for new capabilities or
305
+ config additions, **major** for breaking changes (removed packages, changed
306
+ conventions). Never keep the same version when updating. Propose the bumped version to
307
+ the user and let them confirm or adjust.
308
+ - Write `~/.pi/harnesses/<name>/harness.md` with the confirmed frontmatter + directives.
309
+ - Copy the project's config files into `~/.pi/harnesses/<name>/files/` preserving
310
+ their path relative to the project root — root context (`AGENTS.md`/`CLAUDE.md`),
311
+ `.pi/settings.json`, `.pi/SYSTEM.md` & `.pi/APPEND_SYSTEM.md`, `.pi/extensions/**`
312
+ (incl. subdir extensions), `.pi/skills/**`, `.pi/prompts/**`, `.pi/themes/**`,
313
+ `.agents/skills/**`, `.crew/{agents,teams,workflows}/**` (pi-crew project-local authored
314
+ definitions only), and any per-extension/skill config files (e.g. `.pi/<name>.json`)
315
+ listed in the snapshot. Use `cp -r` for trees. Do NOT copy `./.pi/harness.md`
316
+ (provenance) or the data/runtime dirs (`npm/`, `git/`, `sessions/`, `state/`, `tmp/`,
317
+ the non-config subdirs of `.crew/` i.e. state/artifacts/worktrees/imports/audit/cache/graphs,
318
+ `node_modules/`).
319
+ - **Theme dedup:** before bundling a `.pi/themes/<name>.json`, check `pi list` for a
320
+ package that already provides that theme (package themes surface at
321
+ `~/.pi/agent/themes/`). If one does, do NOT bundle it — reference it via the package
322
+ install directive instead (a bundled copy collides on deploy, like pi-crew's
323
+ `crew-*` themes). Bundle only genuinely custom themes.
324
+ - **pi-crew agents/teams/workflows dedup:** a `.crew/agents/foo.md` (or one under
325
+ `.pi/teams/agents/`, `.pi/agents/`) may be a `--copy-builtins` copy of a pi-crew
326
+ shipped builtin, not custom-authored. Compare against pi-crew's package builtins
327
+ (`<pkg>/agents/`, `teams/`, `workflows/`); if a file is an unmodified builtin copy,
328
+ do NOT bundle it (the `pi-crew` install directive already provides it). Bundle only
329
+ genuinely custom/authored definitions.
330
+ - **Write a README.md:** also write `~/.pi/harnesses/<name>/README.md` — a human-readable
331
+ description of the distro (a few paragraphs: what it sets up, which packages it
332
+ installs and why, what workflow it targets, and any prerequisites). This complements
333
+ the one-liner `description` in the frontmatter. The README lives only in the
334
+ catalogue — it is not copied into the target project on deploy.
335
+
336
+ 5. **Update provenance.** Update `./.pi/harness.md` in the project to reflect the saved
337
+ distro (the saved frontmatter + directives with the provenance header at the top of
338
+ the body — see "Provenance file format" below).
339
+
340
+ 6. **Report.** Tell the user: "Saved distro '<name>'. Run `/pi-distro deploy` elsewhere to
341
+ deploy it."
342
+
343
+ ### Authoring conventions
344
+
345
+ See `docs/authoring.md` (shipped with the `@msdavid/pi-distro` package) for the
346
+ complete distro format reference: frontmatter fields, bundled `files/` directory
347
+ conventions, directive section types, and merge-don't-clobber expectations.
348
+
349
+ ## Provenance file format
350
+
351
+ The provenance file at `./.pi/harness.md` is a living record of which distro was
352
+ applied to the project. It is a valid `harness.md` (same frontmatter + directives as
353
+ the applied distro) with the provenance header comment at the top of the body. The
354
+ extension updates it automatically as a side-effect of `deploy`, `save`, and `undeploy`
355
+ commands.
356
+
357
+ When `/pi-distro save` snapshots a project, the provenance file is regenerated to
358
+ reflect the *current* live config — it becomes the record of "this project's distro
359
+ now".