diff --git a/docs/pr-screenshots/issue-237/codex-extra-skills-terminal.png b/docs/pr-screenshots/issue-237/codex-extra-skills-terminal.png new file mode 100644 index 00000000..9e19d44a Binary files /dev/null and b/docs/pr-screenshots/issue-237/codex-extra-skills-terminal.png differ diff --git a/docs/pr-screenshots/issue-237/prompt-overrides-terminal.png b/docs/pr-screenshots/issue-237/prompt-overrides-terminal.png new file mode 100644 index 00000000..3b341ce8 Binary files /dev/null and b/docs/pr-screenshots/issue-237/prompt-overrides-terminal.png differ diff --git a/experiments/render-examples-output.txt b/experiments/render-examples-output.txt new file mode 100644 index 00000000..bb9a9527 --- /dev/null +++ b/experiments/render-examples-output.txt @@ -0,0 +1,533 @@ + +================================================================================ +CLAUDE.md prompt setup (~/.claude/CLAUDE.md) +================================================================================ + +# Claude Code: managed global memory (CLAUDE.md is auto-loaded by Claude Code) +CLAUDE_GLOBAL_PROMPT_FILE="/home/dev/.claude/CLAUDE.md" +CLAUDE_AUTO_SYSTEM_PROMPT="${CLAUDE_AUTO_SYSTEM_PROMPT:-1}" +CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: repository" +REPO_REF_VALUE="${REPO_REF:-issue-237}" +REPO_URL_VALUE="${REPO_URL:-https://github.com/ProverCoderAI/docker-git.git}" + +if [[ "$REPO_REF_VALUE" == issue-* ]]; then + ISSUE_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -E 's#^issue-##')" + ISSUE_URL_VALUE="" + if [[ "$REPO_URL_VALUE" == https://github.com/* ]]; then + ISSUE_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$ISSUE_REPO_VALUE" ]]; then + ISSUE_URL_VALUE="https://github.com/$ISSUE_REPO_VALUE/issues/$ISSUE_ID_VALUE" + fi + fi + if [[ -n "$ISSUE_URL_VALUE" ]]; then + CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID_VALUE ($ISSUE_URL_VALUE)" + else + CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID_VALUE" + fi +elif [[ "$REPO_REF_VALUE" == refs/pull/*/head ]]; then + PR_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')" + PR_URL_VALUE="" + if [[ "$REPO_URL_VALUE" == https://github.com/* && -n "$PR_ID_VALUE" ]]; then + PR_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$PR_REPO_VALUE" ]]; then + PR_URL_VALUE="https://github.com/$PR_REPO_VALUE/pull/$PR_ID_VALUE" + fi + fi + if [[ -n "$PR_ID_VALUE" && -n "$PR_URL_VALUE" ]]; then + CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID_VALUE ($PR_URL_VALUE)" + elif [[ -n "$PR_ID_VALUE" ]]; then + CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID_VALUE" + else + CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: pull request ($REPO_REF_VALUE)" + fi +fi + +CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE="${CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +CLAUDE_SYSTEM_PROMPT_OVERRIDE="${CLAUDE_SYSTEM_PROMPT_OVERRIDE:-}" +CLAUDE_DEFAULT_PROMPT_BODY="$(cat </dev/null || true + if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then + cat < "$CLAUDE_GLOBAL_PROMPT_FILE" + +$CLAUDE_PROMPT_BODY + +EOF + chmod 0644 "$CLAUDE_GLOBAL_PROMPT_FILE" || true + chown 1000:1000 "$CLAUDE_GLOBAL_PROMPT_FILE" || true + fi +fi + +export CLAUDE_AUTO_SYSTEM_PROMPT + +================================================================================ +.codex/AGENTS.md prompt setup +================================================================================ + +# Ensure global AGENTS.md exists for container context +AGENTS_PATH="/home/dev/.codex/AGENTS.md" +LEGACY_AGENTS_PATH="/home/dev/AGENTS.md" +PROJECT_LINE="Рабочая папка проекта (git clone): /home/dev/workspaces/ProverCoderAI/docker-git/issue-237" +WORKSPACES_LINE="Доступные workspace пути: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237" +WORKSPACE_INFO_LINE="Контекст workspace: repository" +FOCUS_LINE="Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237" +INTERNET_LINE="Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе." +SUBAGENTS_LINE="Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю." +if [[ "$REPO_REF" == issue-* ]]; then + ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')" + ISSUE_URL="" + if [[ "$REPO_URL" == https://github.com/* ]]; then + ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$ISSUE_REPO" ]]; then + ISSUE_URL="https://github.com/$ISSUE_REPO/issues/$ISSUE_ID" + fi + fi + if [[ -n "$ISSUE_URL" ]]; then + WORKSPACE_INFO_LINE="Контекст workspace: issue #$ISSUE_ID ($ISSUE_URL)" + else + WORKSPACE_INFO_LINE="Контекст workspace: issue #$ISSUE_ID" + fi +elif [[ "$REPO_REF" == refs/pull/*/head ]]; then + PR_ID="$(printf "%s" "$REPO_REF" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')" + PR_URL="" + if [[ "$REPO_URL" == https://github.com/* && -n "$PR_ID" ]]; then + PR_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$PR_REPO" ]]; then + PR_URL="https://github.com/$PR_REPO/pull/$PR_ID" + fi + fi + if [[ -n "$PR_ID" && -n "$PR_URL" ]]; then + WORKSPACE_INFO_LINE="Контекст workspace: PR #$PR_ID ($PR_URL)" + elif [[ -n "$PR_ID" ]]; then + WORKSPACE_INFO_LINE="Контекст workspace: PR #$PR_ID" + else + WORKSPACE_INFO_LINE="Контекст workspace: pull request ($REPO_REF)" + fi +fi +MANAGED_START="" +MANAGED_END="" +CODEX_SYSTEM_PROMPT_OVERRIDE_FILE="${CODEX_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +CODEX_SYSTEM_PROMPT_OVERRIDE="${CODEX_SYSTEM_PROMPT_OVERRIDE:-}" +if [[ -n "$CODEX_SYSTEM_PROMPT_OVERRIDE_FILE" && -r "$CODEX_SYSTEM_PROMPT_OVERRIDE_FILE" ]]; then + MANAGED_LINES="$(cat "$CODEX_SYSTEM_PROMPT_OVERRIDE_FILE")" +elif [[ -n "$CODEX_SYSTEM_PROMPT_OVERRIDE" ]]; then + MANAGED_LINES="$CODEX_SYSTEM_PROMPT_OVERRIDE" +else + MANAGED_LINES="$(cat < "$AGENTS_PATH" +Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, bun, codex, opencode, oh-my-opencode, sshpass, git, node и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~ +$MANAGED_BLOCK +Если ты видишь файлы AGENTS.md внутри проекта, ты обязан их читать и соблюдать инструкции. +EOF + chown 1000:1000 "$AGENTS_PATH" || true +fi +if [[ -f "$AGENTS_PATH" ]]; then + MANAGED_BLOCK="$(cat < "$TMP_AGENTS_PATH" + else + sed \ + -e '/^Рабочая папка проекта (git clone):/d' \ + -e '/^Доступные workspace пути:/d' \ + -e '/^Контекст workspace:/d' \ + -e '/^Фокус задачи:/d' \ + -e '/^Issue AGENTS.md:/d' \ + -e '/^Доступ к интернету:/d' \ + -e '/^Для решения задач обязательно используй subagents[.]/d' \ + "$AGENTS_PATH" > "$TMP_AGENTS_PATH" + if [[ -s "$TMP_AGENTS_PATH" ]]; then + printf "\n" >> "$TMP_AGENTS_PATH" + fi + printf "%s\n" "$MANAGED_BLOCK" >> "$TMP_AGENTS_PATH" + fi + mv "$TMP_AGENTS_PATH" "$AGENTS_PATH" + chown 1000:1000 "$AGENTS_PATH" || true +fi +if [[ -f "$LEGACY_AGENTS_PATH" && -f "$AGENTS_PATH" ]]; then + LEGACY_SUM="$(cksum "$LEGACY_AGENTS_PATH" 2>/dev/null | awk '{print $1 \":\" $2}')" + CODEX_SUM="$(cksum "$AGENTS_PATH" 2>/dev/null | awk '{print $1 \":\" $2}')" + if [[ -n "$LEGACY_SUM" && "$LEGACY_SUM" == "$CODEX_SUM" ]]; then + rm -f "$LEGACY_AGENTS_PATH" + fi +fi + +================================================================================ +GEMINI.md prompt setup (full Gemini config block) +================================================================================ + +# Gemini CLI: keep ~/.gemini as a real home directory while sharing auth files from ~/.docker-git/.orch/auth/gemini +GEMINI_LABEL_RAW="$GEMINI_AUTH_LABEL" +if [[ -z "$GEMINI_LABEL_RAW" ]]; then + GEMINI_LABEL_RAW="default" +fi + +GEMINI_LABEL_NORM="$(printf "%s" "$GEMINI_LABEL_RAW" \ + | tr '[:upper:]' '[:lower:]' \ + | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')" +if [[ -z "$GEMINI_LABEL_NORM" ]]; then + GEMINI_LABEL_NORM="default" +fi + +GEMINI_AUTH_ROOT="/home/dev/.docker-git/.orch/auth/gemini" +export GEMINI_CONFIG_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM" + +mkdir -p "$GEMINI_CONFIG_DIR" || true +GEMINI_HOME_DIR="/home/dev/.gemini" +mkdir -p "$GEMINI_HOME_DIR" || true +GEMINI_SHARED_HOME_DIR="$GEMINI_CONFIG_DIR/.gemini" +mkdir -p "$GEMINI_SHARED_HOME_DIR" || true + +docker_git_link_gemini_file() { + local source_path="$1" + local link_path="$2" + + if [[ -e "$link_path" && ! -L "$link_path" ]]; then + if [[ -f "$link_path" && ! -e "$source_path" ]]; then + cp "$link_path" "$source_path" || true + chmod 0600 "$source_path" || true + fi + return 0 + fi + + ln -sfn "$source_path" "$link_path" || true +} + +docker_git_prepare_gemini_home_dir() { + if [[ -L "$GEMINI_HOME_DIR" ]]; then + local previous_target + previous_target="$(readlink -f "$GEMINI_HOME_DIR" || true)" + rm -f "$GEMINI_HOME_DIR" || true + mkdir -p "$GEMINI_HOME_DIR" || true + if [[ -n "$previous_target" && -d "$previous_target" ]]; then + cp -a "$previous_target"/. "$GEMINI_HOME_DIR"/ 2>/dev/null || true + fi + return 0 + fi + + mkdir -p "$GEMINI_HOME_DIR" || true +} + +docker_git_prepare_gemini_home_dir + +# Link .api-key and .env from central auth storage to container home +docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.api-key" "$GEMINI_HOME_DIR/.api-key" +docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.env" "$GEMINI_HOME_DIR/.env" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/oauth_creds.json" "$GEMINI_HOME_DIR/oauth_creds.json" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/oauth-tokens.json" "$GEMINI_HOME_DIR/oauth-tokens.json" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/credentials.json" "$GEMINI_HOME_DIR/credentials.json" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/application_default_credentials.json" "$GEMINI_HOME_DIR/application_default_credentials.json" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/google_accounts.json" "$GEMINI_HOME_DIR/google_accounts.json" +docker_git_link_gemini_file "$GEMINI_SHARED_HOME_DIR/projects.json" "$GEMINI_HOME_DIR/projects.json" + +# Ensure gemini YOLO wrapper exists +GEMINI_REAL_BIN="$(command -v gemini || echo "/usr/local/bin/gemini")" +GEMINI_WRAPPER_BIN="/usr/local/bin/gemini-wrapper" +if [[ -f "$GEMINI_REAL_BIN" && "$GEMINI_REAL_BIN" != "$GEMINI_WRAPPER_BIN" ]]; then + if [[ ! -f "$GEMINI_WRAPPER_BIN" ]]; then + cat <<'EOF' > "$GEMINI_WRAPPER_BIN" +#!/usr/bin/env bash +GEMINI_ORIGINAL_BIN="__GEMINI_REAL_BIN__" +exec "$GEMINI_ORIGINAL_BIN" --yolo "$@" +EOF + sed -i "s#__GEMINI_REAL_BIN__#$GEMINI_REAL_BIN#g" "$GEMINI_WRAPPER_BIN" || true + chmod 0755 "$GEMINI_WRAPPER_BIN" || true + # Create an alias or symlink if needed, but here we just ensure it exists + fi +fi + +docker_git_refresh_gemini_env() { + # If .api-key exists, export it as GEMINI_API_KEY + if [[ -f "$GEMINI_HOME_DIR/.api-key" ]]; then + export GEMINI_API_KEY="$(cat "$GEMINI_HOME_DIR/.api-key" | tr -d '\r\n')" + elif [[ -f "$GEMINI_HOME_DIR/.env" ]]; then + # Parse GEMINI_API_KEY from .env + API_KEY="$(grep "^GEMINI_API_KEY=" "$GEMINI_HOME_DIR/.env" | cut -d'=' -f2- | sed "s/^['\"]//;s/['\"]$//")" + if [[ -n "$API_KEY" ]]; then + export GEMINI_API_KEY="$API_KEY" + fi + fi +} + +docker_git_refresh_gemini_env + +# Gemini CLI: keep trust settings in sync with docker-git defaults +GEMINI_SETTINGS_DIR="/home/dev/.gemini" +GEMINI_TRUST_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/trustedFolders.json" +GEMINI_CONFIG_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/settings.json" + +# Wait for symlink to be established by the auth config step +mkdir -p "$GEMINI_SETTINGS_DIR" || true + +# Disable folder trust prompt and enable auto-approval in settings.json +cat <<'EOF' > "$GEMINI_CONFIG_SETTINGS_FILE" +{ + "model": { + "name": "gemini-3.1-pro-preview", + "compressionThreshold": 0.9, + "disableLoopDetection": true + }, + "modelConfigs": { + "customAliases": { + "yolo-ultra": { + "modelConfig": { + "model": "gemini-3.1-pro-preview", + "generateContentConfig": { + "tools": [ + { + "googleSearch": {} + }, + { + "urlContext": {} + } + ] + } + } + } + } + }, + "general": { + "defaultApprovalMode": "auto_edit" + }, + "tools": { + "allowed": [ + "run_shell_command", + "write_file", + "googleSearch", + "urlContext" + ] + }, + "sandbox": { + "enabled": false + }, + "security": { + "folderTrust": { + "enabled": false + }, + "auth": { + "selectedType": "oauth-personal" + }, + "disableYoloMode": false + }, + "mcpServers": { + "playwright": { + "command": "docker-git-playwright-mcp", + "args": [], + "trust": true + } + } +} +EOF + +# Pre-trust important directories in trustedFolders.json +# Use flat mapping as required by recent Gemini CLI versions +cat <<'EOF' > "$GEMINI_TRUST_SETTINGS_FILE" +{ + "/": "TRUST_FOLDER", + "/home/dev/.gemini": "TRUST_FOLDER", + "/home/dev/workspaces/ProverCoderAI/docker-git/issue-237": "TRUST_FOLDER" +} +EOF + +chown -R 1000:1000 "$GEMINI_SETTINGS_DIR" || true +chmod 0600 "$GEMINI_TRUST_SETTINGS_FILE" "$GEMINI_CONFIG_SETTINGS_FILE" 2>/dev/null || true + +# Gemini CLI: keep Playwright MCP config in sync (TODO: Gemini CLI MCP integration format) +# For now, Gemini CLI uses MCP via ~/.gemini/settings.json or command line. +# We'll ensure it has the same Playwright capability as Claude/Codex once format is confirmed. + +# Gemini CLI: allow passwordless sudo for agent tasks +if [[ -d /etc/sudoers.d ]]; then + echo "dev ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/gemini-agent + chmod 0440 /etc/sudoers.d/gemini-agent +fi + +GEMINI_PROFILE="/etc/profile.d/gemini-config.sh" +printf "export GEMINI_AUTH_LABEL=%q\n" "$GEMINI_AUTH_LABEL" > "$GEMINI_PROFILE" +printf "export GEMINI_HOME=%q\n" "/home/dev/.gemini" >> "$GEMINI_PROFILE" +printf "export GEMINI_CLI_DISABLE_UPDATE_CHECK=true\n" >> "$GEMINI_PROFILE" +printf "export GEMINI_CLI_NONINTERACTIVE=true\n" >> "$GEMINI_PROFILE" +printf "export GEMINI_CLI_APPROVAL_MODE=yolo\n" >> "$GEMINI_PROFILE" +printf "alias gemini='/usr/local/bin/gemini-wrapper'\n" >> "$GEMINI_PROFILE" +cat <<'EOF' >> "$GEMINI_PROFILE" +if [[ -f "$GEMINI_HOME/.api-key" ]]; then + export GEMINI_API_KEY="$(cat "$GEMINI_HOME/.api-key" | tr -d '\r\n')" +fi +EOF +chmod 0644 "$GEMINI_PROFILE" || true + +docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "$GEMINI_AUTH_LABEL" +docker_git_upsert_ssh_env "GEMINI_API_KEY" "\${GEMINI_API_KEY:-}" +docker_git_upsert_ssh_env "GEMINI_CLI_DISABLE_UPDATE_CHECK" "true" +docker_git_upsert_ssh_env "GEMINI_CLI_NONINTERACTIVE" "true" +docker_git_upsert_ssh_env "GEMINI_CLI_APPROVAL_MODE" "yolo" + +# Ensure global GEMINI.md exists for container context +GEMINI_MD_PATH="/home/dev/.gemini/GEMINI.md" +GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository" +if [[ "$REPO_REF" == issue-* ]]; then + ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')" + ISSUE_URL="" + if [[ "$REPO_URL" == https://github.com/* ]]; then + ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$ISSUE_REPO" ]]; then + ISSUE_URL="https://github.com/$ISSUE_REPO/issues/$ISSUE_ID" + fi + fi + if [[ -n "$ISSUE_URL" ]]; then + GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID ($ISSUE_URL)" + else + GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID" + fi +elif [[ "$REPO_REF" == refs/pull/*/head ]]; then + PR_ID="$(printf "%s" "$REPO_REF" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')" + PR_URL="" + if [[ "$REPO_URL" == https://github.com/* && -n "$PR_ID" ]]; then + PR_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$PR_REPO" ]]; then + PR_URL="https://github.com/$PR_REPO/pull/$PR_ID" + fi + fi + if [[ -n "$PR_ID" && -n "$PR_URL" ]]; then + GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID ($PR_URL)" + elif [[ -n "$PR_ID" ]]; then + GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID" + else + GEMINI_WORKSPACE_CONTEXT="Контекст workspace: pull request ($REPO_REF)" + fi +fi + +GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE="${GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +GEMINI_SYSTEM_PROMPT_OVERRIDE="${GEMINI_SYSTEM_PROMPT_OVERRIDE:-}" +GEMINI_DEFAULT_PROMPT_BODY="$(cat < "$GEMINI_MD_PATH" + +$GEMINI_PROMPT_BODY + +EOF +chown 1000:1000 "$GEMINI_MD_PATH" || true + +================================================================================ +Codex project skills sync (with CODEX_EXTRA_SKILLS_PATHS support) +================================================================================ + +# Mirror project-owned Codex skill trees into CODEX_HOME without overwriting global skills. +docker_git_sync_project_codex_skills() { + local codex_home="${CODEX_HOME:-/home/dev/.codex}" + local project_dir="${TARGET_DIR:-}" + local project_skills_root="$codex_home/skills/.docker-git-project" + local linked=0 + local spec="" + local mount_name="" + local relative_path="" + + if [[ -z "$project_dir" || ! -d "$project_dir" ]]; then + return 0 + fi + + mkdir -p "$codex_home/skills" + rm -rf "$project_skills_root" + mkdir -p "$project_skills_root" + + # Priority goes from generic/shared skill trees -> Codex-specific trees. + for spec in \ + "10-root-skills::.skills" \ + "20-agents-skills::.agents/skills" \ + "30-agents-dot-skills::.agents/.skills" \ + "80-codex-skills::.codex/skills" \ + "90-codex-dot-skills::.codex/.skills"; do + mount_name="${spec%%::*}" + relative_path="${spec#*::}" + + if [[ -d "$project_dir/$relative_path" ]]; then + ln -sfn "$project_dir/$relative_path" "$project_skills_root/$mount_name" + chown -h 1000:1000 "$project_skills_root/$mount_name" 2>/dev/null || true + linked=1 + fi + done + + # Extra entries via CODEX_EXTRA_SKILLS_PATHS (comma- or newline-separated "prio-name::relative/path"). + local extra_specs="${CODEX_EXTRA_SKILLS_PATHS:-}" + if [[ -n "$extra_specs" ]]; then + extra_specs="${extra_specs//,/$'\n'}" + while IFS= read -r spec; do + [[ -z "$spec" ]] && continue + mount_name="${spec%%::*}" + relative_path="${spec#*::}" + if [[ -d "$project_dir/$relative_path" ]]; then + ln -sfn "$project_dir/$relative_path" "$project_skills_root/$mount_name" + chown -h 1000:1000 "$project_skills_root/$mount_name" 2>/dev/null || true + linked=1 + fi + done <<< "$extra_specs" + fi + + chown 1000:1000 "$codex_home/skills" "$project_skills_root" 2>/dev/null || true + + if [[ "$linked" -eq 1 ]]; then + echo "[codex-skills] linked project skill trees into $project_skills_root" + fi +} \ No newline at end of file diff --git a/experiments/render-examples.mjs b/experiments/render-examples.mjs new file mode 100644 index 00000000..0430a9d2 --- /dev/null +++ b/experiments/render-examples.mjs @@ -0,0 +1,40 @@ +import { writeFileSync } from "node:fs" +import { defaultTemplateConfig } from "../packages/lib/dist/core/domain.js" +import { renderClaudeGlobalPromptSetup } from "../packages/lib/dist/core/templates-entrypoint/claude-extra-config.js" +import { renderEntrypointAgentsNotice } from "../packages/lib/dist/core/templates-entrypoint/agents-notice.js" +import { renderEntrypointProjectCodexSkillsSync } from "../packages/lib/dist/core/templates-entrypoint/codex.js" +import { renderEntrypointGeminiConfig } from "../packages/lib/dist/core/templates-entrypoint/gemini.js" + +const cfg = { + ...defaultTemplateConfig, + repoUrl: "https://github.com/ProverCoderAI/docker-git.git", + containerName: "dg-docker-git", + serviceName: "dg-docker-git", + sshUser: "dev", + targetDir: "/home/dev/workspaces/ProverCoderAI/docker-git/issue-237", + volumeName: "dg-docker-git-home", + dockerGitPath: "/home/dev/.docker-git", + authorizedKeysPath: "/home/dev/.docker-git/authorized_keys", + envGlobalPath: "/home/dev/.docker-git/.orch/env/global.env", + envProjectPath: "/home/dev/workspaces/ProverCoderAI/docker-git/issue-237/.orch/env/project.env", + codexAuthPath: "/home/dev/.docker-git/.orch/auth/codex", + codexSharedAuthPath: "/home/dev/.docker-git/.orch/auth/codex-shared", + geminiAuthPath: "/home/dev/.docker-git/.orch/auth/gemini", + repoRef: "issue-237" +} + +const banner = (title) => `\n${"=".repeat(80)}\n${title}\n${"=".repeat(80)}\n` + +const output = [ + banner("CLAUDE.md prompt setup (~/.claude/CLAUDE.md)"), + renderClaudeGlobalPromptSetup(cfg), + banner(".codex/AGENTS.md prompt setup"), + renderEntrypointAgentsNotice(cfg), + banner("GEMINI.md prompt setup (full Gemini config block)"), + renderEntrypointGeminiConfig(cfg), + banner("Codex project skills sync (with CODEX_EXTRA_SKILLS_PATHS support)"), + renderEntrypointProjectCodexSkillsSync(cfg) +].join("\n") + +writeFileSync("experiments/render-examples-output.txt", output) +console.log(`Wrote ${output.length} chars`) diff --git a/experiments/render-examples.ts b/experiments/render-examples.ts new file mode 100644 index 00000000..1f01ee07 --- /dev/null +++ b/experiments/render-examples.ts @@ -0,0 +1,44 @@ +import { defaultTemplateConfig, type TemplateConfig } from "../packages/lib/src/core/domain.ts" +import { renderClaudeGlobalPromptSetup } from "../packages/lib/src/core/templates-entrypoint/claude-extra-config.ts" +import { renderEntrypointAgentsNotice } from "../packages/lib/src/core/templates-entrypoint/agents-notice.ts" +import { + renderEntrypointProjectCodexSkillsSync +} from "../packages/lib/src/core/templates-entrypoint/codex.ts" +import { renderEntrypointGeminiConfig } from "../packages/lib/src/core/templates-entrypoint/gemini.ts" + +const cfg: TemplateConfig = { + ...defaultTemplateConfig, + repoUrl: "https://github.com/ProverCoderAI/docker-git.git", + containerName: "dg-docker-git", + serviceName: "dg-docker-git", + sshUser: "dev", + targetDir: "/home/dev/workspaces/ProverCoderAI/docker-git/issue-237", + volumeName: "dg-docker-git-home", + dockerGitPath: "/home/dev/.docker-git", + authorizedKeysPath: "/home/dev/.docker-git/authorized_keys", + envGlobalPath: "/home/dev/.docker-git/.orch/env/global.env", + envProjectPath: "/home/dev/workspaces/ProverCoderAI/docker-git/issue-237/.orch/env/project.env", + codexAuthPath: "/home/dev/.docker-git/.orch/auth/codex", + codexSharedAuthPath: "/home/dev/.docker-git/.orch/auth/codex-shared", + geminiAuthPath: "/home/dev/.docker-git/.orch/auth/gemini", + repoRef: "issue-237" +} + +import { writeFileSync } from "node:fs" + +const banner = (title: string): string => + `\n${"=".repeat(80)}\n${title}\n${"=".repeat(80)}\n` + +const output = [ + banner("CLAUDE.md prompt setup (~/.claude/CLAUDE.md)"), + renderClaudeGlobalPromptSetup(cfg), + banner(".codex/AGENTS.md prompt setup"), + renderEntrypointAgentsNotice(cfg), + banner("GEMINI.md prompt setup (full Gemini config block)"), + renderEntrypointGeminiConfig(cfg), + banner("Codex project skills sync (with CODEX_EXTRA_SKILLS_PATHS support)"), + renderEntrypointProjectCodexSkillsSync(cfg) +].join("\n") + +writeFileSync("experiments/render-examples-output.txt", output) +console.log(`Wrote ${output.length} bytes to experiments/render-examples-output.txt`) diff --git a/experiments/ui-examples.md b/experiments/ui-examples.md new file mode 100644 index 00000000..756e73ea --- /dev/null +++ b/experiments/ui-examples.md @@ -0,0 +1,165 @@ +# Container UI examples (issue #237) + +These are the rendered files that the user sees inside a running container after +the entrypoint executes. The new `*_SYSTEM_PROMPT_OVERRIDE` and +`*_SYSTEM_PROMPT_OVERRIDE_FILE` env vars (plus `CODEX_EXTRA_SKILLS_PATHS`) let +operators replace the body of any of these files without forking the templates. + +--- + +## 1. Default behaviour (no overrides set) + +Container env: `CLAUDE_AUTO_SYSTEM_PROMPT=1` (default), no override vars. + +### `~/.claude/CLAUDE.md` + +```markdown + +Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, bun, codex, opencode, oh-my-opencode, sshpass, claude, git, node и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~ +Рабочая папка проекта (git clone): /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступные workspace пути: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Контекст workspace: issue #237 (https://github.com/ProverCoderAI/docker-git/issues/237) +Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе. +Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю. +Если ты видишь файлы AGENTS.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции. + +``` + +### `~/.codex/AGENTS.md` + +```markdown +Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, bun, codex, opencode, oh-my-opencode, sshpass, git, node и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~ + +Рабочая папка проекта (git clone): /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступные workspace пути: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Контекст workspace: issue #237 (https://github.com/ProverCoderAI/docker-git/issues/237) +Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе. +Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю. + +Если ты видишь файлы AGENTS.md внутри проекта, ты обязан их читать и соблюдать инструкции. +``` + +### `~/.gemini/GEMINI.md` + +```markdown + +Ты автономный агент Gemini, у тебя есть доступ к sudo, gh, gemini-cli, bun, git, node и всем остальным. Проекты с которыми идёт работа лежат по пути ~ +Рабочая папка проекта (git clone): /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступные workspace пути: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Контекст workspace: issue #237 (https://github.com/ProverCoderAI/docker-git/issues/237) +Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237 +Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе. +Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю. + +``` + +### `ls ~/.codex/skills/.docker-git-project/` (no extra skills) + +``` +20-agents-skills -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/.agents/skills +``` + +--- + +## 2. Inline override via `CLAUDE_SYSTEM_PROMPT_OVERRIDE` + +Container env (in `.orch/env/project.env`): + +```bash +CLAUDE_SYSTEM_PROMPT_OVERRIDE="You are a senior reviewer. Be terse. Only modify files in /home/dev/workspaces/ProverCoderAI/docker-git/issue-237." +``` + +### `~/.claude/CLAUDE.md` + +```markdown + +You are a senior reviewer. Be terse. Only modify files in /home/dev/workspaces/ProverCoderAI/docker-git/issue-237. + +``` + +The managed-block markers are preserved, so the next container restart still +detects the file as docker-git-managed and refreshes it idempotently. + +--- + +## 3. File override via `CODEX_SYSTEM_PROMPT_OVERRIDE_FILE` + +```bash +# .orch/env/project.env +CODEX_SYSTEM_PROMPT_OVERRIDE_FILE=/home/dev/.docker-git/prompts/codex.txt +``` + +```bash +# /home/dev/.docker-git/prompts/codex.txt +You are running inside docker-git. Workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237. +Always start by running `git status` and `gh issue view 237`. +``` + +### `~/.codex/AGENTS.md` (managed lines replaced) + +```markdown +Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, bun, codex, opencode, oh-my-opencode, sshpass, git, node и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~ + +You are running inside docker-git. Workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237. +Always start by running `git status` and `gh issue view 237`. + +Если ты видишь файлы AGENTS.md внутри проекта, ты обязан их читать и соблюдать инструкции. +``` + +`*_OVERRIDE_FILE` always wins over `*_OVERRIDE`. If neither is set, the default +content above is used. + +--- + +## 4. Extra skills via `CODEX_EXTRA_SKILLS_PATHS` + +```bash +# .orch/env/project.env +CODEX_EXTRA_SKILLS_PATHS="50-team-skills::team/skills,60-shared-rituals::infra/codex/rituals" +``` + +Project layout: + +``` +/home/dev/workspaces/ProverCoderAI/docker-git/issue-237/ +├── .agents/skills/... +├── team/skills/... +└── infra/codex/rituals/... +``` + +### `ls ~/.codex/skills/.docker-git-project/` (extras now mounted) + +``` +20-agents-skills -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/.agents/skills +50-team-skills -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/team/skills +60-shared-rituals -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/infra/codex/rituals +``` + +The built-in priority list (`.skills`, `.agents/skills`, `.agents/.skills`, +`.codex/skills`, `.codex/.skills`) is preserved. Extras are appended only when +the relative path exists, so misconfigured entries are silently ignored. + +--- + +## 5. Container terminal session showing the override hooks + +```text +dev@dg-docker-git:~$ cat ~/.codex/AGENTS.md | head -3 +Ты автономный агент, который имеет полностью все права управления контейнером... + +You are running inside docker-git. Workspace: /home/dev/workspaces/ProverCoderAI/docker-git/issue-237. + +dev@dg-docker-git:~$ ls -l ~/.codex/skills/.docker-git-project/ +total 0 +lrwxrwxrwx 1 dev dev 65 May 5 12:30 20-agents-skills -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/.agents/skills +lrwxrwxrwx 1 dev dev 60 May 5 12:30 50-team-skills -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/team/skills +lrwxrwxrwx 1 dev dev 64 May 5 12:30 60-shared-rituals -> /home/dev/workspaces/ProverCoderAI/docker-git/issue-237/infra/codex/rituals + +dev@dg-docker-git:~$ env | grep -E '_(SYSTEM_PROMPT_OVERRIDE|EXTRA_SKILLS)' +CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE=/home/dev/.docker-git/prompts/claude.txt +CODEX_SYSTEM_PROMPT_OVERRIDE_FILE=/home/dev/.docker-git/prompts/codex.txt +GEMINI_SYSTEM_PROMPT_OVERRIDE=You are running inside docker-git... +CODEX_EXTRA_SKILLS_PATHS=50-team-skills::team/skills,60-shared-rituals::infra/codex/rituals +``` diff --git a/packages/app/src/docker-git/cli/usage.ts b/packages/app/src/docker-git/cli/usage.ts index c672cdda..56311862 100644 --- a/packages/app/src/docker-git/cli/usage.ts +++ b/packages/app/src/docker-git/cli/usage.ts @@ -80,6 +80,13 @@ Container runtime env (set via .orch/env/project.env): CODEX_SHARE_AUTH=1|0 Share Codex auth.json across projects (default: 1) CODEX_AUTO_UPDATE=1|0 Auto-update Codex CLI on container start (default: 1) CLAUDE_AUTO_SYSTEM_PROMPT=1|0 Auto-attach docker-git managed system prompt to claude (default: 1) + CLAUDE_SYSTEM_PROMPT_OVERRIDE= Custom Claude system prompt body (overrides default Russian template) + CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE= Path to file with custom Claude prompt (takes precedence over OVERRIDE) + CODEX_SYSTEM_PROMPT_OVERRIDE= Custom Codex managed-block content for AGENTS.md + CODEX_SYSTEM_PROMPT_OVERRIDE_FILE= Path to file with custom Codex managed-block content (takes precedence) + GEMINI_SYSTEM_PROMPT_OVERRIDE= Custom Gemini system prompt body + GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE= Path to file with custom Gemini prompt (takes precedence over OVERRIDE) + CODEX_EXTRA_SKILLS_PATHS=[,...] Extra skill trees mounted into Codex (format: "prio-name::relative/path"; comma- or newline-separated) DOCKER_GIT_ZSH_AUTOSUGGEST=1|0 Enable zsh-autosuggestions (default: 0) DOCKER_GIT_ZSH_AUTOSUGGEST_STYLE=... zsh-autosuggestions highlight style (default: fg=8,italic) DOCKER_GIT_ZSH_AUTOSUGGEST_STRATEGY=... Suggestion sources (default: history completion) diff --git a/packages/app/src/lib/core/templates-entrypoint/agents-notice.ts b/packages/app/src/lib/core/templates-entrypoint/agents-notice.ts index 813ee0f6..16cab761 100644 --- a/packages/app/src/lib/core/templates-entrypoint/agents-notice.ts +++ b/packages/app/src/lib/core/templates-entrypoint/agents-notice.ts @@ -43,15 +43,27 @@ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then fi MANAGED_START="" MANAGED_END="" -if [[ ! -f "$AGENTS_PATH" ]]; then - MANAGED_BLOCK="$(cat </dev/null || true - if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then - cat < "$CLAUDE_GLOBAL_PROMPT_FILE" - +CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE="${"$"}{CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +CLAUDE_SYSTEM_PROMPT_OVERRIDE="${"$"}{CLAUDE_SYSTEM_PROMPT_OVERRIDE:-}" +CLAUDE_DEFAULT_PROMPT_BODY="$(cat </dev/null || true + if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then + cat < "$CLAUDE_GLOBAL_PROMPT_FILE" + +$CLAUDE_PROMPT_BODY EOF chmod 0644 "$CLAUDE_GLOBAL_PROMPT_FILE" || true diff --git a/packages/app/src/lib/core/templates-entrypoint/codex.ts b/packages/app/src/lib/core/templates-entrypoint/codex.ts index e63ea77e..718c5800 100644 --- a/packages/app/src/lib/core/templates-entrypoint/codex.ts +++ b/packages/app/src/lib/core/templates-entrypoint/codex.ts @@ -164,6 +164,22 @@ docker_git_sync_project_codex_skills() { fi done + # Extra entries via CODEX_EXTRA_SKILLS_PATHS (comma- or newline-separated "prio-name::relative/path"). + local extra_specs="${"$"}{CODEX_EXTRA_SKILLS_PATHS:-}" + if [[ -n "$extra_specs" ]]; then + extra_specs="${"$"}{extra_specs//,/$'\n'}" + while IFS= read -r spec; do + [[ -z "$spec" ]] && continue + mount_name="${"$"}{spec%%::*}" + relative_path="${"$"}{spec#*::}" + if [[ -d "$project_dir/$relative_path" ]]; then + ln -sfn "$project_dir/$relative_path" "$project_skills_root/$mount_name" + chown -h 1000:1000 "$project_skills_root/$mount_name" 2>/dev/null || true + linked=1 + fi + done <<< "$extra_specs" + fi + chown 1000:1000 "$codex_home/skills" "$project_skills_root" 2>/dev/null || true if [[ "$linked" -eq 1 ]]; then diff --git a/packages/app/src/lib/core/templates-entrypoint/gemini.ts b/packages/app/src/lib/core/templates-entrypoint/gemini.ts index 36c86eeb..fd91cd28 100644 --- a/packages/app/src/lib/core/templates-entrypoint/gemini.ts +++ b/packages/app/src/lib/core/templates-entrypoint/gemini.ts @@ -265,8 +265,9 @@ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then fi fi -cat < "$GEMINI_MD_PATH" - +GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE="${"$"}{GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +GEMINI_SYSTEM_PROMPT_OVERRIDE="${"$"}{GEMINI_SYSTEM_PROMPT_OVERRIDE:-}" +GEMINI_DEFAULT_PROMPT_BODY="$(cat < "$GEMINI_MD_PATH" + +$GEMINI_PROMPT_BODY EOF chown 1000:1000 "$GEMINI_MD_PATH" || true` diff --git a/packages/lib/src/core/templates-entrypoint/agents-notice.ts b/packages/lib/src/core/templates-entrypoint/agents-notice.ts index 9038a3ee..e82e732b 100644 --- a/packages/lib/src/core/templates-entrypoint/agents-notice.ts +++ b/packages/lib/src/core/templates-entrypoint/agents-notice.ts @@ -42,15 +42,27 @@ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then fi MANAGED_START="" MANAGED_END="" -if [[ ! -f "$AGENTS_PATH" ]]; then - MANAGED_BLOCK="$(cat </dev/null || true - if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then - cat < "$CLAUDE_GLOBAL_PROMPT_FILE" - +CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE="${"$"}{CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +CLAUDE_SYSTEM_PROMPT_OVERRIDE="${"$"}{CLAUDE_SYSTEM_PROMPT_OVERRIDE:-}" +CLAUDE_DEFAULT_PROMPT_BODY="$(cat </dev/null || true + if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then + cat < "$CLAUDE_GLOBAL_PROMPT_FILE" + +$CLAUDE_PROMPT_BODY EOF chmod 0644 "$CLAUDE_GLOBAL_PROMPT_FILE" || true diff --git a/packages/lib/src/core/templates-entrypoint/codex.ts b/packages/lib/src/core/templates-entrypoint/codex.ts index 49b9bc17..479c8a5d 100644 --- a/packages/lib/src/core/templates-entrypoint/codex.ts +++ b/packages/lib/src/core/templates-entrypoint/codex.ts @@ -163,6 +163,22 @@ docker_git_sync_project_codex_skills() { fi done + # Extra entries via CODEX_EXTRA_SKILLS_PATHS (comma- or newline-separated "prio-name::relative/path"). + local extra_specs="${"$"}{CODEX_EXTRA_SKILLS_PATHS:-}" + if [[ -n "$extra_specs" ]]; then + extra_specs="${"$"}{extra_specs//,/$'\n'}" + while IFS= read -r spec; do + [[ -z "$spec" ]] && continue + mount_name="${"$"}{spec%%::*}" + relative_path="${"$"}{spec#*::}" + if [[ -d "$project_dir/$relative_path" ]]; then + ln -sfn "$project_dir/$relative_path" "$project_skills_root/$mount_name" + chown -h 1000:1000 "$project_skills_root/$mount_name" 2>/dev/null || true + linked=1 + fi + done <<< "$extra_specs" + fi + chown 1000:1000 "$codex_home/skills" "$project_skills_root" 2>/dev/null || true if [[ "$linked" -eq 1 ]]; then diff --git a/packages/lib/src/core/templates-entrypoint/gemini.ts b/packages/lib/src/core/templates-entrypoint/gemini.ts index cb1092fb..cfe785fd 100644 --- a/packages/lib/src/core/templates-entrypoint/gemini.ts +++ b/packages/lib/src/core/templates-entrypoint/gemini.ts @@ -264,8 +264,9 @@ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then fi fi -cat < "$GEMINI_MD_PATH" - +GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE="${"$"}{GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE:-}" +GEMINI_SYSTEM_PROMPT_OVERRIDE="${"$"}{GEMINI_SYSTEM_PROMPT_OVERRIDE:-}" +GEMINI_DEFAULT_PROMPT_BODY="$(cat < "$GEMINI_MD_PATH" + +$GEMINI_PROMPT_BODY EOF chown 1000:1000 "$GEMINI_MD_PATH" || true` diff --git a/packages/lib/tests/core/templates.test.ts b/packages/lib/tests/core/templates.test.ts index b3808352..982919ff 100644 --- a/packages/lib/tests/core/templates.test.ts +++ b/packages/lib/tests/core/templates.test.ts @@ -217,6 +217,40 @@ describe("renderEntrypoint auth bridge", () => { expect(entrypoint.split("Для решения задач обязательно используй subagents.").length - 1).toBeGreaterThanOrEqual(2) }) + it("renders system-prompt override hooks for codex/claude/gemini", () => { + const entrypoint = renderAuthEntrypoint() + + expectContainsAll(entrypoint, [ + "CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE=\"${CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE:-}\"", + "CLAUDE_SYSTEM_PROMPT_OVERRIDE=\"${CLAUDE_SYSTEM_PROMPT_OVERRIDE:-}\"", + "if [[ -n \"$CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE\" && -r \"$CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE\" ]]; then", + "CLAUDE_PROMPT_BODY=\"$(cat \"$CLAUDE_SYSTEM_PROMPT_OVERRIDE_FILE\")\"", + "CLAUDE_PROMPT_BODY=\"$CLAUDE_SYSTEM_PROMPT_OVERRIDE\"", + "CLAUDE_PROMPT_BODY=\"$CLAUDE_DEFAULT_PROMPT_BODY\"", + "CODEX_SYSTEM_PROMPT_OVERRIDE_FILE=\"${CODEX_SYSTEM_PROMPT_OVERRIDE_FILE:-}\"", + "CODEX_SYSTEM_PROMPT_OVERRIDE=\"${CODEX_SYSTEM_PROMPT_OVERRIDE:-}\"", + "MANAGED_LINES=\"$(cat \"$CODEX_SYSTEM_PROMPT_OVERRIDE_FILE\")\"", + "MANAGED_LINES=\"$CODEX_SYSTEM_PROMPT_OVERRIDE\"", + "GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE=\"${GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE:-}\"", + "GEMINI_SYSTEM_PROMPT_OVERRIDE=\"${GEMINI_SYSTEM_PROMPT_OVERRIDE:-}\"", + "GEMINI_PROMPT_BODY=\"$(cat \"$GEMINI_SYSTEM_PROMPT_OVERRIDE_FILE\")\"", + "GEMINI_PROMPT_BODY=\"$GEMINI_SYSTEM_PROMPT_OVERRIDE\"", + "GEMINI_PROMPT_BODY=\"$GEMINI_DEFAULT_PROMPT_BODY\"" + ]) + }) + + it("renders extra-skills hook for the codex skill sync function", () => { + const entrypoint = renderAuthEntrypoint() + + expectContainsAll(entrypoint, [ + "local extra_specs=\"${CODEX_EXTRA_SKILLS_PATHS:-}\"", + "if [[ -n \"$extra_specs\" ]]; then", + "extra_specs=\"${extra_specs//,/$'\\n'}\"", + "while IFS= read -r spec; do", + "done <<< \"$extra_specs\"" + ]) + }) + it("renders terminal recovery hooks and disables zsh autosuggestions by default", () => { const entrypoint = renderAuthEntrypoint()