Skip to content

Commit fb435b5

Browse files
committed
Merge branch 'main' into pr-387
2 parents 31bcdc6 + fbc50eb commit fb435b5

15 files changed

Lines changed: 562 additions & 136 deletions

File tree

packages/api/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @effect-template/api
22

3+
## 0.1.2
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`8a14af1`](https://github.com/ProverCoderAI/docker-git/commit/8a14af1dc1b2f1de881fff679edbc3117bc69b77)]:
8+
- @effect-template/lib@1.1.1
9+
310
## 0.1.1
411

512
### Patch Changes

packages/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@effect-template/api",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"private": true,
55
"description": "docker-git API controller",
66
"main": "dist/src/main.js",

packages/app/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @prover-coder-ai/docker-git
22

3+
## 1.1.55
4+
5+
### Patch Changes
6+
7+
- [#398](https://github.com/ProverCoderAI/docker-git/pull/398) [`8a14af1`](https://github.com/ProverCoderAI/docker-git/commit/8a14af1dc1b2f1de881fff679edbc3117bc69b77) Thanks [@skulidropek](https://github.com/skulidropek)! - Connect the generated project containers to the new multi-agent plan-to-git build, install Claude Code plan hooks, and route queued agent plans through explicit PR-aware sync.
8+
39
## 1.1.54
410

511
### Patch Changes

packages/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@prover-coder-ai/docker-git",
3-
"version": "1.1.54",
3+
"version": "1.1.55",
44
"description": "docker-git Bun and Gridland CLI plus browser frontend",
55
"main": "dist/src/docker-git/main.js",
66
"bin": {

packages/app/src/lib/core/templates-entrypoint/git-hooks.ts

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { renderEntrypointGitPostPushWrapperInstall } from "./git-post-push-wrapper.js"
2+
import {
3+
renderPlanToGitAgentHooksInstall,
4+
renderPlanToGitHookPaths,
5+
renderPlanToGitPostPushSync,
6+
renderPlanToGitSyncHelperInstall
7+
} from "./plan-to-git.js"
28
import { renderPostPushPrEnsure } from "./post-push-pr.js"
39

410
const entrypointGitHooksTemplate = String
511
.raw`# 3) Install global git hooks to protect main/master + managed AGENTS context
612
HOOKS_DIR="/opt/docker-git/hooks"
713
PRE_PUSH_HOOK="$HOOKS_DIR/pre-push"
814
POST_PUSH_ACTION="$HOOKS_DIR/post-push"
9-
PLAN_TO_GIT_CODEX_HOOK="$HOOKS_DIR/plan-to-git-codex-hook"
10-
CODEX_REQUIREMENTS_FILE="/etc/codex/requirements.toml"
15+
${renderPlanToGitHookPaths()}
1116
mkdir -p "$HOOKS_DIR"
1217
1318
cat <<'EOF' > "$PRE_PUSH_HOOK"
@@ -135,6 +140,8 @@ done
135140
EOF
136141
chmod 0755 "$PRE_PUSH_HOOK"
137142
143+
${renderPlanToGitSyncHelperInstall()}
144+
138145
cat <<'EOF' > "$POST_PUSH_ACTION"
139146
#!/usr/bin/env bash
140147
set -euo pipefail
@@ -148,17 +155,7 @@ cd "$REPO_ROOT"
148155
149156
${renderPostPushPrEnsure()}
150157
151-
# CHANGE: backfill Codex session plans before syncing the current branch PR.
152-
# WHY: live Codex hooks can be unavailable in already-running sessions; session logs are the durable fallback.
153-
# REF: issue-375
154-
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" != "1" ]; then
155-
if ! command -v plan-to-git >/dev/null 2>&1; then
156-
echo "[plan-to-git] Error: plan-to-git not found" >&2
157-
exit 1
158-
fi
159-
plan-to-git import-codex --no-sync
160-
plan-to-git sync
161-
fi
158+
${renderPlanToGitPostPushSync()}
162159
163160
# CHANGE: keep post-push backup logic in a reusable action script
164161
# WHY: git has no client-side post-push hook, so the global git wrapper
@@ -178,46 +175,7 @@ fi
178175
EOF
179176
chmod 0755 "$POST_PUSH_ACTION"
180177
181-
cat <<'EOF' > "$PLAN_TO_GIT_CODEX_HOOK"
182-
#!/usr/bin/env bash
183-
set -euo pipefail
184-
185-
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" = "1" ]; then
186-
exit 0
187-
fi
188-
189-
if ! command -v plan-to-git >/dev/null 2>&1; then
190-
echo "[plan-to-git] Error: plan-to-git not found" >&2
191-
exit 1
192-
fi
193-
194-
plan-to-git hook --source codex
195-
EOF
196-
chmod 0755 "$PLAN_TO_GIT_CODEX_HOOK"
197-
198-
mkdir -p "$(dirname "$CODEX_REQUIREMENTS_FILE")"
199-
cat <<'EOF' > "$CODEX_REQUIREMENTS_FILE"
200-
# docker-git managed Codex requirements
201-
202-
[features]
203-
hooks = true
204-
205-
[hooks]
206-
managed_dir = "/opt/docker-git/hooks"
207-
208-
[[hooks.UserPromptSubmit]]
209-
[[hooks.UserPromptSubmit.hooks]]
210-
type = "command"
211-
command = "/opt/docker-git/hooks/plan-to-git-codex-hook"
212-
statusMessage = "Capturing plan decision"
213-
214-
[[hooks.Stop]]
215-
[[hooks.Stop.hooks]]
216-
type = "command"
217-
command = "/opt/docker-git/hooks/plan-to-git-codex-hook"
218-
statusMessage = "Capturing agent plan"
219-
EOF
220-
chmod 0644 "$CODEX_REQUIREMENTS_FILE"
178+
${renderPlanToGitAgentHooksInstall()}
221179
222180
${renderEntrypointGitPostPushWrapperInstall()}
223181
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
const planToGitHookPathsTemplate = String.raw`PLAN_TO_GIT_SYNC_HELPER="$HOOKS_DIR/plan-to-git-sync"
2+
PLAN_TO_GIT_CODEX_HOOK="$HOOKS_DIR/plan-to-git-codex-hook"
3+
PLAN_TO_GIT_CLAUDE_HOOK="$HOOKS_DIR/plan-to-git-claude-hook"
4+
CODEX_REQUIREMENTS_FILE="/etc/codex/requirements.toml"
5+
CLAUDE_PLAN_TO_GIT_SETTINGS_FILE="$CLAUDE_CONFIG_DIR/settings.json"`
6+
7+
const planToGitSyncHelperInstallTemplate = String.raw`cat <<'EOF' > "$PLAN_TO_GIT_SYNC_HELPER"
8+
#!/usr/bin/env bash
9+
set -euo pipefail
10+
11+
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" = "1" ]; then
12+
exit 0
13+
fi
14+
15+
if ! command -v plan-to-git >/dev/null 2>&1; then
16+
echo "[plan-to-git] Error: plan-to-git not found" >&2
17+
exit 1
18+
fi
19+
20+
export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"
21+
22+
docker_git_plan_to_git_explicit_pr_supported() {
23+
plan-to-git sync --help 2>/dev/null | grep -q -- "--pr <PR>"
24+
}
25+
26+
docker_git_plan_to_git_resolve_pr_number() {
27+
local candidate=""
28+
local key=""
29+
for key in DOCKER_GIT_PR_NUMBER PR_NUMBER GITHUB_PR_NUMBER; do
30+
candidate="${"${"}!key:-}"
31+
if [[ "$candidate" =~ ^[0-9]+$ ]]; then
32+
printf "%s\n" "$candidate"
33+
return 0
34+
fi
35+
done
36+
37+
candidate="${"${"}REPO_REF:-}"
38+
if [[ "$candidate" =~ ^refs/pull/([0-9]+)/head$ ]]; then
39+
printf "%s\n" "${"${"}BASH_REMATCH[1]}"
40+
return 0
41+
fi
42+
if [[ "$candidate" =~ ^pull/([0-9]+)$ ]]; then
43+
printf "%s\n" "${"${"}BASH_REMATCH[1]}"
44+
return 0
45+
fi
46+
47+
if command -v gh >/dev/null 2>&1; then
48+
candidate="$(gh pr view --json number --jq .number 2>/dev/null || true)"
49+
if [[ "$candidate" =~ ^[0-9]+$ ]]; then
50+
printf "%s\n" "$candidate"
51+
return 0
52+
fi
53+
fi
54+
55+
return 0
56+
}
57+
58+
docker_git_plan_to_git_sync() {
59+
local pr_number=""
60+
pr_number="$(docker_git_plan_to_git_resolve_pr_number || true)"
61+
62+
if [[ -n "$pr_number" ]] && docker_git_plan_to_git_explicit_pr_supported; then
63+
echo "[plan-to-git] Syncing queued agent plans to PR #$pr_number"
64+
plan-to-git sync --pr "$pr_number"
65+
return 0
66+
fi
67+
68+
echo "[plan-to-git] Syncing queued agent plans via current branch discovery"
69+
plan-to-git sync
70+
}
71+
72+
docker_git_plan_to_git_sync
73+
EOF
74+
chmod 0755 "$PLAN_TO_GIT_SYNC_HELPER"`
75+
76+
const planToGitPostPushSyncTemplate = String
77+
.raw`# CHANGE: backfill agent session plans before syncing the current branch or explicit PR.
78+
# WHY: live agent hooks can be unavailable in already-running sessions; session logs are the durable fallback.
79+
# QUOTE(ТЗ): "что бы всё уходило на гитхаб автоматически"
80+
# REF: issue-397
81+
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" != "1" ]; then
82+
if ! command -v plan-to-git >/dev/null 2>&1; then
83+
echo "[plan-to-git] Error: plan-to-git not found" >&2
84+
exit 1
85+
fi
86+
plan-to-git import-codex --no-sync
87+
plan-to-git import-claude --no-sync
88+
PLAN_TO_GIT_SYNC_HELPER="${"${"}DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
89+
if [[ -x "$PLAN_TO_GIT_SYNC_HELPER" ]]; then
90+
"$PLAN_TO_GIT_SYNC_HELPER"
91+
else
92+
echo "[plan-to-git] Sync helper not found; falling back to current branch discovery" >&2
93+
plan-to-git sync
94+
fi
95+
fi`
96+
97+
const planToGitAgentHooksInstallTemplate = String.raw`cat <<'EOF' > "$PLAN_TO_GIT_CODEX_HOOK"
98+
#!/usr/bin/env bash
99+
set -euo pipefail
100+
101+
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" = "1" ]; then
102+
exit 0
103+
fi
104+
105+
if ! command -v plan-to-git >/dev/null 2>&1; then
106+
echo "[plan-to-git] Error: plan-to-git not found" >&2
107+
exit 1
108+
fi
109+
110+
export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"
111+
plan-to-git hook --source codex
112+
PLAN_TO_GIT_SYNC_HELPER="${"${"}DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
113+
"$PLAN_TO_GIT_SYNC_HELPER" >&2 || true
114+
EOF
115+
chmod 0755 "$PLAN_TO_GIT_CODEX_HOOK"
116+
117+
cat <<'EOF' > "$PLAN_TO_GIT_CLAUDE_HOOK"
118+
#!/usr/bin/env bash
119+
set -euo pipefail
120+
121+
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" = "1" ]; then
122+
exit 0
123+
fi
124+
125+
if ! command -v plan-to-git >/dev/null 2>&1; then
126+
echo "[plan-to-git] Error: plan-to-git not found" >&2
127+
exit 1
128+
fi
129+
130+
export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"
131+
plan-to-git hook --source claude
132+
PLAN_TO_GIT_SYNC_HELPER="${"${"}DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
133+
"$PLAN_TO_GIT_SYNC_HELPER" >&2 || true
134+
EOF
135+
chmod 0755 "$PLAN_TO_GIT_CLAUDE_HOOK"
136+
137+
mkdir -p "$(dirname "$CODEX_REQUIREMENTS_FILE")"
138+
cat <<'EOF' > "$CODEX_REQUIREMENTS_FILE"
139+
# docker-git managed Codex requirements
140+
141+
[features]
142+
hooks = true
143+
144+
[hooks]
145+
managed_dir = "/opt/docker-git/hooks"
146+
147+
[[hooks.UserPromptSubmit]]
148+
[[hooks.UserPromptSubmit.hooks]]
149+
type = "command"
150+
command = "/opt/docker-git/hooks/plan-to-git-codex-hook"
151+
statusMessage = "Capturing plan decision"
152+
153+
[[hooks.Stop]]
154+
[[hooks.Stop.hooks]]
155+
type = "command"
156+
command = "/opt/docker-git/hooks/plan-to-git-codex-hook"
157+
statusMessage = "Capturing agent plan"
158+
EOF
159+
chmod 0644 "$CODEX_REQUIREMENTS_FILE"
160+
161+
docker_git_install_claude_plan_to_git_hooks() {
162+
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" = "1" ]; then
163+
return 0
164+
fi
165+
166+
CLAUDE_PLAN_TO_GIT_SETTINGS_FILE="${"${"}CLAUDE_PLAN_TO_GIT_SETTINGS_FILE:-${"${"}CLAUDE_CONFIG_DIR:-/home/dev/.claude}/settings.json}"
167+
CLAUDE_PLAN_TO_GIT_SETTINGS_FILE="$CLAUDE_PLAN_TO_GIT_SETTINGS_FILE" PLAN_TO_GIT_CLAUDE_HOOK="$PLAN_TO_GIT_CLAUDE_HOOK" node - <<'NODE'
168+
const fs = require("node:fs")
169+
const path = require("node:path")
170+
171+
const settingsPath = process.env.CLAUDE_PLAN_TO_GIT_SETTINGS_FILE
172+
const hookCommand = process.env.PLAN_TO_GIT_CLAUDE_HOOK || "/opt/docker-git/hooks/plan-to-git-claude-hook"
173+
if (typeof settingsPath !== "string" || settingsPath.length === 0) {
174+
process.exit(0)
175+
}
176+
177+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
178+
179+
let settings = {}
180+
try {
181+
const parsed = JSON.parse(fs.readFileSync(settingsPath, "utf8"))
182+
settings = isRecord(parsed) ? parsed : {}
183+
} catch {
184+
settings = {}
185+
}
186+
187+
const currentHooks = isRecord(settings.hooks) ? settings.hooks : {}
188+
const nextHooks = { ...currentHooks }
189+
const managedHook = { type: "command", command: hookCommand }
190+
const ensureEventHook = (eventName) => {
191+
const currentEventHooks = Array.isArray(nextHooks[eventName]) ? nextHooks[eventName] : []
192+
const alreadyInstalled = currentEventHooks.some((entry) =>
193+
isRecord(entry) &&
194+
Array.isArray(entry.hooks) &&
195+
entry.hooks.some((hook) => isRecord(hook) && hook.type === "command" && hook.command === hookCommand)
196+
)
197+
nextHooks[eventName] = alreadyInstalled ? currentEventHooks : [...currentEventHooks, { hooks: [managedHook] }]
198+
}
199+
200+
ensureEventHook("UserPromptSubmit")
201+
ensureEventHook("Stop")
202+
203+
const nextSettings = { ...settings, hooks: nextHooks }
204+
if (JSON.stringify(settings) === JSON.stringify(nextSettings)) {
205+
process.exit(0)
206+
}
207+
208+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
209+
fs.writeFileSync(settingsPath, JSON.stringify(nextSettings, null, 2) + "\n", { mode: 0o600 })
210+
NODE
211+
chmod 0600 "$CLAUDE_PLAN_TO_GIT_SETTINGS_FILE" 2>/dev/null || true
212+
chown 1000:1000 "$CLAUDE_PLAN_TO_GIT_SETTINGS_FILE" 2>/dev/null || true
213+
}
214+
215+
docker_git_install_claude_plan_to_git_hooks`
216+
217+
export const renderPlanToGitHookPaths = (): string => planToGitHookPathsTemplate
218+
219+
export const renderPlanToGitSyncHelperInstall = (): string => planToGitSyncHelperInstallTemplate
220+
221+
export const renderPlanToGitPostPushSync = (): string => planToGitPostPushSyncTemplate
222+
223+
export const renderPlanToGitAgentHooksInstall = (): string => planToGitAgentHooksInstallTemplate

packages/app/src/lib/core/templates/dockerfile-prelude.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,24 @@ RUN cargo install --git https://github.com/ProverCoderAI/rust-browser-connection
8383
RUN printf "%s\\n" "ALL ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/zz-all \
8484
&& chmod 0440 /etc/sudoers.d/zz-all`
8585

86-
const planToGitRevision = "06fe8bdf1d2e48a1f5a0218a3bb7af19e63deb5e"
86+
const planToGitRevision = "f60fbe71131854be4c6c1d9fb79abafd2dd6949b"
8787

8888
// CHANGE: install plan-to-git in generated project containers.
89-
// WHY: issue #369 requires agent plans to be captured and uploaded to pull requests.
90-
// QUOTE(ТЗ): "Надо что бы у нас план загружался в PR"
91-
// REF: issue-369
92-
// SOURCE: https://github.com/ProverCoderAI/plan-to-git/tree/v0.19.0
89+
// WHY: issue #397 requires multi-agent plan capture, Claude Code hooks, temp-backed state, and explicit PR sync.
90+
// QUOTE(ТЗ): "подключение новое версии plan-to-git и настройки hooks для claude code и настройки что бы всё уходило на гитхаб автоматически"
91+
// REF: issue-397
92+
// SOURCE: https://github.com/ProverCoderAI/plan-to-git/tree/f60fbe71131854be4c6c1d9fb79abafd2dd6949b
9393
// FORMAT THEOREM: image_build_success -> executable(/usr/local/bin/plan-to-git)
9494
// PURITY: SHELL
9595
// EFFECT: Docker build downloads and installs a pinned Rust CLI from GitHub.
96-
// INVARIANT: plan-to-git is available on PATH before Codex hooks or git post-push actions run.
96+
// INVARIANT: plan-to-git is available on PATH with Claude hooks and sync --pr before agent hooks or git post-push actions run.
9797
// COMPLEXITY: O(network + cargo_build)
9898
const renderDockerfilePlanToGit = (): string =>
99-
`# Install plan-to-git for Codex plan capture and PR sync (issue #369)
99+
`# Install plan-to-git for multi-agent plan capture and explicit PR sync (issue #397)
100100
RUN cargo install --git https://github.com/ProverCoderAI/plan-to-git --rev ${planToGitRevision} --locked --bins --root /usr/local \
101-
&& /usr/local/bin/plan-to-git --help >/dev/null`
101+
&& /usr/local/bin/plan-to-git --help >/dev/null \
102+
&& /usr/local/bin/plan-to-git hook --help | grep -q -- "claude" \
103+
&& /usr/local/bin/plan-to-git sync --help | grep -q -- "--pr <PR>"`
102104

103105
/**
104106
* Renders the base image, package prelude, Rust toolchain, browser module, and plan sync CLI install.

0 commit comments

Comments
 (0)