AgentSkillsCN

Git Commit

Git 提交

SKILL.md

Git Commit Skill

Description

Creates standardized git commits following project conventions for the Uruguay Electoral Map.

Trigger

code
/commit [--type <type>] [--scope <scope>] [--message <message>]

Input Parameters

ParameterTypeRequiredDefaultDescription
--typestringNoautoCommit type (feat, fix, etc.)
--scopestringNoautoScope of changes
--messagestringNoautoCommit message

Commit Types

TypeDescriptionExample
featNew featureAdd Canelones department
fixBug fixFix tooltip display on mobile
refactorCode refactoringSplit RegionMap component
perfPerformance improvementOptimize GeoJSON loading
docsDocumentationUpdate CLAUDE.md
styleCode style/formattingFormat with Prettier
choreMaintenance tasksUpdate dependencies
dataData file updatesAdd electoral data

Commit Message Format

code
<type>(<scope>): <short description>

[optional body]

[optional footer]

Examples

code
feat(canelones): add Canelones department data and map

- Added canelones_odn.csv with 312 lists
- Added canelones_odd.csv with 298 lists
- Added optimized canelones_map.json (2.1MB)
- Updated regions.json with map parameters

Closes #15
code
refactor(map): split RegionMap into smaller components

- Created ElectoralMap.vue (container)
- Created MapLegend.vue (color scale)
- Created MapTooltip.vue (hover content)
- Created SelectedInfo.vue (selection panel)

BREAKING CHANGE: RegionMap.vue removed, use ElectoralMap.vue

Workflow

Step 1: Analyze Changes

javascript
async function analyzeChanges() {
  const status = await git.status();

  const changes = {
    staged: status.staged,
    modified: status.modified,
    untracked: status.not_added,
    deleted: status.deleted
  };

  // Categorize by file type/location
  const categories = categorizeChanges(changes);

  return { changes, categories };
}

function categorizeChanges(changes) {
  const allFiles = [...changes.staged, ...changes.modified];

  return {
    data: allFiles.filter(f => f.match(/public\/.*\.(csv|json)$/)),
    components: allFiles.filter(f => f.match(/src\/components\//)),
    stores: allFiles.filter(f => f.match(/src\/stores\//)),
    config: allFiles.filter(f => f.match(/\.(json|js|ts)$/) && !f.includes('src/')),
    docs: allFiles.filter(f => f.match(/\.(md)$/))
  };
}

Step 2: Determine Commit Type

javascript
function determineCommitType(categories) {
  // Priority-based type detection
  if (categories.data.length > 0) {
    if (categories.data.some(f => f.includes('_map.json'))) {
      return 'feat'; // New department map
    }
    return 'data';
  }

  if (categories.components.length > 0) {
    const componentContent = readFiles(categories.components);
    if (componentContent.some(c => c.includes('// BREAKING'))) {
      return 'refactor';
    }
    return 'feat';
  }

  if (categories.docs.length > 0 && categories.components.length === 0) {
    return 'docs';
  }

  return 'chore';
}

Step 3: Determine Scope

javascript
function determineScope(categories) {
  // Check for department-specific changes
  const deptMatch = categories.data
    .map(f => f.match(/public\/([a-z_]+)_(?:odn|odd|map)/))
    .filter(Boolean);

  if (deptMatch.length > 0) {
    return deptMatch[0][1]; // e.g., "canelones"
  }

  // Check for component changes
  if (categories.components.length > 0) {
    if (categories.components.some(f => f.includes('/map/'))) {
      return 'map';
    }
    if (categories.components.some(f => f.includes('/selectors/'))) {
      return 'selectors';
    }
  }

  return null; // No scope
}

Step 4: Generate Message

javascript
function generateCommitMessage(type, scope, changes) {
  const scopeStr = scope ? `(${scope})` : '';

  // Generate short description based on changes
  let description = '';

  switch (type) {
    case 'feat':
      if (scope && isDepartment(scope)) {
        description = `add ${formatName(scope)} department`;
      } else {
        description = summarizeFeature(changes);
      }
      break;
    case 'refactor':
      description = summarizeRefactoring(changes);
      break;
    case 'data':
      description = `update electoral data for ${scope || 'multiple departments'}`;
      break;
    default:
      description = summarizeChanges(changes);
  }

  return `${type}${scopeStr}: ${description}`;
}

Step 5: Create Commit

javascript
async function createCommit(message, body = null) {
  // Stage all relevant files
  await git.add('.');

  // Build full commit message
  let fullMessage = message;
  if (body) {
    fullMessage += `\n\n${body}`;
  }

  // Create commit
  await git.commit(fullMessage);

  return git.log({ n: 1 });
}

Output

Commit Preview

code
Commit Preview
══════════════

Type: feat
Scope: canelones
Message: feat(canelones): add Canelones department

Files to commit:
  A public/canelones_odn.csv
  A public/canelones_odd.csv
  A public/canelones_map.json
  M public/regions.json
  M CLAUDE.md

Proceed with commit? [Y/n]

Commit Result

code
Committed: feat(canelones): add Canelones department

Commit: a1b2c3d4
Author: Developer <dev@example.com>
Date: 2024-01-15 10:30:00

Changed files: 5
Insertions: 15234
Deletions: 2

Special Cases

Breaking Changes

javascript
// Detect breaking changes
if (hasBreakingChanges(changes)) {
  message += '\n\nBREAKING CHANGE: ' + describeBreakingChange(changes);
}

Multi-Department Commits

javascript
// When adding multiple departments
if (departments.length > 1) {
  type = 'feat';
  scope = 'data';
  description = `add ${departments.length} departments: ${departments.join(', ')}`;
}

Integration

  • Works with standard git workflow
  • Respects .gitignore
  • Compatible with pre-commit hooks

Related Skills

  • add-department
  • validate-csv