Skip to content

Agent config init scripts for start of container#18

Merged
nezhar merged 2 commits into
mainfrom
init-script
Mar 3, 2026
Merged

Agent config init scripts for start of container#18
nezhar merged 2 commits into
mainfrom
init-script

Conversation

@nezhar
Copy link
Copy Markdown
Collaborator

@nezhar nezhar commented Mar 2, 2026

This pull request adds support for per-agent initialization scripts in VibePod, allowing users to specify shell commands to run inside agent containers before the main process starts. It introduces the agents.<agent>.init configuration option, updates documentation to explain this feature, and adds a new vp config init command to bootstrap project-level config files. The implementation includes robust validation, integration with the Docker entrypoint, and comprehensive tests.

Example config:

agents:
  codex:
    init:
      - apt-get update
      - apt-get install -y ripgrep jq

Summary by CodeRabbit

  • New Features

    • Added vp config init to create a minimal project config (use --force to overwrite).
    • Per-agent init scripts: run configurable shell commands inside the container before each agent starts; these run in a shell and halt on first failure.
  • Documentation

    • New docs describing vp config init, agent init config, execution semantics, examples, and config merge/override behavior.
  • Tests

    • Added tests for config init (create/overwrite) and agent init handling, entrypoint behavior, and launch resolution.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Adds per-agent pre-start "init" shell commands, a new vp config init CLI to bootstrap a minimal project config, Docker launch command resolution and entrypoint support, documentation updates, and tests covering config and run behaviors.

Changes

Cohort / File(s) Summary
Documentation
README.md, docs/agents/index.md, docs/configuration.md, docs/quickstart.md
Documented agents.<agent>.init behavior and idempotency requirement; added vp config init bootstrap docs and examples showing project-level override of defaults.
Configuration Defaults
src/vibepod/core/config.py
Added init: [] to each agent entry and moved/added codex into the agents block in the default config.
CLI: config
src/vibepod/commands/config.py
Added vp config init command and PROJECT_CONFIG_MINIMAL constant; writes minimal .vibepod/config.yaml, enforces --force for overwrite, and prints clear success/error messages.
Runtime: run & Docker
src/vibepod/commands/run.py, src/vibepod/core/docker.py
Added _agent_init_commands() and _init_entrypoint() to validate/normalize init commands and build a shell entrypoint. Added DockerManager.resolve_launch_command() and entrypoint support in run_agent(); run flow wraps agent startup with init entrypoint when present.
Tests
tests/test_config.py, tests/test_run.py
Added tests for vp config init (create/overwrite), presence of agents.<agent>.init, parsing/validation of init commands, generated init entrypoint script contents, and Docker launch-command resolution and error cases.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as "vp run / vp config"
    participant Config as "Config Loader"
    participant Init as "Init Handler"
    participant Docker as "DockerManager"
    participant Container as "Container"

    User->>CLI: vp run <agent>
    CLI->>Config: load agent config
    Config-->>CLI: returns agent config (may include init)
    CLI->>Init: _agent_init_commands(agent, cfg)
    Init-->>CLI: list of init commands
    alt init commands present
        CLI->>Init: _init_entrypoint(init_commands)
        Init-->>CLI: entrypoint script (["/bin/sh","-lc","..."])
        CLI->>Docker: resolve_launch_command(image, command)
        Docker-->>CLI: resolved launch command
        CLI->>Docker: run_agent(image, command=resolved, entrypoint=entrypoint)
    else no init
        CLI->>Docker: run_agent(image, command=maybe_overridden)
    end
    Docker->>Container: docker run (entrypoint, command)
    Container->>Container: execute init script (set -e)
    Container->>Container: execute agent startup
    Container-->>User: agent running
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibble config seeds at dawn,
I stitch the init that wakes the lawn.
A script with set -e hums true,
Containers stretch and start anew.
Hops of joy — the agents run!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main feature: agent initialization scripts that run at container start. It directly reflects the core change across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch init-script

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/vibepod/core/docker.py (1)

113-114: Defensively normalize explicit command overrides.

command is typed as list[str] | None, but normalizing it here avoids brittle behavior if non-list values are ever passed at runtime.

Suggested refactor
-        effective_cmd = command if command is not None else image_cmd
+        effective_cmd = _normalize_command(command) if command is not None else image_cmd
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/core/docker.py` around lines 113 - 114, Normalize the explicit
command override before using it to build launch: ensure the variable used
(currently `effective_cmd = command if command is not None else image_cmd`) is
always a list of strings even if callers pass a string/tuple/other iterable at
runtime; replace that assignment with a small normalization step that converts a
string to [string], converts tuples or other iterables to list, and otherwise
wraps non-iterable values into a single-element list, then use that normalized
`effective_cmd` when constructing `launch = [*image_entrypoint,
*effective_cmd]`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/configuration.md`:
- Around line 165-166: Add a clear, explicit trust warning to the description of
agents.<agent>.init: state that since agents.<agent>.init commands run inside
the container with `/bin/sh -lc` and `set -e` before the agent process starts,
any project config can execute arbitrary shell commands on every `vp run` (with
workspace and config mounts), so running configs from untrusted repositories is
unsafe and may lead to code execution or data compromise; mention recommended
mitigations (review config, run in isolated environment, avoid mounting
sensitive paths) and reference agents.<agent>.init and `vp run` so readers can
find the relevant section.

In `@src/vibepod/core/docker.py`:
- Around line 98-106: The images.get(...) block currently only catches NotFound
and APIError; add a final except DockerException as exc clause to convert any
other docker.errors.DockerException into a DockerClientError with a
user-friendly message. Specifically, update the try/except in the method using
self.client.images.get(image) to append: except DockerException as exc: raise
DockerClientError(f"Failed to inspect image {image}: {exc}") from exc so all
Docker SDK errors are translated to CLI-friendly DockerClientError.

In `@tests/test_config.py`:
- Around line 5-12: Reorder the imports in tests/test_config.py to satisfy Ruff
I001: put standard library imports first (Path from pathlib), then third-party
imports (CliRunner from typer.testing), then local/package imports
(SUPPORTED_AGENTS from vibepod.constants, app from vibepod.cli, and
deep_merge/get_config from vibepod.core.config); within each group, sort
alphabetically to match the linter expectations.

---

Nitpick comments:
In `@src/vibepod/core/docker.py`:
- Around line 113-114: Normalize the explicit command override before using it
to build launch: ensure the variable used (currently `effective_cmd = command if
command is not None else image_cmd`) is always a list of strings even if callers
pass a string/tuple/other iterable at runtime; replace that assignment with a
small normalization step that converts a string to [string], converts tuples or
other iterables to list, and otherwise wraps non-iterable values into a
single-element list, then use that normalized `effective_cmd` when constructing
`launch = [*image_entrypoint, *effective_cmd]`.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7d2367 and 6b8fa6a.

📒 Files selected for processing (10)
  • README.md
  • docs/agents/index.md
  • docs/configuration.md
  • docs/quickstart.md
  • src/vibepod/commands/config.py
  • src/vibepod/commands/run.py
  • src/vibepod/core/config.py
  • src/vibepod/core/docker.py
  • tests/test_config.py
  • tests/test_run.py

Comment thread docs/configuration.md
Comment thread src/vibepod/core/docker.py
Comment thread tests/test_config.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/test_run.py (1)

41-52: Consider extracting a shared _FakeClient fixture.

The _FakeContainers and _FakeClient classes are duplicated across multiple tests. A @pytest.fixture or a module-level helper class could reduce boilerplate. This is a minor suggestion since the pattern is consistent with existing tests in the file.

Also applies to: 90-103, 133-146

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_run.py` around lines 41 - 52, Duplicate test helpers
_FakeContainers and _FakeClient are repeated; extract them into a single shared
pytest fixture (e.g., a module-level fixture named fake_client) or a
module-level helper class and replace the per-test duplicates with that fixture.
Update tests that currently define _FakeContainers/_FakeClient (references:
_FakeContainers.run and _FakeClient.containers) to accept the new fake_client
fixture and use fake_client.containers.run to capture run_kwargs, and remove the
duplicate class definitions at the other occurrences.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_run.py`:
- Around line 41-52: Duplicate test helpers _FakeContainers and _FakeClient are
repeated; extract them into a single shared pytest fixture (e.g., a module-level
fixture named fake_client) or a module-level helper class and replace the
per-test duplicates with that fixture. Update tests that currently define
_FakeContainers/_FakeClient (references: _FakeContainers.run and
_FakeClient.containers) to accept the new fake_client fixture and use
fake_client.containers.run to capture run_kwargs, and remove the duplicate class
definitions at the other occurrences.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b8fa6a and addb318.

📒 Files selected for processing (7)
  • docs/agents/index.md
  • docs/configuration.md
  • src/vibepod/commands/run.py
  • src/vibepod/core/config.py
  • src/vibepod/core/docker.py
  • tests/test_config.py
  • tests/test_run.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/agents/index.md
  • docs/configuration.md
  • tests/test_config.py

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds two related features to VibePod: a vp config init command to bootstrap project-level config files, and support for per-agent initialization scripts (agents.<agent>.init) that run shell commands inside agent containers before the main process starts. The implementation wraps the container's original entrypoint in a /bin/sh -lc wrapper with set -e to run the init commands, then exec "$@" to hand off to the original process.

Changes:

  • New vp config init command that creates a minimal .vibepod/config.yaml with --force overwrite support
  • Per-agent init config key (default []) and the corresponding shell entrypoint wrapping logic in run.py and docker.py
  • Documentation and test coverage for both features

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/vibepod/commands/config.py Adds init subcommand to create minimal project config
src/vibepod/commands/run.py Adds _agent_init_commands, _init_entrypoint helpers and wires them into the run flow
src/vibepod/core/docker.py Adds resolve_launch_command method and entrypoint parameter to run_agent
src/vibepod/core/config.py Adds init: [] to every agent's default config block
tests/test_run.py New tests for entrypoint forwarding, init command validation, and launch command resolution
tests/test_config.py New tests for config init command behavior and default config structure
docs/quickstart.md Documents vp config init usage
docs/configuration.md Documents project-level config init and agents.<agent>.init semantics
docs/agents/index.md Documents init scripts section with examples
README.md Adds vp config init to the feature list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/vibepod/commands/run.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/vibepod/commands/run.py (1)

72-81: Init commands with special shell characters may break the entrypoint script.

Commands containing newlines, unescaped quotes, or other shell metacharacters could corrupt the script. Since these come from trusted config, this is an edge case, but a malformed command could cause confusing failures.

Consider either:

  1. Escaping commands before embedding, or
  2. Documenting that init commands must be valid single-line shell commands
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/commands/run.py` around lines 72 - 81, The _init_entrypoint
function currently injects init_commands directly into a shell -lc script which
can break if commands contain newlines, quotes, or shell metacharacters; change
it to emit the commands via a single-quoted here-document so the payload is
preserved literally (no interpolation) before the exec "$@"; specifically,
construct the script using a heredoc with a single-quoted delimiter (to avoid
expansion of quotes/metacharacters) that contains the joined init_commands (from
the init_commands parameter) and then the exec "$@" line, or alternatively
validate/escape each entry in init_commands to ensure they are single-line
shell-safe strings; update the _init_entrypoint return to use that safe script
form.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/vibepod/commands/run.py`:
- Around line 72-81: The _init_entrypoint function currently injects
init_commands directly into a shell -lc script which can break if commands
contain newlines, quotes, or shell metacharacters; change it to emit the
commands via a single-quoted here-document so the payload is preserved literally
(no interpolation) before the exec "$@"; specifically, construct the script
using a heredoc with a single-quoted delimiter (to avoid expansion of
quotes/metacharacters) that contains the joined init_commands (from the
init_commands parameter) and then the exec "$@" line, or alternatively
validate/escape each entry in init_commands to ensure they are single-line
shell-safe strings; update the _init_entrypoint return to use that safe script
form.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between addb318 and 13a0a98.

📒 Files selected for processing (10)
  • README.md
  • docs/agents/index.md
  • docs/configuration.md
  • docs/quickstart.md
  • src/vibepod/commands/config.py
  • src/vibepod/commands/run.py
  • src/vibepod/core/config.py
  • src/vibepod/core/docker.py
  • tests/test_config.py
  • tests/test_run.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/quickstart.md
  • docs/agents/index.md
  • src/vibepod/core/config.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants