| name | ai-submit-work |
| description | Run all status checks locally, fix issues, commit, push, and create an automerge PR. Use when work is ready to submit, or saying 'submit my work' or 'I'm done'. |
| argument-hint | [--no-tests] [--no-security] |
Run all status checks, fix issues, commit, push, and create an automerge PR: $ARGUMENTS
Workflow automation: This skill is part of an automated workflow. Auto-commit with generated messages (do NOT ask Accept/Edit/Abort). Auto-rebase when behind target. Auto-resolve mechanical rebase conflicts (version files, changelogs, lock files -- target branch wins). Auto-fix formatting AND diagnostic pre-commit failures (mypy, uv-lock-check). Auto-fix patch-level security bumps. Auto-stage known project files. Auto-transition to pipeline monitoring after PR creation. Only stop and ask the user for: non-patch security bumps, license compliance issues, test failures, code-level rebase conflicts, and unrecognized untracked files.
Mirrors the CI pipeline locally to catch issues before pushing. Handles file staging, conventional commits, branch updates, and PR creation.
Usage Examples
/ai-submit-work - Full submission (all checks)
/ai-submit-work --no-tests - Skip test suite (pre-commit + security still run)
/ai-submit-work --no-security - Skip security + license checks
/ai-submit-work --no-tests --no-security - Pre-commit only (fastest)
1. Safety Checks
CURRENT_BRANCH=$(git branch --show-current)
Verify work branch:
- If on
main, master, dev, develop, or staging: ABORT with error "You're on $CURRENT_BRANCH. Create a work branch first with /ai-prepare-branch."
- Must be on a feature/fix/etc. branch.
Verify branch is current with target:
DEV_BRANCH=""
for candidate in dev develop staging; do
if git show-ref --verify --quiet refs/remotes/origin/$candidate; then
DEV_BRANCH=$candidate
break
fi
done
TARGET=${DEV_BRANCH:-$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")}
git fetch origin $TARGET
BEHIND=$(git rev-list --count HEAD..origin/$TARGET)
if [ "$BEHIND" -gt 0 ]; then
echo "WARNING: Your branch is $BEHIND commits behind $TARGET."
echo "This usually means /ai-prepare-branch was not run before starting work."
fi
If the branch is behind, automatically rebase:
git rebase origin/$TARGET
Report: "Rebased on $TARGET (was $BEHIND commits behind)." Do NOT ask the user to choose -- just rebase. If the rebase has conflicts, see the conflict resolution rules in Section 5.
Verify there are changes:
if [ -z "$(git status --porcelain)" ]; then
echo "No changes to submit."
exit 0
fi
2. Handle Files
Show git status to the user.
For modified/deleted tracked files, stage them:
git add -u
For untracked files, use smart staging:
Auto-stage without asking (known project files):
- Files in
.claude/, .agents/, .github/ directories
CLAUDE.md, AGENTS.md, NOTES.md
- Files under
src/, tests/ (matches project structure)
- Files matching existing tracked file patterns
Rely on .gitignore for blocking. Do not duplicate exclusion logic in the skill. If .env or other secret files appear as untracked, .gitignore is misconfigured -- suggest fixing it.
Batch-prompt remaining unknowns (if any) in a single table:
New files detected:
Auto-staged: .claude/skills/ai-status/SKILL.md, src/new_module.py, tests/test_new.py
Needs your decision:
1. notes.txt [Ignore?]
2. scratch/debug.py [Ignore?]
Accept defaults, or specify (e.g., "stage 1")?
If >20 untracked files need decisions, warn: "You have N untracked files. This usually means a directory is missing from .gitignore. Fix .gitignore first."
Zero prompts if all untracked files are auto-staged. One prompt if there are unknowns.
If no files are staged after this step, abort.
3. Run Status Checks
Parse $ARGUMENTS for skip flags:
--no-tests -> skip Phase D
--no-security -> skip Phase B and C
Phase A: Pre-commit (always runs)
Detect: Check if .pre-commit-config.yaml exists.
Run (auto-fix loop, max 2 iterations):
uv run pre-commit run --all-files
git diff --name-only
git add -u
uv run pre-commit run --all-files
Auto-fix ALL hook failures (formatting AND diagnostic):
- Formatting hooks (ruff-format, trailing-whitespace, end-of-file-fixer): Auto-fixed by hooks themselves. Re-stage and retry.
- mypy / type checker failures: Analyze the errors and fix the actual types. Hard rule: NEVER use
# type: ignore or Any as escape hatches. Fix the real types (missing return annotation, wrong parameter type, missing import, etc.). If the ONLY available fix requires # type: ignore or Any, then stop and ask the user.
- uv-lock-check failures: Run
uv lock to regenerate the lock file. If uv lock fails (conflicting constraints, yanked package), stop and show the error.
Circuit breaker: Max 2 iterations total. If the same failure repeats after a fix attempt, stop and show errors.
If pre-commit not available: Run individual linters if detected:
uv run ruff check --fix src/ tests/ && uv run ruff format src/ tests/
npx eslint --fix . && npx prettier --write .
Phase B: Security Scan (skip with --no-security)
Detect and run available security tools:
if python -c "import bandit" 2>/dev/null || uv run bandit --version 2>/dev/null; then
uv run bandit -r src/ -ll
fi
if uv run pip-audit --version 2>/dev/null; then
uv export --no-dev --no-hashes -o /tmp/prod-requirements.txt
uv run pip-audit -r /tmp/prod-requirements.txt --no-deps
fi
if [ -f "package-lock.json" ]; then
npm audit --production
fi
On failure -- auto-fix patch bumps, ask for larger changes:
- Check if fixes are available and what version delta they require
- Patch bumps (e.g., 2.28.0 -> 2.28.1): Auto-fix using
uv add package==new_version then uv lock. Continue without asking.
- Minor/major bumps (e.g., 2.28.0 -> 2.31.0 or 3.0.0): Show findings and ask user: Fix / Skip / Abort. These can change API behavior.
- No fix available: Show findings and ask user: Skip / Abort.
- After any auto-fix, re-run the audit once to catch transitive vulnerabilities. Circuit breaker: max 1 auto-fix round for security. If new findings appear after the fix, show them and ask.
Phase C: License Compliance (skip with --no-security)
if uv run pip-licenses --version 2>/dev/null; then
uv run pip-licenses --from=mixed --summary
python -c "
import json, subprocess
result = subprocess.run(['uv', 'run', 'pip-licenses', '--from=mixed', '--format=json'], capture_output=True, text=True)
deps = json.loads(result.stdout)
incompatible = [f\"{d['Name']}: {d['License']}\" for d in deps
if any(g in d.get('License','').upper() for g in ['GPL','AGPL'])
and 'LGPL' not in d.get('License','').upper()]
if incompatible:
print('WARNING: Potentially incompatible licenses:')
for item in incompatible:
print(f' - {item}')
"
fi
On incompatible license found: Warn and ask user to continue or abort.
Phase D: Tests (skip with --no-tests)
Detect and run the project's test suite:
if [ -f "pyproject.toml" ] && grep -q "pytest" pyproject.toml 2>/dev/null; then
uv run pytest --cov=src --cov-fail-under=80 -v
elif [ -d "tests" ] && [ -f "pyproject.toml" ]; then
uv run python -m pytest -v
elif [ -f "package.json" ] && grep -q '"test"' package.json; then
npm test
elif [ -f "Makefile" ] && grep -q '^test:' Makefile; then
make test
fi
On test failure: Show failures and tell the user which tests failed. Do not auto-fix test failures - they require understanding business logic.
4. Commit
Extract issue number from branch name:
BRANCH=$(git branch --show-current)
ISSUE_NUM=$(echo "$BRANCH" | grep -oP 'issue-\K\d+' || \
echo "$BRANCH" | grep -oP '(?<=\/)\d+(?=-)' || \
echo "$BRANCH" | grep -oP '#\K\d+' || \
echo "$BRANCH" | grep -oP 'gh-\K\d+' || \
echo "")
Determine conventional commit type from branch prefix:
COMMIT_TYPE=$(echo "$BRANCH" | grep -oP '^[^/]+')
Generate commit message:
- Type from branch prefix
- Scope from primary directory changed (e.g.,
cli, tests, config)
- Description from diff summary and branch name
- Footer:
Closes #N (for fix/ branches) or Refs #N (for feat/ branches) if issue found
Commit automatically:
Generate the commit message and commit immediately. Show the message in the output so the user can see what was committed, but do NOT ask for approval. Do NOT present Accept/Edit/Abort options.
Committed:
---
feat(cli): add metrics dashboard endpoint
Refs #42
---
git commit -m "<generated message>"
Do NOT ask for approval. Auto-commit with the generated message. The user trusts the conventional commit format derived from the branch prefix and diff analysis.
5. Update Branch
Detect target branch:
Use the branch detection algorithm from CLAUDE.md (Architecture > Branch Detection Algorithm). Use $TARGET from Section 1 if already detected.
Rebase on target:
git fetch origin $TARGET
git rebase origin/$TARGET
Conflict resolution (auto-resolve mechanical, ask for code):
Mechanical files (target branch wins -- main/dev owns versions and changelogs, semantic-release regenerates them):
CHANGELOG.md -- accept target version
pyproject.toml (version field) -- accept target version
__init__.py (version string) -- accept target version
- Lock files (
uv.lock, package-lock.json) -- accept target, then regenerate with uv lock or npm install
For each conflicting file during rebase:
- If ALL hunks are in mechanical files listed above: auto-resolve with
git checkout --theirs <file> then git add <file>, continue rebase
- If a file has MIXED mechanical + code conflicts: auto-resolve the mechanical hunks, leave code hunks for the user
- If ANY file is purely code: show the conflict diff and ask the user to resolve
Rebase conflicts:
- pyproject.toml (version) -- auto-resolved (target branch owns versions)
- CHANGELOG.md -- auto-resolved (target branch owns changelog)
- src/auth.py lines 42-58 -- NEEDS YOUR INPUT
[show conflict diff for auth.py]
After resolving all conflicts: git rebase --continue
6. Push
git push --force-with-lease origin $CURRENT_BRANCH
git push origin $CURRENT_BRANCH
7. Create PR
PR_TITLE="<first line of commit message>"
gh pr create \
--base $TARGET \
--head $CURRENT_BRANCH \
--title "$PR_TITLE" \
--body "## Summary
<brief description of changes>
## Checks run locally
- [x] Pre-commit hooks
- [x] Security scan (bandit + pip-audit)
- [x] License compliance
- [x] Unit tests with coverage
## Issue
<Closes #N or Refs #N if applicable>"
PR_NUMBER=$(gh pr view --json number -q .number)
gh pr merge --auto --merge $PR_NUMBER
8. Final Output and Automatic Transition
Submitted: feat(cli): add metrics dashboard endpoint
PR: https://github.com/owner/repo/pull/99
Target: dev
Automerge: enabled (merge)
Status checks: monitoring...
Automatic next step: After the PR is created and automerge is enabled, immediately invoke /ai-monitor-pipeline to watch the pipeline. Do NOT just print "Next step: /ai-monitor-pipeline" -- actually run it. The user expects the workflow to continue automatically.
Error Handling
- Not on work branch: Abort, suggest
/ai-prepare-branch
- No changes: Exit cleanly
- Pre-commit fails after 2 fix attempts: Show errors, ask user to fix manually
- mypy fix requires type:ignore or Any: Stop, show errors, ask user (never use escape hatches)
- uv lock fails: Stop, show error (conflicting constraints or yanked package)
- Patch security bump: Auto-fix, re-audit once, then continue
- Non-patch security bump: Warn, let user decide (Fix / Skip / Abort)
- Test failures: Show failures, do not auto-fix
- Mechanical rebase conflicts (version, changelog, lock files): Auto-resolve, target branch wins
- Code rebase conflicts: Show conflicts, ask user to resolve
- Push rejected: Check if branch is behind, suggest rebase
- PR creation fails: Show error, the commit and push are still done