Skip to content

Commit 644d98e

Browse files
committed
feat(gemini): improve Gemini CLI authentication and container integration
- Fix Gemini OAuth flow: enable via --web flag, implement automatic port cleanup, and dynamic success detection - Automated environment: pre-configure folder trust, settings, and projects registry to skip interactive prompts - Container lifecycle: implement automatic process termination after successful login and fix permissions for root-owned files - Agent integration: implement GEMINI.md prompts and auto-launch support matching AGENTS.md/CLAUDE.md standards - Permissions: enable passwordless sudo for Gemini tasks inside the container
1 parent eeeb24b commit 644d98e

File tree

9 files changed

+337
-128
lines changed

9 files changed

+337
-128
lines changed

packages/app/src/docker-git/cli/parser-auth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ const buildGeminiCommand = (action: string, options: AuthOptions): Either.Either
136136
Either.right<AuthCommand>({
137137
_tag: "AuthGeminiLogin",
138138
label: options.label,
139-
geminiAuthPath: options.geminiAuthPath
139+
geminiAuthPath: options.geminiAuthPath,
140+
isWeb: options.authWeb
140141
})),
141142
Match.when("status", () =>
142143
Either.right<AuthCommand>({

packages/app/src/docker-git/program.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
authCodexLogout,
1010
authCodexStatus,
1111
authGeminiLoginCli,
12+
authGeminiLoginOauth,
1213
authGeminiLogout,
1314
authGeminiStatus,
1415
authGithubLogin,
@@ -100,7 +101,9 @@ const handleNonBaseCommand = (command: NonBaseCommand) =>
100101
Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd))
101102
)
102103
.pipe(
103-
Match.when({ _tag: "AuthGeminiLogin" }, (cmd) => authGeminiLoginCli(cmd)),
104+
Match.when({ _tag: "AuthGeminiLogin" }, (cmd) =>
105+
cmd.isWeb ? authGeminiLoginOauth(cmd) : authGeminiLoginCli(cmd)
106+
),
104107
Match.when({ _tag: "AuthGeminiStatus" }, (cmd) => authGeminiStatus(cmd)),
105108
Match.when({ _tag: "AuthGeminiLogout" }, (cmd) => authGeminiLogout(cmd)),
106109
Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd)),

packages/lib/src/core/command-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface RawOptions {
4141
readonly scopes?: string
4242
readonly message?: string
4343
readonly authWeb?: boolean
44+
readonly authOauth?: boolean
4445
readonly outDir?: string
4546
readonly projectDir?: string
4647
readonly lines?: string

packages/lib/src/core/domain.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ export interface AuthGeminiLoginCommand {
256256
readonly _tag: "AuthGeminiLogin"
257257
readonly label: string | null
258258
readonly geminiAuthPath: string
259+
readonly isWeb: boolean
259260
}
260261

261262
export interface AuthGeminiStatusCommand {

packages/lib/src/core/templates-entrypoint/agent.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ AGENT_ENV_FILE="/run/docker-git/agent-env.sh"
3535
{
3636
[[ -f /etc/profile.d/gh-token.sh ]] && cat /etc/profile.d/gh-token.sh
3737
[[ -f /etc/profile.d/claude-config.sh ]] && cat /etc/profile.d/claude-config.sh
38+
[[ -f /etc/profile.d/gemini-config.sh ]] && cat /etc/profile.d/gemini-config.sh
3839
} > "$AGENT_ENV_FILE" 2>/dev/null || true
3940
chmod 644 "$AGENT_ENV_FILE"`,
4041
renderAgentPrompt(),
@@ -45,14 +46,20 @@ if [[ -n "$AGENT_PROMPT" ]]; then
4546
fi`
4647
].join("\n\n")
4748

48-
const renderAgentPromptCommand = (mode: "claude" | "codex"): string =>
49-
mode === "claude"
50-
? String.raw`claude --dangerously-skip-permissions -p \"\$(cat \"$AGENT_PROMPT_FILE\")\"`
51-
: String.raw`codex exec \"\$(cat \"$AGENT_PROMPT_FILE\")\"`
49+
const renderAgentPromptCommand = (mode: "claude" | "codex" | "gemini"): string => {
50+
switch (mode) {
51+
case "claude":
52+
return String.raw`claude --dangerously-skip-permissions -p \"\$(cat \"$AGENT_PROMPT_FILE\")\"`
53+
case "codex":
54+
return String.raw`codex exec \"\$(cat \"$AGENT_PROMPT_FILE\")\"`
55+
case "gemini":
56+
return String.raw`gemini \"\$(cat \"$AGENT_PROMPT_FILE\")\"`
57+
}
58+
}
5259

5360
const renderAgentAutoLaunchCommand = (
5461
config: TemplateConfig,
55-
mode: "claude" | "codex"
62+
mode: "claude" | "codex" | "gemini"
5663
): string =>
5764
String
5865
.raw`su - ${config.sshUser} -s /bin/bash -c "bash -lc '. /etc/profile 2>/dev/null || true; . \"$AGENT_ENV_FILE\" 2>/dev/null || true; cd \"$TARGET_DIR\" && ${
@@ -61,7 +68,7 @@ const renderAgentAutoLaunchCommand = (
6168

6269
const renderAgentModeBlock = (
6370
config: TemplateConfig,
64-
mode: "claude" | "codex"
71+
mode: "claude" | "codex" | "gemini"
6572
): string => {
6673
const startMessage = `[agent] starting ${mode}...`
6774
const interactiveMessage = `[agent] ${mode} started in interactive mode (use SSH to connect)`
@@ -84,6 +91,7 @@ const renderAgentModeCase = (config: TemplateConfig): string =>
8491
String.raw`case "$AGENT_MODE" in`,
8592
indentBlock(renderAgentModeBlock(config, "claude")),
8693
indentBlock(renderAgentModeBlock(config, "codex")),
94+
indentBlock(renderAgentModeBlock(config, "gemini")),
8795
indentBlock(
8896
String.raw`*)
8997
echo "[agent] unknown agent mode: $AGENT_MODE"

packages/lib/src/core/templates-entrypoint/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ elif [[ "$TARGET_DIR" == "~/"* ]]; then
1616
fi
1717
CLAUDE_AUTH_LABEL="\${CLAUDE_AUTH_LABEL:-}"
1818
CODEX_AUTH_LABEL="\${CODEX_AUTH_LABEL:-}"
19+
GEMINI_AUTH_LABEL="\${GEMINI_AUTH_LABEL:-}"
1920
GIT_AUTH_USER="\${GIT_AUTH_USER:-\${GITHUB_USER:-x-access-token}}"
2021
GIT_AUTH_TOKEN="\${GIT_AUTH_TOKEN:-\${GITHUB_TOKEN:-\${GH_TOKEN:-}}}"
2122
GH_TOKEN="\${GH_TOKEN:-\${GIT_AUTH_TOKEN:-}}"
Lines changed: 144 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import type { TemplateConfig } from "../domain.js"
22

33
// CHANGE: add Gemini CLI entrypoint configuration
4-
// WHY: enable Gemini CLI authentication and configuration management similar to Claude/Codex
5-
// QUOTE(ТЗ): "Добавь поддержку gemini CLI"
4+
// WHY: enable Gemini CLI in Docker with automated auth, trust settings and MCP
65
// REF: issue-146
7-
// SOURCE: https://geminicli.com/docs/get-started/authentication/
8-
// FORMAT THEOREM: forall config: renderEntrypointGeminiConfig(config) -> valid_bash_script
6+
// SOURCE: https://github.com/google-gemini/gemini-cli
7+
// FORMAT THEOREM: renderEntrypointGeminiConfig(config) -> valid_bash_script
98
// PURITY: CORE
10-
// EFFECT: n/a
11-
// INVARIANT: GEMINI_API_KEY is loaded from shared auth volume
9+
// INVARIANT: configurations are isolated by GEMINI_AUTH_LABEL
1210
// COMPLEXITY: O(1)
1311

1412
const geminiAuthRootContainerPath = (sshUser: string): string => `/home/${sshUser}/.docker-git/.orch/auth/gemini`
1513

16-
const geminiAuthConfigTemplate = String
17-
.raw`# Gemini CLI: expose GEMINI_API_KEY for SSH sessions (API key stored under ~/.docker-git/.orch/auth/gemini)
18-
GEMINI_LABEL_RAW="${"$"}{GEMINI_AUTH_LABEL:-}"
14+
const geminiAuthConfigTemplate = String.raw`# Gemini CLI: expose GEMINI_HOME for sessions (OAuth cache lives under ~/.docker-git/.orch/auth/gemini)
15+
GEMINI_LABEL_RAW="$GEMINI_AUTH_LABEL"
1916
if [[ -z "$GEMINI_LABEL_RAW" ]]; then
2017
GEMINI_LABEL_RAW="default"
2118
fi
@@ -28,29 +25,16 @@ if [[ -z "$GEMINI_LABEL_NORM" ]]; then
2825
fi
2926
3027
GEMINI_AUTH_ROOT="__GEMINI_AUTH_ROOT__"
31-
GEMINI_AUTH_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
28+
GEMINI_CONFIG_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
3229
33-
# Backward compatibility: if default auth is stored directly under gemini root, reuse it.
34-
if [[ "$GEMINI_LABEL_NORM" == "default" ]]; then
35-
GEMINI_ROOT_ENV_FILE="$GEMINI_AUTH_ROOT/.env"
36-
if [[ -f "$GEMINI_ROOT_ENV_FILE" ]]; then
37-
GEMINI_AUTH_DIR="$GEMINI_AUTH_ROOT"
38-
fi
39-
fi
40-
41-
mkdir -p "$GEMINI_AUTH_DIR" || true
30+
mkdir -p "$GEMINI_CONFIG_DIR" || true
4231
GEMINI_HOME_DIR="__GEMINI_HOME_DIR__"
4332
mkdir -p "$GEMINI_HOME_DIR" || true
4433
45-
GEMINI_API_KEY_FILE="$GEMINI_AUTH_DIR/.api-key"
46-
GEMINI_ENV_FILE="$GEMINI_AUTH_DIR/.env"
47-
GEMINI_HOME_ENV_FILE="$GEMINI_HOME_DIR/.env"
48-
4934
docker_git_link_gemini_file() {
5035
local source_path="$1"
5136
local link_path="$2"
5237
53-
# Preserve user-created regular files and seed config dir once.
5438
if [[ -e "$link_path" && ! -L "$link_path" ]]; then
5539
if [[ -f "$link_path" && ! -e "$source_path" ]]; then
5640
cp "$link_path" "$source_path" || true
@@ -62,99 +46,164 @@ docker_git_link_gemini_file() {
6246
ln -sfn "$source_path" "$link_path" || true
6347
}
6448
65-
# Link Gemini .env file from auth dir to home dir
66-
docker_git_link_gemini_file "$GEMINI_ENV_FILE" "$GEMINI_HOME_ENV_FILE"
67-
68-
docker_git_refresh_gemini_api_key() {
69-
local api_key=""
70-
# Try to read from dedicated API key file first
71-
if [[ -f "$GEMINI_API_KEY_FILE" ]]; then
72-
api_key="$(tr -d '\r\n' < "$GEMINI_API_KEY_FILE")"
73-
fi
74-
# Fall back to .env file
75-
if [[ -z "$api_key" && -f "$GEMINI_ENV_FILE" ]]; then
76-
api_key="$(grep -E '^GEMINI_API_KEY=' "$GEMINI_ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '\r\n' | sed "s/^['\"]//;s/['\"]$//")"
77-
fi
78-
if [[ -n "$api_key" ]]; then
79-
export GEMINI_API_KEY="$api_key"
80-
else
81-
unset GEMINI_API_KEY || true
49+
# Link .api-key, .env, and .gemini directory from central auth storage to container home
50+
docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.api-key" "$GEMINI_HOME_DIR/.api-key"
51+
docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.env" "$GEMINI_HOME_DIR/.env"
52+
docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.gemini" "$GEMINI_HOME_DIR/.gemini"
53+
54+
docker_git_refresh_gemini_env() {
55+
# If .api-key exists, export it as GEMINI_API_KEY
56+
if [[ -f "$GEMINI_HOME_DIR/.api-key" ]]; then
57+
export GEMINI_API_KEY="$(cat "$GEMINI_HOME_DIR/.api-key" | tr -d '\r\n')"
58+
elif [[ -f "$GEMINI_HOME_DIR/.env" ]]; then
59+
# Parse GEMINI_API_KEY from .env
60+
API_KEY="$(grep "^GEMINI_API_KEY=" "$GEMINI_HOME_DIR/.env" | cut -d'=' -f2- | sed "s/^['\"]//;s/['\"]$//")"
61+
if [[ -n "$API_KEY" ]]; then
62+
export GEMINI_API_KEY="$API_KEY"
63+
fi
8264
fi
8365
}
8466
85-
docker_git_refresh_gemini_api_key`
67+
docker_git_refresh_gemini_env`
8668

8769
const renderGeminiAuthConfig = (config: TemplateConfig): string =>
8870
geminiAuthConfigTemplate
8971
.replaceAll("__GEMINI_AUTH_ROOT__", geminiAuthRootContainerPath(config.sshUser))
90-
.replaceAll("__GEMINI_HOME_DIR__", `/home/${config.sshUser}/.gemini`)
91-
92-
const renderGeminiCliInstall = (): string =>
93-
String.raw`# Gemini CLI: ensure CLI command exists (non-blocking startup self-heal)
94-
docker_git_ensure_gemini_cli() {
95-
if command -v gemini >/dev/null 2>&1; then
96-
return 0
97-
fi
72+
.replaceAll("__GEMINI_HOME_DIR__", config.geminiHome)
73+
74+
const renderGeminiPermissionSettingsConfig = (config: TemplateConfig): string =>
75+
String.raw`# Gemini CLI: keep trust settings in sync with docker-git defaults
76+
GEMINI_SETTINGS_DIR="${config.geminiHome}/.gemini"
77+
GEMINI_TRUST_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/trustedFolders.json"
78+
GEMINI_CONFIG_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/settings.json"
79+
80+
mkdir -p "$GEMINI_SETTINGS_DIR" || true
81+
82+
# Disable folder trust prompt in settings.json
83+
if [[ ! -f "$GEMINI_CONFIG_SETTINGS_FILE" ]]; then
84+
cat <<'EOF' > "$GEMINI_CONFIG_SETTINGS_FILE"
85+
{
86+
"security": {
87+
"folderTrust": {
88+
"enabled": false
89+
}
90+
}
91+
}
92+
EOF
93+
fi
9894
99-
if ! command -v npm >/dev/null 2>&1; then
100-
return 0
101-
fi
95+
# Pre-trust important directories in trustedFolders.json
96+
cat <<'EOF' > "$GEMINI_TRUST_SETTINGS_FILE"
97+
{
98+
"folders": [
99+
{
100+
"path": "/",
101+
"trustState": "trusted",
102+
"isRecursive": true
103+
},
104+
{
105+
"path": "${config.geminiHome}",
106+
"trustState": "trusted",
107+
"isRecursive": true
108+
},
109+
{
110+
"path": "${config.targetDir}",
111+
"trustState": "trusted",
112+
"isRecursive": true
113+
}
114+
]
115+
}
116+
EOF
102117
103-
NPM_ROOT="$(npm root -g 2>/dev/null || true)"
104-
GEMINI_CLI_JS="$NPM_ROOT/@google/gemini-cli/build/cli.js"
105-
if [[ -z "$NPM_ROOT" || ! -f "$GEMINI_CLI_JS" ]]; then
106-
echo "docker-git: gemini cli.js not found under npm global root; skip shim restore" >&2
107-
return 0
108-
fi
118+
chown -R 1000:1000 "$GEMINI_SETTINGS_DIR" || true
119+
chmod 0600 "$GEMINI_TRUST_SETTINGS_FILE" "$GEMINI_CONFIG_SETTINGS_FILE" 2>/dev/null || true`
109120

110-
# Rebuild a minimal shim when npm package exists but binary link is missing.
111-
cat <<'EOF' > /usr/local/bin/gemini
112-
#!/usr/bin/env bash
113-
set -euo pipefail
121+
const renderGeminiSudoConfig = (config: TemplateConfig): string =>
122+
String.raw`# Gemini CLI: allow passwordless sudo for agent tasks
123+
if [[ -d /etc/sudoers.d ]]; then
124+
echo "${config.sshUser} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/gemini-agent
125+
chmod 0440 /etc/sudoers.d/gemini-agent
126+
fi`
114127

115-
if ! command -v npm >/dev/null 2>&1; then
116-
echo "gemini: npm is required but missing" >&2
117-
exit 127
118-
fi
128+
const renderGeminiMcpPlaywrightConfig = (config: TemplateConfig): string =>
129+
String.raw`# Gemini CLI: keep Playwright MCP config in sync (TODO: Gemini CLI MCP integration format)
130+
# For now, Gemini CLI uses MCP via ~/.gemini/settings.json or command line.
131+
# We'll ensure it has the same Playwright capability as Claude/Codex once format is confirmed.`
119132

120-
NPM_ROOT="$(npm root -g 2>/dev/null || true)"
121-
GEMINI_CLI_JS="$NPM_ROOT/@google/gemini-cli/build/cli.js"
122-
if [[ -z "$NPM_ROOT" || ! -f "$GEMINI_CLI_JS" ]]; then
123-
echo "gemini: cli.js not found under npm global root" >&2
124-
exit 127
133+
const renderGeminiProfileSetup = (config: TemplateConfig): string =>
134+
String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
135+
printf "export GEMINI_AUTH_LABEL=%q\n" "$GEMINI_AUTH_LABEL" > "$GEMINI_PROFILE"
136+
printf "export GEMINI_HOME=%q\n" "${config.geminiHome}" >> "$GEMINI_PROFILE"
137+
cat <<'EOF' >> "$GEMINI_PROFILE"
138+
if [[ -f "$GEMINI_HOME/.api-key" ]]; then
139+
export GEMINI_API_KEY="$(cat "$GEMINI_HOME/.api-key" | tr -d '\r\n')"
125140
fi
126-
127-
exec node "$GEMINI_CLI_JS" "$@"
128141
EOF
129-
chmod 0755 /usr/local/bin/gemini || true
130-
ln -sf /usr/local/bin/gemini /usr/bin/gemini || true
131-
}
142+
chmod 0644 "$GEMINI_PROFILE" || true
132143
133-
docker_git_ensure_gemini_cli`
144+
docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "$GEMINI_AUTH_LABEL"
145+
docker_git_upsert_ssh_env "GEMINI_API_KEY" "${"$"}{GEMINI_API_KEY:-}"`
134146

135-
const renderGeminiProfileSetup = (): string =>
136-
String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
137-
printf "export GEMINI_AUTH_LABEL=%q\n" "${"$"}{GEMINI_AUTH_LABEL:-default}" > "$GEMINI_PROFILE"
138-
cat <<'EOF' >> "$GEMINI_PROFILE"
139-
GEMINI_API_KEY_FILE="${"$"}{GEMINI_AUTH_DIR:-$HOME/.gemini}/.api-key"
140-
GEMINI_ENV_FILE="${"$"}{GEMINI_AUTH_DIR:-$HOME/.gemini}/.env"
141-
if [[ -f "$GEMINI_API_KEY_FILE" ]]; then
142-
export GEMINI_API_KEY="$(tr -d '\r\n' < "$GEMINI_API_KEY_FILE")"
143-
elif [[ -f "$GEMINI_ENV_FILE" ]]; then
144-
GEMINI_KEY="$(grep -E '^GEMINI_API_KEY=' "$GEMINI_ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '\r\n' | sed "s/^['\"]//;s/['\"]$//")"
145-
if [[ -n "$GEMINI_KEY" ]]; then
146-
export GEMINI_API_KEY="$GEMINI_KEY"
147+
const entrypointGeminiNoticeTemplate = String.raw`# Ensure global GEMINI.md exists for container context
148+
GEMINI_MD_PATH="__GEMINI_HOME__/GEMINI.md"
149+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository"
150+
if [[ "$REPO_REF" == issue-* ]]; then
151+
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
152+
ISSUE_URL=""
153+
if [[ "$REPO_URL" == https://github.com/* ]]; then
154+
ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
155+
if [[ -n "$ISSUE_REPO" ]]; then
156+
ISSUE_URL="https://github.com/$ISSUE_REPO/issues/$ISSUE_ID"
157+
fi
158+
fi
159+
if [[ -n "$ISSUE_URL" ]]; then
160+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID ($ISSUE_URL)"
161+
else
162+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID"
163+
fi
164+
elif [[ "$REPO_REF" == refs/pull/*/head ]]; then
165+
PR_ID="$(printf "%s" "$REPO_REF" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')"
166+
PR_URL=""
167+
if [[ "$REPO_URL" == https://github.com/* && -n "$PR_ID" ]]; then
168+
PR_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
169+
if [[ -n "$PR_REPO" ]]; then
170+
PR_URL="https://github.com/$PR_REPO/pull/$PR_ID"
171+
fi
172+
fi
173+
if [[ -n "$PR_ID" && -n "$PR_URL" ]]; then
174+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID ($PR_URL)"
175+
elif [[ -n "$PR_ID" ]]; then
176+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID"
177+
else
178+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: pull request ($REPO_REF)"
147179
fi
148180
fi
181+
182+
cat <<EOF > "$GEMINI_MD_PATH"
183+
<!-- docker-git-managed:gemini-md -->
184+
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
185+
Рабочая папка проекта (git clone): __TARGET_DIR__
186+
Доступные workspace пути: __TARGET_DIR__
187+
\$GEMINI_WORKSPACE_CONTEXT
188+
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
189+
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
190+
Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
191+
Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
192+
<!-- /docker-git-managed:gemini-md -->
149193
EOF
150-
chmod 0644 "$GEMINI_PROFILE" || true
194+
chown 1000:1000 "$GEMINI_MD_PATH" || true`
151195

152-
docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "${"$"}{GEMINI_AUTH_LABEL:-default}"
153-
docker_git_upsert_ssh_env "GEMINI_API_KEY" "${"$"}{GEMINI_API_KEY:-}"`
196+
const renderEntrypointGeminiNotice = (config: TemplateConfig): string =>
197+
entrypointGeminiNoticeTemplate
198+
.replaceAll("__GEMINI_HOME__", config.geminiHome)
199+
.replaceAll("__TARGET_DIR__", config.targetDir)
154200

155201
export const renderEntrypointGeminiConfig = (config: TemplateConfig): string =>
156202
[
157203
renderGeminiAuthConfig(config),
158-
renderGeminiCliInstall(),
159-
renderGeminiProfileSetup()
204+
renderGeminiPermissionSettingsConfig(config),
205+
renderGeminiMcpPlaywrightConfig(config),
206+
renderGeminiSudoConfig(config),
207+
renderGeminiProfileSetup(config),
208+
renderEntrypointGeminiNotice(config)
160209
].join("\n\n")

0 commit comments

Comments
 (0)