From 8e11fe06dea259129ee4f6ebc93603b7b9f86fec Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Fri, 24 Apr 2026 13:00:38 -0400 Subject: [PATCH] feat: add update-package-hashes helper script and bump pi-coding-agent Co-locate update-package-hashes.sh helper script to wrap nurl and show recent releases. Simplify SKILL.md documentation. Bump pi-coding-agent from 0.70.0 to 0.70.2 with updated hashes. --- .agents/skills/update-package-hashes/SKILL.md | 40 +--- .../update-package-hashes.sh | 206 ++++++++++++++++++ packages/pi-coding-agent/default.nix | 6 +- 3 files changed, 219 insertions(+), 33 deletions(-) create mode 100755 .agents/skills/update-package-hashes/update-package-hashes.sh diff --git a/.agents/skills/update-package-hashes/SKILL.md b/.agents/skills/update-package-hashes/SKILL.md index 450fd55..4a324ff 100644 --- a/.agents/skills/update-package-hashes/SKILL.md +++ b/.agents/skills/update-package-hashes/SKILL.md @@ -17,21 +17,15 @@ If the user provides only a **package name** (no version), look up the latest ve ## The Only Two Methods -### Method A — `nurl` (preferred for `src` on any git forge) +### Method A — `nurl` via helper script (preferred for `src` on any git forge) -`nurl` works for **any git URL**, not just GitHub: `gitea.va.reichard.io`, `gitlab`, `codeberg`, `sourcehut`, plain `https://...git`, all fine. It downloads the source and prints a complete fetcher expression with the correct hash. +Use the co-located helper script. It wraps `nurl` and works for any git URL. ```bash -nix run nixpkgs#nurl -- +./update-package-hashes.sh hash ``` -Examples: -```bash -nix run nixpkgs#nurl -- https://github.com/owner/repo v1.2.3 -nix run nixpkgs#nurl -- https://gitea.va.reichard.io/evan/slack-cli.git 0a9484257a2adc414aa4cdab4fb9539a37e04d1f -``` - -Copy the `hash = "sha256-..."` line into the package's `src` block. +Copy the `hash = "sha256-..."` line from the output into the package's `src` block. ### Method B — FOD mismatch trick (for everything else) @@ -51,26 +45,18 @@ Setting the hash to `sha256-AAAA...` (44 A's) or leaving the old one in place bo ## Lookup Latest Version -When the user asks to update a package but doesn't specify a version, look it up and present it for confirmation. +When the user asks to update a package but doesn't specify a version: 1. Read `packages//default.nix` to find the git URL and current version/tag. -2. Determine the tag pattern from the current `tag` field (e.g. `"b${version}"` → tags like `b8815`; `"v${version}"` → tags like `v1.2.3`). -3. Fetch the newest matching tag: +2. Determine the tag pattern from the `tag` field (e.g. `"b${version}"` → `'b*'`, `"v${version}"` → `'v*'`). +3. Run the helper script: ```bash - # Find the latest tag matching the package's pattern - git ls-remote --tags https://github.com/ggml-org/llama.cpp.git 'b*' 2>&1 | tail -1 + ./update-package-hashes.sh releases '' ``` - For non-GitHub repos, use the appropriate remote URL from the package's `fetchFrom*` expression. -4. Present the result to the user and **ask for confirmation** before proceeding: - - ``` - Current: b8815 → Latest: b8914 - Proceed with update? (yes/no) - ``` - - If the user confirms, use the latest version. If they provide a different target, use that instead. If they say no, abort. + Shows main HEAD + 5 newest matching tags with commit hashes. +4. **Ask the user** before proceeding (`Current: b8815 → Latest: b8914 — proceed?`). ## Flow @@ -81,12 +67,6 @@ When the user asks to update a package but doesn't specify a version, look it up 5. **Opaque `outputHash` FODs** (e.g. opencode's `node_modules` which runs `bun install`) — do NOT attempt locally. Leave as-is and flag for CI in the summary. 6. Show `git diff -- packages//` and list any hashes left for CI. -## Resolving Tags Without Cloning - -```bash -git ls-remote refs/tags/ -``` - ## Don't Touch What Didn't Change Skip pinned sub-dependencies whose inputs didn't change — e.g. `slack-cli`'s `python-snappy` / `zstd-python` PyPI tarballs are pinned by the upstream `dfindexeddb`, not by the slack-cli rev. diff --git a/.agents/skills/update-package-hashes/update-package-hashes.sh b/.agents/skills/update-package-hashes/update-package-hashes.sh new file mode 100755 index 0000000..f0fe101 --- /dev/null +++ b/.agents/skills/update-package-hashes/update-package-hashes.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +# +# update-package-hashes.sh — Helper for the update-package-hashes skill. +# Co-located in .agents/skills/update-package-hashes/ +# +# Usage: +# ./update-package-hashes.sh releases [pattern] +# ./update-package-hashes.sh hash +# ./update-package-hashes.sh hash +# ./update-package-hashes.sh all [fetcher] +# +# Examples: +# ./update-package-hashes.sh releases https://github.com/ggml-org/llama.cpp +# ./update-package-hashes.sh releases https://github.com/ggml-org/llama.cpp b* +# ./update-package-hashes.sh hash https://github.com/owner/repo v1.2.3 +# ./update-package-hashes.sh hash https://github.com/owner/repo abc1234 fetchFromGitLab +# ./update-package-hashes.sh all https://github.com/owner/repo v1.2.3 + +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + $0 releases [tag-pattern] + Show main branch HEAD + 5 most recent matching releases/tags. + + $0 hash [fetcher] + Fetch source and print the nix fetcher expression with hash (via nurl). + + $0 all [fetcher] + Same as hash, but also shows main HEAD + recent releases for context. + +Options: + tag-pattern Glob pattern for filtering tags (e.g. 'v*', 'b*'). + If omitted, all tags are shown. + fetcher Override the auto-detected fetcher: + fetchFromGitHub (default) + fetchFromGitLab + fetchFromGitLab (e.g. gitea.va.reichard.io) + fetchFromSourceHut + fetchgit (fallback for non-standard forges) + +Requires: nix (for nurl), git. +EOF + exit 1 +} + +# ── Helpers ────────────────────────────────────────────────────────────────── + +detect_fetcher() { + local url="$1" + local host + host=$(echo "$url" | sed -E 's|^https?://||' | cut -d/ -f1) + + case "$host" in + *github.com) + echo "fetchFromGitHub" + ;; + *gitlab.com) + echo "fetchFromGitLab" + ;; + *gitea*|*gitlab.reichard*|*gitlab.va.reichard*) + echo "fetchFromGitLab" + ;; + *sourcehut*) + echo "fetchFromSourceHut" + ;; + *) + echo "fetchgit" + ;; + esac +} + +normalise_url() { + echo "${1%.git}" +} + +# ── Commands ───────────────────────────────────────────────────────────────── + +# Show main/master branch HEAD. +show_main_head() { + local url="$1" + local clean_url + clean_url=$(normalise_url "$url") + + for branch in main master; do + local line + line=$(git ls-remote "$url" "refs/heads/$branch" 2>/dev/null | head -1) + if [[ -n "$line" ]]; then + local full_rev="${line%% *}" + echo " main: ${full_rev:0:12}" + echo + return + fi + done + + # Fallback: try to find any branch + echo " (no main/master branch found)" + echo +} + +# List recent tags/releases for a repo. +cmd_releases() { + local url="$1" + local pattern="${2:-}" + local clean_url + clean_url=$(normalise_url "$url") + + echo "Recent tags for: $clean_url" + echo "────────────────────────────────────────────────────────" + echo + + # Always show main HEAD first + show_main_head "$url" + + # Get tags sorted by date (newest first), limited to 5 + local tags + if [[ -n "$pattern" ]]; then + tags=$(git ls-remote --tags "$url" "$pattern" 2>&1 \ + | grep -v '\^{}' \ + | sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \ + | sort -V \ + | tail -5) + else + tags=$(git ls-remote --tags "$url" 2>&1 \ + | grep -v '\^{}' \ + | sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \ + | sort -V \ + | tail -5) + fi + + if [[ -z "$tags" ]]; then + echo " (no tags found)" + return + fi + + printf " %-30s %s\n" "TAG" "COMMIT" + echo " $(printf '%.0s-' {1..45})" + + echo "$tags" | tac | while IFS= read -r tag_name; do + local full_rev + full_rev=$(git ls-remote "$url" "refs/tags/$tag_name" 2>/dev/null | head -1 | cut -f1) + if [[ -n "$full_rev" ]]; then + printf " %-30s %s\n" "$tag_name" "${full_rev:0:12}" + else + printf " %-30s %s\n" "$tag_name" "(not found)" + fi + done +} + +# Get the nix fetcher expression with hash for a specific rev/tag. +cmd_hash() { + local url="$1" + local rev="$2" + local fetcher="${3:-}" + + if [[ -z "$fetcher" ]]; then + fetcher=$(detect_fetcher "$url") + fi + + local clean_url + clean_url=$(normalise_url "$url") + + echo "Fetching hash for: $clean_url @ $rev" + echo "Fetcher: $fetcher" + echo "────────────────────────────────────────────────────────" + echo + + nix run nixpkgs#nurl -- "$url" "$rev" +} + +# Get hash + recent releases for context. +cmd_all() { + local url="$1" + local rev="$2" + local fetcher="${3:-}" + + cmd_releases "$url" + echo + cmd_hash "$url" "$rev" "$fetcher" +} + +# ── Main ───────────────────────────────────────────────────────────────────── + +if [[ $# -lt 2 ]]; then + usage +fi + +case "$1" in + releases) + [[ $# -lt 2 ]] && { echo "Error: missing " >&2; usage; } + cmd_releases "$2" "${3:-}" + ;; + hash) + [[ $# -lt 3 ]] && { echo "Error: missing " >&2; usage; } + cmd_hash "$2" "$3" "${4:-}" + ;; + all) + [[ $# -lt 3 ]] && { echo "Error: missing " >&2; usage; } + cmd_all "$2" "$3" "${4:-}" + ;; + *) + echo "Unknown command: $1" >&2 + usage + ;; +esac diff --git a/packages/pi-coding-agent/default.nix b/packages/pi-coding-agent/default.nix index fa705d0..2eead38 100644 --- a/packages/pi-coding-agent/default.nix +++ b/packages/pi-coding-agent/default.nix @@ -14,16 +14,16 @@ buildNpmPackage rec { pname = "pi-coding-agent"; - version = "0.70.0"; + version = "0.70.2"; src = fetchFromGitHub { owner = "badlogic"; repo = "pi-mono"; rev = "v${version}"; - hash = "sha256-gB3QUxA4OZ8Zg5YGbAHmknSnAHrhEGxzz/DXRiKiK50="; + hash = "sha256-qqmJloTp3mWuZBGgpwoyoFyXx6QD8xhJEwCZb7xFabM="; }; - npmDepsHash = "sha256-SBm5GPmHNZ24zYBo3rA9n3XTz8Y7oNOaGJ2dY/X2ccw="; + npmDepsHash = "sha256-ImDvTC0Nm+IGYJuqjwUUfnOtA65uJvjlpP4h2Xt/2vE="; nativeBuildInputs = [ pkg-config ];