|
| 1 | +#!/usr/bin/env bash |
| 2 | +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| 3 | +# SPDX-License-Identifier: Apache-2.0 |
| 4 | +# |
| 5 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +# you may not use this file except in compliance with the License. |
| 7 | +# You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | +# Re-vendor upstream Claude skills from NVIDIA-NeMo/Evaluator at a pinned SHA. |
| 18 | +# |
| 19 | +# Scope: only skills we vendor verbatim (launching-evals, accessing-mlflow). |
| 20 | +# The `evaluation` skill is a *modified* fork of upstream nel-assistant and is |
| 21 | +# NOT managed by this script — update it manually when pulling upstream changes. |
| 22 | +# |
| 23 | +# Usage: |
| 24 | +# .claude/scripts/sync-upstream-skills.sh # re-vendor at the pinned SHA |
| 25 | +# UPSTREAM_SHA=<sha> .claude/scripts/sync-upstream-skills.sh # bump to a new SHA |
| 26 | +# |
| 27 | +# Requires: gh, base64, awk. Run from the repo root. |
| 28 | +# |
| 29 | +# The script overwrites .claude/skills/<skill>/ with upstream contents and |
| 30 | +# re-applies our provenance lines into each SKILL.md frontmatter. If you have |
| 31 | +# local changes to a vendored skill, they will be lost — that is expected, |
| 32 | +# since vendored-verbatim skills should not be modified locally. |
| 33 | + |
| 34 | +set -euo pipefail |
| 35 | + |
| 36 | +# Pinned upstream commit. Bump this (or pass UPSTREAM_SHA=...) when syncing. |
| 37 | +DEFAULT_SHA="8fa16b237d11e213ea665d5bad6b44d393762317" |
| 38 | +SHA="${UPSTREAM_SHA:-$DEFAULT_SHA}" |
| 39 | +SHORT_SHA="${SHA:0:7}" |
| 40 | + |
| 41 | +UPSTREAM_REPO="NVIDIA-NeMo/Evaluator" |
| 42 | +UPSTREAM_BASE="packages/nemo-evaluator-launcher/.claude/skills" |
| 43 | +DEST_BASE=".claude/skills" |
| 44 | + |
| 45 | +if [[ ! -d "$DEST_BASE" ]]; then |
| 46 | + echo "error: run from the repo root (expected $DEST_BASE/ to exist)" >&2 |
| 47 | + exit 1 |
| 48 | +fi |
| 49 | + |
| 50 | +echo "Syncing upstream skills from $UPSTREAM_REPO @ $SHORT_SHA" |
| 51 | + |
| 52 | +fetch_tree() { |
| 53 | + local skill="$1" |
| 54 | + local path="$2" |
| 55 | + gh api "repos/$UPSTREAM_REPO/contents/$UPSTREAM_BASE/$skill/$path?ref=$SHA" \ |
| 56 | + -q '.[] | "\(.type)\t\(.name)"' |
| 57 | +} |
| 58 | + |
| 59 | +fetch_file() { |
| 60 | + local src="$1" |
| 61 | + local dst="$2" |
| 62 | + mkdir -p "$(dirname "$dst")" |
| 63 | + gh api "repos/$UPSTREAM_REPO/contents/$src?ref=$SHA" -q '.content' | base64 -d > "$dst" |
| 64 | +} |
| 65 | + |
| 66 | +fetch_skill_recursive() { |
| 67 | + local skill="$1" |
| 68 | + local subpath="${2:-}" |
| 69 | + local remote="$UPSTREAM_BASE/$skill" |
| 70 | + [[ -n "$subpath" ]] && remote="$remote/$subpath" |
| 71 | + |
| 72 | + local entries |
| 73 | + entries=$(gh api "repos/$UPSTREAM_REPO/contents/$remote?ref=$SHA" -q '.[] | "\(.type)\t\(.name)"') |
| 74 | + |
| 75 | + while IFS=$'\t' read -r type name; do |
| 76 | + local rel_path |
| 77 | + if [[ -n "$subpath" ]]; then |
| 78 | + rel_path="$subpath/$name" |
| 79 | + else |
| 80 | + rel_path="$name" |
| 81 | + fi |
| 82 | + |
| 83 | + if [[ "$type" == "file" ]]; then |
| 84 | + local dst="$DEST_BASE/$skill/$rel_path" |
| 85 | + echo " fetch: $dst" |
| 86 | + fetch_file "$UPSTREAM_BASE/$skill/$rel_path" "$dst" |
| 87 | + elif [[ "$type" == "dir" ]]; then |
| 88 | + fetch_skill_recursive "$skill" "$rel_path" |
| 89 | + fi |
| 90 | + done <<< "$entries" |
| 91 | +} |
| 92 | + |
| 93 | +# Inject our provenance lines into a SKILL.md frontmatter, right after the |
| 94 | +# `description:` line. Idempotent — removes any existing provenance block first. |
| 95 | +inject_provenance() { |
| 96 | + local skill="$1" |
| 97 | + local extra_note="${2:-}" |
| 98 | + local path="$DEST_BASE/$skill/SKILL.md" |
| 99 | + |
| 100 | + awk -v sha="$SHA" -v short="$SHORT_SHA" -v skill="$skill" -v extra="$extra_note" ' |
| 101 | + BEGIN { in_fm = 0; injected = 0; fm_end_seen = 0 } |
| 102 | + # Skip any pre-existing provenance or license lines we own |
| 103 | + /^license: Apache-2\.0$/ && in_fm && !fm_end_seen { next } |
| 104 | + /^# Vendored verbatim/ && in_fm && !fm_end_seen { next } |
| 105 | + /^# https:\/\/github\.com\/NVIDIA-NeMo\/Evaluator\/tree\// && in_fm && !fm_end_seen { next } |
| 106 | + /^# To re-sync:/ && in_fm && !fm_end_seen { next } |
| 107 | + /^# Note: this skill depends on the mlflow-mcp/ && in_fm && !fm_end_seen { next } |
| 108 | + /^# configured in the user/ && in_fm && !fm_end_seen { next } |
| 109 | + { |
| 110 | + print |
| 111 | + if ($0 == "---") { |
| 112 | + if (in_fm == 0) { in_fm = 1 } |
| 113 | + else { in_fm = 0; fm_end_seen = 1 } |
| 114 | + } |
| 115 | + if (in_fm && !injected && $0 ~ /^description: /) { |
| 116 | + print "license: Apache-2.0" |
| 117 | + print "# Vendored verbatim from NVIDIA NeMo Evaluator (commit " short ")" |
| 118 | + print "# https://github.com/NVIDIA-NeMo/Evaluator/tree/" sha "/packages/nemo-evaluator-launcher/.claude/skills/" skill |
| 119 | + print "# To re-sync: .claude/scripts/sync-upstream-skills.sh" |
| 120 | + if (extra != "") { |
| 121 | + n = split(extra, lines, "\\|") |
| 122 | + for (i = 1; i <= n; i++) print "# " lines[i] |
| 123 | + } |
| 124 | + injected = 1 |
| 125 | + } |
| 126 | + } |
| 127 | + ' "$path" > "$path.tmp" |
| 128 | + mv "$path.tmp" "$path" |
| 129 | +} |
| 130 | + |
| 131 | +for skill in launching-evals accessing-mlflow; do |
| 132 | + echo "" |
| 133 | + echo "== $skill ==" |
| 134 | + rm -rf "${DEST_BASE:?}/$skill" |
| 135 | + fetch_skill_recursive "$skill" |
| 136 | + |
| 137 | + case "$skill" in |
| 138 | + accessing-mlflow) |
| 139 | + inject_provenance "$skill" \ |
| 140 | + "Note: this skill depends on the mlflow-mcp MCP server (https://github.com/kkruglik/mlflow-mcp)|configured in the user's Claude Code setup." |
| 141 | + ;; |
| 142 | + *) |
| 143 | + inject_provenance "$skill" |
| 144 | + ;; |
| 145 | + esac |
| 146 | +done |
| 147 | + |
| 148 | +echo "" |
| 149 | +echo "Done. Review with: git diff $DEST_BASE/launching-evals $DEST_BASE/accessing-mlflow" |
| 150 | +echo "If the SHA changed, update DEFAULT_SHA at the top of this script before committing." |
0 commit comments