Skip to content

Commit 6538e98

Browse files
committed
feat(shell): add issue workspace AGENTS context
1 parent 72f3e7a commit 6538e98

4 files changed

Lines changed: 133 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ When you clone GitHub issue or PR URLs, docker-git creates isolated project path
3535

3636
This lets you run multiple issues/PRs for the same repository in parallel without container/path collisions.
3737

38+
Agent context for issue workspaces:
39+
- Global `${CODEX_HOME}/AGENTS.md` includes workspace path + issue/PR context.
40+
- For `issue-*` workspaces, docker-git creates `${TARGET_DIR}/AGENTS.md` (if missing) with issue context and auto-adds it to `.git/info/exclude`.
41+
3842
## Projects Root Layout
3943

4044
The projects root is:

packages/docker-git/tests/core/templates.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,39 @@ describe("planFiles", () => {
9595
expect(browserDockerfile !== undefined && browserDockerfile._tag === "File").toBe(true)
9696
expect(browserScript !== undefined && browserScript._tag === "File").toBe(true)
9797
}))
98+
99+
it.effect("embeds issue workspace AGENTS context in entrypoint", () =>
100+
Effect.sync(() => {
101+
const config: TemplateConfig = {
102+
containerName: "dg-repo-issue-5",
103+
serviceName: "dg-repo-issue-5",
104+
sshUser: "dev",
105+
sshPort: 2222,
106+
repoUrl: "https://github.com/org/repo.git",
107+
repoRef: "issue-5",
108+
targetDir: "/home/dev/org/repo/issue-5",
109+
volumeName: "dg-repo-issue-5-home",
110+
authorizedKeysPath: "./authorized_keys",
111+
envGlobalPath: "./.orch/env/global.env",
112+
envProjectPath: "./.orch/env/project.env",
113+
codexAuthPath: "./.orch/auth/codex",
114+
codexSharedAuthPath: "../../.orch/auth/codex",
115+
codexHome: "/home/dev/.codex",
116+
enableMcpPlaywright: false,
117+
pnpmVersion: "10.27.0"
118+
}
119+
120+
const specs = planFiles(config)
121+
const entrypointSpec = specs.find(
122+
(spec) => spec._tag === "File" && spec.relativePath === "entrypoint.sh"
123+
)
124+
expect(entrypointSpec !== undefined && entrypointSpec._tag === "File").toBe(true)
125+
if (entrypointSpec && entrypointSpec._tag === "File") {
126+
expect(entrypointSpec.contents).toContain("Доступные workspace пути:")
127+
expect(entrypointSpec.contents).toContain("Контекст workspace:")
128+
expect(entrypointSpec.contents).toContain("Issue AGENTS.md:")
129+
expect(entrypointSpec.contents).toContain("ISSUE_AGENTS_PATH=\"$TARGET_DIR/AGENTS.md\"")
130+
expect(entrypointSpec.contents).toContain("grep -qx \"AGENTS.md\" \"$EXCLUDE_PATH\"")
131+
}
132+
}))
98133
})

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,42 @@ export const renderEntrypointAgentsNotice = (config: TemplateConfig): string =>
140140
AGENTS_PATH="${config.codexHome}/AGENTS.md"
141141
LEGACY_AGENTS_PATH="/home/${config.sshUser}/AGENTS.md"
142142
PROJECT_LINE="Рабочая папка проекта (git clone): ${config.targetDir}"
143+
WORKSPACES_LINE="Доступные workspace пути: ${config.targetDir}"
144+
WORKSPACE_INFO_LINE="Контекст workspace: repository"
145+
FOCUS_LINE="Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: ${config.targetDir}"
146+
ISSUE_AGENTS_HINT_LINE="Issue AGENTS.md: n/a"
143147
INTERNET_LINE="Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе."
148+
if [[ "$REPO_REF" == issue-* ]]; then
149+
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
150+
ISSUE_URL=""
151+
if [[ "$REPO_URL" == https://github.com/* ]]; then
152+
ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#\.git$##; s#/*$##')"
153+
if [[ -n "$ISSUE_REPO" ]]; then
154+
ISSUE_URL="https://github.com/\${ISSUE_REPO}/issues/\${ISSUE_ID}"
155+
fi
156+
fi
157+
if [[ -n "$ISSUE_URL" ]]; then
158+
WORKSPACE_INFO_LINE="Контекст workspace: issue #\${ISSUE_ID} (\${ISSUE_URL})"
159+
else
160+
WORKSPACE_INFO_LINE="Контекст workspace: issue #\${ISSUE_ID}"
161+
fi
162+
ISSUE_AGENTS_HINT_LINE="Issue AGENTS.md: ${config.targetDir}/AGENTS.md"
163+
elif [[ "$REPO_REF" == refs/pull/*/head ]]; then
164+
PR_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^refs/pull/([0-9]+)/head$#\1#')"
165+
if [[ -n "$PR_ID" ]]; then
166+
WORKSPACE_INFO_LINE="Контекст workspace: PR #\${PR_ID}"
167+
else
168+
WORKSPACE_INFO_LINE="Контекст workspace: pull request (\${REPO_REF})"
169+
fi
170+
fi
144171
if [[ ! -f "$AGENTS_PATH" ]]; then
145172
cat <<'AGENTS_EOF' > "$AGENTS_PATH"
146173
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
147174
Рабочая папка проекта (git clone): ${config.targetDir}
175+
Доступные workspace пути: ${config.targetDir}
176+
Контекст workspace: repository
177+
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: ${config.targetDir}
178+
Issue AGENTS.md: n/a
148179
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
149180
Если ты видишь файлы AGENTS.md внутри проекта, ты обязан их читать и соблюдать инструкции.
150181
AGENTS_EOF
@@ -156,6 +187,26 @@ if [[ -f "$AGENTS_PATH" ]]; then
156187
else
157188
printf "%s\n" "$PROJECT_LINE" >> "$AGENTS_PATH"
158189
fi
190+
if grep -q "^Доступные workspace пути:" "$AGENTS_PATH"; then
191+
sed -i "s|^Доступные workspace пути:.*$|$WORKSPACES_LINE|" "$AGENTS_PATH"
192+
else
193+
printf "%s\n" "$WORKSPACES_LINE" >> "$AGENTS_PATH"
194+
fi
195+
if grep -q "^Контекст workspace:" "$AGENTS_PATH"; then
196+
sed -i "s|^Контекст workspace:.*$|$WORKSPACE_INFO_LINE|" "$AGENTS_PATH"
197+
else
198+
printf "%s\n" "$WORKSPACE_INFO_LINE" >> "$AGENTS_PATH"
199+
fi
200+
if grep -q "^Фокус задачи:" "$AGENTS_PATH"; then
201+
sed -i "s|^Фокус задачи:.*$|$FOCUS_LINE|" "$AGENTS_PATH"
202+
else
203+
printf "%s\n" "$FOCUS_LINE" >> "$AGENTS_PATH"
204+
fi
205+
if grep -q "^Issue AGENTS.md:" "$AGENTS_PATH"; then
206+
sed -i "s|^Issue AGENTS.md:.*$|$ISSUE_AGENTS_HINT_LINE|" "$AGENTS_PATH"
207+
else
208+
printf "%s\n" "$ISSUE_AGENTS_HINT_LINE" >> "$AGENTS_PATH"
209+
fi
159210
if grep -q "^Доступ к интернету:" "$AGENTS_PATH"; then
160211
sed -i "s|^Доступ к интернету:.*$|$INTERNET_LINE|" "$AGENTS_PATH"
161212
else

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,50 @@ const renderCloneBodyRef = (config: TemplateConfig): string =>
9090
fi
9191
fi`
9292

93+
const renderIssueWorkspaceAgents = (): string =>
94+
`if [[ "$CLONE_OK" -eq 1 && "$REPO_REF" == issue-* && -d "$TARGET_DIR/.git" ]]; then
95+
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
96+
ISSUE_URL=""
97+
if [[ "$REPO_URL" == https://github.com/* ]]; then
98+
ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#\.git$##; s#/*$##')"
99+
if [[ -n "$ISSUE_REPO" ]]; then
100+
ISSUE_URL="https://github.com/\${ISSUE_REPO}/issues/\${ISSUE_ID}"
101+
fi
102+
fi
103+
104+
ISSUE_AGENTS_PATH="$TARGET_DIR/AGENTS.md"
105+
if [[ ! -e "$ISSUE_AGENTS_PATH" ]]; then
106+
cat <<EOF > "$ISSUE_AGENTS_PATH"
107+
# docker-git issue workspace
108+
Issue workspace: #\${ISSUE_ID}
109+
Issue URL: \${ISSUE_URL:-n/a}
110+
Workspace path: $TARGET_DIR
111+
112+
Работай только над этим issue, если пользователь не попросил другое.
113+
Если нужен первоисточник требований, открой Issue URL.
114+
EOF
115+
chown 1000:1000 "$ISSUE_AGENTS_PATH" || true
116+
fi
117+
118+
EXCLUDE_PATH="$TARGET_DIR/.git/info/exclude"
119+
if [[ -f "$ISSUE_AGENTS_PATH" ]]; then
120+
touch "$EXCLUDE_PATH"
121+
if ! grep -qx "AGENTS.md" "$EXCLUDE_PATH"; then
122+
printf "%s\n" "AGENTS.md" >> "$EXCLUDE_PATH"
123+
fi
124+
fi
125+
fi`
126+
93127
const renderCloneBody = (config: TemplateConfig): string =>
94-
[renderCloneBodyStart(config), renderCloneBodyRef(config), "", renderCloneRemotes(config), "fi"].join("\n")
128+
[
129+
renderCloneBodyStart(config),
130+
renderCloneBodyRef(config),
131+
"",
132+
renderCloneRemotes(config),
133+
"",
134+
renderIssueWorkspaceAgents(),
135+
"fi"
136+
].join("\n")
95137

96138
const renderCloneFinalize = (): string =>
97139
`if [[ "$CLONE_OK" -eq 1 ]]; then

0 commit comments

Comments
 (0)