@revotools/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/revo +446 -1
- package/package.json +1 -1
package/dist/revo
CHANGED
|
@@ -8,7 +8,7 @@ set -euo pipefail
|
|
|
8
8
|
# Exit cleanly on SIGPIPE (e.g., revo clone | grep, revo status | head)
|
|
9
9
|
trap 'exit 0' PIPE
|
|
10
10
|
|
|
11
|
-
REVO_VERSION="0.
|
|
11
|
+
REVO_VERSION="0.5.0"
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
# === lib/ui.sh ===
|
|
@@ -3002,6 +3002,15 @@ _context_write_file() {
|
|
|
3002
3002
|
printf -- '- `revo exec "cmd" [--tag t]` — run command in filtered repos\n'
|
|
3003
3003
|
printf -- '- `revo checkout <branch> [--tag t]` — switch branch across repos\n'
|
|
3004
3004
|
printf '\n'
|
|
3005
|
+
printf '**Issues (cross-repo, via gh CLI):**\n'
|
|
3006
|
+
printf -- '- `revo issue list [--tag t] [--state open|closed|all] [--label L] [--json]` — list issues across repos\n'
|
|
3007
|
+
printf -- '- `revo issue create --repo <name> "title" [--body b] [--label L] [--feature F]` — create in one repo\n'
|
|
3008
|
+
printf -- '- `revo issue create --tag <t> "title" [--body b] [--feature F]` — create in every matching repo, cross-referenced\n'
|
|
3009
|
+
printf '\n'
|
|
3010
|
+
printf 'Use `--json` on `revo issue list` to get a flat JSON array (each entry has\n'
|
|
3011
|
+
printf 'a `repo` field) — easy to filter or pipe into jq when reasoning about\n'
|
|
3012
|
+
printf 'cross-repo issue state.\n'
|
|
3013
|
+
printf '\n'
|
|
3005
3014
|
printf '### Tag filtering\n'
|
|
3006
3015
|
printf '\n'
|
|
3007
3016
|
printf 'All commands support `--tag <tag>` to target specific repos:\n'
|
|
@@ -3658,6 +3667,433 @@ cmd_pr() {
|
|
|
3658
3667
|
return 0
|
|
3659
3668
|
}
|
|
3660
3669
|
|
|
3670
|
+
# === lib/commands/issue.sh ===
|
|
3671
|
+
# Revo CLI - issue command
|
|
3672
|
+
# List and create GitHub issues across workspace repos via the gh CLI.
|
|
3673
|
+
|
|
3674
|
+
cmd_issue() {
|
|
3675
|
+
local subcommand="${1:-}"
|
|
3676
|
+
[[ $# -gt 0 ]] && shift
|
|
3677
|
+
|
|
3678
|
+
case "$subcommand" in
|
|
3679
|
+
list|ls)
|
|
3680
|
+
_issue_list "$@"
|
|
3681
|
+
;;
|
|
3682
|
+
create|new)
|
|
3683
|
+
_issue_create "$@"
|
|
3684
|
+
;;
|
|
3685
|
+
--help|-h|help|"")
|
|
3686
|
+
_issue_help
|
|
3687
|
+
return 0
|
|
3688
|
+
;;
|
|
3689
|
+
*)
|
|
3690
|
+
ui_step_error "Unknown subcommand: $subcommand"
|
|
3691
|
+
_issue_help
|
|
3692
|
+
return 1
|
|
3693
|
+
;;
|
|
3694
|
+
esac
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
_issue_help() {
|
|
3698
|
+
cat << 'EOF'
|
|
3699
|
+
Usage: revo issue <subcommand> [options]
|
|
3700
|
+
|
|
3701
|
+
Subcommands:
|
|
3702
|
+
list List issues across workspace repos
|
|
3703
|
+
create Create issue(s) in workspace repos
|
|
3704
|
+
|
|
3705
|
+
revo issue list [options]
|
|
3706
|
+
--tag TAG Filter repos by tag
|
|
3707
|
+
--state open|closed|all Issue state (default: open)
|
|
3708
|
+
--label LABEL Filter by label
|
|
3709
|
+
--limit N Per-repo limit (default: 30)
|
|
3710
|
+
--json Emit a flat JSON array (one entry per issue,
|
|
3711
|
+
with a "repo" field)
|
|
3712
|
+
|
|
3713
|
+
revo issue create (--repo NAME | --tag TAG) "TITLE" [options]
|
|
3714
|
+
--repo NAME Target a single repo by path basename
|
|
3715
|
+
--tag TAG Target every repo matching the tag (cross-references
|
|
3716
|
+
all created issues in each body)
|
|
3717
|
+
--body BODY Issue body (default: revo-generated stub)
|
|
3718
|
+
--label L,L Comma-separated labels
|
|
3719
|
+
--assignee USER GitHub username to assign
|
|
3720
|
+
--feature NAME Append issue links to .revo/features/NAME.md and
|
|
3721
|
+
reference the brief from each issue body
|
|
3722
|
+
EOF
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
# --- list ----------------------------------------------------------------
|
|
3726
|
+
|
|
3727
|
+
_issue_list() {
|
|
3728
|
+
local tag=""
|
|
3729
|
+
local state="open"
|
|
3730
|
+
local label=""
|
|
3731
|
+
local limit="30"
|
|
3732
|
+
local as_json=0
|
|
3733
|
+
|
|
3734
|
+
while [[ $# -gt 0 ]]; do
|
|
3735
|
+
case "$1" in
|
|
3736
|
+
--tag) tag="$2"; shift 2 ;;
|
|
3737
|
+
--state) state="$2"; shift 2 ;;
|
|
3738
|
+
--label) label="$2"; shift 2 ;;
|
|
3739
|
+
--limit) limit="$2"; shift 2 ;;
|
|
3740
|
+
--json) as_json=1; shift ;;
|
|
3741
|
+
--help|-h) _issue_help; return 0 ;;
|
|
3742
|
+
*) ui_step_error "Unknown option: $1"; return 1 ;;
|
|
3743
|
+
esac
|
|
3744
|
+
done
|
|
3745
|
+
|
|
3746
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
3747
|
+
ui_step_error "gh CLI not found. Install from https://cli.github.com/"
|
|
3748
|
+
return 1
|
|
3749
|
+
fi
|
|
3750
|
+
|
|
3751
|
+
config_require_workspace || return 1
|
|
3752
|
+
|
|
3753
|
+
local repos
|
|
3754
|
+
repos=$(config_get_repos "$tag")
|
|
3755
|
+
if [[ -z "$repos" ]]; then
|
|
3756
|
+
if [[ $as_json -eq 1 ]]; then
|
|
3757
|
+
printf '[]\n'
|
|
3758
|
+
return 0
|
|
3759
|
+
fi
|
|
3760
|
+
ui_step_error "No repositories configured"
|
|
3761
|
+
return 1
|
|
3762
|
+
fi
|
|
3763
|
+
|
|
3764
|
+
# Build the per-repo gh args once
|
|
3765
|
+
local gh_extra=()
|
|
3766
|
+
if [[ -n "$label" ]]; then
|
|
3767
|
+
gh_extra+=( --label "$label" )
|
|
3768
|
+
fi
|
|
3769
|
+
|
|
3770
|
+
if [[ $as_json -eq 1 ]]; then
|
|
3771
|
+
_issue_list_json "$repos" "$state" "$limit" "${gh_extra[@]}"
|
|
3772
|
+
return $?
|
|
3773
|
+
fi
|
|
3774
|
+
|
|
3775
|
+
_issue_list_human "$repos" "$state" "$limit" "${gh_extra[@]}"
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
# Print a flat JSON array of issues across all repos. Each entry has a
|
|
3779
|
+
# "repo" field added in addition to the gh-provided fields. Output goes to
|
|
3780
|
+
# stdout, errors are silent (we want callers like Claude to be able to
|
|
3781
|
+
# pipe into jq).
|
|
3782
|
+
_issue_list_json() {
|
|
3783
|
+
local repos="$1"
|
|
3784
|
+
local state="$2"
|
|
3785
|
+
local limit="$3"
|
|
3786
|
+
shift 3
|
|
3787
|
+
local gh_extra=( "$@" )
|
|
3788
|
+
|
|
3789
|
+
local entries=""
|
|
3790
|
+
local first=1
|
|
3791
|
+
|
|
3792
|
+
local repo
|
|
3793
|
+
while IFS= read -r repo; do
|
|
3794
|
+
[[ -z "$repo" ]] && continue
|
|
3795
|
+
local path
|
|
3796
|
+
path=$(yaml_get_path "$repo")
|
|
3797
|
+
local full_path="$REVO_REPOS_DIR/$path"
|
|
3798
|
+
[[ ! -d "$full_path" ]] && continue
|
|
3799
|
+
|
|
3800
|
+
# gh's --jq runs the embedded jq engine and emits one compact JSON
|
|
3801
|
+
# value per line by default. We map each issue, prepend a "repo"
|
|
3802
|
+
# field, and stream the results into our concatenated array.
|
|
3803
|
+
local lines
|
|
3804
|
+
if lines=$(cd "$full_path" && gh issue list \
|
|
3805
|
+
--state "$state" --limit "$limit" "${gh_extra[@]}" \
|
|
3806
|
+
--json number,title,state,labels,assignees,url,updatedAt,author \
|
|
3807
|
+
--jq ".[] | {repo: \"$path\"} + ." 2>/dev/null); then
|
|
3808
|
+
local line
|
|
3809
|
+
while IFS= read -r line; do
|
|
3810
|
+
[[ -z "$line" ]] && continue
|
|
3811
|
+
if [[ $first -eq 1 ]]; then
|
|
3812
|
+
entries="$line"
|
|
3813
|
+
first=0
|
|
3814
|
+
else
|
|
3815
|
+
entries="$entries,$line"
|
|
3816
|
+
fi
|
|
3817
|
+
done <<< "$lines"
|
|
3818
|
+
fi
|
|
3819
|
+
done <<< "$repos"
|
|
3820
|
+
|
|
3821
|
+
printf '[%s]\n' "$entries"
|
|
3822
|
+
return 0
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
# Print a human-readable per-repo summary of issues. Each repo gets its
|
|
3826
|
+
# own block separated by ui_bar_line.
|
|
3827
|
+
_issue_list_human() {
|
|
3828
|
+
local repos="$1"
|
|
3829
|
+
local state="$2"
|
|
3830
|
+
local limit="$3"
|
|
3831
|
+
shift 3
|
|
3832
|
+
local gh_extra=( "$@" )
|
|
3833
|
+
|
|
3834
|
+
ui_intro "Revo - Issues ($state)"
|
|
3835
|
+
|
|
3836
|
+
local total=0
|
|
3837
|
+
local repo_count=0
|
|
3838
|
+
local skip_count=0
|
|
3839
|
+
|
|
3840
|
+
local repo
|
|
3841
|
+
while IFS= read -r repo; do
|
|
3842
|
+
[[ -z "$repo" ]] && continue
|
|
3843
|
+
local path
|
|
3844
|
+
path=$(yaml_get_path "$repo")
|
|
3845
|
+
local full_path="$REVO_REPOS_DIR/$path"
|
|
3846
|
+
|
|
3847
|
+
if [[ ! -d "$full_path" ]]; then
|
|
3848
|
+
ui_step_done "Skipped (not cloned):" "$path"
|
|
3849
|
+
skip_count=$((skip_count + 1))
|
|
3850
|
+
continue
|
|
3851
|
+
fi
|
|
3852
|
+
|
|
3853
|
+
ui_bar_line
|
|
3854
|
+
ui_step "$path"
|
|
3855
|
+
|
|
3856
|
+
local count_output
|
|
3857
|
+
if ! count_output=$(cd "$full_path" && gh issue list \
|
|
3858
|
+
--state "$state" --limit "$limit" "${gh_extra[@]}" \
|
|
3859
|
+
--json number --jq 'length' 2>&1); then
|
|
3860
|
+
ui_step_error "Failed: $count_output"
|
|
3861
|
+
continue
|
|
3862
|
+
fi
|
|
3863
|
+
|
|
3864
|
+
local repo_total="${count_output:-0}"
|
|
3865
|
+
if [[ "$repo_total" == "0" ]]; then
|
|
3866
|
+
printf '%s %s\n' "$(ui_bar)" "$(ui_dim "No $state issues")"
|
|
3867
|
+
continue
|
|
3868
|
+
fi
|
|
3869
|
+
|
|
3870
|
+
local pretty
|
|
3871
|
+
if pretty=$(cd "$full_path" && gh issue list \
|
|
3872
|
+
--state "$state" --limit "$limit" "${gh_extra[@]}" 2>&1); then
|
|
3873
|
+
local line
|
|
3874
|
+
while IFS= read -r line; do
|
|
3875
|
+
printf '%s %s\n' "$(ui_bar)" "$line"
|
|
3876
|
+
done <<< "$pretty"
|
|
3877
|
+
fi
|
|
3878
|
+
|
|
3879
|
+
total=$((total + repo_total))
|
|
3880
|
+
repo_count=$((repo_count + 1))
|
|
3881
|
+
done <<< "$repos"
|
|
3882
|
+
|
|
3883
|
+
ui_bar_line
|
|
3884
|
+
local msg="Found $total issue(s) across $repo_count repo(s)"
|
|
3885
|
+
[[ $skip_count -gt 0 ]] && msg+=", $skip_count skipped"
|
|
3886
|
+
ui_outro "$msg"
|
|
3887
|
+
return 0
|
|
3888
|
+
}
|
|
3889
|
+
|
|
3890
|
+
# --- create --------------------------------------------------------------
|
|
3891
|
+
|
|
3892
|
+
_issue_create() {
|
|
3893
|
+
local title=""
|
|
3894
|
+
local repo=""
|
|
3895
|
+
local tag=""
|
|
3896
|
+
local body=""
|
|
3897
|
+
local labels=""
|
|
3898
|
+
local assignee=""
|
|
3899
|
+
local feature=""
|
|
3900
|
+
|
|
3901
|
+
while [[ $# -gt 0 ]]; do
|
|
3902
|
+
case "$1" in
|
|
3903
|
+
--repo) repo="$2"; shift 2 ;;
|
|
3904
|
+
--tag) tag="$2"; shift 2 ;;
|
|
3905
|
+
--body) body="$2"; shift 2 ;;
|
|
3906
|
+
--label) labels="$2"; shift 2 ;;
|
|
3907
|
+
--assignee) assignee="$2"; shift 2 ;;
|
|
3908
|
+
--feature) feature="$2"; shift 2 ;;
|
|
3909
|
+
--help|-h) _issue_help; return 0 ;;
|
|
3910
|
+
-*) ui_step_error "Unknown option: $1"; return 1 ;;
|
|
3911
|
+
*)
|
|
3912
|
+
if [[ -z "$title" ]]; then
|
|
3913
|
+
title="$1"
|
|
3914
|
+
else
|
|
3915
|
+
ui_step_error "Unexpected argument: $1"
|
|
3916
|
+
return 1
|
|
3917
|
+
fi
|
|
3918
|
+
shift
|
|
3919
|
+
;;
|
|
3920
|
+
esac
|
|
3921
|
+
done
|
|
3922
|
+
|
|
3923
|
+
if [[ -z "$title" ]]; then
|
|
3924
|
+
ui_step_error "Usage: revo issue create (--repo NAME | --tag TAG) \"TITLE\""
|
|
3925
|
+
return 1
|
|
3926
|
+
fi
|
|
3927
|
+
|
|
3928
|
+
if [[ -z "$repo" ]] && [[ -z "$tag" ]]; then
|
|
3929
|
+
ui_step_error "Either --repo or --tag is required"
|
|
3930
|
+
return 1
|
|
3931
|
+
fi
|
|
3932
|
+
|
|
3933
|
+
if [[ -n "$repo" ]] && [[ -n "$tag" ]]; then
|
|
3934
|
+
ui_step_error "Use either --repo or --tag, not both"
|
|
3935
|
+
return 1
|
|
3936
|
+
fi
|
|
3937
|
+
|
|
3938
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
3939
|
+
ui_step_error "gh CLI not found. Install from https://cli.github.com/"
|
|
3940
|
+
return 1
|
|
3941
|
+
fi
|
|
3942
|
+
|
|
3943
|
+
config_require_workspace || return 1
|
|
3944
|
+
|
|
3945
|
+
# Resolve target repo paths
|
|
3946
|
+
local target_paths=()
|
|
3947
|
+
if [[ -n "$repo" ]]; then
|
|
3948
|
+
local idx
|
|
3949
|
+
idx=$(yaml_find_by_name "$repo")
|
|
3950
|
+
if [[ $idx -lt 0 ]]; then
|
|
3951
|
+
ui_step_error "Repo not found in revo.yaml: $repo"
|
|
3952
|
+
return 1
|
|
3953
|
+
fi
|
|
3954
|
+
target_paths+=("$repo")
|
|
3955
|
+
else
|
|
3956
|
+
local repos
|
|
3957
|
+
repos=$(config_get_repos "$tag")
|
|
3958
|
+
local r
|
|
3959
|
+
while IFS= read -r r; do
|
|
3960
|
+
[[ -z "$r" ]] && continue
|
|
3961
|
+
local p
|
|
3962
|
+
p=$(yaml_get_path "$r")
|
|
3963
|
+
target_paths+=("$p")
|
|
3964
|
+
done <<< "$repos"
|
|
3965
|
+
fi
|
|
3966
|
+
|
|
3967
|
+
if [[ ${#target_paths[@]} -eq 0 ]]; then
|
|
3968
|
+
ui_step_error "No matching repos"
|
|
3969
|
+
return 1
|
|
3970
|
+
fi
|
|
3971
|
+
|
|
3972
|
+
ui_intro "Revo - Create issue: $title"
|
|
3973
|
+
|
|
3974
|
+
# Validate feature brief if --feature was given
|
|
3975
|
+
local feature_brief=""
|
|
3976
|
+
if [[ -n "$feature" ]]; then
|
|
3977
|
+
feature_brief="$REVO_WORKSPACE_ROOT/.revo/features/$feature.md"
|
|
3978
|
+
if [[ ! -f "$feature_brief" ]]; then
|
|
3979
|
+
ui_step_error "Feature brief not found: .revo/features/$feature.md"
|
|
3980
|
+
ui_info "$(ui_dim "Run 'revo feature $feature' first to create it")"
|
|
3981
|
+
return 1
|
|
3982
|
+
fi
|
|
3983
|
+
fi
|
|
3984
|
+
|
|
3985
|
+
# Build the initial body that all created issues share
|
|
3986
|
+
local effective_body="$body"
|
|
3987
|
+
if [[ -z "$effective_body" ]]; then
|
|
3988
|
+
effective_body="Created by \`revo issue create\`."
|
|
3989
|
+
fi
|
|
3990
|
+
if [[ -n "$feature" ]]; then
|
|
3991
|
+
effective_body="$effective_body"$'\n\n'"Part of feature: \`$feature\` (see \`.revo/features/$feature.md\`)"
|
|
3992
|
+
fi
|
|
3993
|
+
|
|
3994
|
+
# --- Pass 1: create issues -------------------------------------------
|
|
3995
|
+
local issue_paths=()
|
|
3996
|
+
local issue_urls=()
|
|
3997
|
+
local issue_numbers=()
|
|
3998
|
+
local fail_count=0
|
|
3999
|
+
local skip_count=0
|
|
4000
|
+
|
|
4001
|
+
local p
|
|
4002
|
+
for p in "${target_paths[@]}"; do
|
|
4003
|
+
local full_path="$REVO_REPOS_DIR/$p"
|
|
4004
|
+
|
|
4005
|
+
if [[ ! -d "$full_path" ]]; then
|
|
4006
|
+
ui_step_done "Skipped (not cloned):" "$p"
|
|
4007
|
+
skip_count=$((skip_count + 1))
|
|
4008
|
+
continue
|
|
4009
|
+
fi
|
|
4010
|
+
|
|
4011
|
+
local gh_args=( --title "$title" --body "$effective_body" )
|
|
4012
|
+
if [[ -n "$labels" ]]; then
|
|
4013
|
+
gh_args+=( --label "$labels" )
|
|
4014
|
+
fi
|
|
4015
|
+
if [[ -n "$assignee" ]]; then
|
|
4016
|
+
gh_args+=( --assignee "$assignee" )
|
|
4017
|
+
fi
|
|
4018
|
+
|
|
4019
|
+
local output
|
|
4020
|
+
if output=$(cd "$full_path" && gh issue create "${gh_args[@]}" 2>&1); then
|
|
4021
|
+
local url
|
|
4022
|
+
url=$(printf '%s' "$output" | grep -Eo 'https://github\.com/[^ ]+/issues/[0-9]+' | tail -1)
|
|
4023
|
+
if [[ -z "$url" ]]; then
|
|
4024
|
+
url="$output"
|
|
4025
|
+
fi
|
|
4026
|
+
local number="${url##*/}"
|
|
4027
|
+
issue_paths+=("$p")
|
|
4028
|
+
issue_urls+=("$url")
|
|
4029
|
+
issue_numbers+=("$number")
|
|
4030
|
+
ui_step_done "Created:" "$p → $url"
|
|
4031
|
+
else
|
|
4032
|
+
ui_step_error "Failed: $p"
|
|
4033
|
+
ui_info "$(ui_dim "$output")"
|
|
4034
|
+
fail_count=$((fail_count + 1))
|
|
4035
|
+
fi
|
|
4036
|
+
done
|
|
4037
|
+
|
|
4038
|
+
# --- Pass 2: cross-references ----------------------------------------
|
|
4039
|
+
if [[ ${#issue_paths[@]} -gt 1 ]]; then
|
|
4040
|
+
ui_bar_line
|
|
4041
|
+
ui_step "Linking issues with cross-references..."
|
|
4042
|
+
|
|
4043
|
+
local xref
|
|
4044
|
+
xref=$'\n\n---\n**Coordinated issues (revo):**\n'
|
|
4045
|
+
local i
|
|
4046
|
+
for ((i = 0; i < ${#issue_paths[@]}; i++)); do
|
|
4047
|
+
xref+="- ${issue_paths[$i]}: ${issue_urls[$i]}"$'\n'
|
|
4048
|
+
done
|
|
4049
|
+
|
|
4050
|
+
for ((i = 0; i < ${#issue_paths[@]}; i++)); do
|
|
4051
|
+
local repo_path="${issue_paths[$i]}"
|
|
4052
|
+
local issue_number="${issue_numbers[$i]}"
|
|
4053
|
+
local full_path="$REVO_REPOS_DIR/$repo_path"
|
|
4054
|
+
local combined_body="$effective_body$xref"
|
|
4055
|
+
|
|
4056
|
+
if (cd "$full_path" && gh issue edit "$issue_number" --body "$combined_body" >/dev/null 2>&1); then
|
|
4057
|
+
ui_step_done "Linked:" "$repo_path"
|
|
4058
|
+
else
|
|
4059
|
+
ui_step_error "Failed to link: $repo_path"
|
|
4060
|
+
fi
|
|
4061
|
+
done
|
|
4062
|
+
fi
|
|
4063
|
+
|
|
4064
|
+
# --- Pass 3: append to feature brief ---------------------------------
|
|
4065
|
+
if [[ -n "$feature_brief" ]] && [[ ${#issue_paths[@]} -gt 0 ]]; then
|
|
4066
|
+
ui_bar_line
|
|
4067
|
+
ui_step "Appending to feature brief: $feature"
|
|
4068
|
+
{
|
|
4069
|
+
printf '\n## Issues (created by revo)\n\n'
|
|
4070
|
+
local i
|
|
4071
|
+
for ((i = 0; i < ${#issue_paths[@]}; i++)); do
|
|
4072
|
+
printf -- '- **%s**: %s\n' "${issue_paths[$i]}" "${issue_urls[$i]}"
|
|
4073
|
+
done
|
|
4074
|
+
} >> "$feature_brief"
|
|
4075
|
+
ui_step_done "Updated:" ".revo/features/$feature.md"
|
|
4076
|
+
fi
|
|
4077
|
+
|
|
4078
|
+
ui_bar_line
|
|
4079
|
+
|
|
4080
|
+
# Print URLs to stdout (LLM-friendly: one URL per line, parseable)
|
|
4081
|
+
local i
|
|
4082
|
+
for ((i = 0; i < ${#issue_urls[@]}; i++)); do
|
|
4083
|
+
printf '%s\n' "${issue_urls[$i]}"
|
|
4084
|
+
done
|
|
4085
|
+
|
|
4086
|
+
if [[ $fail_count -eq 0 ]]; then
|
|
4087
|
+
local msg="Created ${#issue_paths[@]} issue(s)"
|
|
4088
|
+
[[ $skip_count -gt 0 ]] && msg+=", $skip_count skipped"
|
|
4089
|
+
ui_outro "$msg"
|
|
4090
|
+
return 0
|
|
4091
|
+
else
|
|
4092
|
+
ui_outro_cancel "${#issue_paths[@]} created, $fail_count failed"
|
|
4093
|
+
return 1
|
|
4094
|
+
fi
|
|
4095
|
+
}
|
|
4096
|
+
|
|
3661
4097
|
# === Main ===
|
|
3662
4098
|
# --- Help ---
|
|
3663
4099
|
show_help() {
|
|
@@ -3684,6 +4120,8 @@ Claude-first commands:
|
|
|
3684
4120
|
commit MSG [--tag TAG] Commit changes across dirty repos
|
|
3685
4121
|
push [--tag TAG] Push branches across repositories
|
|
3686
4122
|
pr TITLE [--tag TAG] Create coordinated PRs via gh CLI
|
|
4123
|
+
issue list [options] List issues across workspace repos
|
|
4124
|
+
issue create [options] TITLE Create issue(s) in workspace repos
|
|
3687
4125
|
|
|
3688
4126
|
Options:
|
|
3689
4127
|
--tag TAG Filter repositories by tag
|
|
@@ -3705,6 +4143,10 @@ Examples:
|
|
|
3705
4143
|
revo commit "wire up clock endpoint"
|
|
3706
4144
|
revo push
|
|
3707
4145
|
revo pr "Clock endpoint for students" --tag backend
|
|
4146
|
+
revo issue list --state open # all open issues
|
|
4147
|
+
revo issue list --tag backend --json # JSON for Claude/jq
|
|
4148
|
+
revo issue create --repo backend "Add stats endpoint"
|
|
4149
|
+
revo issue create --tag mobile --feature stats "Add statistics screen"
|
|
3708
4150
|
|
|
3709
4151
|
Documentation: https://github.com/jippylong12/revo
|
|
3710
4152
|
EOF
|
|
@@ -3773,6 +4215,9 @@ main() {
|
|
|
3773
4215
|
pr)
|
|
3774
4216
|
cmd_pr "$@"
|
|
3775
4217
|
;;
|
|
4218
|
+
issue|issues)
|
|
4219
|
+
cmd_issue "$@"
|
|
4220
|
+
;;
|
|
3776
4221
|
--help|-h|help)
|
|
3777
4222
|
show_help
|
|
3778
4223
|
;;
|