configure-agent-pr-attestation · v1.0.0 · 2026-10-14 · sha256 7a60afdd52cd2f4c
configure-agent-pr-attestation v1.0.0B
Immutable. This exact content never changes and is served at /api/v1/blob/7a60afdd52cd2f4c.
---
name: configure-agent-pr-attestation
description: >-
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.
metadata:
author: Harness
version: 1.0.0
mcp-server: harness-mcp-v2
summary: Install and configure the Claude Code PR attestation hook for signed in-toto attestations to the Harness Evidence Vault
license: Apache-2.0
compatibility: Requires harness-scs CLI binary and cosign for signing. Currently supports Claude Code as the agent runtime.
---
# Configure Agent PR Attestation
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.
## Instructions
Follow these steps in exact order. Do NOT skip steps or reorder them.
### Step 1: Check Prerequisites
Run these checks and report what's missing. Do not proceed until all are installed.
```bash
command -v harness-scs
command -v jq
command -v git
command -v cosign
```
If `harness-scs` is missing, tell the user:
```bash
task compile-harness-scs && sudo mv harness-scs /usr/local/bin/
```
### Step 2: Ask for SCM
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":
1. Ask for the exact command they run.
2. Ask for an example PR URL their tool prints.
3. Derive `command_filter` and `url_pattern`, show to user, confirm.
### Step 3: Set up Harness Identity (auth.json)
Check if `~/.harness/auth.json` already exists:
```bash
test -r ~/.harness/auth.json && jq -e '.token and .account_id' ~/.harness/auth.json >/dev/null 2>&1
```
**If 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:
- API key (PAT, format: `pat.<account>.<random>.<random>`)
- API URL (default: `https://app.harness.io`)
- Account ID (can be auto-derived from PAT if empty)
- Org ID
- Project ID
Then create `~/.harness/auth.json`:
```bash
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.json
```
### Step 4: Set up Cosign Key and scs.env
Ask 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:**
```bash
mkdir -p ~/.harness
cd ~/.harness && COSIGN_PASSWORD="" cosign generate-key-pair
chmod 600 ~/.harness/cosign.key
```
Set `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):
```bash
cat > ~/.harness/scs.env << 'EOF'
export HARNESS_SIGNER_KEY_PATH="<key_path>"
export COSIGN_PASSWORD=""
EOF
```
`scs.env` contains ONLY cosign signer variables. Never put Harness identity here.
### Step 5: Generate the Hook Script
Write to `<project>/.claude/hooks/agent-pr-attestation.sh`, substituting:
- `{{COMMAND_FILTER}}` ← from Step 2
- `{{URL_REGEX}}` ← from Step 2
- `{{KEY_PATH}}` ← from Step 4
Then `chmod +x` the script.
```bash
#!/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
```
### Step 6: Patch `.claude/settings.json`
Register the hook with Claude Code. Use project-scoped settings by default.
Read existing `.claude/settings.json` (create `{}` if absent), then merge in:
```json
{
"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.
## Troubleshooting
| 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 |
## Reconfiguration
If the user re-runs the skill, detect existing files and offer:
- "An existing hook was found at `<path>`. Replace it?"
- "Existing cosign key at `<path>` — keep it or regenerate?"
- "Found `~/.harness/auth.json` — keep or update?"
Never silently overwrite.
## Limitations
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.