Skip to content
Back to skills

Worktrees

BSecurity

Manage .worktrees/ lifecycle: create worktree, list worktrees, remove worktree, clean worktrees, stale worktrees audit, isolate work, project clone. TRIGGER when: any git worktree operation, branch isolation needed, stale worktrees review, project clone under projects/ (e.g. "clone <owner>/<repo> to projects", "add <repo> to projects", "clone this repo into projects"), worktree cleanup. A leading-slash harness dir like "/worktrees" still means the repo-relative .worktrees/ — never a literal f...

  • 40 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 11, 2026
ai-agentsshellbashgitdocumentation

Security analysis

B85/100
  • highPerforms destructive filesystem operations

Pro scans all 2 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add mifunedev/openharness --skill worktrees --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Worktrees?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Worktrees
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/mifunedev-worktrees/badge)](https://www.skillsdirectory.com/skills/mifunedev-worktrees)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: worktrees
description: |
  Manage .worktrees/ lifecycle: create worktree, list worktrees, remove worktree,
  clean worktrees, stale worktrees audit, isolate work, project clone.
  TRIGGER when: any git worktree operation, branch isolation needed, stale worktrees
  review, project clone under projects/ (e.g. "clone <owner>/<repo> to projects",
  "add <repo> to projects", "clone this repo into projects"), worktree
  cleanup. A leading-slash harness dir like "/worktrees" still means the repo-relative
  .worktrees/ — never a literal filesystem-root path.
allowed-tools: Bash
---

# Worktrees

Manage `.worktrees/` and `projects/`. Full policy: `/git` § Worktrees.

A repository's worktrees live at that repository's own root, in `.worktrees/`.
The harness is the first instance of that rule; a project clone under `projects/`
follows it too, in `projects/<owner>/<repo>/.worktrees/`.

## CHANGE ROUTING — TRACK VS RESET

`~/harness` and `$PROJECTS_ROOT/<owner>/<repo>` are different Git
boundaries. The parent harness ignores the latter; a project clone's files are
never changes to the harness. Classify changes before committing, resetting, or
removing anything. A dirty checkout is not disposable merely because it lives
under `.worktrees/`.

### `~/harness` — harness control plane

**Track/preserve** deliberate, durable harness changes, including:

- `.agro/skills/`, `.agro/hooks/`, `.agro/scripts/`, `.devcontainer/`,
  `.github/`, docs, templates, and supported configuration
  defaults.
- `.worktrees/AGENTS.md`, `projects/AGENTS.md`, and other lifecycle
  documentation.
- Curated wiki entries and the wiki index when intentionally promoted. The wiki
  corpus is ignored by default; use `git add -f` only for reviewed entries.
  Never promote `corpus/raw/` snapshots by accident.
- A handoff only when it is deliberately shared, scrubbed of secrets, and meant
  to be durable in this harness checkout or its private operator fork.

**Reset/remove** only known local, generated, or abandoned state, such as:

- worktree contents, task progress, memory/log state, plan scratch,
  screenshots, build output, dependencies, local env/auth files, and caches
  covered by `.gitignore`;
- an explicit operator-local setting or a tracked edit that was reviewed and
  rejected.

Do not reset a tracked source/config/doc change just because it is unstaged, and
do not delete an untracked file before inspecting it. For root state, inspect
both index and worktree changes:

```bash
git -C ~/harness status --short --untracked-files=all
git -C ~/harness diff --name-status
git -C ~/harness diff --cached --name-status
```

Use `git restore <path>` only for identified files. Preview cleanup with
`git clean -nd` (or `git clean -ndX` for ignored files); route destructive
`reset`/`clean` operations through `.agro/scripts/git-maintenance.sh` as required
by `/git`.

### `$PROJECTS_ROOT/<owner>/<repo>` — independent project clone

These directories have their own `.git` and their own branch/remote policy.
Run status and diffs with `git -C "$PROJECT"`, never from `~/harness`.

**Track/preserve in the project repository** intentional source, tests,
migrations, documentation, CI, deployment, and project configuration changes.
A reviewed `.example.env` or environment-variable guide is project source;
real `.env*`, credentials, auth files, and local overrides are not. Commit
preserved work on a project branch and push it to that project's remote, not to
the harness remote.

**Reset/remove in the project repository** only generated output, dependency
and build caches, logs, local secret/config files, nested worktree state after
confirming no child worktree is active, or changes the owner explicitly
abandons. An active nested worktree is operational state: preserve it, do not
add its path to the parent clone, and inspect the child repository separately.
Do not blanket-reset source or docs because a clone is being cleaned.

Before removing or resetting a project clone:

```bash
PROJECT="$PROJECTS_ROOT/<owner>/<repo>"
git -C "$PROJECT" status --short --untracked-files=all
git -C "$PROJECT" diff --name-status
git -C "$PROJECT" ls-files --others --exclude-standard
git -C "$PROJECT" worktree list --porcelain
```

If status is non-empty, stop and classify each path. Preserve intentional work
by committing it on the project branch, or by making an explicit patch/backup;
discard only after the owner confirms. A project clone with dirty, unclassified
work must not be removed with `rm -rf`.

## DETECT BASE

Run first. Every create/remove op needs `$BASE` and `$WORKTREES_ROOT`.

`$WORKTREES_ROOT` is always `.worktrees/` inside **the repository you are standing
in** — run this from the harness root for harness branches, or from
`projects/<owner>/<repo>/` to cut a worktree of that project. `agro-path` resolves the
fixed root and only exists at the harness root, so the project case
falls through to the repository toplevel.

```bash
BASE=$(git show-ref --verify --quiet refs/heads/development && echo development || \
       git show-ref --verify --quiet refs/heads/main && echo main || echo master)
TOPLEVEL="$(git rev-parse --show-toplevel)"
if [ -x "$TOPLEVEL/.agro/scripts/agro-path" ]; then
  WORKTREES_ROOT="$(bash "$TOPLEVEL/.agro/scripts/agro-path" worktrees --no-create 2>/dev/null || printf '%s' "$TOPLEVEL/.worktrees")"
  PROJECTS_ROOT="$(bash "$TOPLEVEL/.agro/scripts/agro-path" projects --no-create 2>/dev/null || printf '%s' "$TOPLEVEL/projects")"
else
  WORKTREES_ROOT="$TOPLEVEL/.worktrees"
  PROJECTS_ROOT=""
fi
echo "$BASE"
echo "$WORKTREES_ROOT"
```

## CREATE — new branch

```bash
PREFIX=feat   # feat bug task audit skill agent
ISSUE=42
DESC=short-desc
BRANCH="$PREFIX/$ISSUE-$DESC"
mkdir -p "$WORKTREES_ROOT"
git worktree add -b "$BRANCH" "$WORKTREES_ROOT/$BRANCH" "$BASE"
```

## CREATE — existing branch

```bash
BRANCH=feat/42-short-desc
mkdir -p "$WORKTREES_ROOT"
git worktree add "$WORKTREES_ROOT/$BRANCH" "$BRANCH"
```

## LIST — all worktrees + age + PR status

```bash
git worktree list --porcelain | awk '/^worktree /{wt=$2} /^branch /{
  sub("refs/heads/",""); br=$2; print wt, br}' | while read -r path branch; do
  age=$(( ( $(date +%s) - $(git -C "$path" log -1 --format=%ct 2>/dev/null || echo $(date +%s)) ) / 86400 ))
  pr=$(gh pr list --head "$branch" --state open --json number,title \
       --jq '.[0] | if . then "#\(.number) \(.title)" else "no PR" end' 2>/dev/null)
  printf "%-50s  %3dd  %s\n" "$branch" "$age" "$pr"
done
```

## ISOLATE — in-flight work

Main checkout has loose files. Don't stash. Don't switch.

```bash
# 1. Cut worktree off base
BRANCH=feat/42-my-work
git worktree add -b "$BRANCH" "$WORKTREES_ROOT/$BRANCH" "$BASE"

# 2. Copy files in
cp path/to/file1 path/to/file2 "$WORKTREES_ROOT/$BRANCH/<destination>/"

# 3. Commit in worktree
cd "$WORKTREES_ROOT/$BRANCH"
git add . && git commit -m "feat: ..."
```

Before cleaning main checkout — byte-check every file first:

```bash
for f in path/to/file1 path/to/file2; do
  a=$(md5sum "$f" | awk '{print $1}')
  b=$(git show "$BRANCH:$f" | md5sum | awk '{print $1}')
  [ "$a" = "$b" ] && echo "same:  $f" || echo "DRIFT: $f"
done
```

All `same:`? Then clean:

```bash
git restore path/to/file1 path/to/file2
```

Any `DRIFT:`? Stop. File not committed right. Fix first.

## REMOVE — clean

```bash
BRANCH=feat/42-short-desc
git worktree remove "$WORKTREES_ROOT/$BRANCH"
git worktree prune
```

Corrupted (not in `git worktree list`):

```bash
rm -rf "$WORKTREES_ROOT/$BRANCH"
git worktree prune
```

### Forced removal under the cc-safety-net guard

`git worktree remove --force` (and `git branch -D`) are denied inline by cc-safety-net. In agent (hook-mediated) contexts route them through the file-invoked shim:

```bash
bash .agro/scripts/git-maintenance.sh worktree-remove "$WORKTREES_ROOT/$BRANCH"
bash .agro/scripts/git-maintenance.sh branch-delete "$BRANCH"
```

Scope: only **non-agent-mediated** invocations (raw scheduler/tmux shell scripts) bypass PreToolUse hooks. Agent-driven crons do **not** bypass them, so they must use the shim too. Plain `git worktree remove` (no `--force`) stays allowed.

## STALE AUDIT — review only, no auto-delete

List worktrees older than 30 days with no open PR. Surface. Don't remove.

```bash
git worktree list --porcelain | awk '/^worktree /{wt=$2} /^branch /{
  sub("refs/heads/",""); br=$2; print wt, br}' | while read -r path branch; do
  age=$(( ( $(date +%s) - $(git -C "$path" log -1 --format=%ct 2>/dev/null || echo $(date +%s)) ) / 86400 ))
  [ "$age" -lt 30 ] && continue
  pr=$(gh pr list --head "$branch" --state open --json number \
       --jq 'length' 2>/dev/null)
  [ "$pr" = "0" ] && printf "STALE %3dd  %s\n" "$age" "$branch"
done
```

Review each `STALE` line. Remove manually if safe (see REMOVE above).

## PROJECT CLONE

Independent repo — not a harness branch. Has its own `.git`.

```bash
# Clone
OWNER=ryaneggz
REPO=some-project
mkdir -p "$PROJECTS_ROOT/$OWNER"
git clone "https://github.com/$OWNER/$REPO.git" "$PROJECTS_ROOT/$OWNER/$REPO"

# Remove only after the CHANGE ROUTING preflight above.
PROJECT="$PROJECTS_ROOT/$OWNER/$REPO"
if ! git -C "$PROJECT" status --porcelain=v1 --untracked-files=all; then
  echo "Cannot inspect project clone; refusing removal: $PROJECT" >&2
  exit 1
fi
if [ -n "$(git -C "$PROJECT" status --porcelain=v1 --untracked-files=all)" ]; then
  echo "Dirty project clone; preserve or explicitly classify changes first: $PROJECT" >&2
  exit 1
fi
rm -rf "$PROJECT"
```

No `git worktree` for these. Plain `git clone` / `rm -rf`.

To cut a worktree **of** a project clone, re-run DETECT BASE from inside it —
`$WORKTREES_ROOT` then resolves to `$PROJECT/.worktrees` and every CREATE, REMOVE,
and STALE AUDIT block above applies unchanged.

Files in this skill

  • LICENSE1.1 KB
  • SKILL.md3.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…