Git-Steer Advanced Patterns: Workflows, Multi-Repo Ops, and Extensions
Overview
This post covers advanced git-steer patterns for power users:
- Custom GitHub Actions Workflows - Building your own worker workflows
- Multi-Repo Operations - Orchestrating changes across many repositories
- Extending git-steer - Adding new MCP tools
- The Pattern Applied - Adapting “Bare Tin Foil” to other domains
Git-Steer Series:
- Introduction - Architecture and philosophy
- Setup Guide - Step-by-step installation
- Advanced Patterns (this post) - Custom workflows and extensions
Custom GitHub Actions Workflows
git-steer’s power comes from dispatching GitHub Actions workflows to do heavy lifting. The built-in security-fix.yml workflow is just the beginning.
Anatomy of a git-steer Worker Workflow
name: Custom Worker
on:
workflow_dispatch:
inputs:
target_repo:
description: 'Target repository (owner/repo)'
required: true
type: string
# Add your custom inputs here
custom_param:
description: 'Your custom parameter'
required: false
type: string
job_id:
description: 'Job ID for tracking'
required: false
type: string
jobs:
worker:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Parse target repo
id: parse
run: |
echo "owner=$(echo '${{ inputs.target_repo }}' | cut -d'/' -f1)" >> $GITHUB_OUTPUT
echo "repo=$(echo '${{ inputs.target_repo }}' | cut -d'/' -f2)" >> $GITHUB_OUTPUT
- name: Generate token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.GIT_STEER_APP_ID }}
private-key: ${{ secrets.GIT_STEER_PRIVATE_KEY }}
owner: ${{ steps.parse.outputs.owner }}
repositories: ${{ steps.parse.outputs.repo }}
- name: Checkout target repo
uses: actions/checkout@v4
with:
repository: ${{ inputs.target_repo }}
token: ${{ steps.app-token.outputs.token }}
fetch-depth: 0
# Your custom logic here
- name: Do the work
run: |
echo "Working on ${{ inputs.target_repo }}"
echo "Custom param: ${{ inputs.custom_param }}"
# Your code here
- name: Create PR (if changes)
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
if ! git diff --quiet; then
git checkout -b "automated/custom-change-$(date +%s)"
git add -A
git commit -m "chore: automated change via git-steer"
git push -u origin HEAD
gh pr create --title "Automated Change" --body "Via git-steer"
fi
Example: Dependency Update Workflow
A workflow that updates all dependencies to their latest versions:
name: Dependency Updater
on:
workflow_dispatch:
inputs:
target_repo:
required: true
type: string
update_type:
description: 'Type of update'
type: choice
options:
- patch
- minor
- major
default: minor
jobs:
update-deps:
runs-on: ubuntu-latest
steps:
# ... token generation steps ...
- name: Update npm dependencies
run: |
find . -name "package.json" -not -path "*/node_modules/*" | while read pkg; do
DIR=$(dirname "$pkg")
cd "$DIR"
case "${{ inputs.update_type }}" in
patch) npx npm-check-updates -u --target patch ;;
minor) npx npm-check-updates -u --target minor ;;
major) npx npm-check-updates -u ;;
esac
npm install
cd - > /dev/null
done
- name: Update Python dependencies
run: |
find . -name "requirements*.txt" -type f | while read req; do
pip-compile --upgrade "$req" 2>/dev/null || true
done
# ... PR creation steps ...
Example: License Compliance Workflow
A workflow that adds or updates LICENSE files:
name: License Compliance
on:
workflow_dispatch:
inputs:
target_repo:
required: true
type: string
license_type:
type: choice
options:
- MIT
- Apache-2.0
- GPL-3.0
default: MIT
jobs:
add-license:
runs-on: ubuntu-latest
steps:
# ... setup steps ...
- name: Add LICENSE file
run: |
YEAR=$(date +%Y)
OWNER="${{ steps.parse.outputs.owner }}"
case "${{ inputs.license_type }}" in
MIT)
cat > LICENSE << EOF
MIT License
Copyright (c) $YEAR $OWNER
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...
EOF
;;
# ... other license types ...
esac
Multi-Repo Operations
One of git-steer’s strengths is orchestrating operations across many repositories.
Pattern: Scan → Filter → Act
// Pseudocode for multi-repo operations
const repos = await github.listRepos();
// Filter to repos matching criteria
const targets = repos.filter(r =>
r.name.startsWith('mcp-') &&
!r.archived
);
// Dispatch workflow to each
for (const repo of targets) {
await github.dispatchWorkflow('security-fix.yml', {
target_repo: repo.fullName,
severity: 'high'
});
}
Example: Organization-Wide Security Sweep
Using git-steer via Claude:
You: "List all repos that start with 'mcp-'"
Claude: [calls repo_list with filter="mcp-*"]
Found 5 repos:
- mcp-unifi
- mcp-proxmox
- mcp-wazuh
- mcp-talos
- mcp-github
You: "Scan all of them for security vulnerabilities"
Claude: [calls security_scan for each]
mcp-unifi: 2 high, 5 medium
mcp-proxmox: 0 alerts
mcp-wazuh: 1 critical, 3 high
mcp-talos: 0 alerts
mcp-github: 4 medium
You: "Fix everything critical and high"
Claude: [calls security_fix_pr for mcp-unifi, mcp-wazuh]
Dispatched 2 workflows
- mcp-unifi: job-id-123
- mcp-wazuh: job-id-124
You: "Check status in 5 minutes"
Claude: [calls workflow_status]
Both completed successfully
PRs created:
- mcp-unifi#12
- mcp-wazuh#8
Pattern: Templated Multi-Repo Changes
When you need to make the same change across many repos:
You: "Add a CODEOWNERS file to all my repos that assigns @ry-ops as owner"
Claude: [for each repo, calls repo_commit]
repo_commit(owner: "ry-ops", repo: "cortex",
files: [{path: "CODEOWNERS", content: "* @ry-ops"}],
message: "Add CODEOWNERS")
Committed to 30 repos
Pattern: Coordinated Releases
For projects with multiple repos that release together:
You: "Bump version to 2.0.0 in all @cortex/* repos"
Claude: [for each cortex repo]
1. Read package.json/pyproject.toml
2. Update version field
3. Commit with "chore: bump version to 2.0.0"
4. Create git tag v2.0.0
Extending git-steer
Adding new MCP tools to git-steer is straightforward.
Adding a New Tool
1. Define the tool in src/mcp/server.ts:
const TOOLS: Tool[] = [
// ... existing tools ...
{
name: 'repo_add_codeowners',
description: 'Add or update CODEOWNERS file',
inputSchema: {
type: 'object',
properties: {
owner: { type: 'string' },
repo: { type: 'string' },
owners: {
type: 'array',
items: { type: 'string' },
description: 'GitHub usernames to add as owners'
},
patterns: {
type: 'object',
description: 'Path patterns to owner mappings',
additionalProperties: {
type: 'array',
items: { type: 'string' }
}
}
},
required: ['owner', 'repo', 'owners']
}
}
];
2. Implement the handler:
case 'repo_add_codeowners': {
const defaultOwners = args.owners.map(o => `@${o}`).join(' ');
let content = `# Default owners\n* ${defaultOwners}\n`;
if (args.patterns) {
content += '\n# Path-specific owners\n';
for (const [pattern, owners] of Object.entries(args.patterns)) {
const ownerList = (owners as string[]).map(o => `@${o}`).join(' ');
content += `${pattern} ${ownerList}\n`;
}
}
await this.github.commitFiles(args.owner, args.repo, {
branch: 'main',
message: 'Add CODEOWNERS file',
files: [{ path: 'CODEOWNERS', content }]
});
return { success: true, file: 'CODEOWNERS' };
}
Adding a New Workflow
1. Create the workflow file:
# .github/workflows/your-workflow.yml
name: Your Custom Workflow
on:
workflow_dispatch:
inputs:
target_repo:
required: true
# your inputs
2. Add a dispatch method to the client:
async dispatchYourWorkflow(
targetRepo: string,
options: YourOptions
): Promise<{ dispatched: boolean; jobId: string }> {
const jobId = `your-workflow-${Date.now()}`;
await this.triggerWorkflow('ry-ops', 'git-steer', 'your-workflow.yml', 'main', {
target_repo: targetRepo,
...options,
job_id: jobId
});
return { dispatched: true, jobId };
}
3. Add the MCP tool that dispatches it:
case 'your_tool': {
const result = await this.github.dispatchYourWorkflow(
`${args.owner}/${args.repo}`,
{ /* options */ }
);
return {
success: true,
mode: 'workflow_dispatch',
jobId: result.jobId
};
}
The Pattern Applied: Beyond GitHub
The “Bare Tin Foil” architecture isn’t GitHub-specific. Here’s how to apply it elsewhere:
Pattern Components
| Component | Purpose | Implementation |
|---|---|---|
| Local Agent | Orchestration | MCP server |
| Credential Store | Security | OS Keychain |
| State Store | Configuration/Audit | Git repo, S3, etc. |
| Worker Compute | Heavy lifting | Actions, Lambda, Cloud Run |
| API Layer | Integration | REST/GraphQL client |
Example: Kubernetes Orchestration
┌─────────────────────────────────────────────────────────────────┐
│ YOUR MAC │
│ │
│ Keychain: kubeconfig, service account token │
│ │
│ k8s-steer MCP server │
│ ├─► Tools: pod_list, deploy_scale, job_run │
│ └─► Dispatches K8s Jobs for heavy work │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
│ │
│ k8s-steer-state ConfigMap (config, audit) │
│ k8s-steer-worker Job (ephemeral compute) │
└─────────────────────────────────────────────────────────────────┘
Example: Cloud Infrastructure
┌─────────────────────────────────────────────────────────────────┐
│ YOUR MAC │
│ │
│ Keychain: AWS credentials, GCP service account │
│ │
│ infra-steer MCP server │
│ ├─► Tools: ec2_list, lambda_invoke, s3_sync │
│ └─► Dispatches via Step Functions / Cloud Workflows │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ AWS / GCP │
│ │
│ S3 / GCS bucket (state) │
│ Lambda / Cloud Run (workers) │
│ Step Functions / Workflows (orchestration) │
└─────────────────────────────────────────────────────────────────┘
Example: Database Administration
┌─────────────────────────────────────────────────────────────────┐
│ YOUR MAC │
│ │
│ Keychain: database credentials │
│ │
│ db-steer MCP server │
│ ├─► Tools: query, migrate, backup, analyze │
│ └─► Dispatches via Cloud Functions │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLOUD │
│ │
│ Cloud SQL / RDS (managed database) │
│ Cloud Functions (migration runners, backup jobs) │
│ GCS / S3 (backup storage, audit logs) │
└─────────────────────────────────────────────────────────────────┘
Key Principles
- Credentials stay local - Never in code, config files, or remote state
- State is versioned - Use Git or a versioned object store
- Compute is ephemeral - Spin up, do work, spin down
- Operations are audited - Every action logged
- Interface is natural language - MCP enables Claude integration
Performance Considerations
Rate Limits
GitHub Apps have rate limits:
- 5,000 requests per hour per installation
- 1,000 requests per hour for GraphQL
Mitigation strategies:
- Batch operations where possible
- Use ETags for caching
- Implement backoff on 429 responses
Workflow Concurrency
GitHub Actions has concurrency limits:
- 20 concurrent jobs (free tier)
- 180 concurrent jobs (enterprise)
For large operations:
- Implement queuing in git-steer-state
- Use workflow concurrency groups
- Stagger dispatches
Large File Operations
The GitHub Contents API has a 100MB file limit.
For larger files:
- Use Git LFS
- Dispatch a workflow that clones and commits directly
- Use the Git Data API for streaming
Debugging Tips
Enable Verbose Logging
# In Claude Desktop config
{
"mcpServers": {
"git-steer": {
"command": "node",
"args": ["/path/to/git-steer/bin/cli.js", "start", "--stdio", "--verbose"]
}
}
}
Check Workflow Logs
# Get recent workflow runs
gh run list --repo ry-ops/git-steer --workflow security-fix.yml
# View specific run logs
gh run view RUN_ID --repo ry-ops/git-steer --log
Inspect State
# View audit log
gh api repos/ry-ops/git-steer-state/contents/state/audit.jsonl \
--jq '.content' | base64 -d | jq -s '.[-10:]'
# View job history
gh api repos/ry-ops/git-steer-state/contents/state/jobs.jsonl \
--jq '.content' | base64 -d | jq -s '.[-5:]'
What’s Next
git-steer is under active development. Planned features:
- Scheduled operations - Cron-based security scans, branch cleanup
- Webhooks - React to GitHub events in real-time
- Multi-account - Manage multiple GitHub accounts/orgs
- Metrics dashboard - Visualize operations, rate limits, audit logs
Contributions welcome at github.com/ry-ops/git-steer.
Conclusion
git-steer represents a new way of thinking about developer tooling:
- Your machine is a controller, not a warehouse
- Cloud is the compute layer, not just deployment target
- Git is the database, not just version control
- Natural language is the interface, not just documentation
The pattern scales from a single developer managing a few repos to teams managing hundreds. The architecture stays the same—only the workflows and policies change.
Build your own workflows. Extend the tools. Adapt the pattern. Make your machine a steering wheel, not an engine.
Repository: github.com/ry-ops/git-steer
Previous in series:
Explore git-steer on GitHub
Check out the source code, documentation, and contribute to the project.