Skip to content
Back to skills

Pi Update

ASecurity

Load to check the kendex Pi extensions against a Pi release, fix what breaks, and record the verdict.

  • 83 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsrustgobashnodegitapi

Works with

  • cursor
  • cli
  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 6, 2026

npx -y skills add vanillagreencom/kendex --skill pi-update --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pi Update?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pi Update
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vanillagreencom-pi-update/badge)](https://www.skillsdirectory.com/skills/vanillagreencom-pi-update)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pi-update
description: "Load to check the kendex Pi extensions against a Pi release, fix what breaks, and record the verdict."
summary: "Pi compatibility audit: capture the target release, audit its changelogs, apply the pi-hooks blocking rule, prove the real extensions against its SDK, fix forward, record one verdict, notify the fleet and hand publication to npm-deploy."
---

<!-- kendex:project-instructions:start -->
## Project Instructions

<!-- kendex:shared-instructions:start -->
Problems with a kendex-owned skill go through `kendex report`; check ownership in the file first.
<!-- kendex:shared-instructions:end -->
<!-- kendex:project-instructions:end -->

# Pi update

Checks `pi-extensions/*` against a Pi release and records one verdict. The record is `pi-extensions/pi-update.audit.md`, the one compatibility record. The marker `pi-extensions/pi-update.state.json` only says which releases were read. The rule the verdict applies is `pi-extensions/AGENTS.md`.

## Release handling

- Pin nothing, freeze nothing, hold no release back. Fleet lanes follow the vendor's latest Pi through the fleet's `fleet-cli-update`. A `hold` verdict stops a peer floor from rising, never a lane from updating.
- Start the run as soon as a new release is seen.
- The Linear item a new Pi release creates, and every follow-up a failing run files, is priority High (2), never Backlog. A failing run is fixed forward, never by pinning Pi to an older release.

## Input

- No argument: the target is npm's current `latest` of `@earendil-works/pi-coding-agent`.
- A version: that version is the target. Every source is still fetched.
- A pasted changelog: the authoritative entry list for the range; changelog fetching is skipped. The target is the highest released version header in the paste, and step 1 captures that version.

## 1. Capture the target

Read the target once, at the start, and use these values for the whole run:

```bash
npm view @earendil-works/pi-coding-agent@<latest|version> version dist.integrity gitHead
gh api repos/earendil-works/pi/commits/v<version> --jq .sha
```

The two commit values must match; a mismatch stops the run. Record the version, the integrity, the upstream source commit and `git rev-parse HEAD` as the run-start head.

Create this run's own directory with `mkdir -p tmp/pi-update && mktemp -d tmp/pi-update/run.XXXXXX`; `<run>` is the path it prints, and the record's Evidence names it. A retry starts a new `<run>`. Write the candidate record under `tmp/pi-update/`. The committed `pi-update.audit.md` keeps the last cleared record until step 6 replaces it.

## 2. Audit the range

- **Marker.** In scope is every released version header above `lastVersion` and at or below the target. A target at or below `lastVersion` means nothing to audit: refresh `lastRun` and `lastRunHead`, commit the marker, stop. `lastVersion` and `lastDate` never move backward.
- **First run** (marker absent): propose a baseline, the newest Pi version named in `git log`, and ask the user to confirm it before any work. Seed the marker there.
- **Sources.** Enumerate every changelog on each run; never trust a stored list:

  ```bash
  gh api 'repos/earendil-works/pi/git/trees/main?recursive=1' --jq '.tree[].path | select(endswith("/CHANGELOG.md"))'
  gh api repos/earendil-works/pi/contents/<path> -H "Accept: application/vnd.github.raw"
  ```

  A source key is the path without `packages/` and `/CHANGELOG.md`. Record every key in the marker's `sourcesCovered`; report a key present last run and absent now. <https://pi.dev/news/releases> is a cross-check only. A `## [Unreleased]` block is a heads-up, never in scope.

- **Classify** each entry into one bucket:
  - Required parity fix: Pi changed a behaviour, event shape, provider input or setting that an extension overrides, mirrors, reads or copied.
  - Optional improvement: a new Pi field, helper or event that could simplify an extension that is still correct.
  - Non-impact: outside every extension surface. One line of reason each.
- For an entry that adds or changes a field of a tool result, an event or a provider stream option, find every kendex listener or override of that surface, including `tool_result` handlers and `registerProvider` shims. Record each listener or override beside the entry with its classification.
- **Triage by import.** A source reaches every package that imports its npm package (`coding-agent` is `@earendil-works/pi-coding-agent`, `ai` is `@earendil-works/pi-ai`, `agent` is `@earendil-works/pi-agent-core`, `tui` is `@earendil-works/pi-tui`, and so on). Find them on each run, never from a stored list: `grep -rlE "['\"]@earendil-works/pi-<name>['\"/]" pi-extensions/*/ --include='*.ts' --include='*.mjs' --include='*.js' --exclude-dir=node_modules --exclude-dir=tests --exclude-dir=__tests__`. For an `ai` entry that names a provider, also grep `pi-extensions/*/` for that provider id: a package that registers the provider is in reach. A source that no package imports is Non-impact.
- For each Required or Optional entry, cite the affected `path::symbol` and read the Pi source when a field name is unclear.

## 3. Apply the blocking rule

Read `pi-extensions/pi-hooks/pi-contract.json` for the events and calls pi-hooks uses; never a fixed list. Check every `### Breaking Changes` entry in range, from every source, against it. The rule for an entry that names one is `pi-extensions/AGENTS.md`. List each Breaking Changes entry in the record's Verdict table with the contract names it carries.

## 4. Prove behaviour against the target

Package suites run on older pinned Pi versions and fake hosts, so a green suite does not prove a contract change. For every Required entry and every blocking entry:

- **One install location.** Node resolves an import from the importing file's own directories, so an install beside a package is invisible to it. Copy the whole `pi-extensions` tree without any `node_modules`, so a test that imports a sibling package by relative path finds it. Each time this step runs, make a new directory with `mktemp -d <run>/work.XXXXXX` and copy into it with `rsync -a --exclude node_modules pi-extensions <that directory>/`. `<copy>` is `pi-extensions/<package>` inside that directory. In `<copy>`, run `npm install --no-save --include=dev --include=peer --include=optional --no-package-lock --ignore-scripts --no-audit --no-fund` with every `@earendil-works/pi-*` package named in its dependency declarations, including `peerDependencies` and `devDependencies`, each at `@<version>`, plus the other packages its `DEVELOPMENT.md` install line names. Keep `package.json` unchanged. The extension, its other dependencies, every check and the suite then run from `<copy>`.
- **Prove the version.** Before any check or suite, print the `@earendil-works/pi-coding-agent` version that resolves from the extension's entry file and from the suite's directory. The record states it. A version other than the target stops the run.

  ```bash
  node --input-type=module -e 'import { findPackageJSON } from "node:module"; import { readFileSync } from "node:fs"; import { pathToFileURL } from "node:url"; for (const at of process.argv.slice(1)) console.log(at, JSON.parse(readFileSync(findPackageJSON("@earendil-works/pi-coding-agent", pathToFileURL(at)), "utf8")).version)' <copy>/<entry file> <copy>/<suite directory>/
  ```

- Put each check in `<copy>`'s own test directory, and load the real extension from `<copy>` into a real `createAgentSession` with a controlled provider: a faux provider as in `pi-extensions/pi-hooks/tests/pi-session.ts`, or a stubbed endpoint for a provider an extension registers, as in `pi-extensions/pi-codex-minimal-tools/tests/transcript-context.test.ts`.
- Pair each check with a regression control that fails on the old behaviour: the check run on a copy of the pre-fix code set up the same way, or a fixture that plants the old behaviour. A check whose control passes proves nothing.
- Run each touched package's suite in `<copy>` with the command its `test` script runs, less any `npm install` the script runs first: that install puts the package's own pin back (`pi-hooks` has one).

When a behaviour cannot be exercised inside Pi, the record says so; it never asserts parity.

## 5. Repair and simplify

- Edit the canonical files under `pi-extensions/` only, never a harness mirror (`.pi/`, `.claude/`, `.codex/`, `.agents/`, `.opencode/`, `.cursor/`).
- A fix to behaviour a `hooks/*.sh` script holds changes `pi-extensions/pi-hooks/extensions/hooks.ts` in the same commit.
- Each fix ships a test per the code-quality skill and a `### Unreleased` entry in the package's `CHANGELOG.md`. Bump no package version; bump no CLI version.
- Take an Optional entry that removes code, or defer it with its reason.
- One commit per logical change. A fix that spans packages for one entry is one commit naming them all.
- A fix you cannot land in this run becomes a Linear item at priority High (2) through the linear skill, and the verdict follows `pi-extensions/AGENTS.md`.

## 6. Merge and record

1. Land the fixes through a pull request (orch). Stage only intended files.
2. Test the exact merged commit: extract the `pi-extensions` tree of the full default-branch commit that holds the fixes into a new directory from `mktemp -d <run>/merged.XXXXXX` with `git archive <sha> pi-extensions | tar -x -C <that directory>`. `<copy>` is now `pi-extensions/<package>` inside it. Copy each step 4 check from the newest work copy's test directory into `<copy>`'s, then run step 4 from its install on in `<copy>`: the install, the version proof, the checks and the suite, and no `rsync`. That full SHA is the tested extension commit.
3. Replace `pi-update.audit.md` with the candidate record (§ Audit record) and update the marker: under `roll`, `lastVersion` and `lastDate` move to the target; under `hold` they stay at the last cleared release. Either way `lastRun` and `lastRunHead` refresh.
4. Run `node --test pi-extensions/package-policy.test.mjs` and land the record and marker through a pull request whose subject says `audited through v<version>`.
5. From the main checkout, run `kendex refresh` and report the Pi packages it updated.

## 7. Notify and publish

- Write the result to `tmp/pi-update/result.md`, naming the Pi release, the verdict and the tested extension commit. From a lane, send it to your own overseer with `.agents/skills/orch/scripts/lane-mail notice --item <ITEM> --file tmp/pi-update/result.md`, the text opening with `For the fleet overseer:`; the overseer relays it with `lane-mail peer send --repo fleet --file <PATH>`. From a session on the control VM's kendex checkout, `.agents/skills/orch/scripts/lane-mail peer send --repo fleet --file tmp/pi-update/result.md` reaches the fleet overseer directly.

- Publication is the `npm-deploy` skill, run as linked work: a Linear item related to this one. A failed publish leaves that item open. It does not block fleet rollout, and it does not block the next compatibility run. Add no publication recovery step.

## Audit record

Name each Linear item by id only, beside the Pi entry and package it concerns. Do not state its status, priority or adoption.

The record holds: the ``Marker `<old>` → `<new>`.`` line, where `<old>` is `lastVersion` at run start and `<new>` the target; the sources fetched; every classified entry; and a `## Verdict` section. The Verdict section opens with the verdict and then this summary, in this form:

```markdown
Verdict: `roll`.

- Target release: `<version>`, npm `@earendil-works/pi-coding-agent@<version>` integrity `<sha512-…>`, upstream source commit `<40-hex>`.
- Entries examined: <the releases and sources read, and the Breaking Changes entries the table lists>.
- Tested extension commit: `<40-hex>`.
- Evidence: <the `<run>` directory, the fixtures, tests and CI runs step 4 and step 6 ran, and the Pi version each resolved>.
```

The Breaking Changes table follows. A `roll` clears `<new>`; a `hold` clears `<old>`. What `pi-extensions/package-policy.test.mjs` refuses in the record is the Pi update audit bullet of `pi-extensions/AGENTS.md`.

## Final report

- Releases covered, old marker to target, with dates, and the source keys that carried entries.
- Entry counts per bucket, and the verdict with each blocking entry and the pi-hooks change it waits for.
- Commits shipped, packages touched, and tests run with pass counts.
- Deferred Optional entries with their reasons; the Non-impact log.
- The tested extension commit, the marker commit, the npm-deploy item, and the refresh result.
- How step 7's result was sent: from a lane, the exit status of `lane-mail notice`, which prints nothing on success; from the control VM, the receipt `lane-mail peer send` prints.
- `git status --short` is clean.

## Notes

- Pi releases every package in lockstep under one version, so `lastVersion` covers the whole release.
- `pi update` reconciles only `git:` and `npm:` entries in Pi's `settings.json`. kendex installs extensions as path packages, so a changelog entry about `pi update` does not reach them; say so in the record.
- `pi-tool-renderer` replaces Pi's `read`, `bash`, `edit`, `write`, search and list renderers. A Pi change to their default look is a choice for the user, not an automatic mirror.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.