| name | smart-submit |
| description | Stage, audit, commit, and submit uncommitted changes via Graphite, with parallel code-review agents. Use when user says "submit this", "ship it", or "commit and push", or has uncommitted work to turn into a PR. Requires uncommitted changes — a clean, already-committed branch needs only gt submit; amending an already-submitted branch is the gt-amend skill. |
What It Does
Conducts a systematic code quality audit of all uncommitted changes using
specialized agents, then creates a conventional commit and submits via
Graphite. Ensures no anti-patterns, secrets, or silent failures enter the
codebase.
When to Use
- User says "submit this", "ship it", or "commit and push", or has
uncommitted work to turn into a PR.
- Requires uncommitted changes — a clean, already-committed branch needs
only
gt submit; amending an already-submitted branch is the gt-amend
skill instead.
Usage
Optional arguments:
--amend — Amend the current branch commit instead of creating a new branch
--dry-run — Run the audit but skip the actual submission
--no-verify — Skip the audit and submit directly (use with caution)
--publish — Override draft mode: submit as published even if submit.draft: true in .graphite.yml
The argument text provided after the skill name (if any) is available as
context for this invocation.
Phase 0: Read Convention File
Before any other work, check for a .graphite.yml convention file and parse
repo-level settings. Run a single Bash call:
REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
GW_DRAFT=""
GW_MERGE_WHEN_READY=""
GW_RESTACK_BEFORE=""
GW_AUDIT_AGENTS=""
GW_SKIP_ON_DRAFT=""
GW_BRANCH_PREFIX=""
if command -v yq >/dev/null 2>&1 && \
yq --help 2>&1 | grep -qi 'jq wrapper\|kislyuk' && \
[ -f "$REPO_TOP/.graphite.yml" ]; then
yq_err=""
GW_DRAFT=$(yq -r '.submit.draft // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="submit.draft"
GW_MERGE_WHEN_READY=$(yq -r '.submit.merge_when_ready // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="${yq_err:+$yq_err, }merge_when_ready"
GW_RESTACK_BEFORE=$(yq -r '.submit.restack_before // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="${yq_err:+$yq_err, }restack_before"
GW_AUDIT_AGENTS=$(yq -r '.audit.agents // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="${yq_err:+$yq_err, }audit.agents"
GW_SKIP_ON_DRAFT=$(yq -r '.audit.skip_on_draft // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="${yq_err:+$yq_err, }skip_on_draft"
GW_BRANCH_PREFIX=$(yq -r '.branch.prefix // ""' "$REPO_TOP/.graphite.yml" 2>/dev/null) || yq_err="${yq_err:+$yq_err, }branch.prefix"
if [ -n "$yq_err" ]; then
printf '[gt-workflow] Warning: yq failed to parse fields: %s. Using defaults for those.\n' "$yq_err" >&2
else
printf '[gt-workflow] Convention file loaded: %s/.graphite.yml\n' "$REPO_TOP" >&2
fi
elif [ -f "$REPO_TOP/.graphite.yml" ]; then
printf '[gt-workflow] Warning: .graphite.yml exists but yq (kislyuk) is not installed. Using defaults.\n' >&2
printf '[gt-workflow] Install yq: pip install yq\n' >&2
fi
if [ -n "$GW_AUDIT_AGENTS" ]; then
case "$GW_AUDIT_AGENTS" in
*[!0-9]*) printf '[gt-workflow] Warning: audit.agents value "%s" is not an integer. Using default 3.\n' "$GW_AUDIT_AGENTS" >&2; GW_AUDIT_AGENTS=3 ;;
esac
if [ "$GW_AUDIT_AGENTS" -lt 1 ] 2>/dev/null; then
printf '[gt-workflow] Warning: audit.agents=%s is below minimum. Using 1.\n' "$GW_AUDIT_AGENTS" >&2
GW_AUDIT_AGENTS=1
elif [ "$GW_AUDIT_AGENTS" -gt 3 ] 2>/dev/null; then
printf '[gt-workflow] Warning: audit.agents=%s exceeds maximum. Using 3.\n' "$GW_AUDIT_AGENTS" >&2
GW_AUDIT_AGENTS=3
fi
fi
if [ -n "$GW_BRANCH_PREFIX" ]; then
if ! printf '%s' "$GW_BRANCH_PREFIX" | grep -qE '^[a-z0-9][a-z0-9/_-]*$'; then
printf '[gt-workflow] Error: branch.prefix "%s" contains invalid characters. Using empty prefix.\n' "$GW_BRANCH_PREFIX" >&2
GW_BRANCH_PREFIX=""
fi
fi
Store these values for use in subsequent phases. When a value is empty, use the
hardcoded default (draft=false, merge_when_ready=false, restack_before=true,
audit_agents=3, skip_on_draft=false, branch_prefix="").
Phase 1: Understand Current State
1. Check for Changes
Run these commands in parallel to understand the working tree and stack
position:
git status --short
git diff --stat
gt log short
gt trunk
If there are no uncommitted changes, tell the user and exit.
2. Determine Branch Context
Check whether the current branch is trunk or a feature branch:
current=$(git branch --show-current)
trunk=$(gt trunk)
echo "current=$current trunk=$trunk"
- If
current == trunk → will use gt create <branch-name> -m "message" to
create a new branch
- If
current != trunk → will use gt modify -m "message" to update the
current branch
- If
--amend argument was passed and on a feature branch → will use
gt commit amend instead
Phase 2: Audit (skip if --no-verify)
Skip-on-draft check: If $GW_SKIP_ON_DRAFT is true and $GW_DRAFT is
true (from .graphite.yml), skip the entire audit phase and proceed to
Phase 3.
Note: This check uses the config-derived values in $GW_SKIP_ON_DRAFT
and $GW_DRAFT from .graphite.yml. CLI flags such as --publish affect
the eventual submit mode, but they do not override the audit skip decision in
this phase.
0. Capture the Diff Once
Before spawning auditors, capture the full diff — staged and unstaged — so
all agents can use it without redundant calls. Use git diff HEAD rather
than a bare git diff: a bare git diff only shows unstaged changes, so
any changes the user staged with git add before invoking this skill would
be silently excluded from the audit while still ending up in the commit
(Phase 3 generates the commit message from git diff --cached and commits
the full index).
git diff HEAD
Store this output as $DIFF_OUTPUT (pass it as context to each agent below).
1. Run the Audit
Invoke the Skill tool with skill: "audit-review", passing $DIFF_OUTPUT
and $GW_AUDIT_AGENTS as context. (On Codex there is no Skill tool — read
the Codex-exposed audit-review skill and follow it directly with the same
context.) It runs the appropriate quick-code-review
/ quick-security-scan / quick-error-check prompts (1-3 of them, per
$GW_AUDIT_AGENTS) and returns a CRITICAL / MINOR / CLEAN verdict with
file:line findings.
2. Gate Check
If audit-review reports a dispatch failure (an audit failed to run or
timed out), inform the user which audit is missing and ask whether to
proceed with partial results or abort.
IF CRITICAL ISSUES (secrets, production mocks, silent failures):
- Display blocking issues with file:line references
- Use AskUserQuestion (on Codex, which lacks that tool: present the same
options as a numbered list in your reply and wait for the user's answer)
to ask:
- "Fix issues before submitting (Recommended)"
- "Submit anyway"
- "Abort"
- If "Fix issues" → stop and let the user fix them
- If "Abort" → stop entirely
IF MINOR ISSUES (TODOs, style, minor logic):
- Display warnings
- Proceed but note them in the output
IF CLEAN: Proceed automatically.
Phase 3: Stage & Commit
1. Stage Specific Files
IMPORTANT: Do NOT use git add . or git add -A. Instead, stage only the
specific changed files to avoid accidentally committing secrets, binaries, or
unrelated files.
git diff --name-only
git ls-files --others --exclude-standard
Review the file list. Exclude any files that look like:
.env* files
- Credential/key files
- Large binaries
- Build artifacts
Stage the appropriate files by name:
git add -- "<file1>" "<file2>"
2. Generate Conventional Commit Message
Analyze the staged diff to determine the commit type and scope:
git diff --cached --stat
git diff --cached
Generate a conventional commit message following this format:
feat: — new feature
fix: — bug fix
refactor: — code restructuring
docs: — documentation changes
test: — test additions/changes
chore: — maintenance, config, dependencies
The message should be concise (under 72 chars for the subject line). Include a
body if the changes are complex.
3. Create Commit via Graphite
If on trunk (creating new branch):
Derive the branch name from the commit type and a short slug (e.g.,
feat/add-user-auth, fix/null-pointer-crash). If $GW_BRANCH_PREFIX is set,
prepend it to the branch name:
gt create "<GW_BRANCH_PREFIX><branch-name>" -m "<conventional commit message>"
For example, with branch.prefix: "agent/" and slug feat/add-user-auth, the
branch name becomes agent/feat/add-user-auth.
If on feature branch (adding to existing):
gt modify -m "<conventional commit message>"
If --amend (modifying current branch):
gt commit amend -m "<conventional commit message>"
Phase 4: Submit
1. Push to GitHub
If --dry-run was provided, skip this step and do not run gt submit; instead,
only simulate the submission and proceed to the result summary without making
any remote changes.
Build the submit command with convention file flags. First, if $GW_RESTACK_BEFORE
is true, run restack as a separate preceding command:
gt stack restack
Then submit with flags based on .graphite.yml values:
gt submit --no-interactive
Append flags to the submit command (only if set and non-empty):
- If
$GW_DRAFT is true (and no explicit --publish argument): add --draft
- If
$GW_MERGE_WHEN_READY is true: add --merge-when-ready
After submitting, confirm the new stack state:
gt log short
gt pr
Output a summary:
- Commit message used
- Branch created/amended
- PR link
- Stack visualization
- Any audit warnings that were noted
Success Criteria
- All uncommitted code audited by 1-3 specialized agents (unless
--no-verify)
- No critical issues committed
- Files staged individually (no blanket
git add .)
- Conventional commit message generated from diff analysis
- Graphite stack created/updated and submitted (or a dry-run summary produced
when
--dry-run is used)
- User provided with PR link and stack context