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
gtcommands fail.
Usage
Phase 1: Validate Prerequisites
Step 1: Check Graphite Prerequisites
Run a single Bash call:
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:
gtmissing: "Graphite CLI is required. Install it from https://graphite.dev/docs/cli and re-rungt-setup."jqmissing: "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_reponot ok: "gt-workflow must be run inside a git repository."auth_configmissing: "Graphite auth was not detected. Rungt author sign in through the Graphite CLI, then re-rungt-setup."repo_configmissing ORgt_trunkunavailable: "This repository is not initialized for Graphite. Rungt init, confirmgt trunkworks, then re-rungt-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_serverUPGRADE NEEDED: "Graphite MCP server requires gt v1.6.7+. Thegt mcpstdio server registered in plugin.json will fail to start and Graphite MCP tools will be unavailable until you upgrade. Runnpm i -g @withgraphite/graphite-cli@latestto upgrade, then re-rungt-setup. All CLI-based skills (smart-submit,gt-sync, etc.) continue to work without MCP."mcp_serverSKIPPED or UNKNOWN: note accordingly.yqNOT 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.ymlsettings. Install with:pip install yq"gh_native_queueWARNING (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///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 rungt-setupuntil resolved."gh_native_queueCOULD 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, orgh 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 therepository.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 butmergeQueue.urlis null. Any of these can produce COULD NOT CHECK or even a misleadingok (not configured)while the queue is actually active. If your token is scope-limited (e.g., a CI/automation token), re-rungt-setupwith 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:
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:
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 rungt user pager --disable"Keep current pager setting"— no change
If user chooses "Keep", note this in the summary. If user chooses "Disable",
include reversal instructions in the final report: "To re-enable: gt user pager --enable"
Step 7: Apply Settings
Apply settings via gt user commands. Run each in a separate Bash call to
isolate failures. Track the result of each command.
Settings to apply (in this order):
gt user branch-date --disable(if not already disabled)gt user restack-date --use-author-date(if not already set)gt user submit-body --include-commit-messages(if not already set)- Branch prefix (if user provided one in Step 5): run
Substitute the validated prefix as a literal value in single quotes, e.g.,
gt user branch-prefix --set 'agent/'— never use shell variable interpolation for user-supplied text gt user pager --disable(only if user chose to disable in Step 6)
Failure handling: If any command fails:
- Record the error output
- Continue applying remaining settings (do not stop on first failure)
- After all commands, show a summary with status for each:
- "Applied" — command succeeded
- "Already set" — current value matches target, no change needed
- "Failed" — command failed (show error)
- "Skipped" — user chose not to change this setting
If any commands failed, note the failures in the summary and proceed to
Phase 3. The user can re-run gt-setup to retry.
Step 8: Settings Summary
Show the final state of all 5 settings:
Graphite Settings Configuration
────────────────────────────────
branch-date: disabled (Applied)
restack-date: use-author-date (Applied)
submit-body: include-commit-messages (Applied)
branch-prefix: agent/ (Applied)
pager: disabled (Applied) — to re-enable: gt user pager --enable
Phase 3: Generate Convention File
Step 9: Check for Existing .graphite.yml
Determine the repo root and check for an existing convention file:
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
[ -f "$repo_top/.graphite.yml" ] && printf 'EXISTS\n' || printf 'NOT_FOUND\n'
If the file exists, read it and show the current contents. Then use
AskUserQuestion:
"Update with new values"— overwrite with wizard-generated values"Skip"— keep the existing file unchanged
If the file exists but is malformed YAML (read fails or structure is
unexpected), warn the user and use AskUserQuestion:
"Overwrite with valid configuration"— replace entirely"Skip"— keep the broken file as-is
If the file does not exist, proceed to Step 10.
If the user chose "Skip", jump to Step 11.
Step 10: Generate .graphite.yml
Build the convention file content using values from Phase 2 (branch prefix from
Step 5) and sensible defaults. Use the Write tool to create the file at
<repo_root>/.graphite.yml.
The file content:
# gt-workflow convention file — read by smart-submit, gt-stack-plan, gt-amend, gt-setup
# This is NOT a Graphite CLI feature. It is a gt-workflow plugin convention.
# Docs: https://github.com/KingInYellows/yellow-plugins/tree/main/plugins/gt-workflow
submit:
draft: false
merge_when_ready: false
restack_before: true
audit:
agents: 3
skip_on_draft: false
branch:
prefix: "<prefix-from-step-5-or-empty>"
pr_template:
create: true
Substitute the actual branch prefix chosen in Step 5. If the user skipped the
prefix, use an empty string: prefix: "".
After writing, fix CRLF line endings (WSL2 safety):
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
sed -i 's/\r$//' "$repo_top/.graphite.yml" 2>/dev/null || \
sed -i '' 's/\r$//' "$repo_top/.graphite.yml" 2>/dev/null || \
printf '[gt-workflow] Warning: could not strip CRLF from .graphite.yml\n' >&2
Step 11: PR Template
First, if .graphite.yml was loaded in Step 9 and pr_template.create is
false, skip this step entirely and note "PR template: skipped
(pr_template.create is false in .graphite.yml)" in the final report.
Otherwise, check for an existing PR template:
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
[ -f "$repo_top/.github/pull_request_template.md" ] && printf 'EXISTS\n' || printf 'NOT_FOUND\n'
If the template exists, use AskUserQuestion:
"View current template"— show contents, then re-prompt with Regenerate/Skip"Regenerate"— overwrite with the agent-optimized template"Skip"— keep existing
If the template does not exist, create .github/ directory if needed and
write the template using the Write tool:
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
mkdir -p "$repo_top/.github"
Template content:
## Summary
<!-- 2-3 bullet points of what this PR does -->
## Stack context
<!-- What branch is below this one and why (critical for stack reviewers) -->
## Test plan
<!-- What was verified before submit -->
## Notes for reviewers
<!-- Anything the author wants to call attention to -->
After writing, fix CRLF:
repo_top=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
sed -i 's/\r$//' "$repo_top/.github/pull_request_template.md" 2>/dev/null || \
sed -i '' 's/\r$//' "$repo_top/.github/pull_request_template.md" 2>/dev/null || \
printf '[gt-workflow] Warning: could not strip CRLF from PR template\n' >&2
Step 12: Final Report
Show the complete setup summary:
gt-workflow Setup Complete
──────────────────────────
Phase 1: Validation PASSED
Phase 2: Graphite Settings 5/5 configured
Phase 3: Convention File .graphite.yml created
PR Template .github/pull_request_template.md created
Consumer skills (smart-submit, gt-stack-plan, gt-amend) will read
.graphite.yml for repo-level behavior overrides.
Next steps:
- Review and commit .graphite.yml and .github/pull_request_template.md
- Run smart-submit or gt-sync to verify your workflow
Adjust the summary to reflect actual outcomes (skipped items, partial configuration, existing files kept, etc.).