diff --git a/plugins/source-control/.claude-plugin/plugin.json b/plugins/source-control/.claude-plugin/plugin.json index d13b24bad9..bb8cd9ef08 100644 --- a/plugins/source-control/.claude-plugin/plugin.json +++ b/plugins/source-control/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "source-control", - "version": "0.53.17", + "version": "0.53.18", "description": "Git and GitHub delivery workflow: /commit (Conventional Commits + Co-authored-by trailer via safe heredoc mechanics), /pull-request (prep, create, CI monitoring, review-comment triage, merge, CI-log fetch), /babysit-prs (self-pacing fleet loop \u2014 safe by default; opt-in worker/autopilot tiers add gate-checked merge and thread resolution behind a deterministic Python engine), /babysit-loop (the loop-lane merge lane: a standing or drain loop that invokes babysit-prs per cycle, configured through repo-scoped babysit_loop_* keys on the layered source-control.md seam, with merge authority human-only until the target repo's tracked config adopts the lane, a gate-proven C2-mechanical baseline once adopted, and standing merge-rung raises binding from the team-tracked layer only \u2014 with one named exception, where an invocation line explicitly typing both the autopilot tier keyword and the dedicated raise argument --merge c3-this-run widens that single invocation's merge authority up to C3 behind a fresh independent frontier-tier resolver, while C4-structural and C5-untrusted-provenance stay unconditionally human-merge), /worktree (create, status, cleanup, audit for parallel-session isolation), /setup (check the effective commit-subject / PR-title convention merged across its config layers and the babysit-prs config, or apply \u2014 interview the repo and write the convention config to a chosen layer), and /resolve-conflicts (intent-first merge/rebase conflict resolution with a semantic-conflict sweep \u2014 never --abort). The commit-subject / PR-title convention is configurable via a source-control.md config written by a re-runnable setup skill, layered across a ~/.claude user-global file, the tracked team file, and a gitignored .claude/source-control.local.md personal overlay merged per key; Conventional Commits is the default when no convention is declared.", "author": { "name": "Melodic Software", diff --git a/plugins/source-control/CHANGELOG.md b/plugins/source-control/CHANGELOG.md index f3cd180dad..c7afa44f5f 100644 --- a/plugins/source-control/CHANGELOG.md +++ b/plugins/source-control/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to the `source-control` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.53.18] + +### Fixed + +- **`worktree-create` copies `.claude/settings.local.json` from the main checkout root, not + `.git/.claude/` (#2119).** `git rev-parse --git-common-dir` points at metadata, so the carry-in + step now resolves the sibling working-tree path and copies untracked local settings into new + worktrees. + ## [0.53.17] ### Fixed diff --git a/plugins/source-control/scripts/worktree-create.sh b/plugins/source-control/scripts/worktree-create.sh index e51ed4e651..5168e61138 100755 --- a/plugins/source-control/scripts/worktree-create.sh +++ b/plugins/source-control/scripts/worktree-create.sh @@ -714,6 +714,26 @@ if [[ -f "$include_file" ]]; then fi fi +# Carry `.claude/settings.local.json` from the main checkout's git common dir +# into the new worktree. Claude Code documents worktree resolution for settings, +# but env overrides such as `DISK_HYGIENE_GUARD_WATCHDOG_SECONDS` only load when +# the file is present locally — worktree sessions otherwise inherit the hook +# defaults (#2119). +common_dir="$(git -C "$toplevel" rev-parse --git-common-dir)" +if [[ "$common_dir" != /* ]]; then + common_dir="$toplevel/$common_dir" +fi +main_worktree="$(dirname "$common_dir")" +settings_src="$main_worktree/.claude/settings.local.json" +if [[ -f "$settings_src" ]]; then + settings_dest="$worktree_path/.claude/settings.local.json" + if mkdir -p "$(dirname "$settings_dest")" && cp -p "$settings_src" "$settings_dest"; then + printf '%s: copied settings.local.json into the worktree\n' "$PROG" >&2 + else + printf '%s: warning: failed to copy settings.local.json into the worktree\n' "$PROG" >&2 + fi +fi + printf '%s: created worktree on branch %q (base %s)\n' "$PROG" "$name" "$base_ref" >&2 printf '%s\n' "$worktree_path" if ((lock_failed)); then diff --git a/plugins/source-control/scripts/worktree-create.test.sh b/plugins/source-control/scripts/worktree-create.test.sh index 7c9fe66e3c..189e4ea5a8 100755 --- a/plugins/source-control/scripts/worktree-create.test.sh +++ b/plugins/source-control/scripts/worktree-create.test.sh @@ -444,6 +444,14 @@ assert_file_exists "worktreeinclude: matched+ignored env copied" "$out/secrets.e assert_file_absent "worktreeinclude: gitignored-but-unmatched skipped" "$out/.work/task1/OTHER.md" assert_file_absent "worktreeinclude: unignored file never copied" "$out/loose.txt" +# --- Case: settings.local.json copied from the git common dir (#2119) --- +repo=$(mkrepo --origin "git@github.com:acme/widget.git") +mkdir -p "$repo/.claude" +printf '{"env":{"DISK_HYGIENE_GUARD_WATCHDOG_SECONDS":"50"}}\n' > "$repo/.claude/settings.local.json" +root="$TEST_TMPDIR/wtroot-settings" +out=$(bash "$HELPER" --name feat/settings --root "$root" --repo-dir "$repo" 2>/dev/null) +assert_file_exists "settings.local.json copied into the worktree" "$out/.claude/settings.local.json" + # --- Case: a value-taking flag as the last token errors, does not hang (exit 2) --- # Regression guard: a failed `shift 2` on a lone positional once spun forever. for flag in --name --root --base-ref --repo-dir; do