Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/source-control/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "source-control",
"version": "0.55.33",
"version": "0.55.34",
"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",
Expand Down
15 changes: 15 additions & 0 deletions plugins/source-control/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
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.55.34]

### Fixed

- **The worktree `create` snippet no longer emits a bare MSYS temp path on Windows.** The
`mktemp -d` step in `skills/worktree/context/create.md` printed the POSIX literal
`/tmp/tmp.XXXXXXXXXX`, which the native `Write` tool resolves against the current drive —
creating a phantom `<drive>:\tmp\...` while the real directory sits in `%TEMP%` (the silent
drive-root emit class the marketplace's windows-path-emit convention owns). The snippet now
converts at the boundary with `cygpath -m -l` (mixed form works for both the `Write` tool and
the later Bash consumers; `-l` expands an 8.3 short name), fails loud rather than falling back
to the unconverted literal, and passes through unchanged on non-Windows hosts. The
load-bearing-details list documents the conversion — including why `mktemp -d -p "$TEMP"` is
rejected — so it is not reverted as noise.

## [0.55.33]

### Fixed
Expand Down
9 changes: 7 additions & 2 deletions plugins/source-control/skills/worktree/context/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ Two steps — the helper creates and places the worktree; `EnterWorktree(path:)`
Four steps:

```bash
root_dir="$(mktemp -d)"; printf '%s\n' "$root_dir"
root_dir="$(mktemp -d)"
Comment thread
kyle-sexton marked this conversation as resolved.
case "${OSTYPE:-}" in
msys* | cygwin* | win32) root_dir="$(cygpath -m -l -- "$root_dir")" || exit 2 ;;
esac
printf '%s\n' "$root_dir"
```

`Write(file_path: "<printed root_dir>/worktree-root", content: "${user_config.worktree_root}")` — the substituted value is the entire `content`, written byte-exact with nothing appended (no trailing newline).
Expand All @@ -101,9 +105,10 @@ Two steps — the helper creates and places the worktree; `EnterWorktree(path:)`
exit "$status"
```

Two details in that last block are load-bearing:
Three details in those blocks are load-bearing:

- **`mktemp -d`, not `mktemp`** — `Write` refuses to overwrite a file it has not read, so the directory must exist and the file inside it must not.
- **The `cygpath -m -l` conversion on Windows** — the printed path crosses the Git Bash → native boundary: it becomes a `Write` tool `file_path`, and node's Win32 side resolves an MSYS literal like `/tmp/tmp.XXX` against the **current drive**, silently creating a phantom `<drive>:\tmp\...` while the real directory sits in `%TEMP%` ([the windows-path-emit convention](../../../../../docs/conventions/windows-path-emit/README.md), Rules 3–4). Mixed form (`-m`) is correct for **both** consumers — the `Write` tool and the later Bash block — so one converted value round-trips everywhere; `-l` expands an 8.3 short name (`KYLESE~1`) whose `~` misbehaves downstream. The `|| exit 2` is the fail-loud posture: never fall back to the unconverted literal, because the unconverted literal is exactly what writes to the wrong place. On non-Windows the `case` passes the path through unchanged. Do **not** replace this with `mktemp -d -p "$TEMP"`: `mktemp -p` is a flagged GNU/BSD-divergence token in the portability gate, and it yields mixed separators anyway.
- **`status=$?` before the cleanup, `exit "$status"` after** — `rm` almost always succeeds, so leaving it last would make the whole invocation report 0 and hide a helper refusal (exit 3) behind a green result, which step 2's "on a non-zero exit, STOP" would then never see.

The helper prints the created worktree path as its **sole stdout line**; capture it. Resolution is most-specific-first: `melodic.worktreeroot` (if set on the target repository) outranks the plugin option in `--fallback-root-file`. When `worktree_root` is unset, Claude leaves the literal `${user_config.worktree_root}` token — `Write` puts that token in the file verbatim, the helper reads it as "unconfigured", and the root resolves from the data-root file instead (`<data-dir>/worktrees`, announced on stderr, exit 0) unless the git config key already supplied one. Only when no rung yields a usable root does it refuse. A value carrying a newline byte anywhere — including a trailing one — is rejected loudly by the helper (exit 2); a path with a newline in it is malformed configuration, not a root to silently trim.
Expand Down