Use when asked to set up PR attestation, install harness-scs attestation hook, configure agent attestor, troubleshoot why agent attestation isn't uploading, or reconfigure an existing attestation hook (different SCM, new cosign key, updated Harness identity). Trigger phrases: set up PR attestation, install harness-scs attestation hook, configure agent attestor, why isn't my agent attestation uploading, set up agent attestation, configure PR signing, install attestation hook, configure claude attestation.
74
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Medium
Suggest reviewing before use
Sets up a PostToolUse hook that signs and uploads an in-toto attestation
to the Harness Evidence Vault every time an AI coding agent creates a PR/MR.
Follow these steps in exact order. Do NOT skip steps or reorder them.
Run these checks and report what's missing. Do not proceed until all are installed.
command -v harness-scs
command -v jq
command -v git
command -v cosignIf harness-scs is missing, tell the user:
task compile-harness-scs && sudo mv harness-scs /usr/local/bin/Ask the user which tool they use to create PRs/MRs. Use AskUserQuestion.
Options:
| Choice | command_filter | url_pattern |
|---|---|---|
| Harness CLI (codepulse pr create) | codepulse pr create | https://[^/]*\.harness\.io/.*/pulls/[0-9]+(/.*)? |
| GitHub CLI (gh pr create) | gh pr create | https://github\.com/[^ ]+/pull/[0-9]+ |
| GitLab CLI (glab mr create) | glab mr create | https://[^ ]+/-/merge_requests/[0-9]+ |
| Bitbucket CLI (bb pr create) | bb pr create | https://[^ ]+/pull-requests/[0-9]+ |
| Azure DevOps CLI (az repos pr create) | az repos pr create | https://[^ ]+/_git/[^ ]+/pullrequest/[0-9]+ |
| Gitea / Forgejo (tea pr create) | tea pr create | https://[^ ]+/pulls/[0-9]+ |
If user picks GitLab, Bitbucket, Gitea, or Custom — also ask if self-hosted.
If self-hosted, ask for hostname and replace the generic [^ ]+ host in the
url_pattern with their specific escaped hostname.
If user picks "Other / custom":
command_filter and url_pattern, show to user, confirm.Check if ~/.harness/auth.json already exists:
test -r ~/.harness/auth.json && jq -e '.token and .account_id' ~/.harness/auth.json >/dev/null 2>&1If auth.json exists and is valid: Tell the user "Found existing ~/.harness/auth.json" and move to Step 4.
If auth.json does NOT exist: Ask the user for these values:
pat.<account>.<random>.<random>)https://app.harness.io)Then create ~/.harness/auth.json:
mkdir -p ~/.harness
cat > ~/.harness/auth.json << 'EOF'
{
"token": "<API key>",
"base_url": "<API URL>",
"account_id": "<Account ID>",
"org_id": "<Org ID>",
"project_id": "<Project ID>"
}
EOF
chmod 600 ~/.harness/auth.jsonAsk the user via AskUserQuestion:
Cosign signing key:
- I already have one → ask for absolute path
- Generate a new one for me → generate in ~/.harness/If generating:
mkdir -p ~/.harness
cd ~/.harness && COSIGN_PASSWORD="" cosign generate-key-pair
chmod 600 ~/.harness/cosign.keySet key_path="$HOME/.harness/cosign.key".
If existing key: Ask for the absolute path. Validate it's readable.
Warn if password-protected — they'll need to set COSIGN_PASSWORD for unattended runs.
Then write ~/.harness/scs.env (do NOT ask where to put it — always this path):
cat > ~/.harness/scs.env << 'EOF'
export HARNESS_SIGNER_KEY_PATH="<key_path>"
export COSIGN_PASSWORD=""
EOFscs.env contains ONLY cosign signer variables. Never put Harness identity here.
Write to <project>/.claude/hooks/agent-pr-attestation.sh, substituting:
{{COMMAND_FILTER}} ← from Step 2{{URL_REGEX}} ← from Step 2{{KEY_PATH}} ← from Step 4Then chmod +x the script.
#!/bin/bash
# Generated by configure-agent-pr-attestation skill.
# Captures a signed in-toto attestation when an AI agent creates a PR.
#
# To reconfigure: re-run /configure-agent-pr-attestation in your agent.
set -uo pipefail
[ -f ~/.harness/scs.env ] && source ~/.harness/scs.env
COMMAND_FILTER='{{COMMAND_FILTER}}'
URL_REGEX='{{URL_REGEX}}'
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if ! echo "$COMMAND" | grep -qE "$COMMAND_FILTER"; then
exit 0
fi
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path')
TOOL_STDOUT=$(echo "$INPUT" | jq -r '.tool_response.stdout // empty')
TOOL_STDERR=$(echo "$INPUT" | jq -r '.tool_response.stderr // empty')
CWD=$(echo "$INPUT" | jq -r '.cwd')
PR_URL=$(printf '%s\n%s\n' "$TOOL_STDOUT" "$TOOL_STDERR" | grep -oE "$URL_REGEX" | head -1)
COMMIT_SHA=$(cd "$CWD" && git rev-parse HEAD 2>/dev/null)
if [ -z "$PR_URL" ] || [ -z "$COMMIT_SHA" ]; then
exit 0
fi
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
EXECUTION_ID="harness-pr-attestor-${PR_NUMBER:-$(date +%s)}"
PASSPHRASE_FILE=$(mktemp -t harness-scs-passphrase.XXXXXX)
trap 'rm -f "$PASSPHRASE_FILE"' EXIT
chmod 600 "$PASSPHRASE_FILE"
printf '%s' "${COSIGN_PASSWORD:-}" >"$PASSPHRASE_FILE"
harness-scs attestation run \
--step "agent-pull-request" \
--attestations claudecode \
--attestor-claudecode-session-file "$TRANSCRIPT_PATH" \
--attestor-claudecode-include-thinking \
--attestor-claudecode-commit-sha "$COMMIT_SHA" \
--attestor-claudecode-pr-url "$PR_URL" \
--signer-file-key-path "${HARNESS_SIGNER_KEY_PATH:-{{KEY_PATH}}}" \
--signer-file-key-passphrase-path "$PASSPHRASE_FILE" \
--enable-attestation-upload \
--execution-id "$EXECUTION_ID" \
>/dev/null 2>&1
exit 0.claude/settings.jsonRegister the hook with Claude Code. Use project-scoped settings by default.
Read existing .claude/settings.json (create {} if absent), then merge in:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/agent-pr-attestation.sh"
}
]
}
]
}
}Make this idempotent — don't duplicate if already present.
After patching, confirm setup is complete and tell the user to verify by creating a test PR in a new session.
| Symptom | Likely cause | Fix |
|---|---|---|
| Hook never fires | Matcher not registered, or settings.json malformed | jq . .claude/settings.json to validate; re-run skill |
| No attestation after PR created | command_filter doesn't match the command, OR url_regex doesn't match the PR URL | Add echo "$COMMAND" > /tmp/debug-hook.txt to inspect; run PR command manually and compare URL against regex |
harness-scs: command not found | Binary not on PATH for non-interactive shells | Install to /usr/local/bin/ or hardcode absolute path |
failed to load any signers | Missing or unreadable cosign key | Check $HARNESS_SIGNER_KEY_PATH is set and file is readable |
| Upload fails with 401/403 | Stale or wrong API key | Check ~/.harness/auth.json token and account_id |
If the user re-runs the skill, detect existing files and offer:
<path>. Replace it?"<path> — keep it or regenerate?"~/.harness/auth.json — keep or update?"Never silently overwrite.
Only cosign file keys are supported for signing. If the user asks about keyless signing (Fulcio), KMS signers, or non-Claude-Code agent runtimes, tell them these are not yet available.
23e9b8b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.