framework tools/git: strict value guards + provider-failure normalization family-wide (codex blockers on PR #1464)
ci/woodpecker/pr/ci Pipeline is pending

- Value guards now reject option-like values: --anything always, and
  single-dash flag shapes (-h, -i). Previously -b --help consumed
  --help as the body and performed the write (codex example:
  issue-close -i --help proceeding to 'Closed GitHub issue #--help').
  Multi-char dash-leading text (-start of a list) stays a legal value.
- Every remaining direct provider exec (gh/tea/CMD arrays across
  issue-create/edit/assign/list/view, milestone-*, pr-create/edit,
  pr-close, issue-close/reopen) wrapped with rc capture and normalized
  to exit 1 with a stderr message — provider exit 2 no longer collides
  with the reserved usage-error status.
- All 16 usage-contract suites gained option-like and short-flag arms
  (16/16 green). Existing suites re-verified; test-pr-edit and
  test-issue-create-interactive-auth fail identically with these
  changes stashed (environment-coupled, not regressions; documented).
This commit is contained in:
2026-08-28 18:26:10 -05:00
parent ca91586222
commit d326e6572a
32 changed files with 264 additions and 90 deletions
@@ -47,22 +47,22 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE="$2" ISSUE="$2"
shift 2 shift 2
;; ;;
-a|--assignee) -a|--assignee)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ASSIGNEE="$2" ASSIGNEE="$2"
shift 2 shift 2
;; ;;
-l|--labels) -l|--labels)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LABELS="$2" LABELS="$2"
shift 2 shift 2
;; ;;
-m|--milestone) -m|--milestone)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
MILESTONE="$2" MILESTONE="$2"
shift 2 shift 2
;; ;;
@@ -90,20 +90,28 @@ PLATFORM=$(detect_platform)
case "$PLATFORM" in case "$PLATFORM" in
github) github)
if [[ -n "$ASSIGNEE" ]]; then if [[ -n "$ASSIGNEE" ]]; then
gh issue edit "$ISSUE" --add-assignee "$ASSIGNEE" prov_rc=0
gh issue edit "$ISSUE" --add-assignee "$ASSIGNEE" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
if [[ "$REMOVE_ASSIGNEE" == true ]]; then if [[ "$REMOVE_ASSIGNEE" == true ]]; then
# Get current assignees and remove them # Get current assignees and remove them
CURRENT=$(gh issue view "$ISSUE" --json assignees -q '.assignees[].login' 2>/dev/null | tr '\n' ',') CURRENT=$(gh issue view "$ISSUE" --json assignees -q '.assignees[].login' 2>/dev/null | tr '\n' ',')
if [[ -n "$CURRENT" ]]; then if [[ -n "$CURRENT" ]]; then
gh issue edit "$ISSUE" --remove-assignee "${CURRENT%,}" prov_rc=0
gh issue edit "$ISSUE" --remove-assignee "${CURRENT%,}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
fi fi
if [[ -n "$LABELS" ]]; then if [[ -n "$LABELS" ]]; then
gh issue edit "$ISSUE" --add-label "$LABELS" prov_rc=0
gh issue edit "$ISSUE" --add-label "$LABELS" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
if [[ -n "$MILESTONE" ]]; then if [[ -n "$MILESTONE" ]]; then
gh issue edit "$ISSUE" --milestone "$MILESTONE" prov_rc=0
gh issue edit "$ISSUE" --milestone "$MILESTONE" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
echo "Issue #$ISSUE updated successfully" echo "Issue #$ISSUE updated successfully"
;; ;;
@@ -142,7 +150,9 @@ case "$PLATFORM" in
fi fi
if [[ "$NEEDS_EDIT" == true ]]; then if [[ "$NEEDS_EDIT" == true ]]; then
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Issue #$ISSUE updated successfully" echo "Issue #$ISSUE updated successfully"
else else
echo "No changes specified" echo "No changes specified"
@@ -25,14 +25,14 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE_NUMBER="$2" ISSUE_NUMBER="$2"
shift 2 shift 2
;; ;;
-b|--body|-c|--comment) -b|--body|-c|--comment)
# R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays
# a backward-compatible alias. # a backward-compatible alias.
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
COMMENT="$2" COMMENT="$2"
shift 2 shift 2
;; ;;
@@ -134,7 +134,9 @@ elif [[ "$PLATFORM" == "gitea" ]]; then
exit 1 exit 1
} }
fi fi
tea issue close "$ISSUE_NUMBER" --repo "$OWNER/$REPO" --login "$GITEA_LOGIN_NAME" prov_rc=0
tea issue close "$ISSUE_NUMBER" --repo "$OWNER/$REPO" --login "$GITEA_LOGIN_NAME" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
else else
echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2 echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2
if [[ -n "$COMMENT" ]]; then if [[ -n "$COMMENT" ]]; then
@@ -46,7 +46,7 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE_NUMBER="$2" ISSUE_NUMBER="$2"
shift 2 shift 2
;; ;;
@@ -54,12 +54,12 @@ while [[ $# -gt 0 ]]; do
# R1 (2026-08-28): --body is the canonical flag, matching # R1 (2026-08-28): --body is the canonical flag, matching
# issue-create/issue-edit/pr-create/pr-edit; -c/--comment stays a # issue-create/issue-edit/pr-create/pr-edit; -c/--comment stays a
# backward-compatible alias. # backward-compatible alias.
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
COMMENT="$2" COMMENT="$2"
shift 2 shift 2
;; ;;
-l|--login) -l|--login)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LOGIN_OVERRIDE="$2" LOGIN_OVERRIDE="$2"
shift 2 shift 2
;; ;;
@@ -91,22 +91,22 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-t|--title) -t|--title)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
TITLE="$2" TITLE="$2"
shift 2 shift 2
;; ;;
-b|--body) -b|--body)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
BODY="$2" BODY="$2"
shift 2 shift 2
;; ;;
-l|--labels) -l|--labels)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LABELS="$2" LABELS="$2"
shift 2 shift 2
;; ;;
-m|--milestone) -m|--milestone)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
MILESTONE="$2" MILESTONE="$2"
shift 2 shift 2
;; ;;
@@ -144,7 +144,9 @@ case "$PLATFORM" in
[[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$BODY" ]] && CMD+=(--body "$BODY")
[[ -n "$LABELS" ]] && CMD+=(--label "$LABELS") [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS")
[[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
gitea) gitea)
if command -v tea >/dev/null 2>&1; then if command -v tea >/dev/null 2>&1; then
@@ -26,27 +26,27 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE_NUMBER="$2" ISSUE_NUMBER="$2"
shift 2 shift 2
;; ;;
-t|--title) -t|--title)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
TITLE="$2" TITLE="$2"
shift 2 shift 2
;; ;;
-b|--body) -b|--body)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
BODY="$2" BODY="$2"
shift 2 shift 2
;; ;;
-l|--labels) -l|--labels)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LABELS="$2" LABELS="$2"
shift 2 shift 2
;; ;;
-m|--milestone) -m|--milestone)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
MILESTONE="$2" MILESTONE="$2"
shift 2 shift 2
;; ;;
@@ -82,7 +82,9 @@ if [[ "$PLATFORM" == "github" ]]; then
[[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$BODY" ]] && CMD+=(--body "$BODY")
[[ -n "$LABELS" ]] && CMD+=(--add-label "$LABELS") [[ -n "$LABELS" ]] && CMD+=(--add-label "$LABELS")
[[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Updated GitHub issue #$ISSUE_NUMBER" echo "Updated GitHub issue #$ISSUE_NUMBER"
elif [[ "$PLATFORM" == "gitea" ]]; then elif [[ "$PLATFORM" == "gitea" ]]; then
REPO_SLUG=$(get_repo_slug) || { REPO_SLUG=$(get_repo_slug) || {
@@ -98,7 +100,9 @@ elif [[ "$PLATFORM" == "gitea" ]]; then
[[ -n "$BODY" ]] && CMD+=(--description "$BODY") [[ -n "$BODY" ]] && CMD+=(--description "$BODY")
[[ -n "$LABELS" ]] && CMD+=(--add-labels "$LABELS") [[ -n "$LABELS" ]] && CMD+=(--add-labels "$LABELS")
[[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Updated Gitea issue #$ISSUE_NUMBER" echo "Updated Gitea issue #$ISSUE_NUMBER"
else else
echo "Error: Unknown platform" echo "Error: Unknown platform"
@@ -50,32 +50,32 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-s|--state) -s|--state)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
STATE="$2" STATE="$2"
shift 2 shift 2
;; ;;
-l|--label) -l|--label)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LABEL="$2" LABEL="$2"
shift 2 shift 2
;; ;;
-m|--milestone) -m|--milestone)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
MILESTONE="$2" MILESTONE="$2"
shift 2 shift 2
;; ;;
-a|--assignee) -a|--assignee)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ASSIGNEE="$2" ASSIGNEE="$2"
shift 2 shift 2
;; ;;
-n|--limit) -n|--limit)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LIMIT="$2" LIMIT="$2"
shift 2 shift 2
;; ;;
-r|--repo) -r|--repo)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
REPO_OVERRIDE="$2" REPO_OVERRIDE="$2"
shift 2 shift 2
;; ;;
@@ -108,7 +108,9 @@ case "$PLATFORM" in
[[ -n "$LABEL" ]] && CMD+=(--label "$LABEL") [[ -n "$LABEL" ]] && CMD+=(--label "$LABEL")
[[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
[[ -n "$ASSIGNEE" ]] && CMD+=(--assignee "$ASSIGNEE") [[ -n "$ASSIGNEE" ]] && CMD+=(--assignee "$ASSIGNEE")
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
gitea) gitea)
if [[ -n "$REPO_OVERRIDE" ]]; then if [[ -n "$REPO_OVERRIDE" ]]; then
@@ -127,7 +129,9 @@ case "$PLATFORM" in
[[ -n "$MILESTONE" ]] && CMD+=(--milestones "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestones "$MILESTONE")
# Note: tea may not support assignee filter directly in all versions. # Note: tea may not support assignee filter directly in all versions.
[[ -n "$ASSIGNEE" ]] && echo "Note: Assignee filtering may require manual review for Gitea" >&2 [[ -n "$ASSIGNEE" ]] && echo "Note: Assignee filtering may require manual review for Gitea" >&2
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
*) *)
echo "Error: Could not detect git platform" >&2 echo "Error: Could not detect git platform" >&2
@@ -24,14 +24,14 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE_NUMBER="$2" ISSUE_NUMBER="$2"
shift 2 shift 2
;; ;;
-b|--body|-c|--comment) -b|--body|-c|--comment)
# R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays
# a backward-compatible alias. # a backward-compatible alias.
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
COMMENT="$2" COMMENT="$2"
shift 2 shift 2
;; ;;
@@ -116,9 +116,13 @@ elif [[ "$PLATFORM" == "gitea" ]]; then
REPO_ARGS=$(get_gitea_repo_args || true) REPO_ARGS=$(get_gitea_repo_args || true)
if [[ -n "$REPO_ARGS" ]]; then if [[ -n "$REPO_ARGS" ]]; then
if [[ -n "$COMMENT" ]]; then if [[ -n "$COMMENT" ]]; then
tea issue comment "$ISSUE_NUMBER" "$COMMENT" $REPO_ARGS prov_rc=0
tea issue comment "$ISSUE_NUMBER" "$COMMENT" $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
tea issue reopen "$ISSUE_NUMBER" $REPO_ARGS prov_rc=0
tea issue reopen "$ISSUE_NUMBER" $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
else else
echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2 echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2
if [[ -n "$COMMENT" ]]; then if [[ -n "$COMMENT" ]]; then
@@ -82,7 +82,7 @@ if comments:
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE_NUMBER="$2" ISSUE_NUMBER="$2"
shift 2 shift 2
;; ;;
@@ -109,7 +109,9 @@ fi
detect_platform >/dev/null detect_platform >/dev/null
if [[ "$PLATFORM" == "github" ]]; then if [[ "$PLATFORM" == "github" ]]; then
gh issue view "$ISSUE_NUMBER" prov_rc=0
gh issue view "$ISSUE_NUMBER" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
elif [[ "$PLATFORM" == "gitea" ]]; then elif [[ "$PLATFORM" == "gitea" ]]; then
if command -v tea >/dev/null 2>&1; then if command -v tea >/dev/null 2>&1; then
# --comments is what makes tea print the comment bodies (#1357 F3). # --comments is what makes tea print the comment bodies (#1357 F3).
@@ -37,11 +37,11 @@ usage_error() {
} }
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-r|--repo) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; REPO="$2"; shift 2 ;; -r|--repo) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; REPO="$2"; shift 2 ;;
-m|--milestone) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; MILESTONE="$2"; shift 2 ;; -m|--milestone) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; MILESTONE="$2"; shift 2 ;;
-l|--label|--labels) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LABEL="$2"; shift 2 ;; -l|--label|--labels) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LABEL="$2"; shift 2 ;;
-L|--login) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LOGIN="$2"; shift 2 ;; -L|--login) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LOGIN="$2"; shift 2 ;;
-n|--limit) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LIMIT="$2"; shift 2 ;; -n|--limit) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LIMIT="$2"; shift 2 ;;
-h|--help) grep '^#' "$0" | sed 's/^# \?//'; exit 0 ;; -h|--help) grep '^#' "$0" | sed 's/^# \?//'; exit 0 ;;
*) usage_error "unknown option: $1" ;; *) usage_error "unknown option: $1" ;;
esac esac
@@ -21,7 +21,7 @@ TITLE=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-t|--title) -t|--title)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
TITLE="$2" TITLE="$2"
shift 2 shift 2
;; ;;
@@ -46,14 +46,18 @@ fi
detect_platform >/dev/null detect_platform >/dev/null
if [[ "$PLATFORM" == "github" ]]; then 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 prov_rc=0
gh api -X PATCH "/repos/{owner}/{repo}/milestones/$(gh api "/repos/{owner}/{repo}/milestones" --jq ".[] | select(.title==\"$TITLE\") | .number")" -f state=closed || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Closed GitHub milestone: $TITLE" echo "Closed GitHub milestone: $TITLE"
elif [[ "$PLATFORM" == "gitea" ]]; then elif [[ "$PLATFORM" == "gitea" ]]; then
REPO_ARGS=$(get_gitea_repo_args) || { REPO_ARGS=$(get_gitea_repo_args) || {
echo "Error: Could not resolve Gitea repo/login for remote host" >&2 echo "Error: Could not resolve Gitea repo/login for remote host" >&2
exit 1 exit 1
} }
tea milestone close "$TITLE" $REPO_ARGS prov_rc=0
tea milestone close "$TITLE" $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Closed Gitea milestone: $TITLE" echo "Closed Gitea milestone: $TITLE"
else else
echo "Error: Unknown platform" echo "Error: Unknown platform"
@@ -51,17 +51,17 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-t|--title) -t|--title)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
TITLE="$2" TITLE="$2"
shift 2 shift 2
;; ;;
-d|--desc) -d|--desc)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
DESCRIPTION="$2" DESCRIPTION="$2"
shift 2 shift 2
;; ;;
--due) --due)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
DUE_DATE="$2" DUE_DATE="$2"
shift 2 shift 2
;; ;;
@@ -84,14 +84,18 @@ PLATFORM=$(detect_platform)
if [[ "$LIST_ONLY" == true ]]; then if [[ "$LIST_ONLY" == true ]]; then
case "$PLATFORM" in case "$PLATFORM" in
github) github)
gh api repos/:owner/:repo/milestones --jq '.[] | "\(.number)\t\(.title)\t\(.state)\t\(.open_issues)/\(.closed_issues) issues"' prov_rc=0
gh api repos/:owner/:repo/milestones --jq '.[] | "\(.number)\t\(.title)\t\(.state)\t\(.open_issues)/\(.closed_issues) issues"' || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
gitea) gitea)
REPO_ARGS=$(get_gitea_repo_args) || { REPO_ARGS=$(get_gitea_repo_args) || {
echo "Error: Could not resolve Gitea repo/login for remote host" >&2 echo "Error: Could not resolve Gitea repo/login for remote host" >&2
exit 1 exit 1
} }
tea milestones list $REPO_ARGS prov_rc=0
tea milestones list $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
*) *)
echo "Error: Could not detect git platform" >&2 echo "Error: Could not detect git platform" >&2
@@ -118,7 +122,9 @@ case "$PLATFORM" in
+ (if $d != "" then {"description": $d} else {} end) + (if $d != "" then {"description": $d} else {} end)
+ (if $due != "" then {"due_on": ($due + "T00:00:00Z")} else {} end)') + (if $due != "" then {"due_on": ($due + "T00:00:00Z")} else {} end)')
gh api repos/:owner/:repo/milestones --method POST --input - <<< "$JSON_PAYLOAD" prov_rc=0
gh api repos/:owner/:repo/milestones --method POST --input - <<< "$JSON_PAYLOAD" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Milestone '$TITLE' created successfully" echo "Milestone '$TITLE' created successfully"
;; ;;
gitea) gitea)
@@ -129,7 +135,9 @@ case "$PLATFORM" in
CMD=(tea milestones create --title "$TITLE") CMD=(tea milestones create --title "$TITLE")
[[ -n "$DESCRIPTION" ]] && CMD+=(--description "$DESCRIPTION") [[ -n "$DESCRIPTION" ]] && CMD+=(--description "$DESCRIPTION")
[[ -n "$DUE_DATE" ]] && CMD+=(--deadline "$DUE_DATE") [[ -n "$DUE_DATE" ]] && CMD+=(--deadline "$DUE_DATE")
"${CMD[@]}" $REPO_ARGS prov_rc=0
"${CMD[@]}" $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Milestone '$TITLE' created successfully" echo "Milestone '$TITLE' created successfully"
;; ;;
*) *)
@@ -21,7 +21,7 @@ STATE="open"
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-s|--state) -s|--state)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
STATE="$2" STATE="$2"
shift 2 shift 2
;; ;;
@@ -42,13 +42,17 @@ done
detect_platform >/dev/null detect_platform >/dev/null
if [[ "$PLATFORM" == "github" ]]; then if [[ "$PLATFORM" == "github" ]]; then
gh api "/repos/{owner}/{repo}/milestones?state=$STATE" --jq '.[] | "\(.title) (\(.state)) - \(.open_issues) open, \(.closed_issues) closed"' prov_rc=0
gh api "/repos/{owner}/{repo}/milestones?state=$STATE" --jq '.[] | "\(.title) (\(.state)) - \(.open_issues) open, \(.closed_issues) closed"' || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
elif [[ "$PLATFORM" == "gitea" ]]; then elif [[ "$PLATFORM" == "gitea" ]]; then
REPO_ARGS=$(get_gitea_repo_args) || { REPO_ARGS=$(get_gitea_repo_args) || {
echo "Error: Could not resolve Gitea repo/login for remote host" >&2 echo "Error: Could not resolve Gitea repo/login for remote host" >&2
exit 1 exit 1
} }
tea milestone list $REPO_ARGS prov_rc=0
tea milestone list $REPO_ARGS || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
else else
echo "Error: Unknown platform" echo "Error: Unknown platform"
exit 1 exit 1
@@ -24,14 +24,14 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-n|--number) -n|--number)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
PR_NUMBER="$2" PR_NUMBER="$2"
shift 2 shift 2
;; ;;
-b|--body|-c|--comment) -b|--body|-c|--comment)
# R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays
# a backward-compatible alias. # a backward-compatible alias.
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
COMMENT="$2" COMMENT="$2"
shift 2 shift 2
;; ;;
@@ -79,9 +79,13 @@ if [[ "$PLATFORM" == "github" ]]; then
echo "Closed GitHub PR #$PR_NUMBER" echo "Closed GitHub PR #$PR_NUMBER"
elif [[ "$PLATFORM" == "gitea" ]]; then elif [[ "$PLATFORM" == "gitea" ]]; then
if [[ -n "$COMMENT" ]]; then if [[ -n "$COMMENT" ]]; then
tea pr comment "$PR_NUMBER" "$COMMENT" $(get_gitea_repo_args) prov_rc=0
tea pr comment "$PR_NUMBER" "$COMMENT" $(get_gitea_repo_args) || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
tea pr close "$PR_NUMBER" $(get_gitea_repo_args) prov_rc=0
tea pr close "$PR_NUMBER" $(get_gitea_repo_args) || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
echo "Closed Gitea PR #$PR_NUMBER" echo "Closed Gitea PR #$PR_NUMBER"
else else
echo "Error: Unknown platform" echo "Error: Unknown platform"
@@ -149,37 +149,37 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-t|--title) -t|--title)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
TITLE="$2" TITLE="$2"
shift 2 shift 2
;; ;;
-b|--body) -b|--body)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
BODY="$2" BODY="$2"
shift 2 shift 2
;; ;;
-B|--base) -B|--base)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
BASE_BRANCH="$2" BASE_BRANCH="$2"
shift 2 shift 2
;; ;;
-H|--head) -H|--head)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
HEAD_BRANCH="$2" HEAD_BRANCH="$2"
shift 2 shift 2
;; ;;
-l|--labels) -l|--labels)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LABELS="$2" LABELS="$2"
shift 2 shift 2
;; ;;
-m|--milestone) -m|--milestone)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
MILESTONE="$2" MILESTONE="$2"
shift 2 shift 2
;; ;;
-i|--issue) -i|--issue)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ISSUE="$2" ISSUE="$2"
shift 2 shift 2
;; ;;
@@ -280,7 +280,9 @@ case "$PLATFORM" in
[[ -n "$LABELS" ]] && CMD+=(--label "$LABELS") [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS")
[[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
[[ "$DRAFT" == true ]] && CMD+=(--draft) [[ "$DRAFT" == true ]] && CMD+=(--draft)
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
;; ;;
gitea) gitea)
# tea pull create syntax. Always pass --repo because tea repo inference # tea pull create syntax. Always pass --repo because tea repo inference
+16 -10
View File
@@ -62,19 +62,19 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-n|--number) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; PR_NUMBER="${2:-}"; shift 2 ;; -n|--number) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; PR_NUMBER="${2:-}"; shift 2 ;;
-t|--title) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; TITLE="${2:-}"; shift 2 ;; -t|--title) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; TITLE="${2:-}"; shift 2 ;;
-b|--body) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; BODY="${2:-}"; shift 2 ;; -b|--body) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; BODY="${2:-}"; shift 2 ;;
-B|--base) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; BASE_BRANCH="${2:-}"; shift 2 ;; -B|--base) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; BASE_BRANCH="${2:-}"; shift 2 ;;
--draft) --draft)
[[ "$DRAFT_MODE" != "ready" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; } [[ "$DRAFT_MODE" != "ready" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; }
DRAFT_MODE="draft"; shift ;; DRAFT_MODE="draft"; shift ;;
--ready) --ready)
[[ "$DRAFT_MODE" != "draft" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; } [[ "$DRAFT_MODE" != "draft" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; }
DRAFT_MODE="ready"; shift ;; DRAFT_MODE="ready"; shift ;;
-l|--login) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LOGIN_OVERRIDE="${2:-}"; shift 2 ;; -l|--login) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LOGIN_OVERRIDE="${2:-}"; shift 2 ;;
-r|--repo) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; REPO_OVERRIDE="${2:-}"; shift 2 ;; -r|--repo) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; REPO_OVERRIDE="${2:-}"; shift 2 ;;
-H|--host) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; HOST_OVERRIDE="${2:-}"; shift 2 ;; -H|--host) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; HOST_OVERRIDE="${2:-}"; shift 2 ;;
-h|--help) usage 0 ;; -h|--help) usage 0 ;;
*) echo "Unknown option: $1" >&2; usage ;; *) echo "Unknown option: $1" >&2; usage ;;
esac esac
@@ -105,12 +105,18 @@ case "$PLATFORM" in
[[ -n "$TITLE" ]] && CMD+=(--title "$TITLE") [[ -n "$TITLE" ]] && CMD+=(--title "$TITLE")
[[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$BODY" ]] && CMD+=(--body "$BODY")
[[ -n "$BASE_BRANCH" ]] && CMD+=(--base "$BASE_BRANCH") [[ -n "$BASE_BRANCH" ]] && CMD+=(--base "$BASE_BRANCH")
"${CMD[@]}" prov_rc=0
"${CMD[@]}" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
if [[ "$DRAFT_MODE" == "draft" ]]; then if [[ "$DRAFT_MODE" == "draft" ]]; then
gh pr ready "$PR_NUMBER" --undo prov_rc=0
gh pr ready "$PR_NUMBER" --undo || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
elif [[ "$DRAFT_MODE" == "ready" ]]; then elif [[ "$DRAFT_MODE" == "ready" ]]; then
gh pr ready "$PR_NUMBER" prov_rc=0
gh pr ready "$PR_NUMBER" || prov_rc=$?
[[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; }
fi fi
;; ;;
gitea) gitea)
@@ -55,34 +55,34 @@ usage_error() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-n|--number) -n|--number)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
PR_NUMBER="$2" PR_NUMBER="$2"
shift 2 shift 2
;; ;;
-a|--action) -a|--action)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
ACTION="$2" ACTION="$2"
shift 2 shift 2
;; ;;
-b|--body|-c|--comment) -b|--body|-c|--comment)
# R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays
# a backward-compatible alias. # a backward-compatible alias.
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
COMMENT="$2" COMMENT="$2"
shift 2 shift 2
;; ;;
-l|--login) -l|--login)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
LOGIN_OVERRIDE="$2" LOGIN_OVERRIDE="$2"
shift 2 shift 2
;; ;;
-r|--repo) -r|--repo)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
REPO_OVERRIDE="$2" REPO_OVERRIDE="$2"
shift 2 shift 2
;; ;;
-H|--host) -H|--host)
[[ $# -ge 2 ]] || usage_error "option $1 requires a value" [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"
HOST_OVERRIDE="$2" HOST_OVERRIDE="$2"
shift 2 shift 2
;; ;;
@@ -711,14 +711,18 @@ PY
if [[ "$PLATFORM" == "github" ]]; then if [[ "$PLATFORM" == "github" ]]; then
case $ACTION in case $ACTION in
approve) approve)
gh pr review "$PR_NUMBER" --approve ${COMMENT:+--body "$COMMENT"} gh_rc=0
gh pr review "$PR_NUMBER" --approve ${COMMENT:+--body "$COMMENT"} || gh_rc=$?
[[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub approve failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; }
echo "Approved GitHub PR #$PR_NUMBER" echo "Approved GitHub PR #$PR_NUMBER"
;; ;;
request-changes) request-changes)
if [[ -z "$COMMENT" ]]; then if [[ -z "$COMMENT" ]]; then
usage_error "comment required for request-changes (-b/--body)" usage_error "comment required for request-changes (-b/--body)"
fi fi
gh pr review "$PR_NUMBER" --request-changes --body "$COMMENT" gh_rc=0
gh pr review "$PR_NUMBER" --request-changes --body "$COMMENT" || gh_rc=$?
[[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub request-changes failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; }
echo "Requested changes on GitHub PR #$PR_NUMBER" echo "Requested changes on GitHub PR #$PR_NUMBER"
;; ;;
comment) comment)
@@ -726,7 +730,9 @@ if [[ "$PLATFORM" == "github" ]]; then
echo "Error: Comment required" echo "Error: Comment required"
exit 1 exit 1
fi fi
gh pr review "$PR_NUMBER" --comment --body "$COMMENT" gh_rc=0
gh pr review "$PR_NUMBER" --comment --body "$COMMENT" || gh_rc=$?
[[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub review comment failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; }
echo "Added review comment to GitHub PR #$PR_NUMBER" echo "Added review comment to GitHub PR #$PR_NUMBER"
;; ;;
*) *)
@@ -99,6 +99,13 @@ for flag in -i -a -l -m --issue --assignee --labels --milestone; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i 5 -a --help
expect_rc 2 "short flag value rejected" -i 5 -a -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -97,6 +97,13 @@ for flag in -i -b -c --issue --body --comment; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i 5 -b --help
expect_rc 2 "short flag value rejected" -i 5 -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -124,6 +124,13 @@ for flag in -i -b -c -l --issue --body --comment --login; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i 5 -b --help
expect_rc 2 "short flag value rejected" -i 5 -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 6. Alias acceptance at parse level: both -b and -c carry a value past # 6. Alias acceptance at parse level: both -b and -c carry a value past
# parsing; the wrapper then fails at platform detection (not a git repo) # parsing; the wrapper then fails at platform detection (not a git repo)
# nonzero but NOT as a usage error (rc must not be 2). # nonzero but NOT as a usage error (rc must not be 2).
@@ -99,6 +99,13 @@ for flag in -t -b -l -m --title --body --labels --milestone; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -t smoke -b --help
expect_rc 2 "short flag value rejected" -t smoke -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -i -t -b -l -m --issue --title --body --labels --milestone; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i 5 -b --help
expect_rc 2 "short flag value rejected" -i 5 -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -97,6 +97,13 @@ for flag in -s -l -m -a -n -r --state --label --milestone --assignee --limit --r
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -s --help
expect_rc 2 "short flag value rejected" -s -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -97,6 +97,13 @@ for flag in -i -b -c --issue --body --comment; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i 5 -b --help
expect_rc 2 "short flag value rejected" -i 5 -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -i --issue; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -i --help
expect_rc 2 "short flag value rejected" -i -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -r -m -l -L -n --repo --milestone --label --login --limit; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -r owner/repo -m --help
expect_rc 2 "short flag value rejected" -r owner/repo -m -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -t --title; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -t --help --help
expect_rc 2 "short flag value rejected" -t --help -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -t -d --due --title --desc; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -t smoke -d --help
expect_rc 2 "short flag value rejected" -t smoke -d -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -98,6 +98,13 @@ for flag in -s --state; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -s --help
expect_rc 2 "short flag value rejected" -s -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -100,6 +100,13 @@ for flag in -n -b -c --number --body --comment; do
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -n 5 -b --help
expect_rc 2 "short flag value rejected" -n 5 -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -99,6 +99,13 @@ for flag in -t -b -B -H -l -m -i --title --body --base --head --labels --milesto
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -t smoke -b --help
expect_rc 2 "short flag value rejected" -t smoke -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -107,6 +107,13 @@ for flag in -n -t -b -B -l -r -H --number --title --body --base --login --repo -
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -n 5 -t smoke -b --help
expect_rc 2 "short flag value rejected" -n 5 -t smoke -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2
@@ -115,6 +115,13 @@ for flag in -n -a -b -c -l -r --number --action --body --comment --login --repo;
expect_stderr "requires a value" "value-less $flag message on stderr" expect_stderr "requires a value" "value-less $flag message on stderr"
done done
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
# -b --help previously consumed --help as the body and performed the write).
expect_rc 2 "option-like value rejected" -n 5 -a comment -b --help
expect_rc 2 "short flag value rejected" -n 5 -a comment -b -h
expect_stderr "requires a value" "short flag value message on stderr"
expect_stderr "requires a value" "option-like value message on stderr"
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
if [[ -s "$PROBE_LOG" ]]; then if [[ -s "$PROBE_LOG" ]]; then
echo "FAIL: a parser-failure arm contacted a provider:" >&2 echo "FAIL: a parser-failure arm contacted a provider:" >&2