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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"permissions": {
"deny": [
"mcp__github__merge_pull_request",
"mcp__github__delete_file",
"mcp__github__fork_repository",
"mcp__github__create_repository",
"mcp__github__actions_run_trigger"
]
}
}
84 changes: 84 additions & 0 deletions .claude/skills/dev-loop/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
name: dev-loop
description: Full autonomous development loop — implement, build, test, commit, push, create PR, monitor CI, fix failures until green
disable-model-invocation: true
allowed-tools: Bash(cmake *), Bash(ctest *), Bash(nproc), Bash(git add *), Bash(git commit *), Bash(git diff *), Bash(git describe *), Bash(git branch *), Bash(git status), Bash(git log *), Bash(git rev-parse *), Bash(clang-format *), Bash(sleep *), Bash(date *), Read, Write, Edit, Glob, Grep, Agent
---

# Dev Loop

Complete a development task end-to-end: implement, build, test, push, create PR, monitor CI, fix failures.
Do NOT stop until CI is green or you are blocked.

## Phase 1: Implement

1. Read and understand the task from $ARGUMENTS
2. Explore relevant code
3. Implement the changes
4. Build: `cmake -S . -B cmake-build -DCMAKE_BUILD_TYPE=Release -DCOLLECTOR_VERSION=$(git describe --tags --abbrev=10 --long) && cmake --build cmake-build -- -j$(nproc)`
- If build fails, fix and retry
5. Test: `ctest --no-tests=error -V --test-dir cmake-build`
- If tests fail, fix and retry
6. Format: `clang-format --style=file -i <changed .cpp/.h files>`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to make this more general purpose if we teach claude to use the pre-commit hooks, and should mean we're covered if it changes any non-C++ code

7. Commit: `git add` changed files, `git commit` with a descriptive message

## Phase 2: Push and create PR

Use the GitHub MCP server to push files and create a PR.
Do NOT use `git push` — it will fail (no SSH keys in this container).

1. Get the current branch name and the list of changed files:
- `git branch --show-current` for the branch
- `git diff --name-only origin/HEAD..HEAD` for changed files
2. Use the GitHub MCP `push_files` tool to push the changed files directly to
the remote branch. This creates a commit via the GitHub API using the file
contents from your local workspace — it does not sync git history.
- owner: stackrox, repo: collector, branch: <current branch>
- Read each changed file and include its content
- Provide a commit message
3. Search for an open PR for this branch via GitHub MCP
4. If no PR exists, create a draft PR via GitHub MCP

## Phase 3: Monitor CI

Loop until all checks pass or blocked (max 6 cycles, ~3 hours):

1. Wait 10 minutes: `sleep 600`
2. Check CI status via GitHub MCP (PR checks, workflow runs)
3. Update PR body with an `## Agent Status` section:
```
## Agent Status
**Last updated:** <`date -u +"%Y-%m-%d %H:%M UTC"`>
**CI cycle:** N of 6
**Status:** PENDING | PASSED | FIXED | FLAKE | BLOCKED
**Details:** <one-line summary>
```
4. Evaluate:
- **All checks passed** → update PR body, report success, stop
- **Still running** → continue loop
- **Failed** →
- Get job logs via GitHub MCP
- Diagnose: build error, test assertion, lint, infra flake
- If fixable: fix → build → test → push changed files via MCP → continue
- If infra flake: note as FLAKE, continue
- If not fixable: update PR body, report BLOCKED, stop

## Phase 4: Check PR comments

Before each CI cycle, check if there are new PR review comments via GitHub MCP.
If a reviewer left feedback:
- Address the feedback (edit code, fix issues)
- Build and test
- Push changed files via MCP
- Note in the Agent Status section what feedback was addressed

## Completion

Print summary:
```
STATUS: PASSED | BLOCKED | TIMEOUT
Branch: <branch>
PR: <url>
Cycles: N
Changes: <list of files modified>
```
43 changes: 43 additions & 0 deletions .claude/skills/task/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: task
description: Implement a change — edit code, build, test, format, commit locally. No push.
disable-model-invocation: true
allowed-tools: Bash(cmake *), Bash(ctest *), Bash(nproc), Bash(git add *), Bash(git commit *), Bash(git diff *), Bash(git describe *), Bash(git branch *), Bash(git status), Bash(clang-format *), Read, Write, Edit, Glob, Grep, Agent
---

# Task

Implement a change locally: edit, build, test, format, commit.
Do NOT push or create PRs — use /watch-ci for that.

## Steps

1. Read and understand the task from $ARGUMENTS
2. Explore relevant code in the repository
3. Implement the changes
4. Build:
- `cmake -S . -B cmake-build -DCMAKE_BUILD_TYPE=Release -DCOLLECTOR_VERSION=$(git describe --tags --abbrev=10 --long) && cmake --build cmake-build -- -j$(nproc)`
- If build fails, fix and retry
5. Run unit tests:
- `ctest --no-tests=error -V --test-dir cmake-build`
- If tests fail, fix and retry
6. Format changed C++ files:
- `clang-format --style=file -i <changed .cpp/.h files>`
7. Commit:
- `git add` the changed files
- `git commit` with a descriptive message

## STOP here. Report and wait.

Print this summary and then STOP. Do not continue with any other actions.

```
TASK COMPLETE
Branch: <current branch>
Commit: <commit hash>
Files changed: <list>
Tests: <pass/fail count>
```

The user will review and decide whether to run /watch-ci.
Do NOT push, create branches, or create PRs.
68 changes: 68 additions & 0 deletions .claude/skills/watch-ci/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: watch-ci
description: Push files to existing remote branch via GitHub MCP, create PR if needed, monitor CI, fix failures until green
disable-model-invocation: true
allowed-tools: Bash(cmake *), Bash(ctest *), Bash(nproc), Bash(git add *), Bash(git commit *), Bash(git diff *), Bash(git describe *), Bash(git branch *), Bash(git status), Bash(git log *), Bash(git rev-parse *), Bash(clang-format *), Bash(sleep *), Bash(date *), Read, Write, Edit, Glob, Grep
---

# Watch CI

Push changed files via the GitHub MCP server, create PR if needed, and monitor CI until green.
Do NOT use `git push` — it will fail (no SSH keys in this container).

## How pushing works

Use the GitHub MCP `push_files` tool to send file contents directly to the remote
branch via the GitHub API. This does NOT sync local git history — it creates a new
commit on the remote with the file contents you provide.

1. Get the branch name: `git branch --show-current`
2. Get changed files: `git diff --name-only origin/HEAD..HEAD`
3. Read each changed file's content
4. Call `push_files` with owner: stackrox, repo: collector, branch, files, and commit message

## Steps

1. **Push** changed files:
- Use the GitHub MCP `push_files` tool as described above
- If no files have changed since last push, skip

2. **Find or create PR**:
- Use the GitHub MCP server to search for an open PR for this branch
- If no PR exists, create a draft PR via the GitHub MCP server

3. **Monitor CI loop** (repeat until all checks pass or blocked):
- Wait 10 minutes: `sleep 600`
- Use the GitHub MCP server to get PR check status and workflow runs
- Update PR body with an `## Agent Status` section:
```
## Agent Status
**Last updated:** <`date -u +"%Y-%m-%d %H:%M UTC"`>
**CI cycle:** N of 6
**Status:** PENDING | PASSED | FIXED | FLAKE | BLOCKED
**Details:** <one-line summary>
```
- Evaluate:
- **All checks passed** → update PR body, report success and stop
- **Checks still running** → report progress, continue loop
- **Checks failed** →
- Get job logs via the GitHub MCP server
- Diagnose:
- Build failure: read error, fix code
- Unit test failure: read assertion, fix code
- Lint failure: run `clang-format --style=file -i`
- Integration test infra flake (VM timeout, network): report as flake, continue
- Integration test real failure: analyze and fix code
- If fixable: fix → build → test → push changed files via MCP → continue loop
- If not fixable: update PR body, report diagnosis and stop

4. **Safety limits**:
- Maximum 6 CI cycles (about 3 hours of monitoring)
- If exceeded, update PR body and stop
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once a cycle has been stopped, how would we restart it? It'd be ideal if we could add a comment with guidance on the specific problem and then prompt claude to fix and restart the cycle. Not sure if this is possible though


5. **Summary**: end with a status line:
- `PASSED` — all checks green
- `PENDING` — checks still running
- `FIXED` — failure diagnosed and fix pushed
- `FLAKE` — infra failure, not a code issue
- `BLOCKED` — failure requires human intervention
82 changes: 82 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Collector development container
# Based on the collector-builder image which has all C++ dependencies pre-installed.
# Adds Claude Code, Go, and developer tooling for agent-driven development.
#
# Build environment: CentOS Stream 10 with clang, llvm, cmake, grpc, protobuf,
# libbpf, bpftool, and all other collector dependencies.

ARG COLLECTOR_BUILDER_TAG=master
FROM quay.io/stackrox-io/collector-builder:${COLLECTOR_BUILDER_TAG}

# Install developer tooling not in the builder image
# Note: git, findutils, which, openssh-clients already in builder
# bubblewrap: Claude Code uses this for built-in command sandboxing
RUN dnf install -y \
bubblewrap \
clang-tools-extra \
jq \
socat \
zsh \
procps-ng \
sudo \
python3-pip \
iptables \
ipset \
&& dnf clean all

# Determine architecture strings used by various download URLs
# uname -m gives aarch64 or x86_64
# Go uses arm64/amd64, ripgrep/fd use aarch64/x86_64
RUN ARCH=$(uname -m) \
&& GOARCH=$([ "$ARCH" = "aarch64" ] && echo "arm64" || echo "amd64") \
# Install Go
&& curl -fsSL "https://go.dev/dl/go1.23.6.linux-${GOARCH}.tar.gz" | tar -C /usr/local -xzf - \
# Install ripgrep
&& curl -fsSL "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-${ARCH}-unknown-linux-gnu.tar.gz" \
| tar -xzf - --strip-components=1 -C /usr/local/bin "ripgrep-14.1.1-${ARCH}-unknown-linux-gnu/rg" \
# Install fd
&& curl -fsSL "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-${ARCH}-unknown-linux-gnu.tar.gz" \
| tar -xzf - --strip-components=1 -C /usr/local/bin "fd-v10.2.0-${ARCH}-unknown-linux-gnu/fd"

ENV PATH="/usr/local/go/bin:${PATH}"
ENV GOPATH="/home/dev/go"
ENV PATH="${GOPATH}/bin:${PATH}"

# Install Node.js (needed for Claude Code)
ARG NODE_VERSION=22
RUN curl -fsSL https://rpm.nodesource.com/setup_${NODE_VERSION}.x | bash - \
&& dnf install -y nodejs \
&& dnf clean all

# Install Claude Code
RUN npm install -g @anthropic-ai/claude-code

# Install gcloud CLI (for Vertex AI auth and GCP VM management)
RUN curl -fsSL https://sdk.cloud.google.com > /tmp/install-gcloud.sh \
&& bash /tmp/install-gcloud.sh --disable-prompts --install-dir=/opt \
&& rm /tmp/install-gcloud.sh
ENV PATH="/opt/google-cloud-sdk/bin:${PATH}"

# Create non-root dev user with passwordless sudo
RUN useradd -m -s /bin/zsh dev \
&& echo "dev ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/dev \
&& mkdir -p /home/dev/.claude/debug /home/dev/.commandhistory \
&& chown -R dev:dev /home/dev/.claude /home/dev/.commandhistory

# Install ansible for VM-based testing (optional, lightweight)
RUN pip3 install ansible-core

# Firewall script for network isolation (optional, used with --dangerously-skip-permissions)
COPY --chmod=755 init-firewall.sh /usr/local/bin/init-firewall.sh
COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh

USER dev
WORKDIR /workspace

# Persist shell history and Claude state across rebuilds (volumes in devcontainer.json)
ENV HISTFILE=/home/dev/.commandhistory/.zsh_history

ENV SHELL=/bin/zsh
ENV DEVCONTAINER=true

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Loading
Loading