Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
123 changes: 90 additions & 33 deletions .github/workflows/deploy-plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# cursor -> getsentry/plugin-cursor
# codex -> getsentry/plugin-codex
# grok -> getsentry/plugin-grok
# pi -> getsentry/plugin-pi
#
# Each plugin repository carries two rolling branches, and which one a run writes
# is the whole difference between a deploy and a release:
Expand All @@ -25,14 +26,16 @@
# A release also tags the plugin repository (`v<version>`), giving each shipped
# version an addressable ref for pinning and rollback.
#
# Marketplaces consume `getsentry/plugin-<agent>` by git ref. Each job builds its
# agent's tree from this repo, then commits it onto the target branch of the
# target repo, replacing the previous contents. The four jobs target four
# different repos, so they run in parallel without contention.
# Marketplaces consume `getsentry/plugin-<agent>` by git ref. Each build job
# validates its agent's tree without deployment credentials and uploads it as an
# artifact. A separate deploy job then mints a repository-scoped token and
# commits only that validated artifact onto the target branch of the target
# repo. The five matrix entries target five different repos, so they run in
# parallel without contention.
#
# Cross-repo writes use a GitHub App token scoped per-job to a single plugin
# repo; the default GITHUB_TOKEN cannot push to other repositories. The app must
# be installed on the org with contents:write on the four plugin repos, its ID
# be installed on the org with contents:write on the five plugin repos, its ID
# stored as the PLUGIN_DEPLOY_APP_ID variable and its private key as the
# PLUGIN_DEPLOY_KEY secret. Each target repo must already exist with `main` as
# its default branch; `develop` is branched off it on the first deploy.
Expand Down Expand Up @@ -77,16 +80,18 @@ permissions:
contents: read

jobs:
deploy:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
# Every matrix entry builds the same checkout, so the last writer wins
# with the same value. Read out of the checkout rather than github.sha,
# which in a called workflow reports the caller's commit instead.
src_sha: ${{ steps.build.outputs.src_sha }}
strategy:
fail-fast: false
matrix:
agent: [claude, cursor, codex, grok]
concurrency:
group: deploy-plugin-${{ matrix.agent }}-${{ inputs.target_branch || 'develop' }}
cancel-in-progress: false
agent: [claude, cursor, codex, grok, pi]
steps:
- name: Checkout source
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
Expand All @@ -96,6 +101,76 @@ jobs:
# which needs the tags and the commits since the last one.
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0

- name: Set up Node for Pi validation
if: matrix.agent == 'pi'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24

- name: Install Pi CLI
if: matrix.agent == 'pi'
Comment thread
sentry-warden[bot] marked this conversation as resolved.
run: npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.83.0

- name: Build plugin-${{ matrix.agent }}
id: build
env:
AGENT: ${{ matrix.agent }}
DIST_TAG: ${{ inputs.dist_tag || '' }}
run: |
set -euo pipefail

echo "src_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

# A release stamps the version being released; anything else is a
# develop build and gets labelled with its distance from the last tag.
if [[ -z "$DIST_TAG" ]]; then
PLUGIN_VERSION="$(scripts/dev-version.sh)"
export PLUGIN_VERSION
echo "::notice::stamping develop build as ${PLUGIN_VERSION}"
fi

DIST_DIR="$(mktemp -d)/dist"
"src/plugins/${AGENT}/build.sh" "$DIST_DIR"
"src/plugins/${AGENT}/validate.sh" "$DIST_DIR"

mkdir -p "artifacts/${AGENT}"
rsync -a --delete "$DIST_DIR/" "artifacts/${AGENT}/"

- name: Upload validated plugin-${{ matrix.agent }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: plugin-${{ matrix.agent }}-${{ github.sha }}
path: artifacts/${{ matrix.agent }}/
if-no-files-found: error
include-hidden-files: true
retention-days: 1

deploy:
needs: build
# A failed build omits only that agent's artifact. Run the deploy matrix for
# the remaining artifacts instead of treating the build matrix as one gate.
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
agent: [claude, cursor, codex, grok, pi]
Comment thread
sergical marked this conversation as resolved.
concurrency:
group: deploy-plugin-${{ matrix.agent }}-${{ inputs.target_branch || 'develop' }}
cancel-in-progress: false
steps:
- name: Download validated plugin-${{ matrix.agent }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: plugin-${{ matrix.agent }}-${{ github.sha }}
path: artifact

# Mint the write credential only after every third-party package and
# artifact action has finished executing in this job.
- name: Mint deploy token
id: token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
Expand All @@ -105,33 +180,19 @@ jobs:
owner: getsentry
repositories: plugin-${{ matrix.agent }}

- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0

- name: Build and deploy plugin-${{ matrix.agent }}
- name: Deploy plugin-${{ matrix.agent }}
env:
AGENT: ${{ matrix.agent }}
TARGET_BRANCH: ${{ inputs.target_branch || 'develop' }}
DIST_TAG: ${{ inputs.dist_tag || '' }}
SRC_SHA: ${{ needs.build.outputs.src_sha }}
GH_TOKEN: ${{ steps.token.outputs.token }}
run: |
set -euo pipefail

TARGET_REPO="plugin-${AGENT}"
WORKTREE="$(mktemp -d)/dist"

# Read the built commit out of the checkout rather than github.sha,
# which in a called workflow reports the caller's commit instead.
SRC_SHA="$(git rev-parse HEAD)"

# A release stamps the version being released; anything else is a
# develop build and gets labelled with its distance from the last tag.
if [[ -z "$DIST_TAG" ]]; then
PLUGIN_VERSION="$(scripts/dev-version.sh)"
export PLUGIN_VERSION
echo "::notice::stamping develop build as ${PLUGIN_VERSION}"
fi

# Clone the target repo (lands on its default branch, `main`).
git clone "https://x-access-token:${GH_TOKEN}@github.com/getsentry/${TARGET_REPO}.git" "$WORKTREE"
git -C "$WORKTREE" config user.name "github-actions[bot]"
Expand All @@ -146,14 +207,10 @@ jobs:
git -C "$WORKTREE" checkout -b "$TARGET_BRANCH"
fi

# Rewrite the whole tree: clear tracked content (preserve the .git
# dir), then repopulate from source via the agent's build script.
# Rewrite the whole tree from the already validated artifact. No build
# tool or third-party package executes while GH_TOKEN is in scope.
git -C "$WORKTREE" rm -rfq --ignore-unmatch .
"src/plugins/${AGENT}/build.sh" "$WORKTREE"

# Validate the built tree against the agent's schema/validator before
# it can be deployed.
"src/plugins/${AGENT}/validate.sh" "$WORKTREE"
rsync -a --delete --exclude='.git/' artifact/ "$WORKTREE/"

# Commit only if something changed.
git -C "$WORKTREE" add -A
Expand Down
28 changes: 18 additions & 10 deletions .github/workflows/smoke-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# (--no-interactive) so it installs the Sentry plugin for every detected agent
# without a prompt, and verifies each plugin actually landed.
#
# Claude Code, Codex, and Grok ship cross-platform npm CLIs, so they get a real
# install against their default marketplaces. Cursor has no headless CLI to
# Claude Code, Codex, Grok, and Pi ship cross-platform npm CLIs, so they get a
# real install against their package sources. Cursor has no headless CLI to
# install in CI, but our Cursor install is only a `git clone` of the public
# plugin repo into ~/.cursor/plugins/local/sentry — so we put a `cursor` stub on
# PATH to satisfy detection and verify the real clone (this is what exercises
Expand Down Expand Up @@ -57,8 +57,8 @@ jobs:
- name: Build
run: pnpm --filter @sentry/ai build

- name: Install Claude Code, Codex, and Grok CLIs
run: npm install -g @anthropic-ai/claude-code @openai/codex @xai-official/grok
- name: Install Claude Code, Codex, Grok, and Pi CLIs
run: npm install -g @anthropic-ai/claude-code @openai/codex @xai-official/grok @earendil-works/pi-coding-agent

- name: Put a Cursor stub on PATH
run: |
Expand All @@ -82,18 +82,26 @@ jobs:
set -euo pipefail

echo "== claude =="
claude plugin list
claude plugin list | grep -iq sentry || { echo "claude: sentry plugin missing"; exit 1; }
claude_plugins=$(claude plugin list)
printf '%s\n' "$claude_plugins"
printf '%s\n' "$claude_plugins" | grep -iq sentry || { echo "claude: sentry plugin missing"; exit 1; }

echo "== codex =="
codex plugin list
codex plugin list | grep -iq sentry || { echo "codex: sentry plugin missing"; exit 1; }
codex_plugins=$(codex plugin list)
printf '%s\n' "$codex_plugins"
printf '%s\n' "$codex_plugins" | grep -iq sentry || { echo "codex: sentry plugin missing"; exit 1; }

echo "== grok =="
grok plugin list
grok plugin list | grep -iq sentry || { echo "grok: sentry plugin missing"; exit 1; }
grok_plugins=$(grok plugin list)
printf '%s\n' "$grok_plugins"
printf '%s\n' "$grok_plugins" | grep -iq sentry || { echo "grok: sentry plugin missing"; exit 1; }

echo "== cursor =="
node -e "const p=require('path').join(require('os').homedir(),'.cursor','plugins','local','sentry'); if(!require('fs').existsSync(p)){console.error('cursor: plugin dir missing at '+p);process.exit(1)} console.log(p)"

echo "== pi =="
pi_plugins=$(pi list --no-approve)
printf '%s\n' "$pi_plugins"
printf '%s\n' "$pi_plugins" | grep -iq 'getsentry/plugin-pi' || { echo "pi: sentry package missing"; exit 1; }

echo "All agents have the Sentry plugin."
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

Sentry plugin for AI coding assistants (Claude Code, Cursor, Codex, and Grok).
Sentry plugin for AI coding assistants (Claude Code, Cursor, Codex, Grok, and Pi).
Provides MCP server integration and skills.

## Commit Attribution
Expand All @@ -26,6 +26,8 @@ src/SKILL_TREE.md # Generated skill index (see Skill Tree Navigation belo

Per-agent plugin metadata is generated by the `src/plugins/<agent>/build.sh` scripts and
published to each agent’s distribution repository; it is not committed here.
Pi is packaged as a native Pi package (`package.json` with a `pi` manifest) and includes
a small `pi-mcp-adapter` extension because Pi has no built-in MCP client.

Skill frontmatter is `name`, `description`, and `license`. Nothing else is required, and
the retired router fields (`category`, `parent`, `role`, `disable-model-invocation`) are
Expand All @@ -52,6 +54,8 @@ emit it as `.mcp.json` (Codex’s validator requires the dotted name; Grok auto-
it).
Claude declares the server inline in its `plugin.json` (`mcpServers`), so the Claude
build ships no MCP file.
Pi declares the same server in `src/plugins/pi/extensions/sentry-mcp.ts`, which adapts
it into native Pi tools.

## Releasing the Plugins

Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
> skills here are built from this source into installable plugins for
> [Claude Code](https://github.com/getsentry/plugin-claude),
> [Cursor](https://github.com/getsentry/plugin-cursor),
> [Codex](https://github.com/getsentry/plugin-codex), and
> [Grok](https://github.com/getsentry/plugin-grok) — install one of those, not this
> repo. They’re also served over HTTP at [skills.sentry.dev](https://skills.sentry.dev)
> for agents to fetch directly.
> [Codex](https://github.com/getsentry/plugin-codex),
> [Grok](https://github.com/getsentry/plugin-grok), and
> [Pi](https://github.com/getsentry/plugin-pi) — install one of those, not this repo.
> They’re also served over HTTP at [skills.sentry.dev](https://skills.sentry.dev) for
> agents to fetch directly.
> In the future we may also publish the skills as a generic, standalone skills
> repository.

Expand All @@ -22,8 +23,9 @@ The plugin gives your assistant the context it needs to do it right.

Supports [**Claude Code**](https://github.com/getsentry/plugin-claude),
[**Cursor**](https://github.com/getsentry/plugin-cursor),
[**Codex**](https://github.com/getsentry/plugin-codex), and
[**Grok**](https://github.com/getsentry/plugin-grok).
[**Codex**](https://github.com/getsentry/plugin-codex),
[**Grok**](https://github.com/getsentry/plugin-grok), and
[**Pi**](https://github.com/getsentry/plugin-pi).

## What You Can Do

Expand Down Expand Up @@ -77,6 +79,7 @@ its own **distribution repository**, whose root is exactly that agent’s plugin
| Cursor | [`getsentry/plugin-cursor`](https://github.com/getsentry/plugin-cursor) |
| Codex | [`getsentry/plugin-codex`](https://github.com/getsentry/plugin-codex) |
| Grok | [`getsentry/plugin-grok`](https://github.com/getsentry/plugin-grok) |
| Pi | [`getsentry/plugin-pi`](https://github.com/getsentry/plugin-pi) |

These repositories are generated; do not edit them.
Each one’s README has the install instructions for that agent.
Expand All @@ -94,11 +97,11 @@ skill tree’s `disable-model-invocation` flags for Codex’s `agents/openai.yam
```bash
git clone https://github.com/getsentry/sentry-for-ai.git
cd sentry-for-ai
src/plugins/codex/build.sh /tmp/sentry-codex # or src/plugins/{claude,cursor,grok}
src/plugins/codex/build.sh /tmp/sentry-codex # or src/plugins/{claude,cursor,grok,pi}
```

To build any target locally, run `src/plugins/<agent>/build.sh <output-dir>` (`claude`,
`cursor`, `codex`, or `grok`).
`cursor`, `codex`, `grok`, or `pi`).

## Skills

Expand Down
Loading
Loading