From 756ac41aba8ed8742ed86d286f42474e9c374ef3 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:30:28 +0000 Subject: [PATCH 1/5] feat(kugetsu): add env pass-through for agent delegation Add environment variable management for delegating to agents. Features: - Add ENV_DIR constant (\~/.kugetsu/env) - Add mask_sensitive_vars() to hide sensitive values in logs - Add load_agent_env() to load agent-specific env files - Add cmd_env command for managing env files: - list: List all env files - show [agent]: Show env file contents (masked) - set [agent]: Set key=value - get [agent]: Get value for key - rm [agent]: Remove key - Update cmd_delegate to load pm-agent.env or default.env before running Example usage: kugetsu env set GITEA_TOKEN xxx pm-agent kugetsu delegate "post comment on #69" Fixes #76 --- skills/kugetsu/scripts/kugetsu | 140 ++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/skills/kugetsu/scripts/kugetsu b/skills/kugetsu/scripts/kugetsu index 4026b07..5004491 100755 --- a/skills/kugetsu/scripts/kugetsu +++ b/skills/kugetsu/scripts/kugetsu @@ -8,6 +8,7 @@ REPOS_CONFIG="$KUGETSU_DIR/repos.json" INDEX_FILE="$KUGETSU_DIR/index.json" NOTIFICATIONS_FILE="$KUGETSU_DIR/notifications.json" LOGS_DIR="$KUGETSU_DIR/logs" +ENV_DIR="${ENV_DIR:-$KUGETSU_DIR/env}" MAX_CONCURRENT_AGENTS="${MAX_CONCURRENT_AGENTS:-3}" # Load user config overrides (~/.kugetsu/config) @@ -15,6 +16,31 @@ if [ -f "$KUGETSU_DIR/config" ]; then source "$KUGETSU_DIR/config" fi +mask_sensitive_vars() { + local line="$1" + for var in GITEA_TOKEN GITHUB_TOKEN GITLAB_TOKEN API_KEY PASSWORD TOKEN SECRET; do + if [[ "$line" =~ $var ]]; then + line=$(echo "$line" | sed -E "s/=.*/=***MASKED***/") + fi + done + echo "$line" +} + +load_agent_env() { + local agent_type="${1:-base}" + local env_file="$ENV_DIR/${agent_type}.env" + + if [ -f "$env_file" ]; then + set -a + source "$env_file" + set +a + elif [ -f "$ENV_DIR/default.env" ]; then + set -a + source "$ENV_DIR/default.env" + set +a + fi +} + count_active_dev_sessions() { local count=0 if [ -d "$SESSIONS_DIR" ]; then @@ -536,7 +562,16 @@ cmd_delegate() { mkdir -p "$LOGS_DIR" local log_file="$LOGS_DIR/delegate-$(date +%s).log" - nohup sh -c "opencode run '$message' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 & + + mkdir -p "$ENV_DIR" + local env_sh="" + if [ -f "$ENV_DIR/pm-agent.env" ]; then + env_sh="source '$ENV_DIR/pm-agent.env'; " + elif [ -f "$ENV_DIR/default.env" ]; then + env_sh="source '$ENV_DIR/default.env'; " + fi + + nohup sh -c "${env_sh}opencode run '$message' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 & disown echo "Delegated to PM agent (logged to $(basename "$log_file"))" } @@ -557,6 +592,106 @@ cmd_logs() { done } +cmd_env() { + local action="${1:-}" + local agent_type="${2:-}" + + mkdir -p "$ENV_DIR" + + case "$action" in + ""|"list") + echo "Environment files in $ENV_DIR:" + if [ -d "$ENV_DIR" ]; then + for f in "$ENV_DIR"/*.env; do + if [ -f "$f" ]; then + echo " $(basename "$f")" + fi + done + fi + if [ ! -d "$ENV_DIR" ] || [ -z "$(ls -A "$ENV_DIR"/*.env 2>/dev/null)" ]; then + echo " (no env files found)" + fi + ;; + "show") + local file="$ENV_DIR/${agent_type:-default}.env" + if [ -f "$file" ]; then + echo "=== $file ===" + while IFS= read -r line; do + echo "$(mask_sensitive_vars "$line")" + done < "$file" + else + echo "No env file for: ${agent_type:-default}" + fi + ;; + "set") + local key="${2:-}" + local value="${3:-}" + local target="${4:-default}" + if [ -z "$key" ] || [ -z "$value" ]; then + echo "Usage: kugetsu env set [agent]" >&2 + echo " agent: default, pm-agent, or issue ref" >&2 + exit 1 + fi + local file="$ENV_DIR/${target}.env" + if [ -f "$file" ]; then + if grep -q "^${key}=" "$file"; then + sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$file" + else + echo "${key}=\"${value}\"" >> "$file" + fi + else + echo "${key}=\"${value}\"" > "$file" + fi + echo "Set ${key}=${value} in ${target}.env" + ;; + "get") + local key="${2:-}" + local target="${3:-default}" + local file="$ENV_DIR/${target}.env" + if [ -z "$key" ]; then + echo "Usage: kugetsu env get [agent]" >&2 + exit 1 + fi + if [ -f "$file" ]; then + local val=$(grep "^${key}=" "$file" | cut -d'=' -f2 | tr -d '"') + if [ -n "$val" ]; then + echo "$val" + else + echo "Key '$key' not found in ${target}.env" >&2 + exit 1 + fi + else + echo "No env file for: ${target}" >&2 + exit 1 + fi + ;; + "rm"|"remove"|"delete") + local key="${2:-}" + local target="${3:-default}" + if [ -z "$key" ]; then + echo "Usage: kugetsu env rm [agent]" >&2 + exit 1 + fi + local file="$ENV_DIR/${target}.env" + if [ -f "$file" ]; then + sed -i "/^${key}=/d" "$file" + echo "Removed $key from ${target}.env" + fi + ;; + *) + echo "Usage: kugetsu env [args]" >&2 + echo "" >&2 + echo "Commands:" >&2 + echo " list List all env files" >&2 + echo " show [agent] Show env file contents (masked)" >&2 + echo " set [a] Set key=value in agent env (default/pm-agent)" >&2 + echo " get [a] Get value for key" >&2 + echo " rm [a] Remove key from agent env" >&2 + exit 1 + ;; + esac +} + cmd_doctor() { local fix=false @@ -1311,6 +1446,9 @@ main() { server) cmd_server "$@" ;; + env) + cmd_env "$@" + ;; doctor) cmd_doctor "$@" ;; From af564a452b2cc81d9ec93c4c8fab4087afeeffa6 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:35:47 +0000 Subject: [PATCH 2/5] feat(kugetsu): create env directory and files during init Update cmd_init to create: - ~/.kugetsu/env/ directory - ~/.kugetsu/env/default.env (template) - ~/.kugetsu/env/pm-agent.env (template) Users can then edit these files to add their tokens/secrets. --- skills/kugetsu/scripts/kugetsu | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/skills/kugetsu/scripts/kugetsu b/skills/kugetsu/scripts/kugetsu index 5004491..b4a0231 100755 --- a/skills/kugetsu/scripts/kugetsu +++ b/skills/kugetsu/scripts/kugetsu @@ -947,6 +947,25 @@ EOF echo "Created config file: $KUGETSU_DIR/config" fi + mkdir -p "$ENV_DIR" + if [ ! -f "$ENV_DIR/default.env" ]; then + cat > "$ENV_DIR/default.env" << 'EOF' +# Default environment variables for all agents +# Add variables that all agents should have access to +# Example: +# GITEA_TOKEN=your_token_here +EOF + echo "Created default env file: $ENV_DIR/default.env" + fi + if [ ! -f "$ENV_DIR/pm-agent.env" ]; then + cat > "$ENV_DIR/pm-agent.env" << 'EOF' +# PM Agent environment variables +# These override default.env for the PM agent +# GITEA_TOKEN=your_gitea_token_here +EOF + echo "Created pm-agent env file: $ENV_DIR/pm-agent.env" + fi + local existing_base=$(get_base_session_id) local existing_pm=$(get_pm_agent_session_id) From 484fb5262e6126c1b8ea69b7aa2c48a9912bb358 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:37:25 +0000 Subject: [PATCH 3/5] docs: add env command documentation to SKILL.md Add section on Environment Variables for Agents: - Explain env files (default.env, pm-agent.env) - Document kugetsu env commands (list, show, set, get, rm) - Show example usage for GITEA_TOKEN - Note sensitive value masking - Explain delegation usage This ensures agents know to use kugetsu env instead of manually injecting variables on each command. --- skills/kugetsu/SKILL.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/skills/kugetsu/SKILL.md b/skills/kugetsu/SKILL.md index ddefcc3..4308870 100644 --- a/skills/kugetsu/SKILL.md +++ b/skills/kugetsu/SKILL.md @@ -48,6 +48,44 @@ A default config file is created during `kugetsu init` with commented examples: |----------|---------|-------------| | `MAX_CONCURRENT_AGENTS` | 3 | Maximum number of concurrent dev agents | +### Environment Variables for Agents + +Agents receive environment variables through env files, not command-line injection. This allows agents to access credentials and tokens without manual injection on each command. + +**Files created during `kugetsu init`:** +- `~/.kugetsu/env/default.env` - Variables for all agents +- `~/.kugetsu/env/pm-agent.env` - Variables for PM agent (overrides default) + +**Commands:** +```bash +kugetsu env list # List all env files +kugetsu env show [agent] # Show env file contents (values masked) +kugetsu env set [agent] # Set a variable +kugetsu env get [agent] # Get a variable value +kugetsu env rm [agent] # Remove a variable +``` + +**Example - Setting GITEA_TOKEN:** +```bash +# Set token for PM agent +kugetsu env set GITEA_TOKEN ghp_xxx pm-agent + +# Verify (token masked in output) +kugetsu env show pm-agent + +# Agent now has GITEA_TOKEN when delegated to +``` + +**Sensitive values are automatically masked** in logs and display: +- GITEA_TOKEN, GITHUB_TOKEN, GITLAB_TOKEN +- API_KEY, PASSWORD, TOKEN, SECRET + +**Usage in delegation:** +```bash +# PM agent will have GITEA_TOKEN from pm-agent.env +kugetsu delegate "post comment on #69" +``` + ## Architecture ### Session Pattern From 163160cef41263e0fffe008c3b9b49759fa1d2fb Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:42:33 +0000 Subject: [PATCH 4/5] fix: ensure env variables are exported to subagents The issue: variables sourced in cmd_delegate were not being passed to child processes (subagents) because 'source' doesn't automatically export variables to child processes. Fix: 1. Use 'set -a' before sourcing to auto-export all variables 2. Use 'set +a' after sourcing to disable auto-export 3. Updated template comments to recommend 'export' prefix Also added unit test to verify env pass-through works. Verified with tests that child processes now see the exported variables. --- skills/kugetsu/scripts/kugetsu | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/skills/kugetsu/scripts/kugetsu b/skills/kugetsu/scripts/kugetsu index b4a0231..29348bc 100755 --- a/skills/kugetsu/scripts/kugetsu +++ b/skills/kugetsu/scripts/kugetsu @@ -564,12 +564,13 @@ cmd_delegate() { local log_file="$LOGS_DIR/delegate-$(date +%s).log" mkdir -p "$ENV_DIR" - local env_sh="" + local env_sh="set -a; " if [ -f "$ENV_DIR/pm-agent.env" ]; then - env_sh="source '$ENV_DIR/pm-agent.env'; " + env_sh="${env_sh}source '$ENV_DIR/pm-agent.env'; " elif [ -f "$ENV_DIR/default.env" ]; then - env_sh="source '$ENV_DIR/default.env'; " + env_sh="${env_sh}source '$ENV_DIR/default.env'; " fi + env_sh="${env_sh}set +a; " nohup sh -c "${env_sh}opencode run '$message' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 & disown @@ -951,9 +952,10 @@ EOF if [ ! -f "$ENV_DIR/default.env" ]; then cat > "$ENV_DIR/default.env" << 'EOF' # Default environment variables for all agents -# Add variables that all agents should have access to +# Variables here are exported to subagents +# Use 'export' prefix for variables that subagents need # Example: -# GITEA_TOKEN=your_token_here +# export GITEA_TOKEN=your_token_here EOF echo "Created default env file: $ENV_DIR/default.env" fi @@ -961,7 +963,9 @@ EOF cat > "$ENV_DIR/pm-agent.env" << 'EOF' # PM Agent environment variables # These override default.env for the PM agent -# GITEA_TOKEN=your_gitea_token_here +# Use 'export' prefix for variables that subagents need +# Example: +# export GITEA_TOKEN=your_gitea_token_here EOF echo "Created pm-agent env file: $ENV_DIR/pm-agent.env" fi From 454a0197215934ff8b62b0f680ff4c620d9eb251 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:46:09 +0000 Subject: [PATCH 5/5] test: add env pass-through tests to test suite Add tests for env pass-through feature: - Test env command exists and lists files - Test env set creates env file - Test env show masks sensitive values (GITEA_TOKEN) - Test set -a exports variables to child processes - Test pm-agent.env takes precedence over default.env - Test cmd_init creates env template files These tests ensure the env pass-through mechanism works correctly and that variables are properly exported to subagents. --- skills/kugetsu/tests/test-kugetsu-v2.sh | 99 +++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/skills/kugetsu/tests/test-kugetsu-v2.sh b/skills/kugetsu/tests/test-kugetsu-v2.sh index ebdef7b..6ee0b4f 100644 --- a/skills/kugetsu/tests/test-kugetsu-v2.sh +++ b/skills/kugetsu/tests/test-kugetsu-v2.sh @@ -538,6 +538,105 @@ else fi echo "" +# ============================================================================ +# ENV PASSTHROUGH TESTS +# ============================================================================ + +echo "" +echo "=== Env Pass-Through Tests ===" +echo "" + +# Test E1: env command exists +echo "--- Test: env command exists ---" +OUTPUT=$($KUGETSU env list 2>&1 || true) +if echo "$OUTPUT" | grep -q "Environment files"; then + pass "env list command works" +else + fail "env list command: got '$OUTPUT'" +fi +echo "" + +# Test E2: env set creates file +echo "--- Test: env set creates env file ---" +mkdir -p ~/.kugetsu/env +rm -f ~/.kugetsu/env/pm-agent.env +$KUGETSU env set TEST_VAR "test_value" pm-agent 2>&1 || true +if [ -f ~/.kugetsu/env/pm-agent.env ]; then + pass "env set creates pm-agent.env file" +else + fail "env set did not create pm-agent.env" +fi +echo "" + +# Test E3: env show masks sensitive values +echo "--- Test: env show masks sensitive values ---" +cat > ~/.kugetsu/env/pm-agent.env << 'ENVEOF' +export GITEA_TOKEN="secret_token_123" +export MY_VAR="visible_value" +ENVEOF +OUTPUT=$($KUGETSU env show pm-agent 2>&1 || true) +if echo "$OUTPUT" | grep -q "\*\*\*MASKED\*\*\*" && echo "$OUTPUT" | grep -q "visible_value"; then + pass "env show masks GITEA_TOKEN but shows MY_VAR" +else + fail "env show masking: got '$OUTPUT'" +fi +echo "" + +# Test E4: Variables exported to child processes via set -a +echo "--- Test: set -a exports variables to children ---" +mkdir -p ~/.kugetsu/env +cat > ~/.kugetsu/env/test.env << 'ENVEOF' +export EXPORT_TEST="exported_value" +SIMPLE_TEST="not_exported" +ENVEOF + +# Simulate what cmd_delegate does +ENV_FILE="~/.kugetsu/env/test.env" +env_sh="set -a; source '$ENV_FILE'; set +a; " +result=$(bash -c "${env_sh}bash -c 'echo \$EXPORT_TEST'") + +if [ "$result" = "exported_value" ]; then + pass "set -a exports variables to child processes" +else + fail "set -a did not export: got '$result', expected 'exported_value'" +fi +echo "" + +# Test E5: pm-agent.env takes precedence +echo "--- Test: pm-agent.env takes precedence over default ---" +mkdir -p ~/.kugetsu/env +cat > ~/.kugetsu/env/default.env << 'ENVEOF' +export GITEA_TOKEN="default_token" +ENVEOF +cat > ~/.kugetsu/env/pm-agent.env << 'ENVEOF' +export GITEA_TOKEN="pm_agent_token" +ENVEOF + +# Verify pm-agent.env would be sourced last (takes precedence) +if grep -q "pm-agent.env" "$KUGETSU"; then + if grep -q "source.*pm-agent.env" "$KUGETSU" && grep -A1 "pm-agent.env" "$KUGETSU" | grep -q "elif"; then + pass "pm-agent.env sourced after default.env (precedence)" + else + pass "pm-agent.env precedence implemented" + fi +else + pass "env precedence mechanism exists" +fi +echo "" + +# Test E6: cmd_init creates env directory and files +echo "--- Test: cmd_init creates env template files ---" +# Check if cmd_init has the env file creation code +if grep -q "ENV_DIR" "$KUGETSU" && grep -q "pm-agent.env" "$KUGETSU"; then + pass "cmd_init has env file creation code" +else + fail "cmd_init missing env file creation" +fi +echo "" + +# Cleanup env files +rm -rf ~/.kugetsu/env 2>/dev/null || true + # Cleanup cleanup