Skip to content
Open
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
50 changes: 21 additions & 29 deletions packages/lib/src/core/templates-entrypoint/claude-extra-config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import type { TemplateConfig } from "../domain.js"
import { renderSharedPrompt } from "./claude-system-prompt.js"

const entrypointClaudeGlobalPromptTemplate = String
.raw`# Claude Code: managed global memory (CLAUDE.md is auto-loaded by Claude Code)
CLAUDE_GLOBAL_PROMPT_FILE="/home/__SSH_USER__/.claude/CLAUDE.md"
const escapeForDoubleQuotes = (value: string): string => {
const backslash = String.fromCodePoint(92)
const quote = String.fromCodePoint(34)
const escapedBackslash = `${backslash}${backslash}`
const escapedQuote = `${backslash}${quote}`
return value
.replaceAll(backslash, escapedBackslash)
.replaceAll(quote, escapedQuote)
}

export const renderClaudeGlobalPromptSetup = (config: TemplateConfig): string => {
const promptContent = renderSharedPrompt(config.targetDir, "$CLAUDE_WORKSPACE_CONTEXT")
const repoRefDefault = escapeForDoubleQuotes(config.repoRef)
const repoUrlDefault = escapeForDoubleQuotes(config.repoUrl)

return String.raw`# Claude Code: managed global memory (CLAUDE.md is auto-loaded by Claude Code)
CLAUDE_GLOBAL_PROMPT_FILE="/home/${config.sshUser}/.claude/CLAUDE.md"
CLAUDE_AUTO_SYSTEM_PROMPT="${"$"}{CLAUDE_AUTO_SYSTEM_PROMPT:-1}"
CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: repository"
REPO_REF_VALUE="${"$"}{REPO_REF:-__REPO_REF_DEFAULT__}"
REPO_URL_VALUE="${"$"}{REPO_URL:-__REPO_URL_DEFAULT__}"
REPO_REF_VALUE="${"$"}{REPO_REF:-${repoRefDefault}}"
REPO_URL_VALUE="${"$"}{REPO_URL:-${repoUrlDefault}}"

if [[ "$REPO_REF_VALUE" == issue-* ]]; then
ISSUE_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -E 's#^issue-##')"
Expand Down Expand Up @@ -46,14 +61,7 @@ if [[ "$CLAUDE_AUTO_SYSTEM_PROMPT" == "1" ]]; then
if [[ ! -f "$CLAUDE_GLOBAL_PROMPT_FILE" ]] || grep -q "^<!-- docker-git-managed:claude-md -->$" "$CLAUDE_GLOBAL_PROMPT_FILE"; then
cat <<EOF > "$CLAUDE_GLOBAL_PROMPT_FILE"
<!-- docker-git-managed:claude-md -->
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, opencode, oh-my-opencode, sshpass, claude, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
Рабочая папка проекта (git clone): __TARGET_DIR__
Доступные workspace пути: __TARGET_DIR__
$CLAUDE_WORKSPACE_CONTEXT
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
Если ты видишь файлы AGENTS.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
${promptContent}
<!-- /docker-git-managed:claude-md -->
EOF
chmod 0644 "$CLAUDE_GLOBAL_PROMPT_FILE" || true
Expand All @@ -62,24 +70,8 @@ EOF
fi

export CLAUDE_AUTO_SYSTEM_PROMPT`

const escapeForDoubleQuotes = (value: string): string => {
const backslash = String.fromCodePoint(92)
const quote = String.fromCodePoint(34)
const escapedBackslash = `${backslash}${backslash}`
const escapedQuote = `${backslash}${quote}`
return value
.replaceAll(backslash, escapedBackslash)
.replaceAll(quote, escapedQuote)
}

export const renderClaudeGlobalPromptSetup = (config: TemplateConfig): string =>
entrypointClaudeGlobalPromptTemplate
.replaceAll("__TARGET_DIR__", config.targetDir)
.replaceAll("__SSH_USER__", config.sshUser)
.replaceAll("__REPO_REF_DEFAULT__", escapeForDoubleQuotes(config.repoRef))
.replaceAll("__REPO_URL_DEFAULT__", escapeForDoubleQuotes(config.repoUrl))

export const renderClaudeWrapperSetup = (): string =>
String.raw`CLAUDE_WRAPPER_BIN="/usr/local/bin/claude"
if command -v claude >/dev/null 2>&1; then
Expand Down
42 changes: 42 additions & 0 deletions packages/lib/src/core/templates-entrypoint/claude-system-prompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Single source of truth for the agent system prompt.
* Edit this file to change behaviour across all providers (Claude, Gemini, Codex).
*
* @param targetDir – absolute path to the repo inside the container
* @param workspaceContext – bash variable reference resolved at container start-up,
* e.g. "$CLAUDE_WORKSPACE_CONTEXT"
*/

const INTRO =
`Ты автономный агент, который имеет полностью все права управления контейнером. ` +
`У тебя есть доступ к командам sudo, gh, codex, opencode, oh-my-opencode, sshpass, ` +
`claude, gemini, git, node, pnpm и всем остальным другим. ` +
`Проекты с которыми идёт работа лежат по пути ~`

const INTERNET =
`Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.`

const SUBAGENTS =
`Для решения задач обязательно используй subagents. ` +
`Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.`

const FILES =
`Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.`

// Used by Codex's bash-variable injection pattern
export const PROMPT_INTRO = INTRO
export const PROMPT_INTERNET = INTERNET
export const PROMPT_SUBAGENTS = SUBAGENTS
export const PROMPT_FILES = FILES
export const renderFocusLine = (targetDir: string): string =>
`Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: ${targetDir}`

export const renderSharedPrompt = (targetDir: string, workspaceContext: string): string =>
`${INTRO}
Рабочая папка проекта (git clone): ${targetDir}
Доступные workspace пути: ${targetDir}
${workspaceContext}
${renderFocusLine(targetDir)}
${INTERNET}
${SUBAGENTS}
${FILES}`
20 changes: 11 additions & 9 deletions packages/lib/src/core/templates-entrypoint/codex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TemplateConfig } from "../domain.js"
import { PROMPT_FILES, PROMPT_INTERNET, PROMPT_INTRO, PROMPT_SUBAGENTS, renderFocusLine } from "./claude-system-prompt.js"

export { renderEntrypointCodexResumeHint } from "./codex-resume-hint.js"

Expand Down Expand Up @@ -127,9 +128,9 @@ LEGACY_AGENTS_PATH="/home/__SSH_USER__/AGENTS.md"
PROJECT_LINE="Рабочая папка проекта (git clone): __TARGET_DIR__"
WORKSPACES_LINE="Доступные workspace пути: __TARGET_DIR__"
WORKSPACE_INFO_LINE="Контекст workspace: repository"
FOCUS_LINE="Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__"
INTERNET_LINE="Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе."
SUBAGENTS_LINE="Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю."
FOCUS_LINE="__FOCUS_LINE__"
INTERNET_LINE="${PROMPT_INTERNET}"
SUBAGENTS_LINE="${PROMPT_SUBAGENTS}"
if [[ "$REPO_REF" == issue-* ]]; then
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
ISSUE_URL=""
Expand Down Expand Up @@ -176,9 +177,9 @@ $MANAGED_END
EOF
)"
cat <<EOF > "$AGENTS_PATH"
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
${PROMPT_INTRO}
$MANAGED_BLOCK
Если ты видишь файлы AGENTS.md внутри проекта, ты обязан их читать и соблюдать инструкции.
${PROMPT_FILES}
EOF
chown 1000:1000 "$AGENTS_PATH" || true
fi
Expand Down Expand Up @@ -229,7 +230,8 @@ if [[ -f "$LEGACY_AGENTS_PATH" && -f "$AGENTS_PATH" ]]; then
fi`

export const renderEntrypointAgentsNotice = (config: TemplateConfig): string =>
entrypointAgentsNoticeTemplate.replaceAll("__CODEX_HOME__", config.codexHome).replaceAll(
"__SSH_USER__",
config.sshUser
).replaceAll("__TARGET_DIR__", config.targetDir)
entrypointAgentsNoticeTemplate
.replaceAll("__CODEX_HOME__", config.codexHome)
.replaceAll("__SSH_USER__", config.sshUser)
.replaceAll("__TARGET_DIR__", config.targetDir)
.replaceAll("__FOCUS_LINE__", renderFocusLine(config.targetDir))
23 changes: 8 additions & 15 deletions packages/lib/src/core/templates-entrypoint/gemini.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TemplateConfig } from "../domain.js"
import { renderSharedPrompt } from "./claude-system-prompt.js"

// CHANGE: add Gemini CLI entrypoint configuration
// WHY: enable Gemini CLI in Docker with automated auth, trust settings and MCP
Expand Down Expand Up @@ -229,8 +230,11 @@ 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"`

const entrypointGeminiNoticeTemplate = String.raw`# Ensure global GEMINI.md exists for container context
GEMINI_MD_PATH="__GEMINI_HOME__/GEMINI.md"
const renderEntrypointGeminiNotice = (config: TemplateConfig): string => {
const promptContent = renderSharedPrompt(config.targetDir, "$GEMINI_WORKSPACE_CONTEXT")

return String.raw`# Ensure global GEMINI.md exists for container context
GEMINI_MD_PATH="${config.geminiHome}/GEMINI.md"
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository"
if [[ "$REPO_REF" == issue-* ]]; then
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
Expand Down Expand Up @@ -266,22 +270,11 @@ fi

cat <<EOF > "$GEMINI_MD_PATH"
<!-- docker-git-managed:gemini-md -->
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
Рабочая папка проекта (git clone): __TARGET_DIR__
Доступные workspace пути: __TARGET_DIR__
$GEMINI_WORKSPACE_CONTEXT
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
${promptContent}
<!-- /docker-git-managed:gemini-md -->
EOF
chown 1000:1000 "$GEMINI_MD_PATH" || true`

const renderEntrypointGeminiNotice = (config: TemplateConfig): string =>
entrypointGeminiNoticeTemplate
.replaceAll("__GEMINI_HOME__", config.geminiHome)
.replaceAll("__TARGET_DIR__", config.targetDir)
}

export const renderEntrypointGeminiConfig = (config: TemplateConfig): string =>
[
Expand Down
Loading