diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..95cb3a3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,37 @@ +name: Bug report +description: A skill gives wrong instructions or an example does not work +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Search existing issues first. Keep it short and specific. + - type: input + id: skill + attributes: + label: Skill + description: The skill directory, e.g. `blocks-api`, and the reference file if relevant. + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened + description: What went wrong, and what did you expect instead? + validations: + required: true + - type: textarea + id: request + attributes: + label: Request and response + description: The API call you made and what came back. Reference the key, never paste it. + render: text + validations: + required: false + - type: input + id: agent + attributes: + label: Agent and installer + description: e.g. `Claude Code`, installed with `npx skills add --global`. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..fd74836 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Blocks documentation + url: https://docs.blocks.team + about: Platform docs, including the REST API these skills call. + - name: Report a security vulnerability + url: https://github.com/BlocksOrg/skills/security/policy + about: Report vulnerabilities privately — do not open a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..b5a7058 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,27 @@ +name: Feature request +description: Suggest a new skill or an addition to an existing one +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: What problem does this solve? + description: What are you trying to get an agent to do with Blocks that the skills make hard or impossible today? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + description: A new skill, a new reference playbook, a changed recommendation — whatever you have in mind. + validations: + required: true + - type: checkboxes + id: checks + attributes: + label: Before submitting + options: + - label: I searched existing issues and this isn't a duplicate. + required: true + - label: I checked the existing skills and the Blocks docs and this isn't already covered. + required: true diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 0000000..15991c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,14 @@ +name: Question +description: Ask a question about using the skills +labels: ["question"] +body: + - type: markdown + attributes: + value: | + Check the [README](https://github.com/BlocksOrg/skills#readme) and the [Blocks docs](https://docs.blocks.team) first. For security reports, see SECURITY.md — do not open a public issue. + - type: textarea + id: question + attributes: + label: Your question + validations: + required: true diff --git a/.github/assets/blocks-logo-dark.svg b/.github/assets/blocks-logo-dark.svg new file mode 100644 index 0000000..e698bb8 --- /dev/null +++ b/.github/assets/blocks-logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.github/assets/blocks-logo-light.svg b/.github/assets/blocks-logo-light.svg new file mode 100644 index 0000000..2d691d3 --- /dev/null +++ b/.github/assets/blocks-logo-light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..aeb74cb --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +## Summary + + + +## Linked issue + +Fixes # + +## How I verified + + + +## Checklist + +- [ ] `npx skills add . --list` lists every skill under `skills/` +- [ ] Frontmatter `name` matches the skill directory and `description` says when to trigger +- [ ] Examples use `https://api.blocks.team` and `$BLOCKS_API_KEY`; no real keys or internal hosts +- [ ] Docs links resolve and the skill does not restate what the public docs already cover +- [ ] PR title follows Conventional Commits (`type(scope): summary`) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e9d4d34 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + # Lints the workflow files themselves: unknown runner labels, bad expressions, + # shellcheck over every `run:` block. A workflow that fails to parse posts no + # check-run at all, so nothing else in this file would ever catch it. + actionlint: + name: Lint workflows + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + ACTIONLINT_VERSION: 1.7.12 + # sha256 of actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz, from the + # release's published checksums file. + ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - name: Install actionlint + run: | + tarball="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + curl -fsSL -o "$tarball" \ + "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${tarball}" + echo "${ACTIONLINT_SHA256} ${tarball}" | sha256sum --check --strict - + tar -xzf "$tarball" actionlint + - run: ./actionlint -color + + # Every skill must be discoverable by `npx skills add`, its frontmatter name + # must match its directory, and nothing in the repo may leak a real key or an + # internal host. The install script must at least parse. + validate: + name: Validate skills + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + SKILLS_CLI_VERSION: 1.7.0 + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: 24 + - name: Frontmatter name matches directory + run: | + set -euo pipefail + status=0 + for file in skills/*/SKILL.md; do + dir=$(basename "$(dirname "$file")") + name=$(sed -n 's/^name:[[:space:]]*//p' "$file" | head -n1 | tr -d '"'"'"'') + if [ "$name" != "$dir" ]; then + echo "::error file=$file::frontmatter name '$name' does not match directory '$dir'" + status=1 + fi + if ! grep -q '^description:' "$file"; then + echo "::error file=$file::frontmatter is missing a description" + status=1 + fi + done + exit "$status" + - name: skills CLI discovers every skill + run: | + set -euo pipefail + listing=$(npx --yes "skills@${SKILLS_CLI_VERSION}" add . --list 2>&1 | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g') + echo "$listing" + status=0 + for dir in skills/*/; do + name=$(basename "$dir") + if ! printf '%s\n' "$listing" | grep -qE "^[^A-Za-z0-9_-]*${name}[[:space:]]*$"; then + echo "::error::skills CLI did not list '$name'" + status=1 + fi + done + exit "$status" + - name: No real keys or internal hosts + run: | + set -euo pipefail + if grep -rnE 'ApiKey [A-Za-z0-9_-]{30,}|api\.dev\.blocks\.team|appdev\.blocks\.team' \ + --exclude-dir=.git --exclude-dir=.github .; then + echo "::error::found what looks like a real API key or an internal host" + exit 1 + fi + - name: install.sh parses + run: sh -n install.sh diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..4d5aa23 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,37 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '23 5 * * 1' + workflow_dispatch: + +permissions: + actions: read + contents: read + security-events: write + +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + analyze: + # The check-run is always named "CodeQL", whether it runs or is skipped, so + # it can be a required status check. + name: CodeQL + # Code scanning on a private or internal repository needs GitHub Advanced + # Security. Skip until the repo is public: a skipped job still posts a + # check-run, which counts as passing for a required status check. + if: github.event.repository.visibility == 'public' + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 + with: + # This repo ships markdown and workflow files; only the workflows are code. + languages: actions + - uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f52727e --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +

+ + + + Blocks + + +

+ +# Blocks Agent Skills + +[![CI](https://github.com/BlocksOrg/skills/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/BlocksOrg/skills/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) + +Installable `SKILL.md` skills for working with the [Blocks](https://blocks.team) REST API from AI coding agents. + +## Install + +List the skills in this repo: + +```bash +npx skills add https://github.com/BlocksOrg/skills --list +``` + +Install every skill globally: + +```bash +npx skills add https://github.com/BlocksOrg/skills --all --global +``` + +Install a single skill: + +```bash +npx skills add https://github.com/BlocksOrg/skills --skill blocks-api --global +``` + +Omit `--global` to install into the current project instead of your home directory. + +One-liner alternative: + +```bash +curl -fsSL https://raw.githubusercontent.com/BlocksOrg/skills/main/install.sh | sh +``` + +## Skills + +| Skill | What it does | +| --- | --- | +| [`blocks-api`](skills/blocks-api/SKILL.md) | Fetch an existing Blocks session by ID, read its plan and transcript as an overview, and drill into tool calls when more detail is needed. Requires `BLOCKS_API_KEY`. | + +## Verify + +After installing, ask your agent: + +> Fetch the transcript for Blocks session `` and summarize what the agent did. + +## Source of truth + +The skills follow the public REST API docs: https://docs.blocks.team/rest-api/quick-start + +## License + +MIT diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e477215 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,26 @@ +# Security + +## What this repository handles + +- This repository ships markdown skill files and a shell installer. Nothing here runs on + its own; an AI coding agent reads the skills and decides what to do. +- **`BLOCKS_API_KEY`** is the only secret the skills refer to. It stays in your environment + and is sent only as an `Authorization: ApiKey` header to `https://api.blocks.team`. The + skills never ask an agent to print it, write it to disk, or send it anywhere else. +- Presigned artifact URLs returned by the API are fetched without the `Authorization` + header, as the skills state, so the key is never sent to a storage host. +- `install.sh` runs `npx skills add` against this repository and nothing else. CI checks + that every example uses the public host and a placeholder key. + +## Trust boundaries + +- `main` requires review and passing checks, so what a user installs is what was reviewed. +- What an agent does with a session it reads is governed by the agent, its configuration, + and the credentials it holds. These skills only describe how to call the API. + +## Reporting a vulnerability + +Please report suspected vulnerabilities privately via +[GitHub's private vulnerability reporting](https://github.com/BlocksOrg/skills/security/advisories/new) +rather than opening a public issue. Include a description, reproduction steps, and impact. +We aim to acknowledge reports within a few business days. diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..90d1815 --- /dev/null +++ b/install.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +if ! command -v npx >/dev/null 2>&1; then + echo "Error: npx is required but was not found on your PATH." >&2 + echo "npx ships with Node.js (npm). Install Node.js from https://nodejs.org/ (or via your package manager), then re-run this script." >&2 + exit 1 +fi + +npx skills add --all --global --yes https://github.com/BlocksOrg/skills diff --git a/skills/blocks-api/SKILL.md b/skills/blocks-api/SKILL.md new file mode 100644 index 0000000..bb222f2 --- /dev/null +++ b/skills/blocks-api/SKILL.md @@ -0,0 +1,40 @@ +--- +name: blocks-api +description: > + Use when working with the `Blocks` to inspect existing agent sessions: + fetch a session by ID, read its transcript (user prompts and assistant replies), + and drill into tool calls when more detail is needed. Trigger on "Blocks session", + "Blocks API", "fetch blocks session transcript" or any request to read what a + Blocks agent did in a session. Requires BLOCKS_API_KEY. +license: MIT +metadata: + version: "0.1.0" +--- + +## Setup + +```bash +BASE_URL="${BLOCKS_API_BASE_URL:-https://api.blocks.team}" +AUTH="Authorization: ApiKey $BLOCKS_API_KEY" # Settings > API Keys in the Blocks dashboard +``` + +All endpoints live under `/rest/v1`, return JSON, and are rate limited to 100 requests/minute per key. +Array query params repeat the key (`type=a&type=b`); comma lists are rejected. +Endpoint reference: https://docs.blocks.team/rest-api/quick-start + +## Skills + +Read the file for the task at hand. Each one is a complete, ordered walkthrough. + +| Skill | What it covers | +| --- | --- | +| [`references/sessions-get-overview.md`](references/sessions-get-overview.md) | Get up to speed on an existing session by ID. Fetch its metadata, download the latest plan the agent wrote, read the transcript as a compact overview of user prompts and assistant replies, then pull tool calls, single threads, or new messages only when you need more detail. | + +## Errors + +| Status | Meaning | +| --- | --- | +| 401 | Missing or malformed `Authorization` header | +| 403 | Bad key, or key for a different environment | +| 404 | Session not in your workspace, or artifact not attached to it | +| 422 | Bad `limit`, `page`, `type`, `role`, `direction`, non-UUID id, comma lists | diff --git a/skills/blocks-api/references/sessions-get-overview.md b/skills/blocks-api/references/sessions-get-overview.md new file mode 100644 index 0000000..b659ae3 --- /dev/null +++ b/skills/blocks-api/references/sessions-get-overview.md @@ -0,0 +1,74 @@ +# Getting up to speed on a session + +Assumes `BASE_URL` and `AUTH` from the Setup section of `SKILL.md`. + +Do these in order and stop as soon as you have enough: + +1. Fetch the session for metadata and `plan_artifact_ids`. +2. If there is a plan, read it first. It holds most of the useful information. +3. Read the transcript overview (one call). +4. Pull tool calls only if you still need to know how something was done. + +## 1. Fetch a session + +```bash +curl "$BASE_URL/rest/v1/sessions/$SESSION_ID" -H "$AUTH" +``` + +Useful fields: `title`, `pull_requests`, `source_url`, `is_archived`, +`plan_artifact_ids`, `session_html_url`, `_links.messages`. +`thread_id` and `_links.final_message` are always `null` on this endpoint. + +## 2. Read the plan, if there is one + +`plan_artifact_ids` has one entry per plan the agent wrote, already pointing +at that plan's latest version, oldest plan first. Take the last entry. + +```bash +PLAN_ARTIFACT_ID=$(curl -s "$BASE_URL/rest/v1/sessions/$SESSION_ID" -H "$AUTH" | jq -r '.plan_artifact_ids[-1]') +PLAN_URL=$(curl -s "$BASE_URL/rest/v1/sessions/$SESSION_ID/artifacts/$PLAN_ARTIFACT_ID" -H "$AUTH" | jq -r '.url') +curl -s "$PLAN_URL" # presigned, valid 6 days, NO Authorization header +``` + +The artifact response includes `kind` (`plan` or `file`), `file_name`, +`mime_type`, `plan_id`, `created_at`. Confirm `kind == "plan"`. +Skip this step when `plan_artifact_ids` is empty. + +## 3. Transcript overview + +```bash +curl "$BASE_URL/rest/v1/sessions/$SESSION_ID/messages?type=message&role=user&role=assistant" -H "$AUTH" +``` + +- Returns user prompts and assistant messages, oldest first. Do not pass `direction`. +- Each thread (`chat_thread_id`) is one user turn. The last assistant `message` + in a finished thread is its final reply. +- Skip items with `chat_thread_id: null` (system notices). +- A thread whose last item is the user prompt is still running. + +Why `type=message` and not the API default (`message,final_message`): +the last `message` of a finished thread is identical to its `final_message`, +so the default doubles the final text. + +**Fallback if `role=user&role=assistant` returns 422** (older deployments): +make two calls, `?type=message&role=user` and `?type=message`, merge by `ts`. + +## 4. More detail, only when needed + +- **Tool calls:** add `type=tool_call`, e.g. `?type=message&type=tool_call&role=user&role=assistant`. + `message` is a JSON string `{"__name__": "", "input": ""}`. Invocation only, no result. +- **Outcomes only:** `?type=final_message` gives one completed reply per thread. +- **One thread:** `?thread_id=` or `/sessions/{id}/threads/{thread_id}/messages`. + +## Pagination and polling + +`page`, `limit` (1-100, default 50), `meta.total_pages`. +`_links.new_messages.href` carries the `gts` cursor for incremental reads, but only the cursor. +Both `_links` drop your filters. Rebuild pagination and polling URLs yourself. +Unknown thread id returns 200 with empty `items`. + +## Docs + +https://docs.blocks.team/rest-api/sessions/get +https://docs.blocks.team/rest-api/sessions/messages +https://docs.blocks.team/rest-api/sessions/artifacts