paperclip

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

Install
npx skills add 'https://github.com/paperclipai/paperclip/tree/master/skills-releases/paperclip/v7-roster'
Download bundle ↓
master · 5b913e7Scanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗
View on GitHub
← Back to SKILL.md
#!/usr/bin/env bash set -euo pipefail usage() {  cat <<'EOF'Usage:  paperclip-upload-artifact.sh FILE [options] Uploads a generated file from the current workspace to the current Paperclipissue, then creates an attachment-backed artifact work product by default. Required environment for live uploads:  PAPERCLIP_API_URL, PAPERCLIP_API_KEY, PAPERCLIP_COMPANY_ID, PAPERCLIP_TASK_ID, PAPERCLIP_RUN_ID Options:  --issue-id ID          Issue id to attach to (default: PAPERCLIP_TASK_ID)  --company-id ID        Company id (default: PAPERCLIP_COMPANY_ID)  --title TEXT           Work product title (default: file basename)  --summary TEXT         Work product summary  --content-type TYPE    Override detected upload content type  --status STATUS        Work product status (default: ready_for_review)  --no-work-product      Only upload the issue attachment  --no-primary           Do not mark the artifact work product primary for its type  --output FORMAT        markdown or json (default: markdown)  --dry-run              Print resolved upload settings without calling the API  --help, -h             Show this help Examples:  scripts/paperclip-upload-artifact.sh dist/demo.mp4 \    --title "Demo video render" \    --summary "MP4 render for board review"   scripts/paperclip-upload-artifact.sh out/walkthrough.webm \    --title "Walkthrough video" \    --content-type video/webmEOF} require_command() {  if ! command -v "$1" >/dev/null 2>&1; then    printf 'Missing required command: %s\n' "$1" >&2    exit 1  fi} json_bool() {  if [[ "${1:-0}" == "1" ]]; then    printf 'true'  else    printf 'false'  fi} detect_content_type() {  local path="$1"  local lower  lower="$(printf '%s' "$path" | tr '[:upper:]' '[:lower:]')"   case "$lower" in    *.mp4|*.m4v) printf 'video/mp4' ;;    *.webm) printf 'video/webm' ;;    *.mov|*.qt) printf 'video/quicktime' ;;    *.png) printf 'image/png' ;;    *.jpg|*.jpeg) printf 'image/jpeg' ;;    *.gif) printf 'image/gif' ;;    *.webp) printf 'image/webp' ;;    *.svg) printf 'image/svg+xml' ;;    *.pdf) printf 'application/pdf' ;;    *.txt|*.log) printf 'text/plain' ;;    *.md|*.markdown) printf 'text/markdown' ;;    *.json) printf 'application/json' ;;    *.csv) printf 'text/csv' ;;    *.html|*.htm) printf 'text/html' ;;    *.zip) printf 'application/zip' ;;    *)      if command -v file >/dev/null 2>&1; then        file --brief --mime-type "$path"      else        printf 'application/octet-stream'      fi      ;;  esac} request_json() {  local method="$1"  local url="$2"  local body="${3:-}"  local response_file  local status_code   response_file="$(mktemp)"  if [[ -n "$body" ]]; then    status_code="$(      curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \        "$url" \        -H "Authorization: Bearer $PAPERCLIP_API_KEY" \        -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \        -H 'Content-Type: application/json' \        --data-binary "$body"    )"  else    status_code="$(      curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \        "$url" \        -H "Authorization: Bearer $PAPERCLIP_API_KEY" \        -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID"    )"  fi   if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then    printf 'Request failed (%s): %s\n' "$status_code" "$url" >&2    cat "$response_file" >&2    printf '\n' >&2    rm -f "$response_file"    exit 1  fi   cat "$response_file"  rm -f "$response_file"} upload_file() {  local url="$1"  local path="$2"  local content_type="$3"  local escaped_path  local response_file  local status_code   escaped_path="${path//\\/\\\\}"  escaped_path="${escaped_path//\"/\\\"}"  response_file="$(mktemp)"  status_code="$(    curl -sS -X POST -w '%{http_code}' -o "$response_file" \      "$url" \      -H "Authorization: Bearer $PAPERCLIP_API_KEY" \      -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \      -F "file=@\"${escaped_path}\";type=${content_type}"  )"   if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then    printf 'Upload failed (%s): %s\n' "$status_code" "$url" >&2    cat "$response_file" >&2    printf '\n' >&2    rm -f "$response_file"    exit 1  fi   cat "$response_file"  rm -f "$response_file"} file_path=""issue_id="${PAPERCLIP_TASK_ID:-}"company_id="${PAPERCLIP_COMPANY_ID:-}"title=""summary=""content_type=""status="ready_for_review"create_work_product=1is_primary=1output_format="markdown"dry_run=0 while [[ $# -gt 0 ]]; do  case "$1" in    --issue-id)      issue_id="${2:-}"      shift 2      ;;    --company-id)      company_id="${2:-}"      shift 2      ;;    --title)      title="${2:-}"      shift 2      ;;    --summary)      summary="${2:-}"      shift 2      ;;    --content-type)      content_type="${2:-}"      shift 2      ;;    --status)      status="${2:-}"      shift 2      ;;    --no-work-product)      create_work_product=0      shift      ;;    --no-primary)      is_primary=0      shift      ;;    --output)      output_format="${2:-}"      shift 2      ;;    --dry-run)      dry_run=1      shift      ;;    --help|-h)      usage      exit 0      ;;    --*)      printf 'Unknown argument: %s\n' "$1" >&2      usage >&2      exit 1      ;;    *)      if [[ -n "$file_path" ]]; then        printf 'Unexpected positional argument: %s\n' "$1" >&2        usage >&2        exit 1      fi      file_path="$1"      shift      ;;  esacdone if [[ -z "$file_path" ]]; then  printf 'Missing file path.\n' >&2  usage >&2  exit 1fi if [[ ! -f "$file_path" ]]; then  printf 'Artifact file does not exist: %s\n' "$file_path" >&2  exit 1fi if [[ "$output_format" != "markdown" && "$output_format" != "json" ]]; then  printf 'Unsupported output format: %s\n' "$output_format" >&2  exit 1fi require_command curlrequire_command jq if [[ -z "$title" ]]; then  title="$(basename "$file_path")"fi if [[ -z "$content_type" ]]; then  content_type="$(detect_content_type "$file_path")"fi if [[ "$dry_run" == "1" ]]; then  create_work_product_json="$(json_bool "$create_work_product")"  is_primary_json="$(json_bool "$is_primary")"  jq -n \    --arg file "$file_path" \    --arg issueId "$issue_id" \    --arg companyId "$company_id" \    --arg title "$title" \    --arg summary "$summary" \    --arg contentType "$content_type" \    --arg status "$status" \    --argjson createWorkProduct "$create_work_product_json" \    --argjson isPrimary "$is_primary_json" \    '{file: $file, issueId: $issueId, companyId: $companyId, title: $title, summary: $summary, contentType: $contentType, status: $status, createWorkProduct: $createWorkProduct, isPrimary: $isPrimary}'  exit 0fi if [[ -z "${PAPERCLIP_API_URL:-}" || -z "${PAPERCLIP_API_KEY:-}" || -z "${PAPERCLIP_RUN_ID:-}" ]]; then  printf 'Missing PAPERCLIP_API_URL, PAPERCLIP_API_KEY, or PAPERCLIP_RUN_ID.\n' >&2  exit 1fi if [[ -z "$issue_id" || -z "$company_id" ]]; then  printf 'Missing issue or company id. Pass --issue-id/--company-id or set PAPERCLIP_TASK_ID/PAPERCLIP_COMPANY_ID.\n' >&2  exit 1fi api_base="${PAPERCLIP_API_URL%/}/api"attachment="$(  upload_file \    "$api_base/companies/$company_id/issues/$issue_id/attachments" \    "$file_path" \    "$content_type")" work_product="null"if [[ "$create_work_product" == "1" ]]; then  is_primary_json="$(json_bool "$is_primary")"  attachment_id="$(jq -r '.id // empty' <<<"$attachment")"  byte_size="$(jq -r '.byteSize // 0' <<<"$attachment")"  content_path="$(jq -r '.contentPath // empty' <<<"$attachment")"  open_path="$(jq -r '.openPath // .contentPath // empty' <<<"$attachment")"  download_path="$(jq -r '.downloadPath // (if .contentPath then (.contentPath + "?download=1") else "" end)' <<<"$attachment")"  original_filename="$(jq -r '.originalFilename // empty' <<<"$attachment")"   if [[ -z "$attachment_id" || -z "$content_path" || -z "$download_path" ]]; then    printf 'Upload response did not include attachment path metadata.\n' >&2    printf '%s\n' "$attachment" >&2    exit 1  fi   work_product_payload="$(    jq -nc \      --arg title "$title" \      --arg summary "$summary" \      --arg status "$status" \      --arg runId "$PAPERCLIP_RUN_ID" \      --arg attachmentId "$attachment_id" \      --arg contentType "$content_type" \      --argjson byteSize "$byte_size" \      --arg contentPath "$content_path" \      --arg openPath "$open_path" \      --arg downloadPath "$download_path" \      --arg originalFilename "$original_filename" \      --argjson isPrimary "$is_primary_json" \      '{        type: "artifact",        provider: "paperclip",        title: $title,        status: $status,        reviewState: "none",        isPrimary: $isPrimary,        healthStatus: "unknown",        summary: (if $summary == "" then null else $summary end),        createdByRunId: $runId,        metadata: {          attachmentId: $attachmentId,          contentType: $contentType,          byteSize: $byteSize,          contentPath: $contentPath,          openPath: $openPath,          downloadPath: $downloadPath,          originalFilename: (if $originalFilename == "" then null else $originalFilename end)        }      }'  )"   work_product="$(    request_json \      POST \      "$api_base/issues/$issue_id/work-products" \      "$work_product_payload"  )"fi if [[ "$output_format" == "json" ]]; then  jq -n --argjson attachment "$attachment" --argjson workProduct "$work_product" \    '{attachment: $attachment, workProduct: $workProduct}'  exit 0fi content_path="$(jq -r '.contentPath // empty' <<<"$attachment")"download_path="$(jq -r '.downloadPath // (if .contentPath then (.contentPath + "?download=1") else "" end)' <<<"$attachment")"attachment_id="$(jq -r '.id // empty' <<<"$attachment")"work_product_id="$(jq -r '.id // empty' <<<"$work_product")" printf 'Uploaded artifact\n\n'printf -- '- Attachment: [%s](%s)\n' "$title" "$content_path"printf -- '- Download: [%s](%s)\n' "$title" "$download_path"printf -- '- Attachment ID: `%s`\n' "$attachment_id"if [[ -n "$work_product_id" ]]; then  printf -- '- Work product ID: `%s`\n' "$work_product_id"fiprintf '\nFinal comment snippet:\n\n'printf -- '- Artifact: [%s](%s)\n' "$title" "$content_path"