Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .ai-context/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ the canonical current command list.
hygiene, and Project metadata using Base conventions.
- `basectl gh issue create` defaults to category `enhancement` when
`--category` is omitted and prints that default in command output. Pass
`--size <T|S|M|L>` when the issue scope is clear; otherwise Project
metadata defaults to `Size=S`.
`--assignee <login>` to assign an issue, or set
`project.issue_defaults.assignee` in `.github/base-project.yml` for a
repo-local default. Pass `--no-assignee` to ignore that default for one
issue. Pass `--size <T|S|M|L>` when the issue scope is clear; otherwise
Project metadata defaults to `Size=S`.
- `basectl gh pr create` auto-injects `Fixes #<issue>` from Base branch
names; pass `--no-fixes` to suppress that body injection.
- `basectl gh project doctor --project <title>` - inspect Project metadata
Expand Down
4 changes: 4 additions & 0 deletions .ai-context/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Use the smallest accurate `Size` when creating issues: `T` for tiny obvious
work, `S` for normal small work or unknown scope, `M` for interacting changes,
and `L` only for work that should probably be split. The default remains `S`
when automation cannot infer scope.
Issue creation is unassigned by default unless `basectl gh issue create`
receives `--assignee <login>` or `.github/base-project.yml` sets
`project.issue_defaults.assignee`; use `--no-assignee` to skip that repo-local
default for a specific issue.

Base-managed repositories should carry `.github/workflows/project-intake.yml`
as the fallback for issues created outside `basectl gh issue create`.
Expand Down
1 change: 1 addition & 0 deletions .github/base-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ project:
area: Product
initiative: Adoption Polish
size: S
assignee: codeforester
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ them.
- Use one primary category label: `bug`, `enhancement`, `documentation`, `ci`,
or `security`.
- Do not create or apply `type:*` issue labels.
- Assign Codex-created issues to `codeforester` when GitHub allows it.
- Assign Codex-created Base repository issues to `codeforester` when GitHub
allows it; `.github/base-project.yml` carries this repo-local default for
`basectl gh issue create`.
- For issues tracked in Base Roadmap, set Project `Status` to `In Progress`
before implementation starts, move it to `In Review` when the PR opens, and
verify `Done` after merge/closure. If Project V2 access or item state
Expand Down
6 changes: 4 additions & 2 deletions cli/bash/commands/basectl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ such command directories exist. Optional utility CLIs such as `caff` and
hygiene, and GitHub Project metadata using Base's opinionated workflow. It
uses standard GitHub-style issue categories such as `bug`, `enhancement`,
`documentation`, `ci`, and `security`, and derives branch names from those
categories. Prefer this command for Base repository GitHub workflows when it
supports the task.
categories. Issue creation is unassigned by default unless `--assignee` is
passed or `.github/base-project.yml` sets `project.issue_defaults.assignee`.
Prefer this command for Base repository GitHub workflows when it supports the
task.
- `basectl onboard` guides first-run setup around existing setup, check,
doctor, profile, and project-discovery primitives. See
`docs/basectl-onboard.md`.
Expand Down
107 changes: 92 additions & 15 deletions cli/bash/commands/basectl/subcommands/gh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ base_gh_usage() {
cat <<'EOF'
Usage:
basectl gh issue list [gh options...]
basectl gh issue create [--category <bug|enhancement|documentation|ci|security>] --title <title> [--body <body>] [--repo <owner/name>] [--size <T|S|M|L>] [project options...]
basectl gh issue create [--category <bug|enhancement|documentation|ci|security>] --title <title> [--body <body>] [--repo <owner/name>] [--assignee <login>|--no-assignee] [--size <T|S|M|L>] [project options...]
basectl gh issue start <number> [--category <bug|enhancement|documentation|ci|security>] [--title <title>]
basectl gh pr create [--no-fixes] [gh options...]
basectl gh pr status [gh options...]
Expand All @@ -32,6 +32,8 @@ Branch naming:
Issue create project options:
--repo <owner/name> Repository to create the issue in. Defaults to the origin remote.
--category <category> Issue label category. Defaults to enhancement.
--assignee <login> Assign the issue to a GitHub login.
--no-assignee Do not assign the issue, even when repo config has a default.
--project <title> Project to update. Defaults to the repository name.
--project-owner <login> Project owner. Defaults to the repository owner.
--size <T|S|M|L> Project Size value. Defaults to .github/base-project.yml or S.
Expand All @@ -42,7 +44,8 @@ Issue categories:

Notes:
- This command requires the GitHub CLI (`gh`) for GitHub operations.
- Issues created through this command are assigned to codeforester.
- Issues are unassigned unless --assignee is passed or .github/base-project.yml
sets project.issue_defaults.assignee.
- When the GitHub repo is known, issue create also adds the issue to the
repo-named Project and applies defaults from .github/base-project.yml.
- PR creation auto-injects Fixes #<issue> when the branch follows the Base
Expand All @@ -56,7 +59,7 @@ base_gh_issue_usage() {
cat <<'EOF'
Usage:
basectl gh issue list [gh options...]
basectl gh issue create [--category <bug|enhancement|documentation|ci|security>] --title <title> [--body <body>] [--repo <owner/name>] [--size <T|S|M|L>] [project options...]
basectl gh issue create [--category <bug|enhancement|documentation|ci|security>] --title <title> [--body <body>] [--repo <owner/name>] [--assignee <login>|--no-assignee] [--size <T|S|M|L>] [project options...]
basectl gh issue start <number> [--category <bug|enhancement|documentation|ci|security>] [--title <title>]

Purpose:
Expand All @@ -68,12 +71,15 @@ Branch naming:
Issue create project options:
--repo <owner/name> Repository to create the issue in. Defaults to the origin remote.
--category <category> Issue label category. Defaults to enhancement.
--assignee <login> Assign the issue to a GitHub login.
--no-assignee Do not assign the issue, even when repo config has a default.
--project <title> Project to update. Defaults to the repository name.
--project-owner <login> Project owner. Defaults to the repository owner.
--size <T|S|M|L> Project Size value. Defaults to .github/base-project.yml or S.
--no-project Skip Project metadata updates.

Default category: enhancement.
Default assignee: none unless project.issue_defaults.assignee is set in .github/base-project.yml.
Categories: bug, enhancement, documentation, ci, security.
EOF
}
Expand Down Expand Up @@ -330,6 +336,50 @@ base_gh_project_config_path() {
printf '%s\n' "$path"
}

base_gh_trim_scalar() {
printf '%s' "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}

base_gh_issue_default_assignee_from_config() {
local in_defaults=0
local in_project=0
local line path trimmed value

path="$1"
while IFS= read -r line || [[ -n "$line" ]]; do
trimmed="$(base_gh_trim_scalar "$line")"
[[ -n "$trimmed" && "$trimmed" != \#* ]] || continue

if [[ "$line" != [[:space:]]* ]]; then
in_defaults=0
if [[ "$trimmed" == "project:" ]]; then
in_project=1
else
in_project=0
fi
continue
fi

if ((in_project)) && [[ "$line" == " "* && "$line" != " "* ]]; then
if [[ "$trimmed" == "issue_defaults:" ]]; then
in_defaults=1
else
in_defaults=0
fi
continue
fi

if ((in_project)) && ((in_defaults)) && [[ "$line" == " "* && "$trimmed" == assignee:* ]]; then
value="$(base_gh_trim_scalar "${trimmed#assignee:}")"
[[ -n "$value" ]] || return 1
printf '%s\n' "$value"
return 0
fi
done <"$path"

return 1
}

base_gh_issue_number_from_output() {
local output="$1"
local issue_number
Expand Down Expand Up @@ -399,13 +449,17 @@ base_gh_do_issue() {
}

base_gh_issue_create() {
local assignee=""
local assignee_explicit=0
local body=""
local category=""
local configure_project=1
local config_path=""
local github_repo=""
local issue_args=()
local issue_number=""
local issue_output=""
local no_assignee=0
local project_owner=""
local project_size=""
local project_title=""
Expand All @@ -425,6 +479,14 @@ base_gh_issue_create() {
title="${2:-}"
shift
;;
--assignee)
assignee="${2:-}"
assignee_explicit=1
shift
;;
--no-assignee)
no_assignee=1
;;
--body)
body="${2:-}"
shift
Expand Down Expand Up @@ -468,21 +530,37 @@ base_gh_issue_create() {
if [[ -n "$project_size" ]]; then
base_gh_validate_project_size "$project_size" || return 1
fi
if ((assignee_explicit)) && ((no_assignee)); then
base_gh_error "Options '--assignee' and '--no-assignee' cannot be used together."
return 1
fi
if ((assignee_explicit)) && [[ -z "$assignee" ]]; then
base_gh_error "Option '--assignee' requires an argument."
return 1
fi

[[ -n "$github_repo" ]] || github_repo="$(base_gh_infer_github_repo || true)"
config_path="$(base_gh_project_config_path || true)"
if ((assignee_explicit)); then
:
elif ((no_assignee)); then
assignee=""
elif [[ -n "$config_path" ]]; then
assignee="$(base_gh_issue_default_assignee_from_config "$config_path" || true)"
fi

issue_args=(issue create --title "$title")
if [[ -n "$body" ]]; then
if [[ -n "$github_repo" ]]; then
issue_output="$(base_gh_run issue create --title "$title" --body "$body" --label "$category" --assignee codeforester --repo "$github_repo")" || return $?
else
issue_output="$(base_gh_run issue create --title "$title" --body "$body" --label "$category" --assignee codeforester)" || return $?
fi
else
if [[ -n "$github_repo" ]]; then
issue_output="$(base_gh_run issue create --title "$title" --label "$category" --assignee codeforester --repo "$github_repo")" || return $?
else
issue_output="$(base_gh_run issue create --title "$title" --label "$category" --assignee codeforester)" || return $?
fi
issue_args+=(--body "$body")
fi
issue_args+=(--label "$category")
if [[ -n "$assignee" ]]; then
issue_args+=(--assignee "$assignee")
fi
if [[ -n "$github_repo" ]]; then
issue_args+=(--repo "$github_repo")
fi
issue_output="$(base_gh_run "${issue_args[@]}")" || return $?
printf '%s\n' "$issue_output"

if ((configure_project)) && [[ -n "$github_repo" ]]; then
Expand All @@ -492,7 +570,6 @@ base_gh_issue_create() {
}
[[ -n "$project_title" ]] || project_title="$(base_gh_default_project_title "$github_repo")"
[[ -n "$project_owner" ]] || project_owner="$(base_gh_project_owner_from_repo "$github_repo")"
config_path="$(base_gh_project_config_path || true)"
if [[ -n "$config_path" ]]; then
local field_args=(
"$issue_number"
Expand Down
2 changes: 2 additions & 0 deletions cli/bash/commands/basectl/tests/completions.bats
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ EOF
[[ "$output" == *"--category"* ]]
[[ "$output" == *"--title"* ]]
[[ "$output" == *"--body"* ]]
[[ "$output" == *"--assignee"* ]]
[[ "$output" == *"--no-assignee"* ]]
[[ "$output" == *"--size"* ]]
[[ "$output" != *"--type"* ]]
}
Expand Down
Loading
Loading