feat(config): add Jean worktree setup script#764
Conversation
Add jean.json and scripts/jean-setup.sh so new worktrees created via Jean automatically get local config files copied and npm dependencies installed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughIntroduces a new configuration file Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds Jean worktree automation to streamline development workflow by automatically configuring new git worktrees. Jean is a worktree management tool that can execute setup scripts when creating new worktrees, and this PR configures it to copy local configuration files and install dependencies automatically.
Changes:
- Add
jean.jsonconfiguration to trigger automatic setup when Jean creates worktrees - Add
scripts/jean-setup.shthat copies local config files from the main checkout and runs npm install - Enable isolated worktree development with independent configuration files
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| jean.json | Configures Jean to invoke the setup script using the JEAN_ROOT_PATH environment variable |
| scripts/jean-setup.sh | Bash script that copies local configuration files (.env, application-local.yml, etc.) from root and installs npm dependencies |
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | ||
| else | ||
| copy_if_exists() { | ||
| local src="$JEAN_ROOT_PATH/$1" | ||
| if [ -f "$src" ]; then | ||
| mkdir -p "$(dirname "$1")" | ||
| cp "$src" "$1" | ||
| echo " copied $1" | ||
| else | ||
| echo " skipped $1 (not found in root)" | ||
| fi | ||
| } | ||
|
|
||
| echo "Copying local config files..." | ||
| copy_if_exists "server/application-server/src/main/resources/application-local.yml" | ||
| copy_if_exists "server/application-server/src/test/resources/application-live-local.yml" | ||
| copy_if_exists "server/application-server/.env" | ||
| copy_if_exists "server/intelligence-service/.env" | ||
| copy_if_exists "server/webhook-ingest/.env" | ||
| copy_if_exists "docker/.env" | ||
| copy_if_exists ".claude/settings.local.json" |
There was a problem hiding this comment.
This script uses 2-space indentation, but all other bash scripts in the repository (codex-setup.sh, codex-maintenance.sh, local-postgres.sh, db-utils.sh) consistently use 4-space indentation. Update the indentation to use 4 spaces to match the established codebase convention.
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | |
| else | |
| copy_if_exists() { | |
| local src="$JEAN_ROOT_PATH/$1" | |
| if [ -f "$src" ]; then | |
| mkdir -p "$(dirname "$1")" | |
| cp "$src" "$1" | |
| echo " copied $1" | |
| else | |
| echo " skipped $1 (not found in root)" | |
| fi | |
| } | |
| echo "Copying local config files..." | |
| copy_if_exists "server/application-server/src/main/resources/application-local.yml" | |
| copy_if_exists "server/application-server/src/test/resources/application-live-local.yml" | |
| copy_if_exists "server/application-server/.env" | |
| copy_if_exists "server/intelligence-service/.env" | |
| copy_if_exists "server/webhook-ingest/.env" | |
| copy_if_exists "docker/.env" | |
| copy_if_exists ".claude/settings.local.json" | |
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | |
| else | |
| copy_if_exists() { | |
| local src="$JEAN_ROOT_PATH/$1" | |
| if [ -f "$src" ]; then | |
| mkdir -p "$(dirname "$1")" | |
| cp "$src" "$1" | |
| echo " copied $1" | |
| else | |
| echo " skipped $1 (not found in root)" | |
| fi | |
| } | |
| echo "Copying local config files..." | |
| copy_if_exists "server/application-server/src/main/resources/application-local.yml" | |
| copy_if_exists "server/application-server/src/test/resources/application-live-local.yml" | |
| copy_if_exists "server/application-server/.env" | |
| copy_if_exists "server/intelligence-service/.env" | |
| copy_if_exists "server/webhook-ingest/.env" | |
| copy_if_exists "docker/.env" | |
| copy_if_exists ".claude/settings.local.json" |
| if [ -z "${JEAN_ROOT_PATH:-}" ]; then | ||
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | ||
| else | ||
| copy_if_exists() { | ||
| local src="$JEAN_ROOT_PATH/$1" | ||
| if [ -f "$src" ]; then |
There was a problem hiding this comment.
The script uses single brackets [ ] for test conditions (line 9, 14), but all other bash scripts in the repository consistently use double brackets [[ ]] which is the bash-specific, more feature-rich test operator. Update to use [[ ]] for consistency with scripts/codex-setup.sh, scripts/codex-maintenance.sh, and scripts/local-postgres.sh.
| if [ -z "${JEAN_ROOT_PATH:-}" ]; then | |
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | |
| else | |
| copy_if_exists() { | |
| local src="$JEAN_ROOT_PATH/$1" | |
| if [ -f "$src" ]; then | |
| if [[ -z "${JEAN_ROOT_PATH:-}" ]]; then | |
| echo " JEAN_ROOT_PATH is not set — skipping config file copy." | |
| else | |
| copy_if_exists() { | |
| local src="$JEAN_ROOT_PATH/$1" | |
| if [[ -f "$src" ]]; then |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
scripts/jean-setup.sh (2)
12-21: Consider movingcopy_if_existsto the top level.Defining a function inside an
elseblock is unusual. While Bash scopes functions globally regardless of where they're defined, placing the function at the script's top level improves readability and makes the structure more conventional.♻️ Proposed refactor
#!/usr/bin/env bash set -euo pipefail echo "Setting up Jean worktree..." +copy_if_exists() { + local src="${JEAN_ROOT_PATH}/$1" + if [ -f "$src" ]; then + mkdir -p "$(dirname "$1")" + cp "$src" "$1" + echo " copied $1" + else + echo " skipped $1 (not found in root)" + fi +} + # JEAN_ROOT_PATH is set by Jean to the main repo checkout. # We copy local config files from there into this worktree so each # worktree can be modified independently. if [ -z "${JEAN_ROOT_PATH:-}" ]; then echo " JEAN_ROOT_PATH is not set — skipping config file copy." else - copy_if_exists() { - local src="$JEAN_ROOT_PATH/$1" - if [ -f "$src" ]; then - mkdir -p "$(dirname "$1")" - cp "$src" "$1" - echo " copied $1" - else - echo " skipped $1 (not found in root)" - fi - } - echo "Copying local config files..."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/jean-setup.sh` around lines 12 - 21, Move the copy_if_exists function out of the else block and define it at the script's top level so it is immediately visible and conventional; keep the same signature and behavior (local src="$JEAN_ROOT_PATH/$1", mkdir -p, cp, echo messages) and ensure JEAN_ROOT_PATH is already set before any calls to copy_if_exists are made; after relocating, update no other logic — just call copy_if_exists(...) where it was previously invoked.
12-21: Movecopy_if_existsto the top level, outside theelseblock.Defining a function inside a conditional branch is valid Bash but non-idiomatic. The function would be globally scoped anyway once that
elsebranch executes — lifting it to the script's top level makes the intent clearer and the structure easier to follow.♻️ Proposed refactor
#!/usr/bin/env bash set -euo pipefail echo "Setting up Jean worktree..." +copy_if_exists() { + local src="${JEAN_ROOT_PATH:-}/$1" + if [ -f "$src" ]; then + mkdir -p "$(dirname "$1")" + cp "$src" "$1" + echo " copied $1" + else + echo " skipped $1 (not found in root)" + fi +} + # JEAN_ROOT_PATH is set by Jean to the main repo checkout. # We copy local config files from there into this worktree so each # worktree can be modified independently. if [ -z "${JEAN_ROOT_PATH:-}" ]; then echo " JEAN_ROOT_PATH is not set — skipping config file copy." else - copy_if_exists() { - local src="$JEAN_ROOT_PATH/$1" - if [ -f "$src" ]; then - mkdir -p "$(dirname "$1")" - cp "$src" "$1" - echo " copied $1" - else - echo " skipped $1 (not found in root)" - fi - } - echo "Copying local config files..." copy_if_exists "server/application-server/src/main/resources/application-local.yml"Note: with the function at the top level,
srcuses${JEAN_ROOT_PATH:-}as a safe default; the[ -f "$src" ]check already handles the empty-prefix case gracefully (the path won't resolve to a real file).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/jean-setup.sh` around lines 12 - 21, Move the copy_if_exists function out of the conditional branch and define it at the top level of the script so it isn’t declared inside the else block; update its src calculation to use a safe default like "${JEAN_ROOT_PATH:-}/$1" and keep the existing checks (the [ -f "$src" ] test and mkdir/cp logic) intact, referencing the copy_if_exists function and JEAN_ROOT_PATH variable so callers later in the script can reliably invoke copy_if_exists regardless of which branch ran.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/jean-setup.sh`:
- Around line 33-34: Guard the npm install step so the script doesn't abort
under set -e when npm or package.json is missing: before the existing "npm
install" invocation check for the presence of package.json and that npm is on
PATH (same pattern used for config propagation), and only run the "npm install"
command when both checks pass; otherwise log a clear skip/warning and continue.
Ensure you update the block that currently contains the echo "Installing npm
dependencies..." / npm install to use these checks and a non-failing fallback so
partial setup isn't discarded.
- Line 30: Add a short comment above the copy_if_exists
".claude/settings.local.json" call explaining that this copies a developer-local
Claude settings file (non-committed, may contain credentials) for convenience;
then update the repository .gitignore to include either
".claude/settings.local.json" or the entire ".claude/" directory to prevent
accidental commits of credentials. Ensure the comment references copy_if_exists
and the filename ".claude/settings.local.json" so future readers understand
intent, and commit the .gitignore change alongside the script update.
- Line 30: Add an inline comment immediately above the call to copy_if_exists
for ".claude/settings.local.json" that explains this file may contain sensitive
credentials, notes that the repo’s existing "*.local" .gitignore pattern does
not match ".json" filenames (so this specific filename must be ignored elsewhere
or handled securely), states the assumption under which it's safe to copy
silently (e.g., that developers have configured a repo-level or global ignore or
secrets manager), and instructs maintainers to ensure .gitignore or other
controls explicitly exclude this file if that assumption is not met; reference
the copy_if_exists call and the literal ".claude/settings.local.json" when
adding the comment.
---
Nitpick comments:
In `@scripts/jean-setup.sh`:
- Around line 12-21: Move the copy_if_exists function out of the else block and
define it at the script's top level so it is immediately visible and
conventional; keep the same signature and behavior (local
src="$JEAN_ROOT_PATH/$1", mkdir -p, cp, echo messages) and ensure JEAN_ROOT_PATH
is already set before any calls to copy_if_exists are made; after relocating,
update no other logic — just call copy_if_exists(...) where it was previously
invoked.
- Around line 12-21: Move the copy_if_exists function out of the conditional
branch and define it at the top level of the script so it isn’t declared inside
the else block; update its src calculation to use a safe default like
"${JEAN_ROOT_PATH:-}/$1" and keep the existing checks (the [ -f "$src" ] test
and mkdir/cp logic) intact, referencing the copy_if_exists function and
JEAN_ROOT_PATH variable so callers later in the script can reliably invoke
copy_if_exists regardless of which branch ran.
| copy_if_exists "server/intelligence-service/.env" | ||
| copy_if_exists "server/webhook-ingest/.env" | ||
| copy_if_exists "docker/.env" | ||
| copy_if_exists ".claude/settings.local.json" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether .claude/settings.local.json is covered by any .gitignore in the repo
rg -n "settings\.local\.json" --glob ".gitignore" --glob "**/.gitignore"Repository: ls1intum/Hephaestus
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Also check for broader .claude directory patterns in .gitignore
rg -n "\.claude" --glob ".gitignore" --glob "**/.gitignore"Repository: ls1intum/Hephaestus
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Examine the context around line 30 in scripts/jean-setup.sh
cat -n scripts/jean-setup.sh | sed -n '20,40p'Repository: ls1intum/Hephaestus
Length of output: 729
🏁 Script executed:
#!/bin/bash
# Check if there's a .claude directory and settings.local.json file in the repo
find . -type d -name ".claude" 2>/dev/null
find . -type f -name "settings.local.json" 2>/dev/nullRepository: ls1intum/Hephaestus
Length of output: 72
Add documentation and ensure .claude/settings.local.json is in .gitignore.
This file is being copied to every new worktree without explanation or protection. .claude/settings.local.json is not currently covered by any .gitignore rule, creating a risk that developers could accidentally commit credentials if the file is staged in a worktree commit. Add a comment above line 30 explaining the intent, and add .claude/settings.local.json (or .claude/) to the project's .gitignore.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/jean-setup.sh` at line 30, Add a short comment above the
copy_if_exists ".claude/settings.local.json" call explaining that this copies a
developer-local Claude settings file (non-committed, may contain credentials)
for convenience; then update the repository .gitignore to include either
".claude/settings.local.json" or the entire ".claude/" directory to prevent
accidental commits of credentials. Ensure the comment references copy_if_exists
and the filename ".claude/settings.local.json" so future readers understand
intent, and commit the .gitignore change alongside the script update.
🧩 Analysis chain
🏁 Script executed:
# Check if .claude/settings.local.json is covered by any .gitignore
rg -n "settings\.local\.json" --glob "*.gitignore" --glob ".gitignore"Repository: ls1intum/Hephaestus
Length of output: 45
🏁 Script executed:
# Check for .gitignore files in the repo
fd -H ".gitignore" -x cat -n {}Repository: ls1intum/Hephaestus
Length of output: 9525
🏁 Script executed:
# Examine the jean-setup.sh script around line 30
cat -n scripts/jean-setup.sh | head -50Repository: ls1intum/Hephaestus
Length of output: 1431
Add an inline comment explaining the security implications of copying .claude/settings.local.json.
The file .claude/settings.local.json is not explicitly covered by any .gitignore pattern in the repository (the *.local pattern does not match filenames ending in .json). Since this file can contain sensitive credentials, it should have an inline comment explaining why it's safe to copy silently into worktrees, and the assumption that .gitignore coverage is in place elsewhere should be documented.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/jean-setup.sh` at line 30, Add an inline comment immediately above
the call to copy_if_exists for ".claude/settings.local.json" that explains this
file may contain sensitive credentials, notes that the repo’s existing "*.local"
.gitignore pattern does not match ".json" filenames (so this specific filename
must be ignored elsewhere or handled securely), states the assumption under
which it's safe to copy silently (e.g., that developers have configured a
repo-level or global ignore or secrets manager), and instructs maintainers to
ensure .gitignore or other controls explicitly exclude this file if that
assumption is not met; reference the copy_if_exists call and the literal
".claude/settings.local.json" when adding the comment.
| echo "Installing npm dependencies..." | ||
| npm install |
There was a problem hiding this comment.
Guard npm install against a missing package.json.
With set -e, a failed npm install (e.g., no package.json at the worktree root, or npm not on PATH) aborts the entire setup ungracefully. The rest of the script — file copying, config propagation — will have succeeded, so a hard failure here discards useful partial work. At minimum, skip gracefully when there's no manifest; alternatively mirror the same pattern used for config files.
🛡️ Proposed fix
-echo "Installing npm dependencies..."
-npm install
+if [ -f "package.json" ]; then
+ echo "Installing npm dependencies..."
+ npm install
+else
+ echo " skipped npm install (no package.json found)"
+fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| echo "Installing npm dependencies..." | |
| npm install | |
| if [ -f "package.json" ]; then | |
| echo "Installing npm dependencies..." | |
| npm install | |
| else | |
| echo " skipped npm install (no package.json found)" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/jean-setup.sh` around lines 33 - 34, Guard the npm install step so
the script doesn't abort under set -e when npm or package.json is missing:
before the existing "npm install" invocation check for the presence of
package.json and that npm is on PATH (same pattern used for config propagation),
and only run the "npm install" command when both checks pass; otherwise log a
clear skip/warning and continue. Ensure you update the block that currently
contains the echo "Installing npm dependencies..." / npm install to use these
checks and a non-failing fallback so partial setup isn't discarded.
📚 Documentation Preview
|
|
🎉 This PR is included in version 0.20.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Summary
jean.jsonso Jean automatically runs a setup script when creating worktreesscripts/jean-setup.shthat copies local config files (.env,application-local.yml, etc.) from the root checkout and runsnpm installTest plan
node_modules/exists andnpm lssucceeds in the worktree🤖 Generated with Claude Code
Summary by CodeRabbit