Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"allow": [
"WebFetch(domain:registry.npmjs.org)",
"WebSearch",
"WebFetch(domain:skills.sh)"
"WebFetch(domain:skills.sh)",
"Bash(bash .github/scripts/check-urls.sh)"
]
}
}
201 changes: 201 additions & 0 deletions .github/scripts/check-urls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#!/usr/bin/env bash
set -uo pipefail

# Validate every http(s) URL referenced in skill markdown files.
#
# Usage:
# .github/scripts/check-urls.sh # scan all *-skill/ dirs
# .github/scripts/check-urls.sh chainlink-cre-skill
# .github/scripts/check-urls.sh path/to/file.md another-dir
#
# Run from the repo root, in a normal shell with internet access. A full scan
# of all skills takes ~30-60s; scope to one skill (faster) while iterating.
#
# Env:
# PARALLEL number of concurrent checks (default 8)
# TIMEOUT per-request timeout in seconds (default 10)
# GITHUB_ANNOTATIONS=1 emit ::error annotations (auto-on in CI)

# Keep concurrency moderate: bursting too many requests at docs.chain.link
# trips its rate limiter, which makes everything slower, not faster.
PARALLEL="${PARALLEL:-8}"
TIMEOUT="${TIMEOUT:-10}"
[ -n "${CI:-}" ] && GITHUB_ANNOTATIONS="${GITHUB_ANNOTATIONS:-1}"
GITHUB_ANNOTATIONS="${GITHUB_ANNOTATIONS:-0}"

# Reachability-only allowlist: API/RPC/WebSocket endpoints that don't serve a
# browseable page (a GET returns 404/403 even when the host is fine). For these
# we accept any HTTP response and only fail on a connection error (code 000).
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
endpoints_file="$script_dir/../url-check-endpoints.txt"
ENDPOINT_PATTERNS=""
if [ -f "$endpoints_file" ]; then
ENDPOINT_PATTERNS="$(grep -vE '^[[:space:]]*(#|$)' "$endpoints_file" || true)"
fi
export ENDPOINT_PATTERNS

# Colors (disabled when not a TTY)
if [ -t 1 ]; then
BOLD=$'\033[1m'; RED=$'\033[31m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'
DIM=$'\033[2m'; RESET=$'\033[0m'
else
BOLD=""; RED=""; GREEN=""; YELLOW=""; DIM=""; RESET=""
fi

# Resolve scan targets: args, or every *-skill directory at repo root.
targets=("$@")
if [ ${#targets[@]} -eq 0 ]; then
while IFS= read -r d; do targets+=("$d"); done < <(find . -maxdepth 1 -type d -name '*-skill' | sort)
fi

if [ ${#targets[@]} -eq 0 ]; then
echo "No skill directories found and no paths given." >&2
exit 1
fi

# Collect markdown files.
md_files=()
for t in "${targets[@]}"; do
if [ -f "$t" ]; then
md_files+=("$t")
elif [ -d "$t" ]; then
# Skip vendored/third-party trees — we only validate our own docs.
while IFS= read -r f; do md_files+=("$f"); done < <(
find "$t" \( -name node_modules -o -name .git \) -prune -o -type f -name '*.md' -print
)
else
echo "${YELLOW}warning:${RESET} skipping '$t' (not a file or directory)" >&2
fi
done

if [ ${#md_files[@]} -eq 0 ]; then
echo "No markdown files to scan." >&2
exit 0
fi

work_dir="$(mktemp -d)"
trap 'rm -rf "$work_dir"' EXIT
locations="$work_dir/locations" # url<TAB>file:line
: > "$locations"

# Extract URLs with file:line. Strip trailing markdown/punctuation noise and
# skip obvious non-checkable placeholders.
for f in "${md_files[@]}"; do
grep -noE 'https?://[^][:space:]()<>"`'"'"'\\]+' "$f" 2>/dev/null \
| while IFS=: read -r lineno url; do
# Trim trailing punctuation that commonly clings to URLs in prose.
url="${url%%[.,;:]}"
case "$url" in
*localhost*|*127.0.0.1*|*example.com*|*YOUR_*|*'{'*|*'$'*) continue ;;
esac
printf '%s\t%s:%s\n' "$url" "$f" "$lineno" >> "$locations"
done
done

# Unique URL list.
urls=()
while IFS= read -r u; do urls+=("$u"); done < <(cut -f1 "$locations" | sort -u)
total=${#urls[@]}

if [ "$total" -eq 0 ]; then
echo "No checkable URLs found."
exit 0
fi

echo "${BOLD}Checking $total unique URL(s) across ${#md_files[@]} file(s)...${RESET}"
echo

# Check one URL: print "<status>\t<http_code>\t<url>".
# status = OK (2xx/3xx) | WARN (exists but auth/method/rate-limited) | BROKEN.
check_url() {
local url="$1" code
code=$(curl -A "url-checker (chainlink-agent-skills)" \
-sS -o /dev/null -L --max-time "$TIMEOUT" --retry 1 \
-w '%{http_code}' "$url" 2>/dev/null)
code="${code:-000}"

# Reachability-only endpoints: the server answering at all (any non-000 code)
# proves the host is alive; only a connection failure is a real break.
if [ -n "${ENDPOINT_PATTERNS:-}" ]; then
while IFS= read -r pat; do
[ -z "$pat" ] && continue
case "$url" in
*"$pat"*)
if [ "$code" = "000" ]; then
printf 'BROKEN\t%s\t%s\n' "$code" "$url"
else
printf 'OK\t%s\t%s\n' "$code" "$url"
fi
return ;;
esac
done <<< "$ENDPOINT_PATTERNS"
fi

if [ "$code" -ge 200 ] 2>/dev/null && [ "$code" -lt 400 ]; then
printf 'OK\t%s\t%s\n' "$code" "$url"
else
case "$code" in
# Resource exists but the request was refused (needs POST/auth, or
# we got rate-limited). Not a dead link — surface as a warning.
401|403|405|429) printf 'WARN\t%s\t%s\n' "$code" "$url" ;;
*) printf 'BROKEN\t%s\t%s\n' "$code" "$url" ;;
esac
fi
}
export -f check_url
export TIMEOUT

results="$work_dir/results"
# Stream results as each check finishes so we can show live progress. The
# counter lives in this piped subshell; results land in the file regardless.
printf '%s\n' "${urls[@]}" \
| xargs -P "$PARALLEL" -I {} bash -c 'check_url "$@"' _ {} \
| { done=0
while IFS= read -r line; do
printf '%s\n' "$line" >> "$results"
done=$((done + 1))
printf '\r checked %d/%d ...' "$done" "$total" >&2
done
printf '\r%*s\r' 40 "" >&2; } # clear the progress line

ok_count=$(grep -c '^OK' "$results" || true)
warn_count=$(grep -c '^WARN' "$results" || true)
broken_count=$(grep -c '^BROKEN' "$results" || true)

echo "${GREEN}✓ $ok_count OK${RESET} ${YELLOW}! $warn_count warn${RESET} ${RED}✗ $broken_count broken${RESET}"

# List the locations for a given URL, indented, with optional CI annotations.
print_locations() {
local url="$1" code="$2" level="$3" label="$4"
grep -F "$(printf '%s\t' "$url")" "$locations" | cut -f2 | sort -u | while IFS= read -r loc; do
echo " ${DIM}↳ $loc${RESET}"
[ "$GITHUB_ANNOTATIONS" = "1" ] && {
file="${loc%%:*}"; line="${loc##*:}"
echo "::${level} file=$file,line=$line::${label} URL ($code): $url"
}
done
}

if [ "$warn_count" -gt 0 ]; then
echo
echo "${BOLD}${YELLOW}Reachable but refused (auth/method/rate-limit — review, not necessarily broken):${RESET}"
grep '^WARN' "$results" | sort -t$'\t' -k3 | while IFS=$'\t' read -r _ code url; do
echo " ${YELLOW}!${RESET} ${BOLD}$url${RESET} ${DIM}(HTTP ${code})${RESET}"
print_locations "$url" "$code" "warning" "Refused"
done
fi

if [ "$broken_count" -gt 0 ]; then
echo
echo "${BOLD}${RED}Broken URLs:${RESET}"
grep '^BROKEN' "$results" | sort -t$'\t' -k3 | while IFS=$'\t' read -r _ code url; do
echo " ${RED}✗${RESET} ${BOLD}$url${RESET} ${DIM}(HTTP ${code})${RESET}"
print_locations "$url" "$code" "error" "Broken"
done
echo
echo "${RED}URL check failed.${RESET}"
exit 1
fi

echo
echo "${GREEN}All URLs reachable.${RESET}"
12 changes: 12 additions & 0 deletions .github/url-check-endpoints.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Reachability-only URLs for check-urls.sh.
#
# These are API / RPC / WebSocket endpoints that do NOT serve a browseable page,
# so a plain GET returns 404/403/405 even though the host is perfectly alive.
# For any URL matching a pattern below, the checker passes it as long as the
# server completes a connection and returns ANY HTTP status; only a true
# connection failure (DNS/TCP/TLS error, code 000) is reported as broken.
#
# One substring per line. Lines starting with '#' and blank lines are ignored.
# Keep this list tight — anything here is exempt from normal 404 detection.

svr-bid-endpoint.chain.link
45 changes: 45 additions & 0 deletions .github/workflows/url-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: URL Check

# Advisory only: this workflow surfaces broken links in skill docs but never
# blocks a merge. The check step is allowed to fail (continue-on-error), so the
# job always reports success; broken URLs show up as inline annotations and in
# the job summary. Do NOT add this job to required status checks.

on:
pull_request:
branches: [main]
paths:
- '*-skill/**'
- '.github/workflows/url-check.yml'
- '.github/scripts/check-urls.sh'
workflow_dispatch:

permissions:
contents: read

jobs:
check-urls:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Check warning on line 24 in .github/workflows/url-check.yml

View workflow job for this annotation

GitHub Actions / Validate Workflow Changes

1. Action is using node20. Versions older than node24 are being deprecated. Use a newer version of the action if possible. (node-version / warning)

- name: Check URLs in skill docs
id: urlcheck
continue-on-error: true
run: bash .github/scripts/check-urls.sh | tee "$RUNNER_TEMP/url-report.txt"

- name: Publish report to job summary
if: always()
run: |
{
echo '## URL check'
if [ "${{ steps.urlcheck.outcome }}" = "success" ]; then
echo 'All URLs reachable. ✅'
else
echo 'Broken URLs found (advisory — does not block merge). ⚠️'
fi
echo
echo '```'
grep -v '^::' "$RUNNER_TEMP/url-report.txt" || true
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
2 changes: 1 addition & 1 deletion chainlink-ace-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compatibility: Designed for AI agents that implement https://agentskills.io/spec
allowed-tools: Read WebFetch Write Edit Bash
metadata:
purpose: Chainlink ACE core contracts and managed Platform developer onboarding, compliance architecture, product scope, and reference guidance
version: "0.0.5"
version: "0.0.6"
---

# Chainlink ACE Skill
Expand Down
3 changes: 1 addition & 2 deletions chainlink-ace-skill/references/official-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Use this file when an answer depends on current ACE repository facts, source cod
## Repository

- Main repository: `https://github.com/smartcontractkit/chainlink-ace`
- Raw base: `https://raw.githubusercontent.com/smartcontractkit/chainlink-ace/main/`

## ACE Product Docs

Expand Down Expand Up @@ -79,7 +78,7 @@ Use this file when an answer depends on current ACE repository facts, source cod
| Tokens package | `https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/tokens` |
| ERC-20 compliance token | `https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/tokens/erc-20` |
| ERC-3643 compliance token | `https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/tokens/erc-3643` |
| Deploy scripts | `https://github.com/smartcontractkit/chainlink-ace/tree/main/script` |
| Deploy scripts | `https://github.com/smartcontractkit/chainlink-ace/tree/main/scripts` |

## Practical Selection Rules

Expand Down
2 changes: 1 addition & 1 deletion chainlink-cre-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compatibility: Designed for AI agents that implement https://agentskills.io/spec
allowed-tools: Read WebFetch Write Edit Bash
metadata:
purpose: CRE developer onboarding, assistance and reference
version: "0.0.10"
version: "0.0.11"
---

# Chainlink CRE Skill
Expand Down
2 changes: 1 addition & 1 deletion chainlink-cre-skill/references/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,4 @@ cre version
## Official Documentation

- CLI installation: `https://docs.chain.link/cre/getting-started/cli-installation.md`
- CLI reference: `https://docs.chain.link/cre/reference/cre-cli`
- CLI reference: `https://docs.chain.link/cre/reference/cli.md`
2 changes: 1 addition & 1 deletion chainlink-cre-skill/references/official-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Do not use for workflow code patterns, CLI usage, or conceptual questions that a
| CRE SDK TypeScript | `https://github.com/smartcontractkit/cre-sdk-typescript` | TypeScript SDK source |
| CRE SDK Go | `https://github.com/smartcontractkit/cre-sdk-go` | Go SDK source |
| CRE CLI | `https://github.com/smartcontractkit/cre-cli` | CLI source and releases |
| Prediction Market Demo | `https://github.com/smartcontractkit/cre-prediction-market-demo` | Example prediction market workflow |
| Prediction Market Demo | `https://github.com/smartcontractkit/cre-gcp-prediction-market-demo` | Example prediction market workflow |

## Release Notes and Migration

Expand Down
Loading