Skip to content

Latest commit

 

History

History
335 lines (245 loc) · 10.6 KB

File metadata and controls

335 lines (245 loc) · 10.6 KB

Shipit — AI Agent Guide

This document describes how a coding agent can use shipit to drive a git flow lite release process: feature branches land in dev, dev promotes to main, and main is tagged for release.


Workflow Overview

feature/my-feature  ──b2b──►  dev  ──b2b──►  main  ──b2t──►  v1.2.3

Each stage follows the same two-step plan / apply pattern:

  1. Plan — collect commits, generate a description/title/notes, write a YAML file to .shipit/plans/<hash>.yml. Nothing is created on the platform yet.
  2. Apply — read the plan file and execute: open a PR/MR, or create and push the annotated tag.

The plan file is the agent's opportunity to review, enrich, or rewrite any field before anything is published.


Setup

shipit init --platform-token <TOKEN> --platform-domain github.com

This writes shipit.toml and creates .shipit/plans/.


Stage 1 — Feature Branch → Dev

1a. Generate a plan

shipit b2b plan feature/my-feature dev

Shipit collects commits on feature/my-feature that are not yet on dev, enriches them with PR/MR titles from the platform API, and writes a plan file.

Conventional-commit structured description (recommended when commits follow the conventional-commit convention):

shipit b2b plan feature/my-feature dev --conventional-commits

The description will be grouped into sections (## Features, ## Bug Fixes, ## Infrastructure, etc.).

Agent-provided title and description (skip commit collection entirely):

shipit b2b plan feature/my-feature dev \
  --title "feat: add payment integration" \
  --description "$(cat <<'EOF'
## Summary
- Adds Stripe checkout flow
- Introduces `PaymentService` with retry logic
- Updates API contract in `openapi.yml`
EOF
)"

When both --title and --description are supplied, no commits are collected and the plan is written immediately.

1b. Apply the plan

shipit b2b apply <plan-filename>.yml

<plan-filename> is the filename (not a full path) of the file written to .shipit/plans/ in the previous step. Shipit opens the pull/merge request and prints the URL.

Capturing the plan for agent use — pass --yaml to receive the full plan on stdout. The output includes a plan_file field with the filename ready for apply, and the commits list for extracting commit SHAs:

PLAN=$(shipit b2b plan feature/my-feature dev --yaml -y)
PLAN_FILE=$(echo "$PLAN" | yq '.plan_file')
shipit b2b apply "$PLAN_FILE"

Stage 2 — Dev → Main

Same commands, different branches:

# Auto-generate from commits (conventional-commit format)
PLAN=$(shipit b2b plan dev main --conventional-commits -y --yaml)
PLAN_FILE=$(echo "$PLAN" | yq '.plan_file')

# Apply
shipit b2b apply "$PLAN_FILE"

The -y flag skips the interactive title prompt and accepts the suggested "Release Candidate vX.Y.Z" title derived from the commit history.


Stage 3 — Main → Tag

3a. Generate a tag plan

shipit b2t plan main

Shipit finds the most recent tag reachable from main, collects commits since that tag, and suggests the next semantic version.

Conventional-commit structured notes:

shipit b2t plan main --conventional-commits -y

Agent-provided tag name and notes:

shipit b2t plan main v1.2.3 \
  --description "$(cat <<'EOF'
## What's Changed
- New payment integration (#42)
- Fixed session timeout bug (#38)
EOF
)"

3b. Apply the tag plan

shipit b2t apply <plan-filename>.yml

Creates an annotated local tag and pushes refs/tags/<name> to the remote.

Capturing the tag plan for agent use:

PLAN=$(shipit b2t plan main --conventional-commits -y --yaml)
PLAN_FILE=$(echo "$PLAN" | yq '.plan_file')
shipit b2t apply "$PLAN_FILE"

Agent-Enriched Plans (Recommended Pattern)

Important for AI agents: Always present the final plan to the user and wait for explicit approval before running apply. Opening a pull/merge request or pushing a tag is irreversible — the plan step exists precisely to give the user a review checkpoint. Never call apply autonomously.

The most powerful use of shipit for an agent is:

  1. Run b2b plan or b2t plan with --yaml to collect commits, write the plan file, and receive the plan on stdout.
  2. Use yq to extract the plan_file name (for the apply step) and the boundary commit SHAs (for git diff).
  3. Run git diff <first-sha>^..<last-sha> to get the full diff for the range.
  4. Summarise the diff and rerun plan with --description and/or --title to overwrite the auto-generated content with a human-quality summary.
  5. Run apply on the enriched plan.

Example: agent-enriched b2b plan

# Step 1 — write the initial plan and capture the YAML output
PLAN=$(shipit b2b plan feature/payments dev --conventional-commits -y --yaml --allow-dirty)

# Step 2 — extract the plan filename and boundary commit SHAs with yq
PLAN_FILE=$(echo "$PLAN" | yq '.plan_file')
LAST_SHA=$(echo "$PLAN"  | yq '.commits[0]'  | awk '{print $NF}')
FIRST_SHA=$(echo "$PLAN" | yq '.commits[-1]' | awk '{print $NF}')

# Step 3 — get the diff
DIFF=$(git diff "${FIRST_SHA}^".."${LAST_SHA}")

# Step 4 — ask the agent to summarise the diff, then rerun plan with the result
SUMMARY="<agent-generated summary goes here>"

PLAN=$(shipit b2b plan feature/payments dev \
  --title "feat(payments): Stripe checkout integration" \
  --description "$SUMMARY" \
  --yaml --yes --allow-dirty)
PLAN_FILE=$(echo "$PLAN" | yq '.plan_file')

# Step 5 — present the plan to the user for confirmation before applying
echo "$PLAN"

# Step 6 — apply only after the user approves
shipit b2b apply "$PLAN_FILE" --allow-dirty

Commit ordering in the commits list

Commits are stored newest-first under the commits: key. Each entry is the string "<message> <sha>" — the SHA is always the last whitespace-separated token. Use index 0 for the newest commit and -1 for the oldest:

PLAN=$(shipit b2b plan feature/payments dev --yaml -y)

LAST_SHA=$(echo "$PLAN"  | yq '.commits[0]'  | awk '{print $NF}')   # newest
FIRST_SHA=$(echo "$PLAN" | yq '.commits[-1]' | awk '{print $NF}')   # oldest

git diff "${FIRST_SHA}^".."${LAST_SHA}"

The diff can then be passed to the agent's language model to generate a structured description before calling plan again with --description.


Flag Reference

shipit b2b plan <source> <target>

Flag Short Description
--conventional-commits -c Group description by commit type
--title <text> Override the suggested PR/MR title
--description <text> Override the auto-generated description
--only-merges Restrict commit collection to merge commits
--no-sign Omit the "generated by Shipit" footer
--yes -y Accept all prompts non-interactively
--yaml Emit the plan as YAML to stdout (includes plan_file field)
--allow-dirty Continue even if the working directory has uncommitted changes
--remote <name> Git remote name (default: origin)
--dir <path> Repository root (default: cwd)

shipit b2b apply <plan-file>

Flag Description
--allow-dirty Continue even if the working directory has uncommitted changes
--remote <name> Git remote name (default: origin)
--dir <path> Repository root (default: cwd)

shipit b2t plan <branch> [tag]

Argument Description
[tag] Tag name to create (default: next semantic version derived from commits)
Flag Short Description
--conventional-commits -c Group notes by commit type
--description <text> Override the auto-generated tag notes
--latest-tag <name> Compare against a specific tag instead of auto-detecting
--only-merges Restrict commit collection to merge commits
--no-sign Omit the "generated by Shipit" footer
--yes -y Accept all prompts non-interactively
--yaml Emit the plan as YAML to stdout (includes plan_file field)
--allow-dirty Continue even if the working directory has uncommitted changes
--remote <name> Git remote name (default: origin)
--dir <path> Repository root (default: cwd)

shipit b2t apply <plan-file>

Flag Description
--allow-dirty Continue even if the working directory has uncommitted changes
--remote <name> Git remote name (default: origin)
--dir <path> Repository root (default: cwd)

Plan File Format

Files written to .shipit/plans/ look like this:

# Shipit Plan - Generated by shipit v0.5.0 on 2024-06-01T12:00:00Z
shipit_version: 0.5.0
generated_at: "2024-06-01T12:00:00Z"
source: feature/payments
target: dev
title:
  value: "Release Candidate v1.2.0"
  generated_by: default        # "user" | "default" | "conventional-commits" | "raw"
description:
  value: |
    ## Features
    - feat: add Stripe checkout flow abc123
  generated_by: conventional-commits
commits:
  - "feat: add Stripe checkout flow abc123 a1b2c3d4"
  - "fix: handle webhook timeout def456 e5f6a7b8"

The generated_by field records what produced each value so downstream tooling (and the agent) can decide whether to trust it or regenerate it.

When --yaml is passed, the stdout output adds a plan_file field not present in the written file:

plan_file: 3f9a1c2e4d7b0e5f.yml

Use this field to drive the apply step without filesystem globbing.


Complete Git Flow Lite Example

# ── Feature → Dev ──────────────────────────────────────────────────────────
PLAN=$(shipit b2b plan feature/payments dev --conventional-commits -y --yaml --allow-dirty)
shipit b2b apply "$(echo "$PLAN" | yq '.plan_file')" --allow-dirty

# ── Dev → Main ─────────────────────────────────────────────────────────────
PLAN=$(shipit b2b plan dev main --conventional-commits -y --yaml --allow-dirty)
shipit b2b apply "$(echo "$PLAN" | yq '.plan_file')" --allow-dirty

# ── Main → Tag ─────────────────────────────────────────────────────────────
PLAN=$(shipit b2t plan main --conventional-commits -y --yaml --allow-dirty)
shipit b2t apply "$(echo "$PLAN" | yq '.plan_file')" --allow-dirty