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

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

### Features

- `repo workspace-push <DIR> [-b BRANCH] [-m MSG]` — closes the agent edit-and-PR loop. Optionally checks out / creates a branch, optionally stages + commits any uncommitted changes (when `-m` is given), then `git push -u origin HEAD`. Same `/tmp/fledge-gh-*` safety guard as the other workspace commands. In `--json` mode emits `{branch, repo, path}` so the agent can chain straight into `prs create -R … -H …`.
- `prs create --head <BRANCH>` — wires gh's `--head` through so an agent can create a PR for a branch that lives in a different working tree (the typical post-`workspace-push` case where the cwd isn't the cloned repo).

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

### Features
Expand Down
13 changes: 9 additions & 4 deletions bin/fledge-github-prs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ INCLUDE_COMMENTS=false
MERGE_METHOD="squash"
REVIEW_KIND=""
REPO=""
HEAD_BRANCH=""

# When -R/--repo is set, every gh sub-call gets `--repo $REPO`
# appended via REPO_FLAGS. Empty when -R isn't set, so the gh CLI
Expand Down Expand Up @@ -76,6 +77,7 @@ while [ $# -gt 0 ]; do
--request-changes) REVIEW_KIND="request-changes"; shift;;
--comment-review) REVIEW_KIND="comment"; shift;;
-R|--repo) REPO="${2:-}"; shift 2;;
-H|--head) HEAD_BRANCH="${2:-}"; shift 2;;
-h|--help)
cat <<'EOF'
fledge github prs — manage GitHub pull requests via the gh CLI
Expand All @@ -101,6 +103,8 @@ CREATE OPTIONS:
-t, --title <TITLE> PR title
-b, --body <BODY> PR body
--base <BRANCH> Base branch (default: repo default)
-H, --head <BRANCH> Head branch to PR from (default: current local branch).
Required when -R targets a repo that isn't your cwd.
--draft Open as draft PR
--fill Infer title/body from commits
--ai Generate title and body using AI (fledge ask)
Expand Down Expand Up @@ -387,10 +391,11 @@ Output only the title and body. No preamble, no meta-commentary."

# Build gh pr create argument list
ARGS=()
[ -n "$REPO" ] && ARGS+=(--repo "$REPO")
[ -n "$TITLE" ] && ARGS+=(--title "$TITLE")
[ -n "$BODY" ] && ARGS+=(--body "$BODY")
[ -n "$BASE" ] && ARGS+=(--base "$BASE")
[ -n "$REPO" ] && ARGS+=(--repo "$REPO")
[ -n "$TITLE" ] && ARGS+=(--title "$TITLE")
[ -n "$BODY" ] && ARGS+=(--body "$BODY")
[ -n "$BASE" ] && ARGS+=(--base "$BASE")
[ -n "$HEAD_BRANCH" ] && ARGS+=(--head "$HEAD_BRANCH")
[ "$DRAFT" = "true" ] && ARGS+=(--draft)
[ "$FILL" = "true" ] && ARGS+=(--fill)
if [ "$JSON" = "true" ]; then
Expand Down
105 changes: 101 additions & 4 deletions bin/fledge-github-repo
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,30 @@ if [ $# -gt 0 ] && [ "${1:0:1}" != "-" ]; then
shift
fi
;;
workspace-push)
ACTION="workspace-push"
shift
if [ $# -gt 0 ] && [ "${1:0:1}" != "-" ]; then
CLONE_DIR="$1"
shift
fi
;;
esac
fi

# Workspace-push specific args. Reusing -b/--branch and -m/--message;
# parsed alongside the existing options below via a small extension.
WORKSPACE_BRANCH=""
WORKSPACE_MESSAGE=""

while [ $# -gt 0 ]; do
case "$1" in
-R|--repo) REPO="${2:-}"; shift 2;;
-r|--ref) REF="${2:-}"; shift 2;;
--depth) DEPTH="${2:-}"; shift 2;;
--json) JSON=true; shift;;
-R|--repo) REPO="${2:-}"; shift 2;;
-r|--ref) REF="${2:-}"; shift 2;;
-b|--branch) WORKSPACE_BRANCH="${2:-}"; shift 2;;
-m|--message) WORKSPACE_MESSAGE="${2:-}"; shift 2;;
--depth) DEPTH="${2:-}"; shift 2;;
--json) JSON=true; shift;;
-h|--help)
cat <<'EOF'
fledge github repo — read repo metadata and file contents via the gh CLI
Expand All @@ -88,12 +103,15 @@ USAGE:
fledge github repo file <PATH> Read a file OR list a directory
fledge github repo clone <OWNER/NAME> [DIR] Clone a repo via `gh repo clone`
fledge github repo workspace <OWNER/NAME> Clone into a fresh /tmp dir, print path
fledge github repo workspace-push <DIR> Commit (if -m) + push the workspace branch
fledge github repo workspace-clean <DIR> rm -rf a workspace dir (safety-checked)

OPTIONS:
-R, --repo <OWNER/NAME> Target repo (default: current working repo).
view + clone + workspace also accept it as a positional.
-r, --ref <REF> Branch / tag / SHA (file: lookup; workspace: checkout)
-b, --branch <NAME> (workspace-push) target branch — checks out / creates it
-m, --message <MSG> (workspace-push) commit message; omit to push existing commits only
--depth <N> Shallow-clone depth (clone + workspace)
--json Output JSON

Expand Down Expand Up @@ -121,6 +139,7 @@ EXAMPLES:
fledge github repo clone CorvidLabs/corvid-verify /tmp/cv --depth 1
fledge github repo workspace CorvidLabs/corvid-verify
fledge github repo workspace CorvidLabs/corvid-verify -r feat/jwks-endpoint --depth 1
fledge github repo workspace-push /tmp/fledge-gh-... -b fix/kid -m "Add kid claim"
fledge github repo workspace-clean /tmp/fledge-gh-CorvidLabs-corvid-verify-abc123
EOF
exit 0
Expand Down Expand Up @@ -210,6 +229,84 @@ if [ "$ACTION" = "workspace" ]; then
exit 0
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
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.
case "$CLONE_DIR" in
/tmp/fledge-gh-*) ;;
*)
echo "fledge github repo workspace-push: refusing to push from '$CLONE_DIR' (must be a /tmp/fledge-gh-* workspace)" >&2
exit 64
;;
esac
if [ ! -d "$CLONE_DIR/.git" ]; then
echo "fledge github repo workspace-push: '$CLONE_DIR' isn't a git workspace" >&2
exit 1
fi

# If a target branch was given, switch to it (creating from HEAD when
# it doesn't yet exist). Without -b we just use whatever branch is
# currently checked out — convenient when the agent passed -r to
# `workspace` to land on a feature branch directly.
if [ -n "$WORKSPACE_BRANCH" ]; then
if git -C "$CLONE_DIR" rev-parse --verify "$WORKSPACE_BRANCH" >/dev/null 2>&1; then
git -C "$CLONE_DIR" checkout "$WORKSPACE_BRANCH" >&2
else
git -C "$CLONE_DIR" checkout -b "$WORKSPACE_BRANCH" >&2
fi
fi

CURRENT_BRANCH="$(git -C "$CLONE_DIR" rev-parse --abbrev-ref HEAD)"
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
echo "fledge github repo workspace-push: detached HEAD; pass -b BRANCH to create one" >&2
exit 1
fi

# Stage + commit any uncommitted changes when --message is given.
# If the working tree is already clean OR no message was provided,
# skip the commit step and just push whatever's there. This lets
# the agent use the same command for two cases: (1) it edited files
# and wants to commit + push in one go, or (2) it ran git-commit
# via the git plugin already and just needs the push.
if [ -n "$WORKSPACE_MESSAGE" ]; then
# Stage everything. `git add -A` mirrors the typical CLI commit
# flow; the agent doesn't need to enumerate paths.
git -C "$CLONE_DIR" add -A >&2
# Only commit if there's actually something staged — re-running
# workspace-push on a clean tree should still push, not error.
if ! git -C "$CLONE_DIR" diff --cached --quiet; then
git -C "$CLONE_DIR" commit -m "$WORKSPACE_MESSAGE" >&2
fi
fi

# Push the current branch upstream. `-u` sets the tracking branch so
# subsequent pushes are idempotent. Push stdout/stderr to stderr so
# our final JSON / branch-name output isn't muddied.
if ! git -C "$CLONE_DIR" push -u origin "$CURRENT_BRANCH" >&2; then
echo "fledge github repo workspace-push: git push failed" >&2
exit 1
fi

# Detect the repo's owner/name from the cloned remote so the agent
# can chain into `prs create -R OWNER/NAME -H BRANCH` without having
# to remember what it cloned.
ORIGIN_URL="$(git -C "$CLONE_DIR" remote get-url origin 2>/dev/null || true)"
# Strip https:// or git@github.com: prefix and a trailing .git.
REMOTE_REPO="$(printf '%s' "$ORIGIN_URL" | sed -E 's#(https?://[^/]+/|git@[^:]+:)##; s#\.git$##')"

if [ "$JSON" = "true" ]; then
printf '{"branch":"%s","repo":"%s","path":"%s"}\n' "$CURRENT_BRANCH" "$REMOTE_REPO" "$CLONE_DIR"
else
printf 'Pushed branch %s to %s\n' "$CURRENT_BRANCH" "$REMOTE_REPO"
fi
exit 0
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
Expand Down
6 changes: 3 additions & 3 deletions plugin.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[plugin]
name = "fledge-plugin-github"
version = "0.6.3"
version = "0.6.4"
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"

[[commands]]
name = "github"
description = "GitHub commands via the gh CLI. All subcommands accept -R OWNER/NAME to target any repo (the agent's working dir is otherwise used). Subcommands: prs (list/view <num> [--diff --comments]/create/comment <num> -b BODY/review <num> --approve|--request-changes|--comment-review -b BODY/merge <num>/close <num>/reopen <num>), issues (list/view <num> [--comments]/create/comment <num> -b BODY/close <num>/reopen <num>), repo (view OWNER/NAME / file <path> [--ref REF] (handles files and dirs) / clone OWNER/NAME [DIR] / workspace OWNER/NAME [--ref REF] [--depth N] (clones into /tmp/fledge-gh-* and prints the path — preferred for agents needing a local edit sandbox) / workspace-clean DIR), checks (CI status), poll (daemon event polling)."
description = "GitHub commands via the gh CLI. All subcommands accept -R OWNER/NAME to target any repo (the agent's working dir is otherwise used). Subcommands: prs (list/view <num> [--diff --comments]/create [-H HEAD_BRANCH]/comment <num> -b BODY/review <num> --approve|--request-changes|--comment-review -b BODY/merge <num>/close <num>/reopen <num>), issues (list/view <num> [--comments]/create/comment <num> -b BODY/close <num>/reopen <num>), repo (view OWNER/NAME / file <path> [--ref REF] (handles files and dirs) / clone OWNER/NAME [DIR] / workspace OWNER/NAME [--ref REF] [--depth N] (clones into /tmp/fledge-gh-* and prints the path — preferred for agents needing a local edit sandbox) / workspace-push DIR [-b BRANCH] [-m MSG] (commits + pushes the workspace branch — chain into 'prs create -R … -H …') / workspace-clean DIR), checks (CI status), poll (daemon event polling)."
binary = "bin/fledge-github"
args = [
{ name = "args", type = "string", required = true, description = "Subcommand and options. Pass -R OWNER/NAME for cross-repo lookups. For agent edit sandboxes use 'repo workspace OWNER/NAME [-r BRANCH] [--depth 1]' — it returns a /tmp path; clean up with 'repo workspace-clean <DIR>'. Examples: 'prs view 5 -R CorvidLabs/corvid-verify --diff', 'repo file Sources -R CorvidLabs/corvid-verify --ref feat/x', 'repo workspace CorvidLabs/corvid-verify -r feat/jwks-endpoint --depth 1'" },
{ name = "args", type = "string", required = true, description = "Subcommand and options. Edit-and-PR loop for another repo: 1) 'repo workspace OWNER/NAME [-r BRANCH] [--depth 1]' returns a /tmp path, 2) edit files there with files-edit/files-write, 3) 'repo workspace-push <PATH> -b feat/x -m \"commit msg\"' commits + pushes, 4) 'prs create -R OWNER/NAME -H feat/x -t \"…\" -b \"…\"' opens the PR, 5) 'repo workspace-clean <PATH>' tidies /tmp." },
]
Loading