Skip to content

Commit 80ab287

Browse files
authored
v0.6.5: workspaces under ~/.fledge/workspaces (durable, not /tmp) (#11)
* fix(v0.6.5): workspaces under ~/.fledge/workspaces, not /tmp macOS auto-prunes /tmp aggressively, and well-meaning disk-cleanup sweeps (rm -rf /tmp/fledge-gh-*) wipe agent workspaces mid-task — we burned a Discord session today watching the corvidagent edit a JwtTokenService.swift in a workspace that got cleaned up between turns, then try to push to a path that no longer existed. Move the default workspace root to ~/.fledge/workspaces, override- able via FLEDGE_WORKSPACES_DIR for tests / CI. The new location: - survives /tmp pruning, - shows up next to the rest of fledge state for disk-audit visibility, - costs nothing in performance (still local disk). workspace-clean and 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. Bash strict-mode safe; shellcheck clean. * chore: gitignore .DS_Store (accidentally committed in 109bc8a)
1 parent 31349a2 commit 80ab287

4 files changed

Lines changed: 53 additions & 21 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## [v0.6.5] - 2026-05-21
4+
5+
### Fixes
6+
7+
- `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.
8+
- `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.
9+
- Override the workspace root with `FLEDGE_WORKSPACES_DIR` for tests / CI / custom layouts.
10+
311
## [v0.6.4] - 2026-05-21
412

513
### Features

bin/fledge-github-repo

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,17 @@ is a directory — same command, agent can browse and read with one tool.
122122
`clone` wraps `gh repo clone OWNER/NAME [DIR]` directly.
123123
124124
`workspace` is the higher-level "give me a sandbox" command for
125-
agents: creates a fresh `/tmp/fledge-gh-<owner>-<name>-XXXXXX/<name>`
125+
agents: creates a fresh `~/.fledge/workspaces/<owner>-<name>-XXXXXX/<name>`
126126
directory, clones the repo there (optionally shallow + at a specific
127127
ref), and prints the absolute path on stdout. Cleanup via
128128
`workspace-clean <DIR>` which `rm -rf`s after sanity-checking the
129-
path is under `/tmp/fledge-gh-`.
129+
path is under the workspace root.
130+
131+
Override the workspace root with `FLEDGE_WORKSPACES_DIR` if you want
132+
a different location (tests, CI, custom layouts). Workspaces from
133+
the legacy `/tmp/fledge-gh-*` location (v0.6.4 and earlier) still
134+
clean up correctly so in-flight directories aren't orphaned by the
135+
upgrade.
130136
131137
EXAMPLES:
132138
fledge github repo
@@ -182,8 +188,8 @@ if [ "$ACTION" = "workspace" ]; then
182188
# Sanitize OWNER/NAME for use in a path: replace anything that isn't
183189
# alnum/-/./_ with `_`. That keeps the resulting dir name human-
184190
# readable (so the agent's logs make sense) and avoids the very real
185-
# risk of an attacker-controlled repo name escaping the /tmp prefix
186-
# via something cute like `../../etc/passwd`.
191+
# risk of an attacker-controlled repo name escaping the workspace
192+
# root via something cute like `../../etc/passwd`.
187193
OWNER_NAME_SAFE="$(printf '%s' "$REPO" | tr '/' '-' | tr -c 'A-Za-z0-9._-' '_')"
188194

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

201+
# Workspaces live under $FLEDGE_WORKSPACES_DIR (default
202+
# ~/.fledge/workspaces) so they survive macOS /tmp cleanups, OS
203+
# reboots, and well-meaning `rm -rf /tmp/...` sweeps. Plus they
204+
# show up in disk-audit reports alongside the rest of fledge state
205+
# instead of hiding in /tmp. The env var override is for tests.
206+
WORKSPACE_ROOT="${FLEDGE_WORKSPACES_DIR:-$HOME/.fledge/workspaces}"
207+
mkdir -p "$WORKSPACE_ROOT"
208+
195209
# mktemp -d gives us a unique parent. The clone target is one level
196210
# deeper so cleanup of the parent removes both the dir and any junk
197211
# gh / git leave behind.
198-
PARENT="$(mktemp -d "/tmp/fledge-gh-${OWNER_NAME_SAFE}-XXXXXX")"
212+
PARENT="$(mktemp -d "${WORKSPACE_ROOT}/${OWNER_NAME_SAFE}-XXXXXX")"
199213
TARGET="${PARENT}/${NAME_ONLY_SAFE}"
200214

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

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

310327
if [ "$ACTION" = "workspace-clean" ]; then
311328
if [ -z "$CLONE_DIR" ]; then
312-
echo "fledge github repo workspace-clean: missing DIR. Try: fledge github repo workspace-clean /tmp/fledge-gh-XXXX" >&2
329+
echo "fledge github repo workspace-clean: missing DIR. Try: fledge github repo workspace-clean <workspace-path>" >&2
313330
exit 64
314331
fi
315-
# Safety: only delete dirs that look like fledge-gh workspaces. A
316-
# bug in the agent's reasoning shouldn't be able to wipe arbitrary
317-
# paths via this tool. The prefix check is intentionally strict —
318-
# symlinks and `..` segments fail it cleanly.
332+
# Safety: only delete dirs that look like workspaces we ourselves
333+
# created. Accept the v0.6.5+ ~/.fledge/workspaces/ location AND
334+
# the legacy /tmp/fledge-gh-* prefix so this tool can clean up
335+
# in-flight workspaces from older plugin versions. A bug in the
336+
# agent's reasoning shouldn't be able to wipe arbitrary paths via
337+
# this tool — the prefix check is intentionally strict, and
338+
# symlinks / `..` segments fail it cleanly.
339+
WORKSPACE_ROOT="${FLEDGE_WORKSPACES_DIR:-$HOME/.fledge/workspaces}"
319340
case "$CLONE_DIR" in
341+
"$WORKSPACE_ROOT"/*) ;;
320342
/tmp/fledge-gh-*) ;;
321343
*)
322-
echo "fledge github repo workspace-clean: refusing to remove '$CLONE_DIR' (must start with /tmp/fledge-gh-)" >&2
344+
echo "fledge github repo workspace-clean: refusing to remove '$CLONE_DIR' (must be a workspace created by 'repo workspace')" >&2
323345
exit 64
324346
;;
325347
esac
@@ -328,12 +350,13 @@ if [ "$ACTION" = "workspace-clean" ]; then
328350
exit 1
329351
fi
330352
# If the caller passed the inner clone path, we still want to remove
331-
# the parent so we don't leave an empty fledge-gh-* shell behind.
332-
# Walk up only when CLONE_DIR's parent itself matches our prefix.
353+
# the parent so we don't leave an empty shell behind. Walk up only
354+
# when CLONE_DIR's parent itself matches one of our workspace roots.
333355
PARENT_OF="$(dirname "$CLONE_DIR")"
334356
case "$PARENT_OF" in
335-
/tmp/fledge-gh-*) rm -rf -- "$PARENT_OF";;
336-
*) rm -rf -- "$CLONE_DIR";;
357+
"$WORKSPACE_ROOT"/*) rm -rf -- "$PARENT_OF";;
358+
/tmp/fledge-gh-*) rm -rf -- "$PARENT_OF";;
359+
*) rm -rf -- "$CLONE_DIR";;
337360
esac
338361
exit 0
339362
fi

plugin.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[plugin]
22
name = "fledge-plugin-github"
3-
version = "0.6.4"
3+
version = "0.6.5"
44
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"
55
author = "0xLeif"
66
license = "MIT"

0 commit comments

Comments
 (0)