From 32c81e96cf2b198b8ca815877837c73c29e392d4 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 5 Feb 2026 16:42:35 -0600 Subject: [PATCH] feat: Add @mosaic/cli-tools package for git operations New package providing CLI tools that work with both Gitea and GitHub: Commands: - mosaic-issue-{create,list,view,assign,edit,close,reopen,comment} - mosaic-pr-{create,list,view,merge,review,close} - mosaic-milestone-{create,list,close} Features: - Auto-detects platform (Gitea vs GitHub) from git remote - Unified interface regardless of platform - Available via `pnpm exec mosaic-*` in monorepo context Updated docs/claude/orchestrator.md: - Added CLI Tools section with usage examples - Updated issue creation to use package commands This makes Mosaic Stack fully self-contained for orchestration tooling. Co-Authored-By: Claude Opus 4.5 --- docs/claude/orchestrator.md | 35 ++++- packages/cli-tools/README.md | 126 ++++++++++++++++ packages/cli-tools/bin/detect-platform.sh | 80 ++++++++++ packages/cli-tools/bin/issue-assign.sh | 135 +++++++++++++++++ packages/cli-tools/bin/issue-close.sh | 64 ++++++++ packages/cli-tools/bin/issue-comment.sh | 61 ++++++++ packages/cli-tools/bin/issue-create.sh | 92 ++++++++++++ packages/cli-tools/bin/issue-edit.sh | 84 +++++++++++ packages/cli-tools/bin/issue-list.sh | 104 +++++++++++++ packages/cli-tools/bin/issue-reopen.sh | 62 ++++++++ packages/cli-tools/bin/issue-view.sh | 48 ++++++ packages/cli-tools/bin/milestone-close.sh | 50 +++++++ packages/cli-tools/bin/milestone-create.sh | 117 +++++++++++++++ packages/cli-tools/bin/milestone-list.sh | 43 ++++++ packages/cli-tools/bin/pr-close.sh | 62 ++++++++ packages/cli-tools/bin/pr-create.sh | 164 +++++++++++++++++++++ packages/cli-tools/bin/pr-list.sh | 93 ++++++++++++ packages/cli-tools/bin/pr-merge.sh | 110 ++++++++++++++ packages/cli-tools/bin/pr-review.sh | 115 +++++++++++++++ packages/cli-tools/bin/pr-view.sh | 48 ++++++ packages/cli-tools/package.json | 44 ++++++ 21 files changed, 1730 insertions(+), 7 deletions(-) create mode 100644 packages/cli-tools/README.md create mode 100755 packages/cli-tools/bin/detect-platform.sh create mode 100755 packages/cli-tools/bin/issue-assign.sh create mode 100755 packages/cli-tools/bin/issue-close.sh create mode 100755 packages/cli-tools/bin/issue-comment.sh create mode 100755 packages/cli-tools/bin/issue-create.sh create mode 100755 packages/cli-tools/bin/issue-edit.sh create mode 100755 packages/cli-tools/bin/issue-list.sh create mode 100755 packages/cli-tools/bin/issue-reopen.sh create mode 100755 packages/cli-tools/bin/issue-view.sh create mode 100755 packages/cli-tools/bin/milestone-close.sh create mode 100755 packages/cli-tools/bin/milestone-create.sh create mode 100755 packages/cli-tools/bin/milestone-list.sh create mode 100755 packages/cli-tools/bin/pr-close.sh create mode 100755 packages/cli-tools/bin/pr-create.sh create mode 100755 packages/cli-tools/bin/pr-list.sh create mode 100755 packages/cli-tools/bin/pr-merge.sh create mode 100755 packages/cli-tools/bin/pr-review.sh create mode 100755 packages/cli-tools/bin/pr-view.sh create mode 100644 packages/cli-tools/package.json diff --git a/docs/claude/orchestrator.md b/docs/claude/orchestrator.md index a9b2777..b674109 100644 --- a/docs/claude/orchestrator.md +++ b/docs/claude/orchestrator.md @@ -53,6 +53,25 @@ envsubst < docs/templates/orchestrator/orchestrator-learnings.json.template > do See `docs/templates/README.md` for full documentation. +### CLI Tools + +Git operations use `@mosaic/cli-tools` package (auto-detects Gitea vs GitHub): + +```bash +# Issue operations +pnpm exec mosaic-issue-create -t "Title" -b "Body" -m "Milestone" +pnpm exec mosaic-issue-list -s open -m "Milestone" + +# PR operations +pnpm exec mosaic-pr-create -t "Title" -b "Body" -B develop +pnpm exec mosaic-pr-merge -n 42 -m squash -d + +# Milestone operations +pnpm exec mosaic-milestone-create -t "M7-Feature" -d "Description" +``` + +See `packages/cli-tools/README.md` for full command reference. + --- ## Phase 1: Bootstrap @@ -121,23 +140,25 @@ docs/reports/{report-name}/ ### Step 5: Create Gitea Issues (Phase-Level) -Create ONE issue per phase: +Create ONE issue per phase using `@mosaic/cli-tools`: ```bash -# Use tea directly for Mosaic Stack (Gitea) -tea issue create \ - --title "Phase 1: Critical Security Fixes" \ - --description "## Findings +# Use mosaic CLI tools (auto-detects Gitea vs GitHub) +pnpm exec mosaic-issue-create \ + -t "Phase 1: Critical Security Fixes" \ + -b "## Findings - SEC-API-1: Description - SEC-WEB-2: Description ## Acceptance Criteria - [ ] All critical findings remediated - [ ] Quality gates passing" \ - --labels "security" \ - --milestone "{milestone-name}" + -l "security" \ + -m "{milestone-name}" ``` +**CLI tools location:** `packages/cli-tools/bin/` - see `packages/cli-tools/README.md` for full documentation. + ### Step 6: Create docs/tasks.md Create the file with this exact schema: diff --git a/packages/cli-tools/README.md b/packages/cli-tools/README.md new file mode 100644 index 0000000..4563339 --- /dev/null +++ b/packages/cli-tools/README.md @@ -0,0 +1,126 @@ +# @mosaic/cli-tools + +CLI tools for Mosaic Stack orchestration - git operations that work with both Gitea and GitHub. + +## Overview + +These scripts abstract the differences between `tea` (Gitea CLI) and `gh` (GitHub CLI), providing a unified interface for: + +- Issue management (create, list, assign, close, comment) +- Pull request management (create, list, merge, review) +- Milestone management (create, list, close) + +## Installation + +The package is part of the Mosaic Stack monorepo. After `pnpm install`, the commands are available in the monorepo context. + +```bash +# From monorepo root +pnpm install + +# Commands available via pnpm exec or npx +pnpm exec mosaic-issue-create -t "Title" -b "Body" +``` + +## Commands + +### Issues + +| Command | Description | +| ---------------------- | ------------------------------------------- | +| `mosaic-issue-create` | Create a new issue | +| `mosaic-issue-list` | List issues (with filters) | +| `mosaic-issue-view` | View issue details | +| `mosaic-issue-assign` | Assign issue to user | +| `mosaic-issue-edit` | Edit issue (title, body, labels, milestone) | +| `mosaic-issue-close` | Close an issue | +| `mosaic-issue-reopen` | Reopen a closed issue | +| `mosaic-issue-comment` | Add comment to issue | + +### Pull Requests + +| Command | Description | +| ------------------ | --------------------- | +| `mosaic-pr-create` | Create a pull request | +| `mosaic-pr-list` | List pull requests | +| `mosaic-pr-view` | View PR details | +| `mosaic-pr-merge` | Merge a pull request | +| `mosaic-pr-review` | Review a pull request | +| `mosaic-pr-close` | Close a pull request | + +### Milestones + +| Command | Description | +| ------------------------- | ------------------ | +| `mosaic-milestone-create` | Create a milestone | +| `mosaic-milestone-list` | List milestones | +| `mosaic-milestone-close` | Close a milestone | + +## Usage Examples + +### Create an issue with milestone + +```bash +mosaic-issue-create \ + -t "Fix authentication bug" \ + -b "Users cannot log in when..." \ + -l "bug,security" \ + -m "M6-AgentOrchestration" +``` + +### List open issues in milestone + +```bash +mosaic-issue-list -s open -m "M6-AgentOrchestration" +``` + +### Create a pull request + +```bash +mosaic-pr-create \ + -t "Fix authentication bug" \ + -b "Resolves #123" \ + -B develop \ + -H fix/auth-bug +``` + +### Merge with squash + +```bash +mosaic-pr-merge -n 42 -m squash -d +``` + +## Platform Detection + +The scripts automatically detect whether you're working with Gitea or GitHub by examining the git remote URL. No configuration needed. + +- `git.mosaicstack.dev` → Gitea (uses `tea`) +- `github.com` → GitHub (uses `gh`) + +## Requirements + +- **Gitea**: `tea` CLI installed and authenticated +- **GitHub**: `gh` CLI installed and authenticated +- **Both**: `git` available in PATH + +## For Orchestrators + +When using these tools in orchestrator/worker contexts: + +```bash +# In worker prompt, reference via pnpm exec +pnpm exec mosaic-issue-create -t "Title" -m "Milestone" + +# Or add to PATH in orchestrator setup +export PATH="$PATH:./node_modules/.bin" +mosaic-issue-create -t "Title" -m "Milestone" +``` + +## Development + +Scripts are plain bash - edit directly in `bin/`. + +```bash +# Lint with shellcheck (if installed) +pnpm lint +``` diff --git a/packages/cli-tools/bin/detect-platform.sh b/packages/cli-tools/bin/detect-platform.sh new file mode 100755 index 0000000..874f22e --- /dev/null +++ b/packages/cli-tools/bin/detect-platform.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# detect-platform.sh - Detect git platform (Gitea or GitHub) for current repo +# Usage: source detect-platform.sh && detect_platform +# or: ./detect-platform.sh (prints platform name) + +detect_platform() { + local remote_url + remote_url=$(git remote get-url origin 2>/dev/null) + + if [[ -z "$remote_url" ]]; then + echo "error: not a git repository or no origin remote" >&2 + return 1 + fi + + # Check for GitHub + if [[ "$remote_url" == *"github.com"* ]]; then + PLATFORM="github" + export PLATFORM + echo "github" + return 0 + fi + + # Check for common Gitea indicators + # Gitea URLs typically don't contain github.com, gitlab.com, bitbucket.org + if [[ "$remote_url" != *"gitlab.com"* ]] && \ + [[ "$remote_url" != *"bitbucket.org"* ]]; then + # Assume Gitea for self-hosted repos + PLATFORM="gitea" + export PLATFORM + echo "gitea" + return 0 + fi + + PLATFORM="unknown" + export PLATFORM + echo "unknown" + return 1 +} + +get_repo_info() { + local remote_url + remote_url=$(git remote get-url origin 2>/dev/null) + + if [[ -z "$remote_url" ]]; then + echo "error: not a git repository or no origin remote" >&2 + return 1 + fi + + # Extract owner/repo from URL + # Handles: git@host:owner/repo.git, https://host/owner/repo.git, https://host/owner/repo + local repo_path + if [[ "$remote_url" == git@* ]]; then + repo_path="${remote_url#*:}" + else + repo_path="${remote_url#*://}" + repo_path="${repo_path#*/}" + fi + + # Remove .git suffix if present + repo_path="${repo_path%.git}" + + echo "$repo_path" +} + +get_repo_owner() { + local repo_info + repo_info=$(get_repo_info) + echo "${repo_info%%/*}" +} + +get_repo_name() { + local repo_info + repo_info=$(get_repo_info) + echo "${repo_info##*/}" +} + +# If script is run directly (not sourced), output the platform +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + detect_platform +fi diff --git a/packages/cli-tools/bin/issue-assign.sh b/packages/cli-tools/bin/issue-assign.sh new file mode 100755 index 0000000..941f22a --- /dev/null +++ b/packages/cli-tools/bin/issue-assign.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# issue-assign.sh - Assign issues on Gitea or GitHub +# Usage: issue-assign.sh -i ISSUE_NUMBER [-a assignee] [-l labels] [-m milestone] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +ISSUE="" +ASSIGNEE="" +LABELS="" +MILESTONE="" +REMOVE_ASSIGNEE=false + +usage() { + cat <&2 + usage + ;; + esac +done + +if [[ -z "$ISSUE" ]]; then + echo "Error: Issue number is required (-i)" >&2 + usage +fi + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + if [[ -n "$ASSIGNEE" ]]; then + gh issue edit "$ISSUE" --add-assignee "$ASSIGNEE" + fi + if [[ "$REMOVE_ASSIGNEE" == true ]]; then + # Get current assignees and remove them + CURRENT=$(gh issue view "$ISSUE" --json assignees -q '.assignees[].login' 2>/dev/null | tr '\n' ',') + if [[ -n "$CURRENT" ]]; then + gh issue edit "$ISSUE" --remove-assignee "${CURRENT%,}" + fi + fi + if [[ -n "$LABELS" ]]; then + gh issue edit "$ISSUE" --add-label "$LABELS" + fi + if [[ -n "$MILESTONE" ]]; then + gh issue edit "$ISSUE" --milestone "$MILESTONE" + fi + echo "Issue #$ISSUE updated successfully" + ;; + gitea) + # tea issue edit syntax + CMD="tea issue edit $ISSUE" + NEEDS_EDIT=false + + if [[ -n "$ASSIGNEE" ]]; then + # tea uses --assignees flag + CMD="$CMD --assignees \"$ASSIGNEE\"" + NEEDS_EDIT=true + fi + if [[ -n "$LABELS" ]]; then + # tea uses --labels flag (replaces existing) + CMD="$CMD --labels \"$LABELS\"" + NEEDS_EDIT=true + fi + if [[ -n "$MILESTONE" ]]; then + MILESTONE_ID=$(tea milestones list 2>/dev/null | grep -E "^\s*[0-9]+" | grep "$MILESTONE" | awk '{print $1}' | head -1) + if [[ -n "$MILESTONE_ID" ]]; then + CMD="$CMD --milestone $MILESTONE_ID" + NEEDS_EDIT=true + else + echo "Warning: Could not find milestone '$MILESTONE'" >&2 + fi + fi + + if [[ "$NEEDS_EDIT" == true ]]; then + eval "$CMD" + echo "Issue #$ISSUE updated successfully" + else + echo "No changes specified" + fi + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/issue-close.sh b/packages/cli-tools/bin/issue-close.sh new file mode 100755 index 0000000..b831272 --- /dev/null +++ b/packages/cli-tools/bin/issue-close.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# issue-close.sh - Close an issue on GitHub or Gitea +# Usage: issue-close.sh -i [-c ] + +set -e + +# Source platform detection +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +ISSUE_NUMBER="" +COMMENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--issue) + ISSUE_NUMBER="$2" + shift 2 + ;; + -c|--comment) + COMMENT="$2" + shift 2 + ;; + -h|--help) + echo "Usage: issue-close.sh -i [-c ]" + echo "" + echo "Options:" + echo " -i, --issue Issue number (required)" + echo " -c, --comment Comment to add before closing (optional)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$ISSUE_NUMBER" ]]; then + echo "Error: Issue number is required (-i)" + exit 1 +fi + +# Detect platform and close issue +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + if [[ -n "$COMMENT" ]]; then + gh issue comment "$ISSUE_NUMBER" --body "$COMMENT" + fi + gh issue close "$ISSUE_NUMBER" + echo "Closed GitHub issue #$ISSUE_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + if [[ -n "$COMMENT" ]]; then + tea issue comment "$ISSUE_NUMBER" "$COMMENT" + fi + tea issue close "$ISSUE_NUMBER" + echo "Closed Gitea issue #$ISSUE_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/issue-comment.sh b/packages/cli-tools/bin/issue-comment.sh new file mode 100755 index 0000000..3edd64b --- /dev/null +++ b/packages/cli-tools/bin/issue-comment.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# issue-comment.sh - Add a comment to an issue on GitHub or Gitea +# Usage: issue-comment.sh -i -c + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +ISSUE_NUMBER="" +COMMENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--issue) + ISSUE_NUMBER="$2" + shift 2 + ;; + -c|--comment) + COMMENT="$2" + shift 2 + ;; + -h|--help) + echo "Usage: issue-comment.sh -i -c " + echo "" + echo "Options:" + echo " -i, --issue Issue number (required)" + echo " -c, --comment Comment text (required)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$ISSUE_NUMBER" ]]; then + echo "Error: Issue number is required (-i)" + exit 1 +fi + +if [[ -z "$COMMENT" ]]; then + echo "Error: Comment is required (-c)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + gh issue comment "$ISSUE_NUMBER" --body "$COMMENT" + echo "Added comment to GitHub issue #$ISSUE_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + tea issue comment "$ISSUE_NUMBER" "$COMMENT" + echo "Added comment to Gitea issue #$ISSUE_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/issue-create.sh b/packages/cli-tools/bin/issue-create.sh new file mode 100755 index 0000000..9c8cae2 --- /dev/null +++ b/packages/cli-tools/bin/issue-create.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# issue-create.sh - Create issues on Gitea or GitHub +# Usage: issue-create.sh -t "Title" [-b "Body"] [-l "label1,label2"] [-m "milestone"] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +TITLE="" +BODY="" +LABELS="" +MILESTONE="" + +usage() { + cat <&2 + usage + ;; + esac +done + +if [[ -z "$TITLE" ]]; then + echo "Error: Title is required (-t)" >&2 + usage +fi + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + CMD="gh issue create --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --body \"$BODY\"" + [[ -n "$LABELS" ]] && CMD="$CMD --label \"$LABELS\"" + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + eval "$CMD" + ;; + gitea) + CMD="tea issue create --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --description \"$BODY\"" + [[ -n "$LABELS" ]] && CMD="$CMD --labels \"$LABELS\"" + # tea accepts milestone by name directly (verified 2026-02-05) + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + eval "$CMD" + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/issue-edit.sh b/packages/cli-tools/bin/issue-edit.sh new file mode 100755 index 0000000..70d57c9 --- /dev/null +++ b/packages/cli-tools/bin/issue-edit.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# issue-edit.sh - Edit an issue on GitHub or Gitea +# Usage: issue-edit.sh -i [-t ] [-b <body>] [-l <labels>] [-m <milestone>] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +ISSUE_NUMBER="" +TITLE="" +BODY="" +LABELS="" +MILESTONE="" + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--issue) + ISSUE_NUMBER="$2" + shift 2 + ;; + -t|--title) + TITLE="$2" + shift 2 + ;; + -b|--body) + BODY="$2" + shift 2 + ;; + -l|--labels) + LABELS="$2" + shift 2 + ;; + -m|--milestone) + MILESTONE="$2" + shift 2 + ;; + -h|--help) + echo "Usage: issue-edit.sh -i <issue_number> [-t <title>] [-b <body>] [-l <labels>] [-m <milestone>]" + echo "" + echo "Options:" + echo " -i, --issue Issue number (required)" + echo " -t, --title New title" + echo " -b, --body New body/description" + echo " -l, --labels Labels (comma-separated, replaces existing)" + echo " -m, --milestone Milestone name" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$ISSUE_NUMBER" ]]; then + echo "Error: Issue number is required (-i)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + CMD="gh issue edit $ISSUE_NUMBER" + [[ -n "$TITLE" ]] && CMD="$CMD --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --body \"$BODY\"" + [[ -n "$LABELS" ]] && CMD="$CMD --add-label \"$LABELS\"" + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + eval $CMD + echo "Updated GitHub issue #$ISSUE_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + CMD="tea issue edit $ISSUE_NUMBER" + [[ -n "$TITLE" ]] && CMD="$CMD --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --description \"$BODY\"" + [[ -n "$LABELS" ]] && CMD="$CMD --add-labels \"$LABELS\"" + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + eval $CMD + echo "Updated Gitea issue #$ISSUE_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/issue-list.sh b/packages/cli-tools/bin/issue-list.sh new file mode 100755 index 0000000..22950a4 --- /dev/null +++ b/packages/cli-tools/bin/issue-list.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# issue-list.sh - List issues on Gitea or GitHub +# Usage: issue-list.sh [-s state] [-l label] [-m milestone] [-a assignee] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +STATE="open" +LABEL="" +MILESTONE="" +ASSIGNEE="" +LIMIT=30 + +usage() { + cat <<EOF +Usage: $(basename "$0") [OPTIONS] + +List issues from the current repository (Gitea or GitHub). + +Options: + -s, --state STATE Filter by state: open, closed, all (default: open) + -l, --label LABEL Filter by label + -m, --milestone NAME Filter by milestone name + -a, --assignee USER Filter by assignee + -n, --limit N Maximum issues to show (default: 30) + -h, --help Show this help message + +Examples: + $(basename "$0") # List open issues + $(basename "$0") -s all -l bug # All issues with 'bug' label + $(basename "$0") -m "0.2.0" # Issues in milestone 0.2.0 +EOF + exit 1 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -s|--state) + STATE="$2" + shift 2 + ;; + -l|--label) + LABEL="$2" + shift 2 + ;; + -m|--milestone) + MILESTONE="$2" + shift 2 + ;; + -a|--assignee) + ASSIGNEE="$2" + shift 2 + ;; + -n|--limit) + LIMIT="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + usage + ;; + esac +done + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + CMD="gh issue list --state $STATE --limit $LIMIT" + [[ -n "$LABEL" ]] && CMD="$CMD --label \"$LABEL\"" + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + [[ -n "$ASSIGNEE" ]] && CMD="$CMD --assignee \"$ASSIGNEE\"" + eval "$CMD" + ;; + gitea) + CMD="tea issues list --state $STATE --limit $LIMIT" + [[ -n "$LABEL" ]] && CMD="$CMD --labels \"$LABEL\"" + # tea uses different syntax for milestone filtering + if [[ -n "$MILESTONE" ]]; then + MILESTONE_ID=$(tea milestones list 2>/dev/null | grep -E "^\s*[0-9]+" | grep "$MILESTONE" | awk '{print $1}' | head -1) + if [[ -n "$MILESTONE_ID" ]]; then + CMD="$CMD --milestones $MILESTONE_ID" + else + echo "Warning: Could not find milestone '$MILESTONE'" >&2 + fi + fi + # Note: tea may not support assignee filter directly + eval "$CMD" + if [[ -n "$ASSIGNEE" ]]; then + echo "Note: Assignee filtering may require manual review for Gitea" >&2 + fi + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/issue-reopen.sh b/packages/cli-tools/bin/issue-reopen.sh new file mode 100755 index 0000000..136af4b --- /dev/null +++ b/packages/cli-tools/bin/issue-reopen.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# issue-reopen.sh - Reopen a closed issue on GitHub or Gitea +# Usage: issue-reopen.sh -i <issue_number> [-c <comment>] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +ISSUE_NUMBER="" +COMMENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--issue) + ISSUE_NUMBER="$2" + shift 2 + ;; + -c|--comment) + COMMENT="$2" + shift 2 + ;; + -h|--help) + echo "Usage: issue-reopen.sh -i <issue_number> [-c <comment>]" + echo "" + echo "Options:" + echo " -i, --issue Issue number (required)" + echo " -c, --comment Comment to add when reopening (optional)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$ISSUE_NUMBER" ]]; then + echo "Error: Issue number is required (-i)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + if [[ -n "$COMMENT" ]]; then + gh issue comment "$ISSUE_NUMBER" --body "$COMMENT" + fi + gh issue reopen "$ISSUE_NUMBER" + echo "Reopened GitHub issue #$ISSUE_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + if [[ -n "$COMMENT" ]]; then + tea issue comment "$ISSUE_NUMBER" "$COMMENT" + fi + tea issue reopen "$ISSUE_NUMBER" + echo "Reopened Gitea issue #$ISSUE_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/issue-view.sh b/packages/cli-tools/bin/issue-view.sh new file mode 100755 index 0000000..cc7463c --- /dev/null +++ b/packages/cli-tools/bin/issue-view.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# issue-view.sh - View issue details on GitHub or Gitea +# Usage: issue-view.sh -i <issue_number> + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +ISSUE_NUMBER="" + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--issue) + ISSUE_NUMBER="$2" + shift 2 + ;; + -h|--help) + echo "Usage: issue-view.sh -i <issue_number>" + echo "" + echo "Options:" + echo " -i, --issue Issue number (required)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$ISSUE_NUMBER" ]]; then + echo "Error: Issue number is required (-i)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + gh issue view "$ISSUE_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + tea issue "$ISSUE_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/milestone-close.sh b/packages/cli-tools/bin/milestone-close.sh new file mode 100755 index 0000000..ac7ad89 --- /dev/null +++ b/packages/cli-tools/bin/milestone-close.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# milestone-close.sh - Close a milestone on GitHub or Gitea +# Usage: milestone-close.sh -t <title> + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +TITLE="" + +while [[ $# -gt 0 ]]; do + case $1 in + -t|--title) + TITLE="$2" + shift 2 + ;; + -h|--help) + echo "Usage: milestone-close.sh -t <title>" + echo "" + echo "Options:" + echo " -t, --title Milestone title (required)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$TITLE" ]]; then + echo "Error: Milestone title is required (-t)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + gh api -X PATCH "/repos/{owner}/{repo}/milestones/$(gh api "/repos/{owner}/{repo}/milestones" --jq ".[] | select(.title==\"$TITLE\") | .number")" -f state=closed + echo "Closed GitHub milestone: $TITLE" +elif [[ "$PLATFORM" == "gitea" ]]; then + tea milestone close "$TITLE" + echo "Closed Gitea milestone: $TITLE" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/milestone-create.sh b/packages/cli-tools/bin/milestone-create.sh new file mode 100755 index 0000000..1970f06 --- /dev/null +++ b/packages/cli-tools/bin/milestone-create.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# milestone-create.sh - Create milestones on Gitea or GitHub +# Usage: milestone-create.sh -t "Title" [-d "Description"] [--due "YYYY-MM-DD"] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +TITLE="" +DESCRIPTION="" +DUE_DATE="" +LIST_ONLY=false + +usage() { + cat <<EOF +Usage: $(basename "$0") [OPTIONS] + +Create or list milestones on the current repository (Gitea or GitHub). + +Versioning Convention: + - Features get dedicated milestones + - Pre-release: 0.X.0 for breaking changes, 0.X.Y for patches + - Post-release: X.0.0 for breaking changes + - MVP starts at 0.1.0 + +Options: + -t, --title TITLE Milestone title/version (e.g., "0.2.0") + -d, --desc DESCRIPTION Milestone description + --due DATE Due date (YYYY-MM-DD format) + --list List existing milestones + -h, --help Show this help message + +Examples: + $(basename "$0") --list + $(basename "$0") -t "0.1.0" -d "MVP Release" + $(basename "$0") -t "0.2.0" -d "User Authentication Feature" --due "2025-03-01" +EOF + exit 1 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -t|--title) + TITLE="$2" + shift 2 + ;; + -d|--desc) + DESCRIPTION="$2" + shift 2 + ;; + --due) + DUE_DATE="$2" + shift 2 + ;; + --list) + LIST_ONLY=true + shift + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + usage + ;; + esac +done + +PLATFORM=$(detect_platform) + +if [[ "$LIST_ONLY" == true ]]; then + case "$PLATFORM" in + github) + gh api repos/:owner/:repo/milestones --jq '.[] | "\(.number)\t\(.title)\t\(.state)\t\(.open_issues)/\(.closed_issues) issues"' + ;; + gitea) + tea milestones list + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; + esac + exit 0 +fi + +if [[ -z "$TITLE" ]]; then + echo "Error: Title is required (-t) for creating milestones" >&2 + usage +fi + +case "$PLATFORM" in + github) + # GitHub uses the API for milestone creation + JSON_PAYLOAD="{\"title\":\"$TITLE\"" + [[ -n "$DESCRIPTION" ]] && JSON_PAYLOAD="$JSON_PAYLOAD,\"description\":\"$DESCRIPTION\"" + [[ -n "$DUE_DATE" ]] && JSON_PAYLOAD="$JSON_PAYLOAD,\"due_on\":\"${DUE_DATE}T00:00:00Z\"" + JSON_PAYLOAD="$JSON_PAYLOAD}" + + gh api repos/:owner/:repo/milestones --method POST --input - <<< "$JSON_PAYLOAD" + echo "Milestone '$TITLE' created successfully" + ;; + gitea) + CMD="tea milestones create --title \"$TITLE\"" + [[ -n "$DESCRIPTION" ]] && CMD="$CMD --description \"$DESCRIPTION\"" + [[ -n "$DUE_DATE" ]] && CMD="$CMD --deadline \"$DUE_DATE\"" + eval "$CMD" + echo "Milestone '$TITLE' created successfully" + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/milestone-list.sh b/packages/cli-tools/bin/milestone-list.sh new file mode 100755 index 0000000..e9b8656 --- /dev/null +++ b/packages/cli-tools/bin/milestone-list.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# milestone-list.sh - List milestones on GitHub or Gitea +# Usage: milestone-list.sh [-s <state>] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +STATE="open" + +while [[ $# -gt 0 ]]; do + case $1 in + -s|--state) + STATE="$2" + shift 2 + ;; + -h|--help) + echo "Usage: milestone-list.sh [-s <state>]" + echo "" + echo "Options:" + echo " -s, --state Filter by state: open, closed, all (default: open)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + gh api "/repos/{owner}/{repo}/milestones?state=$STATE" --jq '.[] | "\(.title) (\(.state)) - \(.open_issues) open, \(.closed_issues) closed"' +elif [[ "$PLATFORM" == "gitea" ]]; then + tea milestone list +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/pr-close.sh b/packages/cli-tools/bin/pr-close.sh new file mode 100755 index 0000000..4d06580 --- /dev/null +++ b/packages/cli-tools/bin/pr-close.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# pr-close.sh - Close a pull request without merging on GitHub or Gitea +# Usage: pr-close.sh -n <pr_number> [-c <comment>] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +PR_NUMBER="" +COMMENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + -n|--number) + PR_NUMBER="$2" + shift 2 + ;; + -c|--comment) + COMMENT="$2" + shift 2 + ;; + -h|--help) + echo "Usage: pr-close.sh -n <pr_number> [-c <comment>]" + echo "" + echo "Options:" + echo " -n, --number PR number (required)" + echo " -c, --comment Comment before closing (optional)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$PR_NUMBER" ]]; then + echo "Error: PR number is required (-n)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + if [[ -n "$COMMENT" ]]; then + gh pr comment "$PR_NUMBER" --body "$COMMENT" + fi + gh pr close "$PR_NUMBER" + echo "Closed GitHub PR #$PR_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + if [[ -n "$COMMENT" ]]; then + tea pr comment "$PR_NUMBER" "$COMMENT" + fi + tea pr close "$PR_NUMBER" + echo "Closed Gitea PR #$PR_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/pr-create.sh b/packages/cli-tools/bin/pr-create.sh new file mode 100755 index 0000000..6c3c666 --- /dev/null +++ b/packages/cli-tools/bin/pr-create.sh @@ -0,0 +1,164 @@ +#!/bin/bash +# pr-create.sh - Create pull requests on Gitea or GitHub +# Usage: pr-create.sh -t "Title" [-b "Body"] [-B base] [-H head] [-l "labels"] [-m "milestone"] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +TITLE="" +BODY="" +BASE_BRANCH="" +HEAD_BRANCH="" +LABELS="" +MILESTONE="" +DRAFT=false +ISSUE="" + +usage() { + cat <<EOF +Usage: $(basename "$0") [OPTIONS] + +Create a pull request on the current repository (Gitea or GitHub). + +Options: + -t, --title TITLE PR title (required, or use --issue) + -b, --body BODY PR description/body + -B, --base BRANCH Base branch to merge into (default: main/master) + -H, --head BRANCH Head branch with changes (default: current branch) + -l, --labels LABELS Comma-separated labels + -m, --milestone NAME Milestone name + -i, --issue NUMBER Link to issue (auto-generates title if not provided) + -d, --draft Create as draft PR + -h, --help Show this help message + +Examples: + $(basename "$0") -t "Add login feature" -b "Implements user authentication" + $(basename "$0") -t "Fix bug" -B main -H feature/fix-123 + $(basename "$0") -i 42 -b "Implements the feature described in #42" + $(basename "$0") -t "WIP: New feature" --draft +EOF + exit 1 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -t|--title) + TITLE="$2" + shift 2 + ;; + -b|--body) + BODY="$2" + shift 2 + ;; + -B|--base) + BASE_BRANCH="$2" + shift 2 + ;; + -H|--head) + HEAD_BRANCH="$2" + shift 2 + ;; + -l|--labels) + LABELS="$2" + shift 2 + ;; + -m|--milestone) + MILESTONE="$2" + shift 2 + ;; + -i|--issue) + ISSUE="$2" + shift 2 + ;; + -d|--draft) + DRAFT=true + shift + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + usage + ;; + esac +done + +# If no title but issue provided, generate title +if [[ -z "$TITLE" ]] && [[ -n "$ISSUE" ]]; then + TITLE="Fixes #$ISSUE" +fi + +if [[ -z "$TITLE" ]]; then + echo "Error: Title is required (-t) or provide an issue (-i)" >&2 + usage +fi + +# Default head branch to current branch +if [[ -z "$HEAD_BRANCH" ]]; then + HEAD_BRANCH=$(git branch --show-current) +fi + +# Add issue reference to body if provided +if [[ -n "$ISSUE" ]]; then + if [[ -n "$BODY" ]]; then + BODY="$BODY + +Fixes #$ISSUE" + else + BODY="Fixes #$ISSUE" + fi +fi + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + CMD="gh pr create --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --body \"$BODY\"" + [[ -n "$BASE_BRANCH" ]] && CMD="$CMD --base \"$BASE_BRANCH\"" + [[ -n "$HEAD_BRANCH" ]] && CMD="$CMD --head \"$HEAD_BRANCH\"" + [[ -n "$LABELS" ]] && CMD="$CMD --label \"$LABELS\"" + [[ -n "$MILESTONE" ]] && CMD="$CMD --milestone \"$MILESTONE\"" + [[ "$DRAFT" == true ]] && CMD="$CMD --draft" + eval "$CMD" + ;; + gitea) + # tea pull create syntax + CMD="tea pr create --title \"$TITLE\"" + [[ -n "$BODY" ]] && CMD="$CMD --description \"$BODY\"" + [[ -n "$BASE_BRANCH" ]] && CMD="$CMD --base \"$BASE_BRANCH\"" + [[ -n "$HEAD_BRANCH" ]] && CMD="$CMD --head \"$HEAD_BRANCH\"" + + # Handle labels for tea + if [[ -n "$LABELS" ]]; then + # tea may use --labels flag + CMD="$CMD --labels \"$LABELS\"" + fi + + # Handle milestone for tea + if [[ -n "$MILESTONE" ]]; then + MILESTONE_ID=$(tea milestones list 2>/dev/null | grep -E "^\s*[0-9]+" | grep "$MILESTONE" | awk '{print $1}' | head -1) + if [[ -n "$MILESTONE_ID" ]]; then + CMD="$CMD --milestone $MILESTONE_ID" + else + echo "Warning: Could not find milestone '$MILESTONE', creating without milestone" >&2 + fi + fi + + # Note: tea may not support --draft flag in all versions + if [[ "$DRAFT" == true ]]; then + echo "Note: Draft PR may not be supported by your tea version" >&2 + fi + + eval "$CMD" + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/pr-list.sh b/packages/cli-tools/bin/pr-list.sh new file mode 100755 index 0000000..e550c18 --- /dev/null +++ b/packages/cli-tools/bin/pr-list.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# pr-list.sh - List pull requests on Gitea or GitHub +# Usage: pr-list.sh [-s state] [-l label] [-a author] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +STATE="open" +LABEL="" +AUTHOR="" +LIMIT=30 + +usage() { + cat <<EOF +Usage: $(basename "$0") [OPTIONS] + +List pull requests from the current repository (Gitea or GitHub). + +Options: + -s, --state STATE Filter by state: open, closed, merged, all (default: open) + -l, --label LABEL Filter by label + -a, --author USER Filter by author + -n, --limit N Maximum PRs to show (default: 30) + -h, --help Show this help message + +Examples: + $(basename "$0") # List open PRs + $(basename "$0") -s all # All PRs + $(basename "$0") -s merged -a username # Merged PRs by user +EOF + exit 1 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -s|--state) + STATE="$2" + shift 2 + ;; + -l|--label) + LABEL="$2" + shift 2 + ;; + -a|--author) + AUTHOR="$2" + shift 2 + ;; + -n|--limit) + LIMIT="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + usage + ;; + esac +done + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + CMD="gh pr list --state $STATE --limit $LIMIT" + [[ -n "$LABEL" ]] && CMD="$CMD --label \"$LABEL\"" + [[ -n "$AUTHOR" ]] && CMD="$CMD --author \"$AUTHOR\"" + eval "$CMD" + ;; + gitea) + # tea pr list - note: tea uses 'pulls' subcommand in some versions + CMD="tea pr list --state $STATE --limit $LIMIT" + + # tea filtering may be limited + if [[ -n "$LABEL" ]]; then + echo "Note: Label filtering may require manual review for Gitea" >&2 + fi + if [[ -n "$AUTHOR" ]]; then + echo "Note: Author filtering may require manual review for Gitea" >&2 + fi + + eval "$CMD" + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac diff --git a/packages/cli-tools/bin/pr-merge.sh b/packages/cli-tools/bin/pr-merge.sh new file mode 100755 index 0000000..e238b7b --- /dev/null +++ b/packages/cli-tools/bin/pr-merge.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# pr-merge.sh - Merge pull requests on Gitea or GitHub +# Usage: pr-merge.sh -n PR_NUMBER [-m method] [-d] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Default values +PR_NUMBER="" +MERGE_METHOD="merge" # merge, squash, rebase +DELETE_BRANCH=false + +usage() { + cat <<EOF +Usage: $(basename "$0") [OPTIONS] + +Merge a pull request on the current repository (Gitea or GitHub). + +Options: + -n, --number NUMBER PR number to merge (required) + -m, --method METHOD Merge method: merge, squash, rebase (default: merge) + -d, --delete-branch Delete the head branch after merge + -h, --help Show this help message + +Examples: + $(basename "$0") -n 42 # Merge PR #42 + $(basename "$0") -n 42 -m squash # Squash merge + $(basename "$0") -n 42 -m rebase -d # Rebase and delete branch +EOF + exit 1 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -n|--number) + PR_NUMBER="$2" + shift 2 + ;; + -m|--method) + MERGE_METHOD="$2" + shift 2 + ;; + -d|--delete-branch) + DELETE_BRANCH=true + shift + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + usage + ;; + esac +done + +if [[ -z "$PR_NUMBER" ]]; then + echo "Error: PR number is required (-n)" >&2 + usage +fi + +PLATFORM=$(detect_platform) + +case "$PLATFORM" in + github) + CMD="gh pr merge $PR_NUMBER" + case "$MERGE_METHOD" in + merge) CMD="$CMD --merge" ;; + squash) CMD="$CMD --squash" ;; + rebase) CMD="$CMD --rebase" ;; + *) + echo "Error: Invalid merge method '$MERGE_METHOD'" >&2 + exit 1 + ;; + esac + [[ "$DELETE_BRANCH" == true ]] && CMD="$CMD --delete-branch" + eval "$CMD" + ;; + gitea) + # tea pr merge syntax + CMD="tea pr merge $PR_NUMBER" + + # tea merge style flags + case "$MERGE_METHOD" in + merge) CMD="$CMD --style merge" ;; + squash) CMD="$CMD --style squash" ;; + rebase) CMD="$CMD --style rebase" ;; + *) + echo "Error: Invalid merge method '$MERGE_METHOD'" >&2 + exit 1 + ;; + esac + + # Delete branch after merge if requested + if [[ "$DELETE_BRANCH" == true ]]; then + echo "Note: Branch deletion after merge may need to be done separately with tea" >&2 + fi + + eval "$CMD" + ;; + *) + echo "Error: Could not detect git platform" >&2 + exit 1 + ;; +esac + +echo "PR #$PR_NUMBER merged successfully" diff --git a/packages/cli-tools/bin/pr-review.sh b/packages/cli-tools/bin/pr-review.sh new file mode 100755 index 0000000..0ef97f9 --- /dev/null +++ b/packages/cli-tools/bin/pr-review.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# pr-review.sh - Review a pull request on GitHub or Gitea +# Usage: pr-review.sh -n <pr_number> -a <action> [-c <comment>] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +PR_NUMBER="" +ACTION="" +COMMENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + -n|--number) + PR_NUMBER="$2" + shift 2 + ;; + -a|--action) + ACTION="$2" + shift 2 + ;; + -c|--comment) + COMMENT="$2" + shift 2 + ;; + -h|--help) + echo "Usage: pr-review.sh -n <pr_number> -a <action> [-c <comment>]" + echo "" + echo "Options:" + echo " -n, --number PR number (required)" + echo " -a, --action Review action: approve, request-changes, comment (required)" + echo " -c, --comment Review comment (required for request-changes)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$PR_NUMBER" ]]; then + echo "Error: PR number is required (-n)" + exit 1 +fi + +if [[ -z "$ACTION" ]]; then + echo "Error: Action is required (-a): approve, request-changes, comment" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + case $ACTION in + approve) + gh pr review "$PR_NUMBER" --approve ${COMMENT:+--body "$COMMENT"} + echo "Approved GitHub PR #$PR_NUMBER" + ;; + request-changes) + if [[ -z "$COMMENT" ]]; then + echo "Error: Comment required for request-changes" + exit 1 + fi + gh pr review "$PR_NUMBER" --request-changes --body "$COMMENT" + echo "Requested changes on GitHub PR #$PR_NUMBER" + ;; + comment) + if [[ -z "$COMMENT" ]]; then + echo "Error: Comment required" + exit 1 + fi + gh pr review "$PR_NUMBER" --comment --body "$COMMENT" + echo "Added review comment to GitHub PR #$PR_NUMBER" + ;; + *) + echo "Error: Unknown action: $ACTION" + exit 1 + ;; + esac +elif [[ "$PLATFORM" == "gitea" ]]; then + case $ACTION in + approve) + tea pr approve "$PR_NUMBER" ${COMMENT:+--comment "$COMMENT"} + echo "Approved Gitea PR #$PR_NUMBER" + ;; + request-changes) + if [[ -z "$COMMENT" ]]; then + echo "Error: Comment required for request-changes" + exit 1 + fi + tea pr reject "$PR_NUMBER" --comment "$COMMENT" + echo "Requested changes on Gitea PR #$PR_NUMBER" + ;; + comment) + if [[ -z "$COMMENT" ]]; then + echo "Error: Comment required" + exit 1 + fi + tea pr comment "$PR_NUMBER" "$COMMENT" + echo "Added comment to Gitea PR #$PR_NUMBER" + ;; + *) + echo "Error: Unknown action: $ACTION" + exit 1 + ;; + esac +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/bin/pr-view.sh b/packages/cli-tools/bin/pr-view.sh new file mode 100755 index 0000000..7836e09 --- /dev/null +++ b/packages/cli-tools/bin/pr-view.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# pr-view.sh - View pull request details on GitHub or Gitea +# Usage: pr-view.sh -n <pr_number> + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/detect-platform.sh" + +# Parse arguments +PR_NUMBER="" + +while [[ $# -gt 0 ]]; do + case $1 in + -n|--number) + PR_NUMBER="$2" + shift 2 + ;; + -h|--help) + echo "Usage: pr-view.sh -n <pr_number>" + echo "" + echo "Options:" + echo " -n, --number PR number (required)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ -z "$PR_NUMBER" ]]; then + echo "Error: PR number is required (-n)" + exit 1 +fi + +detect_platform + +if [[ "$PLATFORM" == "github" ]]; then + gh pr view "$PR_NUMBER" +elif [[ "$PLATFORM" == "gitea" ]]; then + tea pr "$PR_NUMBER" +else + echo "Error: Unknown platform" + exit 1 +fi diff --git a/packages/cli-tools/package.json b/packages/cli-tools/package.json new file mode 100644 index 0000000..c2836d2 --- /dev/null +++ b/packages/cli-tools/package.json @@ -0,0 +1,44 @@ +{ + "name": "@mosaic/cli-tools", + "version": "0.0.1", + "description": "CLI tools for Mosaic Stack orchestration - git operations for Gitea/GitHub", + "private": true, + "bin": { + "mosaic-detect-platform": "./bin/detect-platform.sh", + "mosaic-issue-assign": "./bin/issue-assign.sh", + "mosaic-issue-close": "./bin/issue-close.sh", + "mosaic-issue-comment": "./bin/issue-comment.sh", + "mosaic-issue-create": "./bin/issue-create.sh", + "mosaic-issue-edit": "./bin/issue-edit.sh", + "mosaic-issue-list": "./bin/issue-list.sh", + "mosaic-issue-reopen": "./bin/issue-reopen.sh", + "mosaic-issue-view": "./bin/issue-view.sh", + "mosaic-milestone-close": "./bin/milestone-close.sh", + "mosaic-milestone-create": "./bin/milestone-create.sh", + "mosaic-milestone-list": "./bin/milestone-list.sh", + "mosaic-pr-close": "./bin/pr-close.sh", + "mosaic-pr-create": "./bin/pr-create.sh", + "mosaic-pr-list": "./bin/pr-list.sh", + "mosaic-pr-merge": "./bin/pr-merge.sh", + "mosaic-pr-review": "./bin/pr-review.sh", + "mosaic-pr-view": "./bin/pr-view.sh" + }, + "scripts": { + "lint": "shellcheck bin/*.sh || true", + "test": "echo 'No tests yet'" + }, + "keywords": [ + "cli", + "git", + "gitea", + "github", + "orchestration" + ], + "engines": { + "node": ">=18" + }, + "os": [ + "linux", + "darwin" + ] +}