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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [v0.6.5] - 2026-05-21

### Fixes

- `repo workspace` now creates clones under `~/.fledge/workspaces/<owner>-<name>-XXXXXX/` instead of `/tmp/fledge-gh-XXXXXX/`. macOS auto-prunes `/tmp` and well-meaning disk-cleanup sweeps wipe it, both of which were destroying agent workspaces mid-task. The new location is stable across reboots and shows up alongside the rest of fledge state.
- `repo workspace-clean` and `repo workspace-push` still accept the legacy `/tmp/fledge-gh-*` prefix so any in-flight workspaces from v0.6.4 and earlier can be pushed or cleaned without manual intervention.
- Override the workspace root with `FLEDGE_WORKSPACES_DIR` for tests / CI / custom layouts.

## [v0.6.4] - 2026-05-21

### Features
Expand Down
63 changes: 43 additions & 20 deletions bin/fledge-github-repo
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,17 @@ is a directory — same command, agent can browse and read with one tool.
`clone` wraps `gh repo clone OWNER/NAME [DIR]` directly.

`workspace` is the higher-level "give me a sandbox" command for
agents: creates a fresh `/tmp/fledge-gh-<owner>-<name>-XXXXXX/<name>`
agents: creates a fresh `~/.fledge/workspaces/<owner>-<name>-XXXXXX/<name>`
directory, clones the repo there (optionally shallow + at a specific
ref), and prints the absolute path on stdout. Cleanup via
`workspace-clean <DIR>` which `rm -rf`s after sanity-checking the
path is under `/tmp/fledge-gh-`.
path is under the workspace root.

Override the workspace root with `FLEDGE_WORKSPACES_DIR` if you want
a different location (tests, CI, custom layouts). Workspaces from
the legacy `/tmp/fledge-gh-*` location (v0.6.4 and earlier) still
clean up correctly so in-flight directories aren't orphaned by the
upgrade.

EXAMPLES:
fledge github repo
Expand Down Expand Up @@ -182,8 +188,8 @@ if [ "$ACTION" = "workspace" ]; then
# Sanitize OWNER/NAME for use in a path: replace anything that isn't
# alnum/-/./_ with `_`. That keeps the resulting dir name human-
# readable (so the agent's logs make sense) and avoids the very real
# risk of an attacker-controlled repo name escaping the /tmp prefix
# via something cute like `../../etc/passwd`.
# risk of an attacker-controlled repo name escaping the workspace
# root via something cute like `../../etc/passwd`.
OWNER_NAME_SAFE="$(printf '%s' "$REPO" | tr '/' '-' | tr -c 'A-Za-z0-9._-' '_')"

# The plain repo name (post-slash) becomes the inner clone dir, so
Expand All @@ -192,10 +198,18 @@ if [ "$ACTION" = "workspace" ]; then
NAME_ONLY="${REPO#*/}"
NAME_ONLY_SAFE="$(printf '%s' "$NAME_ONLY" | tr -c 'A-Za-z0-9._-' '_')"

# Workspaces live under $FLEDGE_WORKSPACES_DIR (default
# ~/.fledge/workspaces) so they survive macOS /tmp cleanups, OS
# reboots, and well-meaning `rm -rf /tmp/...` sweeps. Plus they
# show up in disk-audit reports alongside the rest of fledge state
# instead of hiding in /tmp. The env var override is for tests.
WORKSPACE_ROOT="${FLEDGE_WORKSPACES_DIR:-$HOME/.fledge/workspaces}"
mkdir -p "$WORKSPACE_ROOT"

# mktemp -d gives us a unique parent. The clone target is one level
# deeper so cleanup of the parent removes both the dir and any junk
# gh / git leave behind.
PARENT="$(mktemp -d "/tmp/fledge-gh-${OWNER_NAME_SAFE}-XXXXXX")"
PARENT="$(mktemp -d "${WORKSPACE_ROOT}/${OWNER_NAME_SAFE}-XXXXXX")"
TARGET="${PARENT}/${NAME_ONLY_SAFE}"

# Build the gh repo clone arg list. Everything after `--` is passed
Expand Down Expand Up @@ -231,16 +245,19 @@ fi

if [ "$ACTION" = "workspace-push" ]; then
if [ -z "$CLONE_DIR" ]; then
echo "fledge github repo workspace-push: missing DIR. Try: fledge github repo workspace-push /tmp/fledge-gh-XXX -b feat/fix -m \"msg\"" >&2
echo "fledge github repo workspace-push: missing DIR. Try: fledge github repo workspace-push <workspace-path> -b feat/fix -m \"msg\"" >&2
exit 64
fi
# Same safety guard as workspace-clean: only operate on dirs we
# ourselves created, so an agent reasoning bug can't shove arbitrary
# local changes upstream via this command.
# Safety guard: only operate on dirs we ourselves created. Accept
# both the new ~/.fledge/workspaces/ location (v0.6.5+) AND the
# legacy /tmp/fledge-gh-* prefix so in-flight workspaces from
# older plugin versions still work.
WORKSPACE_ROOT="${FLEDGE_WORKSPACES_DIR:-$HOME/.fledge/workspaces}"
case "$CLONE_DIR" in
"$WORKSPACE_ROOT"/*) ;;
/tmp/fledge-gh-*) ;;
*)
echo "fledge github repo workspace-push: refusing to push from '$CLONE_DIR' (must be a /tmp/fledge-gh-* workspace)" >&2
echo "fledge github repo workspace-push: refusing to push from '$CLONE_DIR' (must be a workspace created by 'repo workspace')" >&2
exit 64
;;
esac
Expand Down Expand Up @@ -309,17 +326,22 @@ fi

if [ "$ACTION" = "workspace-clean" ]; then
if [ -z "$CLONE_DIR" ]; then
echo "fledge github repo workspace-clean: missing DIR. Try: fledge github repo workspace-clean /tmp/fledge-gh-XXXX" >&2
echo "fledge github repo workspace-clean: missing DIR. Try: fledge github repo workspace-clean <workspace-path>" >&2
exit 64
fi
# Safety: only delete dirs that look like fledge-gh workspaces. A
# bug in the agent's reasoning shouldn't be able to wipe arbitrary
# paths via this tool. The prefix check is intentionally strict —
# symlinks and `..` segments fail it cleanly.
# Safety: only delete dirs that look like workspaces we ourselves
# created. Accept the v0.6.5+ ~/.fledge/workspaces/ location AND
# the legacy /tmp/fledge-gh-* prefix so this tool can clean up
# in-flight workspaces from older plugin versions. A bug in the
# agent's reasoning shouldn't be able to wipe arbitrary paths via
# this tool — the prefix check is intentionally strict, and
# symlinks / `..` segments fail it cleanly.
WORKSPACE_ROOT="${FLEDGE_WORKSPACES_DIR:-$HOME/.fledge/workspaces}"
case "$CLONE_DIR" in
"$WORKSPACE_ROOT"/*) ;;
/tmp/fledge-gh-*) ;;
*)
echo "fledge github repo workspace-clean: refusing to remove '$CLONE_DIR' (must start with /tmp/fledge-gh-)" >&2
echo "fledge github repo workspace-clean: refusing to remove '$CLONE_DIR' (must be a workspace created by 'repo workspace')" >&2
exit 64
;;
esac
Expand All @@ -328,12 +350,13 @@ if [ "$ACTION" = "workspace-clean" ]; then
exit 1
fi
# If the caller passed the inner clone path, we still want to remove
# the parent so we don't leave an empty fledge-gh-* shell behind.
# Walk up only when CLONE_DIR's parent itself matches our prefix.
# the parent so we don't leave an empty shell behind. Walk up only
# when CLONE_DIR's parent itself matches one of our workspace roots.
PARENT_OF="$(dirname "$CLONE_DIR")"
case "$PARENT_OF" in
/tmp/fledge-gh-*) rm -rf -- "$PARENT_OF";;
*) rm -rf -- "$CLONE_DIR";;
"$WORKSPACE_ROOT"/*) rm -rf -- "$PARENT_OF";;
/tmp/fledge-gh-*) rm -rf -- "$PARENT_OF";;
*) rm -rf -- "$CLONE_DIR";;
esac
exit 0
fi
Expand Down
2 changes: 1 addition & 1 deletion plugin.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[plugin]
name = "fledge-plugin-github"
version = "0.6.4"
version = "0.6.5"
description = "GitHub commands for fledge — list/view/create/comment/review/merge PRs, list/view/create/comment/close issues, read repo files, view CI checks, and poll for daemon events via the gh CLI"
author = "0xLeif"
license = "MIT"
Expand Down
Loading