Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self) -> None:
self._mcp_servers: dict = {}
self._allowed_tools: list[str] = []
self._system_prompt: dict = {}
self._plugins: list[dict[str, str]] = []
self._stderr_lines: list[str] = []
# Preserved session IDs across adapter rebuilds (e.g. repo additions)
self._saved_session_ids: dict[str, str] = {}
Expand Down Expand Up @@ -364,13 +365,36 @@ async def _setup_platform(self) -> None:

system_prompt = build_sdk_system_prompt(self._context.workspace_path, cwd_path)

# Discover plugins cloned by init container
plugins_json = os.getenv("PLUGINS_JSON", "").strip()
plugins: list[dict[str, str]] = []
if plugins_json:
import json as _json

try:
for entry in _json.loads(plugins_json):
name = entry.get("url", "").rstrip("/").split("/")[-1]
if name.endswith(".git"):
name = name[:-4]
plugin_path = os.path.join(
self._context.workspace_path, "plugins", name
)
if os.path.isdir(plugin_path):
plugins.append({"type": "local", "path": plugin_path})
logger.info(f"Plugin discovered: {name} at {plugin_path}")
else:
logger.warning(f"Plugin dir not found: {plugin_path}")
except Exception as exc:
logger.warning(f"Failed to parse PLUGINS_JSON: {exc}")

# Store results
self._configured_model = configured_model
self._cwd_path = cwd_path
self._add_dirs = add_dirs
self._mcp_servers = mcp_servers
self._allowed_tools = allowed_tools
self._system_prompt = system_prompt
self._plugins = plugins

# ------------------------------------------------------------------
# Private: adapter lifecycle
Expand Down Expand Up @@ -405,6 +429,8 @@ def _stderr_handler(line: str) -> None:
options["add_dirs"] = self._add_dirs
if self._configured_model:
options["model"] = self._configured_model
if self._plugins:
options["plugins"] = self._plugins

adapter = ClaudeAgentAdapter(
name="claude_code_runner",
Expand Down
40 changes: 40 additions & 0 deletions components/runners/state-sync/hydrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,46 @@ else
echo "No repositories configured in spec"
fi

# Clone plugins from PLUGINS_JSON
if [ -n "$PLUGINS_JSON" ] && [ "$PLUGINS_JSON" != "null" ] && [ "$PLUGINS_JSON" != "" ]; then
echo "Cloning plugins from spec..."
PLUGIN_COUNT=$(echo "$PLUGINS_JSON" | jq -e 'if type == "array" then length else 0 end' 2>/dev/null || echo "0")
echo "Found $PLUGIN_COUNT plugins to clone"
if [ "$PLUGIN_COUNT" -gt 0 ]; then
mkdir -p /workspace/plugins
i=0
while [ $i -lt $PLUGIN_COUNT ]; do
PLUGIN_URL=$(echo "$PLUGINS_JSON" | jq -r ".[$i].url // empty" 2>/dev/null || echo "")
PLUGIN_BRANCH=$(echo "$PLUGINS_JSON" | jq -r ".[$i].branch // empty" 2>/dev/null || echo "")
PLUGIN_NAME=$(basename "$PLUGIN_URL" .git 2>/dev/null || echo "")

if [ -n "$PLUGIN_NAME" ] && [ -n "$PLUGIN_URL" ] && [ "$PLUGIN_URL" != "null" ]; then
PLUGIN_DIR="/workspace/plugins/$PLUGIN_NAME"
git config --global --add safe.directory "$PLUGIN_DIR" 2>/dev/null || true

if [ -n "$PLUGIN_BRANCH" ]; then
echo " Cloning plugin $PLUGIN_NAME (branch: $PLUGIN_BRANCH)..."
if git clone --branch "$PLUGIN_BRANCH" --single-branch --depth 1 "$PLUGIN_URL" "$PLUGIN_DIR" 2>&1; then
echo " ✓ Cloned plugin $PLUGIN_NAME"
else
echo " ⚠ Failed to clone plugin $PLUGIN_NAME"
fi
else
echo " Cloning plugin $PLUGIN_NAME (default branch)..."
if git clone --single-branch --depth 1 "$PLUGIN_URL" "$PLUGIN_DIR" 2>&1; then
echo " ✓ Cloned plugin $PLUGIN_NAME"
else
echo " ⚠ Failed to clone plugin $PLUGIN_NAME"
fi
fi
fi
i=$((i + 1))
done
fi
else
echo "No plugins configured in spec"
fi

# Clone workflow repository
if [ -n "$ACTIVE_WORKFLOW_GIT_URL" ] && [ "$ACTIVE_WORKFLOW_GIT_URL" != "null" ]; then
WORKFLOW_BRANCH="${ACTIVE_WORKFLOW_BRANCH:-main}"
Expand Down
Loading