-
Notifications
You must be signed in to change notification settings - Fork 30
feat: add devcontainer with Claude Code agent for autonomous collector development #3118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
robbycochran
wants to merge
51
commits into
master
Choose a base branch
from
rc-claude-dev
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
48248aa
X-Smart-Branch-Parent: master
robbycochran 6191758
feat: add devcontainer and Claude Code agent development environment
robbycochran c77c8a0
docs: add Vertex AI setup and devcontainer build instructions to CLAU…
robbycochran d1e7a46
feat: add launcher script with worktree isolation and GitHub PAT support
robbycochran 7fb9902
feat: use official GitHub MCP server, fix run.sh, add bubblewrap
robbycochran 9ee7372
refactor: convert skills to collector-dev plugin with scoped tool per…
robbycochran 24c1d24
feat: create branch and draft PR upfront, tighten iterate permissions
robbycochran 5eecdb7
feat: add watch-ci skill for CI monitoring loop
robbycochran f75b62d
feat: add end-to-end task skill with CI monitoring loop
robbycochran 7a5bdb1
fix: load collector-dev plugin via --plugin-dir flag
robbycochran 11b7afe
feat: stream agent activity to stdout in autonomous mode
robbycochran e32a662
feat: add --local mode for debugging without worktree or PR
robbycochran 25bf9d8
feat: add --headless mode (worktree + stream-json, no PR)
robbycochran 2cc2a47
fix: initialize submodules in worktree after creation
robbycochran 1cc86dd
fix: only init required submodules, drop --recursive
robbycochran f969766
fix: headless mode now invokes /collector-dev:task skill like default…
robbycochran b609c53
feat: add preflight checks with clear error messages
robbycochran 762e2ce
refactor: remove gh CLI dependency, agent creates PR via GitHub MCP
robbycochran 995b443
refactor: move skills from plugin to standalone, simplify CLAUDE.md
robbycochran bdb95eb
refactor: switch worktree to clone, consolidate to 2 skills, fix audi…
robbycochran 1f8b8f6
fix: move git push deny to container-only settings
robbycochran 768ebb2
security: remove SSH mount, git push blocked by lack of credentials
robbycochran dff9883
fix: set clone remote to GitHub URL instead of local path
robbycochran 5c1b799
refactor: switch back to worktrees, mount .git at same absolute path
robbycochran ab2dd59
perf: shallow submodule checkout in worktrees (--depth 1)
robbycochran ef2fee9
perf: mount submodules from main repo instead of cloning per worktree
robbycochran 09bae7c
feat: make submodule mounting optional via --symlink-submodules
robbycochran e2a4085
feat: add --branch flag, use /tmp/collector-worktrees/ for worktrees
robbycochran 3295488
fix: reject --branch with --local mode
robbycochran bc74ac4
fix: mount .git read-write so git commit works in container
robbycochran 48da9b7
security: mount .git read-only with worktree subdir read-write
robbycochran 356e1d8
fix: set theme and verbose defaults in entrypoint to skip startup pro…
robbycochran 480e9aa
fix: chmod worktree git dir for container uid mismatch
robbycochran e595f88
fix: use chmod a+rwX to fix directory permissions in worktree git dir
robbycochran 4683ad3
fix: mount .git read-write, partial ro/rw overlay doesn't work
robbycochran 94418f7
security: remove SYS_PTRACE/NET_ADMIN/NET_RAW caps, drop --symlink-su…
robbycochran db98d81
feat: specify GitHub MCP toolsets via X-MCP-Toolsets header
robbycochran 0c538c4
feat: watch-ci updates PR body with agent status after each CI cycle
robbycochran 4cf15e2
docs: add MCP Context Protector integration plan
robbycochran 5369867
fix: autonomous mode inlines task instructions instead of invoking /t…
robbycochran d1c71dc
feat: add /dev-loop skill combining task + watch-ci + PR comment hand…
robbycochran 69d62c2
feat: allow specifying skill in autonomous mode, default to /dev-loop
robbycochran 9835cd8
fix: remove local keyword from case branch (not a function)
robbycochran 8672c77
docs: update MCP security plan — recommend mcp-watchdog over mcp-cont…
robbycochran d7d42a0
fix: push branch from host before launching autonomous agent
robbycochran bc0825a
fix: only push branch in autonomous /dev-loop mode, not for explicit …
robbycochran df9991a
feat: TUI is now default for all modes, --no-tui for stream-json
robbycochran cb9610c
docs: clarify push_files usage in dev-loop and watch-ci skills
robbycochran 8feb2b2
docs: add devcontainer README with GitHub PAT permissions and setup g…
robbycochran c938ea8
fix: shfmt formatting and shellcheck SC2064 warning in run.sh
robbycochran 76f1e6c
fix: shfmt formatting — use 4-space indent per .editorconfig
robbycochran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" | ||
| ] | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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>` | ||
| 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> | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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