---
name: gitflow-commit
description: GitFlow commit with EF Core migration validation (OPTIMISE)
color: green
model: haiku
tools: Bash, Read, Glob, Grep
---

# GitFlow Commit Agent

Commit with EF Core migration validation. Executes directly, displays only the result.

> **OPTIMISED:** haiku model, reduced tools (no Edit/Write). Access to current context.

## EXECUTION SEQUENCE

### 1. Guards

```bash
CURRENT=$(git rev-parse --abbrev-ref HEAD)

# BLOCK: main is protected
[[ "$CURRENT" == "main" ]] && { echo "BLOCKED - main is protected"; exit 1; }

# WARNING: develop → offer rescue to feature
[[ "$CURRENT" == "develop" ]] && echo "WARNING: direct commit on develop"
```

### 2. Analyze Changes

```bash
# Run in parallel: status + diff + log
git status --short
git diff --cached --stat
git diff --stat
git ls-files --others --exclude-standard
```

If nothing to commit → exit with "Nothing to commit".

### 2b. Validate appsettings Integrity

```bash
# Check if any appsettings*.json files are staged or modified
APPSETTINGS_CHANGED=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null | grep -E "appsettings.*\.json$" || true)
APPSETTINGS_UNSTAGED=$(git diff --name-only 2>/dev/null | grep -E "appsettings.*\.json$" || true)
ALL_APPSETTINGS=$(echo -e "$APPSETTINGS_CHANGED\n$APPSETTINGS_UNSTAGED" | sort -u | grep -v "^$" || true)
```

If appsettings files detected, for EACH file compare top-level JSON keys against HEAD:

```bash
for FILE in $ALL_APPSETTINGS; do
  # Skip if file is new (not in HEAD)
  git show HEAD:"$FILE" > /dev/null 2>&1 || continue

  git show HEAD:"$FILE" | node -e "
    const fs = require('fs');
    const headContent = fs.readFileSync(0, 'utf8');
    const currContent = fs.readFileSync('$FILE', 'utf8');
    const headKeys = Object.keys(JSON.parse(headContent));
    const currKeys = new Set(Object.keys(JSON.parse(currContent)));
    const missing = headKeys.filter(k => !currKeys.has(k));
    if (missing.length > 0) {
      console.log('SECTIONS REMOVED: ' + missing.join(', '));
      process.exit(1);
    }
    console.log('OK - all sections preserved');
  "
done
```

**BLOCK if any section was removed.** Show removed section names, ask user for explicit confirmation before proceeding.

#### 2b-ii. Detect Hardcoded Secrets

For each appsettings file in `$ALL_APPSETTINGS`, scan for hardcoded secret values:

```bash
for FILE in $ALL_APPSETTINGS; do
  node -e "
    const fs = require('fs');
    const content = fs.readFileSync('$FILE', 'utf8');
    const secrets = [];
    // Connection strings with password
    const connStr = content.match(/Password\s*=\s*[^;\"]+/gi) || [];
    connStr.forEach(m => { if (!/CONFIGURE_HERE|TODO|PLACEHOLDER|\{/i.test(m)) secrets.push(m.trim()); });
    // API keys patterns (sk-, key-, long hex/base64)
    const apiKeys = content.match(/\"[^\"]+\":\s*\"(sk-[a-zA-Z0-9]{20,}|[a-f0-9]{32,}|[A-Za-z0-9+\/=]{40,})\"/g) || [];
    apiKeys.forEach(m => secrets.push(m.trim()));
    // GUID-like secrets in sensitive sections (ClientSecret, SecretKey, ApiKey)
    const sensitiveKeys = content.match(/\"(ClientSecret|SecretKey|ApiKey|Secret|PrivateKey|Password|SmtpPassword)\"\s*:\s*\"(?!<CONFIGURE_HERE>)[^\"]{8,}\"/gi) || [];
    sensitiveKeys.forEach(m => secrets.push(m.trim()));
    if (secrets.length > 0) {
      console.log('SECRETS DETECTED in $FILE:');
      secrets.forEach(s => console.log('  - ' + s));
      console.log('Replace with <CONFIGURE_HERE> placeholder before committing.');
      process.exit(1);
    }
  " 2>/dev/null
done
```

**WARNING if secrets detected.** Show the detected patterns and ask user to replace values with `<CONFIGURE_HERE>` placeholder. The key/section MUST remain — only the VALUE should be replaced.

### 3. Detect EF Core Migrations

```bash
# Find migration files in staged + modified + untracked
MIGRATIONS=$(git diff --cached --name-only && git diff --name-only && git ls-files --others --exclude-standard | grep -E "Migrations/.*\.cs$")
```

If migrations detected, validate **3 required files**:
- `{Name}.cs` (main migration)
- `{Name}.Designer.cs`
- `{DbContext}ModelSnapshot.cs`

**BLOCK if any file is missing.** Check for destructive operations (`DropTable`, `DropColumn`, `DeleteData`) and require confirmation.

### 4. Stage + Commit

```bash
# Stage specific files (prefer explicit over git add -A)
git add {files}

# Generate conventional commit message
# {type}({scope}): {description}
# Types: feat, fix, db(migrations), chore

git commit -m "$(cat <<'EOF'
{type}({scope}): {description}

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"
```

### 5. Push (ALWAYS ASK USER)

**MANDATORY:** Ask user before pushing. Never push automatically.

After push, verify with force-fetch:
```bash
git fetch origin $CURRENT:refs/remotes/origin/$CURRENT --force --quiet
```

### 6. Summary

```
COMMIT CREATED
  Branch:  {branch}
  Commit:  {hash}
  Message: {first line}
  Files:   {N} changed
  EF Core: {Valid | Destructive confirmed | N/A}
  Pushed:  {yes | no}
  NEXT: /gitflow pr
```

## Priority

Correctness > Speed. Never commit incomplete migration.
