Tag Skill (Direct Execution)
Direct execution skill for creating and pushing semantic version tags to trigger CI/CD deployment. This command is user-only - agents MUST NOT invoke it.
CRITICAL: Deployment timing is user-controlled. This command creates and pushes git tags which immediately trigger CI/CD deployment to production.
Command Syntax
/tag [--patch|--minor|--major] [--force] [--dry-run]
| Flag | Description |
|---|
--patch | Increment patch version (default): v0.2.3 -> v0.2.4 |
--minor | Increment minor version, reset patch: v0.2.3 -> v0.3.0 |
--major | Increment major version, reset minor and patch: v0.2.3 -> v1.0.0 |
--force | Skip confirmation prompt |
--dry-run | Show what would be done without executing |
Execution
Step 1: Parse Arguments
Extract flags from command input:
bash
1# Parse from command input
2increment="patch" # default
3force=false
4dry_run=false
5
6if [[ "$*" == *"--minor"* ]]; then
7 increment="minor"
8fi
9if [[ "$*" == *"--major"* ]]; then
10 increment="major"
11fi
12if [[ "$*" == *"--force"* ]]; then
13 force=true
14fi
15if [[ "$*" == *"--dry-run"* ]]; then
16 dry_run=true
17fi
Step 2: Validate Git State
Check that the repository is in a valid state for tagging:
bash
1echo "=== Validating Git State ==="
2echo ""
3
4# Check for uncommitted changes
5if [ -n "$(git status --porcelain)" ]; then
6 echo "Error: Working tree has uncommitted changes."
7 echo ""
8 echo "Uncommitted files:"
9 git status --short
10 echo ""
11 echo "Resolution: Commit or stash changes before tagging."
12 exit 1
13fi
14
15# Check if we're on a branch
16current_branch=$(git rev-parse --abbrev-ref HEAD)
17if [ "$current_branch" = "HEAD" ]; then
18 echo "Error: Detached HEAD state. Checkout a branch before tagging."
19 exit 1
20fi
21
22# Check if local is behind remote
23git fetch origin "$current_branch" --quiet 2>/dev/null || true
24local_sha=$(git rev-parse HEAD)
25remote_sha=$(git rev-parse "origin/$current_branch" 2>/dev/null || echo "")
26
27if [ -n "$remote_sha" ] && [ "$local_sha" != "$remote_sha" ]; then
28 # Check if we're behind
29 behind=$(git rev-list --count "HEAD..origin/$current_branch" 2>/dev/null || echo "0")
30 if [ "$behind" -gt 0 ]; then
31 echo "Error: Local branch is $behind commit(s) behind remote."
32 echo ""
33 echo "Resolution: Pull latest changes with 'git pull' before tagging."
34 exit 1
35 fi
36fi
37
38echo "Git state: OK (clean working tree, up-to-date with remote)"
Step 3: Compute New Version
Get current version and compute new version based on increment type:
bash
1echo ""
2echo "=== Computing Version ==="
3echo ""
4
5# Get current version (latest tag)
6current_version=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
7echo "Current version: $current_version"
8
9# Parse version components
10major=$(echo "$current_version" | sed 's/v\([0-9]*\).*/\1/')
11minor=$(echo "$current_version" | sed 's/v[0-9]*\.\([0-9]*\).*/\1/')
12patch=$(echo "$current_version" | sed 's/v[0-9]*\.[0-9]*\.\([0-9]*\)/\1/')
13
14# Increment based on type
15case "$increment" in
16 patch)
17 patch=$((patch + 1))
18 ;;
19 minor)
20 minor=$((minor + 1))
21 patch=0
22 ;;
23 major)
24 major=$((major + 1))
25 minor=0
26 patch=0
27 ;;
28esac
29
30new_version="v${major}.${minor}.${patch}"
31echo "New version: $new_version ($increment release)"
32
33# Check if tag already exists
34if git rev-parse "$new_version" >/dev/null 2>&1; then
35 echo ""
36 echo "Error: Tag $new_version already exists."
37 echo ""
38 echo "Existing tags:"
39 git tag -l 'v*' --sort=-v:refname | head -5
40 echo ""
41 echo "Resolution: Use a different increment type or check tag history."
42 exit 1
43fi
Step 4: Display Summary
Show what will be deployed:
bash
1echo ""
2echo "=== Deployment Summary ==="
3echo ""
4echo "Version: $current_version -> $new_version"
5echo "Branch: $current_branch"
6echo "Commit: $(git rev-parse --short HEAD)"
7echo ""
8
9# Show commits since last tag
10commits_since=$(git log "$current_version"..HEAD --oneline 2>/dev/null | wc -l)
11if [ "$commits_since" -gt 0 ]; then
12 echo "Commits since $current_version ($commits_since total):"
13 git log "$current_version"..HEAD --oneline | head -10
14 if [ "$commits_since" -gt 10 ]; then
15 echo " ... and $((commits_since - 10)) more"
16 fi
17else
18 echo "No commits since $current_version"
19fi
20
21echo ""
22echo "This will trigger CI/CD deployment to production."
Step 5: Execute Based on Mode
Dry-Run Mode
If --dry-run is set:
bash
1if [ "$dry_run" = true ]; then
2 echo ""
3 echo "=== DRY RUN MODE ==="
4 echo ""
5 echo "Would execute:"
6 echo " git tag $new_version"
7 echo " git push origin $new_version"
8 echo ""
9 echo "No changes made."
10 exit 0
11fi
Force Mode
If --force is set, skip confirmation and proceed to Step 6.
Interactive Mode (Default)
If neither flag is set, prompt for confirmation using AskUserQuestion:
json
1{
2 "question": "Create and push version tag?",
3 "header": "Version Tag Confirmation",
4 "multiSelect": false,
5 "options": [
6 {
7 "label": "Yes, create and push {new_version}",
8 "description": "Triggers CI/CD deployment to production"
9 },
10 {
11 "label": "No, cancel",
12 "description": "Do not create tag"
13 }
14 ]
15}
If user selects "No, cancel":
bash
1echo ""
2echo "Tag creation cancelled."
3exit 0
Step 6: Create and Push Tag
Create the tag locally:
bash
1echo ""
2echo "=== Creating Tag ==="
3echo ""
4
5# Create tag
6if ! git tag "$new_version"; then
7 echo "Error: Failed to create tag $new_version"
8 exit 1
9fi
10
11echo "Created tag: $new_version"
Push the tag to remote:
bash
1echo ""
2echo "=== Pushing Tag ==="
3echo ""
4
5# Push tag to origin
6if ! git push origin "$new_version"; then
7 echo ""
8 echo "Error: Failed to push tag to remote."
9 echo ""
10 echo "The tag was created locally but not pushed."
11 echo "To recover:"
12 echo " 1. Fix network/auth issues"
13 echo " 2. Run: git push origin $new_version"
14 echo ""
15 echo "Or to undo the local tag:"
16 echo " git tag -d $new_version"
17 exit 1
18fi
19
20echo "Pushed tag: $new_version to origin"
Step 7: Update state.json
Update the deployment_versions section in state.json:
bash
1echo ""
2echo "=== Updating State ==="
3echo ""
4
5state_file="specs/state.json"
6timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
7commit_sha=$(git rev-parse HEAD)
8
9# Check if deployment_versions section exists
10if jq -e '.deployment_versions' "$state_file" >/dev/null 2>&1; then
11 # Update existing section
12 jq --arg version "$new_version" \
13 --arg timestamp "$timestamp" \
14 --arg sha "$commit_sha" \
15 '.deployment_versions.last_deployed = $version |
16 .deployment_versions.last_deployed_at = $timestamp |
17 .deployment_versions.deployment_history = ([{
18 "version": $version,
19 "deployed_at": $timestamp,
20 "commit_sha": $sha
21 }] + .deployment_versions.deployment_history[0:9])' \
22 "$state_file" > "${state_file}.tmp" && mv "${state_file}.tmp" "$state_file"
23else
24 # Create new section
25 jq --arg version "$new_version" \
26 --arg timestamp "$timestamp" \
27 --arg sha "$commit_sha" \
28 '.deployment_versions = {
29 "last_deployed": $version,
30 "last_deployed_at": $timestamp,
31 "deployment_history": [{
32 "version": $version,
33 "deployed_at": $timestamp,
34 "commit_sha": $sha
35 }]
36 }' \
37 "$state_file" > "${state_file}.tmp" && mv "${state_file}.tmp" "$state_file"
38fi
39
40echo "Updated state.json with deployment version $new_version"
Step 8: Display Success
Show completion message with next steps:
bash
1echo ""
2echo "========================================"
3echo " Tag Created and Pushed Successfully"
4echo "========================================"
5echo ""
6echo "Version: $new_version"
7echo "Commit: $(git rev-parse --short HEAD)"
8echo "Pushed to: origin"
9echo ""
10echo "CI/CD deployment has been triggered."
11echo ""
12echo "Verify deployment:"
13echo " - CI/CD pipeline: Check pipeline status in your CI provider"
14echo " - Hosting platform: Check deployment status"
15echo " - Live site: Verify changes are live"
16echo ""
Example Execution Flows
Interactive Flow (Default)
bash
1# User runs: /tag
2
3=== Validating Git State ===
4
5Git state: OK (clean working tree, up-to-date with remote)
6
7=== Computing Version ===
8
9Current version: v0.2.3
10New version: v0.2.4 (patch release)
11
12=== Deployment Summary ===
13
14Version: v0.2.3 -> v0.2.4
15Branch: main
16Commit: abc1234
17
18Commits since v0.2.3 (5 total):
19abc1234 task 44: complete implementation
20def5678 task 43: complete research
21...
22
23This will trigger CI/CD deployment to production.
24
25# Prompt appears:
26[Version Tag Confirmation]
27Create and push version tag?
28 1. Yes, create and push v0.2.4 - Triggers CI/CD deployment to production
29 2. No, cancel - Do not create tag
30
31# User selects option 1
32
33=== Creating Tag ===
34
35Created tag: v0.2.4
36
37=== Pushing Tag ===
38
39Pushed tag: v0.2.4 to origin
40
41=== Updating State ===
42
43Updated state.json with deployment version v0.2.4
44
45========================================
46 Tag Created and Pushed Successfully
47========================================
48
49Version: v0.2.4
50Commit: abc1234
51Pushed to: origin
52
53CI/CD deployment has been triggered.
Dry-Run Flow
bash
1# User runs: /tag --minor --dry-run
2
3=== Validating Git State ===
4
5Git state: OK (clean working tree, up-to-date with remote)
6
7=== Computing Version ===
8
9Current version: v0.2.3
10New version: v0.3.0 (minor release)
11
12=== Deployment Summary ===
13
14Version: v0.2.3 -> v0.3.0
15Branch: main
16Commit: abc1234
17
18Commits since v0.2.3 (5 total):
19...
20
21This will trigger CI/CD deployment to production.
22
23=== DRY RUN MODE ===
24
25Would execute:
26 git tag v0.3.0
27 git push origin v0.3.0
28
29No changes made.
Force Flow
bash
1# User runs: /tag --force
2
3# Skips confirmation, proceeds directly:
4
5=== Validating Git State ===
6
7Git state: OK (clean working tree, up-to-date with remote)
8
9=== Computing Version ===
10
11Current version: v0.2.3
12New version: v0.2.4 (patch release)
13
14=== Deployment Summary ===
15...
16
17=== Creating Tag ===
18
19Created tag: v0.2.4
20
21=== Pushing Tag ===
22
23Pushed tag: v0.2.4 to origin
24
25=== Updating State ===
26
27Updated state.json with deployment version v0.2.4
28
29========================================
30 Tag Created and Pushed Successfully
31========================================
32...
Error Handling
Dirty Working Tree
=== Validating Git State ===
Error: Working tree has uncommitted changes.
Uncommitted files:
M src/pages/index.astro
?? src/components/New.astro
Resolution: Commit or stash changes before tagging.
Behind Remote
=== Validating Git State ===
Error: Local branch is 3 commit(s) behind remote.
Resolution: Pull latest changes with 'git pull' before tagging.
Tag Already Exists
=== Computing Version ===
Current version: v0.2.3
New version: v0.2.4 (patch release)
Error: Tag v0.2.4 already exists.
Existing tags:
v0.2.4
v0.2.3
v0.2.2
v0.2.1
v0.2.0
Resolution: Use a different increment type or check tag history.
Push Failed
=== Pushing Tag ===
Error: Failed to push tag to remote.
The tag was created locally but not pushed.
To recover:
1. Fix network/auth issues
2. Run: git push origin v0.2.4
Or to undo the local tag:
git tag -d v0.2.4
Detached HEAD
=== Validating Git State ===
Error: Detached HEAD state. Checkout a branch before tagging.
Agent Restrictions
CRITICAL: This command is user-only. Agents MUST NOT invoke /tag.
Enforcement layers:
- Frontmatter flag:
user-only: true in skill YAML
- Documentation: Clear CRITICAL warnings in this file
- No agent mapping: Not listed in Skill-to-Agent Mapping table in CLAUDE.md
- Git workflow rules: Existing restrictions in git-workflow.md
Rationale: Deployment timing is a business decision that requires human judgment. Agents prepare code changes but never control when those changes go to production.
.claude/rules/git-workflow.md - Git conventions and agent restrictions
.claude/extensions/web/context/project/web/tools/ - CI/CD and deployment guides (project-specific)