feat: Add @mosaic/cli-tools package for git operations
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
126
packages/cli-tools/README.md
Normal file
126
packages/cli-tools/README.md
Normal file
@@ -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
|
||||
```
|
||||
80
packages/cli-tools/bin/detect-platform.sh
Executable file
80
packages/cli-tools/bin/detect-platform.sh
Executable file
@@ -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
|
||||
135
packages/cli-tools/bin/issue-assign.sh
Executable file
135
packages/cli-tools/bin/issue-assign.sh
Executable file
@@ -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 <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Assign or update an issue on the current repository (Gitea or GitHub).
|
||||
|
||||
Options:
|
||||
-i, --issue NUMBER Issue number (required)
|
||||
-a, --assignee USER Assign to user (use @me for self)
|
||||
-l, --labels LABELS Add comma-separated labels
|
||||
-m, --milestone NAME Set milestone
|
||||
-r, --remove-assignee Remove current assignee
|
||||
-h, --help Show this help message
|
||||
|
||||
Examples:
|
||||
$(basename "$0") -i 42 -a "username"
|
||||
$(basename "$0") -i 42 -l "in-progress" -m "0.2.0"
|
||||
$(basename "$0") -i 42 -a @me
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-i|--issue)
|
||||
ISSUE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-a|--assignee)
|
||||
ASSIGNEE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-l|--labels)
|
||||
LABELS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-m|--milestone)
|
||||
MILESTONE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--remove-assignee)
|
||||
REMOVE_ASSIGNEE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&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
|
||||
64
packages/cli-tools/bin/issue-close.sh
Executable file
64
packages/cli-tools/bin/issue-close.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
# issue-close.sh - Close an issue on GitHub or Gitea
|
||||
# Usage: issue-close.sh -i <issue_number> [-c <comment>]
|
||||
|
||||
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 <issue_number> [-c <comment>]"
|
||||
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
|
||||
61
packages/cli-tools/bin/issue-comment.sh
Executable file
61
packages/cli-tools/bin/issue-comment.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
# issue-comment.sh - Add a comment to an issue on GitHub or Gitea
|
||||
# Usage: issue-comment.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-comment.sh -i <issue_number> -c <comment>"
|
||||
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
|
||||
92
packages/cli-tools/bin/issue-create.sh
Executable file
92
packages/cli-tools/bin/issue-create.sh
Executable file
@@ -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 <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Create an issue on the current repository (Gitea or GitHub).
|
||||
|
||||
Options:
|
||||
-t, --title TITLE Issue title (required)
|
||||
-b, --body BODY Issue body/description
|
||||
-l, --labels LABELS Comma-separated labels (e.g., "bug,feature")
|
||||
-m, --milestone NAME Milestone name to assign
|
||||
-h, --help Show this help message
|
||||
|
||||
Examples:
|
||||
$(basename "$0") -t "Fix login bug" -l "bug,priority-high"
|
||||
$(basename "$0") -t "Add dark mode" -b "Implement theme switching" -m "0.2.0"
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-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)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&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
|
||||
84
packages/cli-tools/bin/issue-edit.sh
Executable file
84
packages/cli-tools/bin/issue-edit.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# issue-edit.sh - Edit an issue on GitHub or Gitea
|
||||
# Usage: issue-edit.sh -i <issue_number> [-t <title>] [-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
|
||||
104
packages/cli-tools/bin/issue-list.sh
Executable file
104
packages/cli-tools/bin/issue-list.sh
Executable file
@@ -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
|
||||
62
packages/cli-tools/bin/issue-reopen.sh
Executable file
62
packages/cli-tools/bin/issue-reopen.sh
Executable file
@@ -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
|
||||
48
packages/cli-tools/bin/issue-view.sh
Executable file
48
packages/cli-tools/bin/issue-view.sh
Executable file
@@ -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
|
||||
50
packages/cli-tools/bin/milestone-close.sh
Executable file
50
packages/cli-tools/bin/milestone-close.sh
Executable file
@@ -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
|
||||
117
packages/cli-tools/bin/milestone-create.sh
Executable file
117
packages/cli-tools/bin/milestone-create.sh
Executable file
@@ -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
|
||||
43
packages/cli-tools/bin/milestone-list.sh
Executable file
43
packages/cli-tools/bin/milestone-list.sh
Executable file
@@ -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
|
||||
62
packages/cli-tools/bin/pr-close.sh
Executable file
62
packages/cli-tools/bin/pr-close.sh
Executable file
@@ -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
|
||||
164
packages/cli-tools/bin/pr-create.sh
Executable file
164
packages/cli-tools/bin/pr-create.sh
Executable file
@@ -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
|
||||
93
packages/cli-tools/bin/pr-list.sh
Executable file
93
packages/cli-tools/bin/pr-list.sh
Executable file
@@ -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
|
||||
110
packages/cli-tools/bin/pr-merge.sh
Executable file
110
packages/cli-tools/bin/pr-merge.sh
Executable file
@@ -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"
|
||||
115
packages/cli-tools/bin/pr-review.sh
Executable file
115
packages/cli-tools/bin/pr-review.sh
Executable file
@@ -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
|
||||
48
packages/cli-tools/bin/pr-view.sh
Executable file
48
packages/cli-tools/bin/pr-view.sh
Executable file
@@ -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
|
||||
44
packages/cli-tools/package.json
Normal file
44
packages/cli-tools/package.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user