- name
- gt-setup
- description
- Validate Graphite CLI prerequisites and configure settings for AI agent workflows. Use when first installing the plugin, after Graphite auth changes, or when gt commands fail.
## What It Does
Validates that Graphite CLI is installed, authenticated, and initialized
for the current repository. Then configures Graphite CLI settings for AI
agent workflows and generates a `.graphite.yml` convention file.
## When to Use
- First installing the plugin.
- After Graphite auth changes.
- When `gt` commands fail.
## Usage
### Phase 1: Validate Prerequisites
#### Step 1: Check Graphite Prerequisites
Run a single Bash call:
```bash
version_gte() {
local IFS=.
local i a=($1) b=($2)
for ((i=0; i<${#b[@]}; i++)); do
local av="${a[i]:-0}" bv="${b[i]:-0}"
if ((av > bv)); then return 0; fi
if ((av < bv)); then return 1; fi
done
return 0
}
printf '=== Prerequisites ===\n'
if command -v gt >/dev/null 2>&1; then
gt_version_full=$(gt --version 2>/dev/null)
gt_version_exit=$?
gt_version_raw=$(printf '%s' "$gt_version_full" | head -n1)
if [ "$gt_version_exit" -ne 0 ]; then
printf 'gt: BROKEN (exited with code %s)\n' "$gt_version_exit"
printf 'mcp_server: SKIPPED (gt is broken)\n'
elif [ -n "$gt_version_raw" ]; then
printf 'gt: ok (%s)\n' "$gt_version_raw"
gt_ver=$(printf '%s' "$gt_version_raw" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [ -z "$gt_ver" ]; then
printf 'mcp_server: UNKNOWN (could not parse version from: %s)\n' "$gt_version_raw"
elif version_gte "$gt_ver" "1.6.7"; then
printf 'mcp_server: ok (gt >= 1.6.7)\n'
else
printf 'mcp_server: UPGRADE NEEDED (current: %s, need 1.6.7+)\n' "$gt_ver"
fi
else
printf 'gt: ok (version unknown)\n'
printf 'mcp_server: UNKNOWN (gt --version returned no output)\n'
fi
else
printf 'gt: NOT FOUND\n'
printf 'mcp_server: SKIPPED (gt not found)\n'
fi
command -v jq >/dev/null 2>&1 && printf 'jq: ok\n' || printf 'jq: NOT FOUND\n'
if command -v yq >/dev/null 2>&1; then
if yq --help 2>&1 | grep -qi 'jq wrapper\|kislyuk'; then
printf 'yq: ok\n'
else
printf 'yq: WRONG VARIANT (found non-kislyuk yq; .graphite.yml parsing will be skipped)\n'
fi
else
printf 'yq: NOT FOUND (optional — needed by consumer commands to read .graphite.yml)\n'
fi
printf '\n=== Repository ===\n'
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || true)
[ -n "$repo_top" ] && printf 'git_repo: ok\n' || printf 'git_repo: NOT A GIT REPOSITORY\n'
graphite_repo_config=$(git rev-parse --git-path .graphite_repo_config 2>/dev/null || true)
[ -n "$graphite_repo_config" ] && [ -f "$graphite_repo_config" ] && printf 'repo_config: present (%s)\n' "$graphite_repo_config" || printf 'repo_config: missing\n'
if command -v gt >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
trunk=$(gt trunk 2>/dev/null || true)
[ -n "$trunk" ] && printf 'gt_trunk: %s\n' "$trunk" || printf 'gt_trunk: UNAVAILABLE\n'
else
printf 'gt_trunk: SKIPPED\n'
fi
printf '\n=== Graphite Auth ===\n'
auth_ok=0
for path in \
"$HOME/.graphite_user_config" \
"${XDG_CONFIG_HOME:-$HOME/.config}/graphite/user_config" \
"$HOME/.config/graphite/user_config"; do
if [ -f "$path" ]; then
auth_ok=1
printf 'auth_config: present (%s)\n' "$path"
break
fi
done
[ "$auth_ok" -eq 1 ] || printf 'auth_config: missing\n'
printf '\n=== Convention Files ===\n'
[ -n "$repo_top" ] && [ -f "$repo_top/.graphite.yml" ] && printf 'graphite_yml: present\n' || printf 'graphite_yml: not found\n'
[ -n "$repo_top" ] && [ -f "$repo_top/.github/pull_request_template.md" ] && printf 'pr_template: present\n' || printf 'pr_template: not found\n'
printf '\n=== Merge Queue Compatibility ===\n'
# Capture stderr from gh probes so failure diagnostics survive (vs silent 2>/dev/null).
mq_err_log=$(mktemp 2>/dev/null) || mq_err_log=""
# Trap covers interruption (e.g. a Bash-tool timeout); the explicit rm at the
# end of this block still handles normal completion.
[ -n "$mq_err_log" ] && trap 'rm -f "$mq_err_log"' EXIT
# No `gh auth status` precheck: it exits non-zero if ANY known host has stale
# auth, which produces false COULD NOT CHECK results on multi-host setups
# (gh.com + ghe.example.com). Let `gh repo view` be the auth probe — it's
# scoped to the current repo's host and its stderr is captured below.
if command -v gh >/dev/null 2>&1; then
repo_nwo=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>"${mq_err_log:-/dev/null}")
repo_view_status=$?
if [ "$repo_view_status" -ne 0 ]; then
if [ -n "$mq_err_log" ] && [ -s "$mq_err_log" ]; then
printf '[gt-workflow] Warning: gh repo view failed (exit %d): %s\n' "$repo_view_status" "$(head -c 200 "$mq_err_log" | tr '\n' ' ')" >&2
else
printf '[gt-workflow] Warning: gh repo view failed (exit %d)\n' "$repo_view_status" >&2
fi
printf 'gh_native_queue: COULD NOT CHECK (gh repo view failed)\n'
elif [ -z "$repo_nwo" ]; then
printf 'gh_native_queue: COULD NOT CHECK (gh repo view returned no name)\n'
else
repo_owner="${repo_nwo%/*}"
repo_name="${repo_nwo#*/}"
[ -n "$mq_err_log" ] && : > "$mq_err_log" # truncate before next probe
# shellcheck disable=SC2016 # $owner/$name are GraphQL variable refs, not shell vars — intentionally literal in single quotes
mq_check=$(gh api graphql -f query='
query($owner:String!,$name:String!){
repository(owner:$owner,name:$name){ mergeQueue { url } }
}' -f owner="$repo_owner" -f name="$repo_name" --jq 'if .data.repository == null then error("repo null") else (.data.repository.mergeQueue | if . != null then "configured" else empty end) end' 2>"${mq_err_log:-/dev/null}")
mq_status=$?
if [ "$mq_status" -ne 0 ]; then
if [ -n "$mq_err_log" ] && [ -s "$mq_err_log" ]; then
printf '[gt-workflow] Warning: merge queue check failed (gh api graphql): %s\n' "$(head -c 200 "$mq_err_log" | tr '\n' ' ')" >&2
else
printf '[gt-workflow] Warning: merge queue check failed (gh api graphql)\n' >&2
fi
printf 'gh_native_queue: COULD NOT CHECK (gh api graphql failed)\n'
elif [ -n "$mq_check" ]; then
printf 'gh_native_queue: WARNING (configured — disable at https://github.com/%s/settings/branches)\n' "$repo_nwo"
else
printf 'gh_native_queue: ok (not configured)\n'
fi
fi
else
printf 'gh_native_queue: COULD NOT CHECK (gh not installed)\n'
fi
[ -n "$mq_err_log" ] && rm -f "$mq_err_log"
```
#### Step 2: Interpret Results
**Failures (hard stop)** — stop after reporting all that apply:
- `gt` missing: "Graphite CLI is required. Install it from https://graphite.dev/docs/cli and re-run `gt-setup`."
- `jq` missing: "jq is required by gt-workflow (the gt-cleanup skill uses it to parse PR-status JSON). Install it from https://jqlang.github.io/jq/download/."
- `git_repo` not ok: "gt-workflow must be run inside a git repository."
- `auth_config` missing: "Graphite auth was not detected. Run `gt auth` or sign in through the Graphite CLI, then re-run `gt-setup`."
- `repo_config` missing OR `gt_trunk` unavailable: "This repository is not initialized for Graphite. Run `gt init`, confirm `gt trunk` works, then re-run `gt-setup`."
If any hard-stop failures exist, stop here. Do not proceed to Phase 2.
**Warnings (do not block setup — CLI commands still work):**
- `mcp_server` UPGRADE NEEDED: "Graphite MCP server requires gt v1.6.7+. The `gt mcp` stdio server registered in plugin.json will fail to start and Graphite MCP tools will be unavailable until you upgrade. Run `npm i -g @withgraphite/graphite-cli@latest` to upgrade, then re-run `gt-setup`. All CLI-based skills (`smart-submit`, `gt-sync`, etc.) continue to work without MCP."
- `mcp_server` SKIPPED or UNKNOWN: note accordingly.
- `yq` NOT FOUND: "yq (kislyuk variant) is optional but recommended. Without it, consumer skills (`smart-submit`, `gt-stack-plan`, `gt-amend`) will use hardcoded defaults instead of `.graphite.yml` settings. Install with: `pip install yq`"
- `gh_native_queue` WARNING (configured): "GitHub native merge queue is configured for this repo. Graphite and GitHub native merge queue are incompatible — running both causes Graphite to restart CI on queued commits and may produce out-of-order merges. To disable: open https://github.com/<owner>/<repo>/settings/branches, edit the branch protection rule for your trunk branch, and uncheck **Require merge queue**. Setup proceeds, but the warning will repeat each time you run `gt-setup` until resolved."
- `gh_native_queue` COULD NOT CHECK: informational only — but **not necessarily safe to ignore**. The parenthetical reason in the output line indicates which probe failed (gh missing/unauthenticated, `gh repo view`, or `gh api graphql`); the captured stderr is appended to the `[gt-workflow] Warning:` line on failure to aid debugging. Three documented false-negative paths exist for the `repository.mergeQueue { url }` proxy: (1) the GitHub token lacks the admin scope needed to read merge queue config, (2) the repository is not visible to the token, (3) merge queue is configured but `mergeQueue.url` is null. Any of these can produce COULD NOT CHECK or even a misleading `ok (not configured)` while the queue is actually active. If your token is scope-limited (e.g., a CI/automation token), re-run `gt-setup` with an admin-scoped token before relying on the result. Setup itself proceeds normally; the proxy is fail-open by design.
#### Step 3: Validation Report
If all checks pass, show:
```text
gt-workflow Validation
──────────────────────
Graphite CLI: ready
jq: ready
yq: ready (or: not found — optional)
Auth: detected
Repository: initialized (trunk: <branch>)
MCP Server: available (or: unavailable — gt < 1.6.7)
Merge Queue: ok (or: WARNING — native queue active / COULD NOT CHECK)
Proceeding to AI agent configuration...
```
### Phase 2: Configure Graphite Settings for AI Agents
#### Step 4: Show Planned Changes
Before applying any settings, read current values and show what will change.
Run a single Bash call:
```bash
printf '=== Current Graphite User Settings ===\n'
gt user branch-prefix 2>/dev/null || printf 'branch-prefix: (not set / command unavailable)\n'
gt user branch-date 2>/dev/null || printf 'branch-date: (command unavailable)\n'
gt user restack-date 2>/dev/null || printf 'restack-date: (command unavailable)\n'
gt user submit-body 2>/dev/null || printf 'submit-body: (command unavailable)\n'
gt user pager 2>/dev/null || printf 'pager: (command unavailable)\n'
```
Present a summary table showing current vs recommended AI-agent values for each
setting. Then proceed to the interactive prompts below.
#### Step 5: Branch Prefix Prompt
(`AskUserQuestion` is a Claude Code tool — on Codex, ask each question as a
numbered-option list in your reply and wait for the user's answer before
proceeding; this applies to every AskUserQuestion mention in this skill.)
Use `AskUserQuestion` to ask: "What branch prefix should AI agents use?"
Options:
- `"agent/" (Recommended)` — flat namespace for agent-created branches
- `"Skip"` — keep the current branch-prefix setting unchanged
The "Other" button allows free-text input for a custom prefix.
**If the user provides a custom prefix via "Other", validate it:**
- Must start with a lowercase letter or digit (`[a-z0-9]`)
- Allowed subsequent characters: lowercase letters, digits, `/`, `_`, `-` only
- Reject if it contains `..`, `~`, spaces, or any character outside `[a-z0-9/_-]`
- Normalize: append trailing `/` if missing
- Max length: 20 characters (checked **after** normalization, so the effective
input limit is 19 characters when a trailing `/` is appended)
- If validation fails, explain the constraint and re-prompt with AskUserQuestion
Store the chosen prefix (or empty string if skipped) for use in Step 7 and
Phase 3.
#### Step 6: Pager Prompt
Use `AskUserQuestion` to ask: "Disable the Graphite CLI pager? AI agents hang when pager is enabled."
Options:
- `"Disable pager (Recommended for AI agents)"` — will run `gt user pager --disable`
GitHub에서 보기