14 KiB
gitflow — Build & Run Guide
Prerequisites
| Requirement | Minimum | Check with |
|---|---|---|
| Go toolchain | 1.24 | go version |
| Git | any recent | git --version |
make (optional) |
— | make --version |
golangci-lint (optional) |
any recent | golangci-lint --version |
Linux, macOS, and Windows (WSL / MSYS2) are supported. The binary compiles for all three platforms (ARM64 on macOS verified).
Quick start
# Clone
git clone https://gitea.oblak.solutions/dimitar/gitFlow.git
cd gitFlow
# Build (either method)
make build # produces ./gitflow
# or
go build -o gitflow ./cmd/gitflow
# Run
./gitflow version # → "gitflow dev"
./gitflow scan # scans $PWD, asks for directory if stdin is a tty
./gitflow scan -d ~/projects
Make targets
make build # compile the binary, injects VERSION via ldflags
make test # run the full test suite (go test ./...)
make fmt # format all Go sources (gofmt)
make vet # run go vet over all packages
make lint # run golangci-lint (needs it on PATH)
make install # install into $GOPATH/bin (or $GOBIN)
make clean # remove the binary and coverage output
Stamped version (optional):
VERSION=v2.0.1 make build
./gitflow version # → "gitflow v2.0.1"
Commands
| Command | Purpose |
|---|---|
scan |
One-shot scan of repos under --dir, render and exit |
watch |
Rescan every --interval, detect changes, notify, fire webhooks |
tui |
Interactive terminal UI (bubbletea) with navigation, detail pane, AI panel |
config |
Print the resolved configuration (flags + env + file merged) |
completion |
Emit shell completions for bash, zsh, fish, or PowerShell |
version |
Print version and exit |
Every command accepts the same flag set. Use gitflow <cmd> --help for
the full list.
scan — single pass
gitflow scan # prompt for dir, table output
gitflow scan -d ~/projects # explicit dir
gitflow scan -d ~/projects -f json # JSON to stdout, no human-facing sections
gitflow scan -d ~ --exclude node_modules --exclude vendor
gitflow scan -d ~/projects --max-depth 2 --workers 16
watch — periodic scan ---
gitflow watch -d ~/projects -i 30s # scan every 30 seconds
gitflow watch -d ~/projects -i 5m --notify # also fire desktop notifications
gitflow watch -d ~/projects -i 1m --theme=light --color=always
Webhooks fire automatically when a webhooks: section exists in your
config file (no flag needed — they are config-only, like custom rules).
tui — interactive terminal UI
gitflow tui -d ~/projects # one-shot scan, browse with j/k
gitflow tui -d ~/projects -i 30s # periodic scan, countdown in bar
gitflow tui -d ~/projects --ai --ai-provider ollama
TUI keybindings:
↑/↓ or j/k navigate repository list
g / Home jump to top
G / End jump to bottom
Enter / Space toggle detail pane for selected repo
r trigger immediate rescan
a toggle AI suggestion panel (requires --ai)
? toggle help overlay
q / Ctrl-C quit
config — show effective configuration
gitflow config # defaults
gitflow config -d /tmp -f compact # with flag overrides
GITFLOW_DIR=/tmp gitflow config # with env overrides
completion — shell integration
# bash
eval "$(gitflow completion bash)"
# zsh
gitflow completion zsh > ~/.zsh/completion/_gitflow
# fish
gitflow completion fish > ~/.config/fish/completions/gitflow.fish
# powershell
gitflow completion powershell > gitflow.ps1
Configuration
Settings resolve with this precedence (highest first):
- CLI flags —
--dir,--format, etc. - Environment variables —
GITFLOW_prefix, dots replaced by underscores:GITFLOW_DIR,GITFLOW_FORMAT,GITFLOW_AI_PROVIDER, … - Config file —
~/.gitflow.yaml(or./.gitflow.yaml); override the path withGITFLOW_CONFIG=/some/path.yaml - Built-in defaults —
.directory,tableformat,autocolor,darktheme, 8 workers, AI disabled, etc.
Complete config file reference
# ~/.gitflow.yaml
dir: ~/projects
interval: 5m
format: table # table | json | compact
color: auto # auto | always | never
theme: dark # dark | light
notify: true # desktop notifications (linux notify-send, macOS osascript)
max_depth: 3 # 0 = unlimited
workers: 8
exclude: # glob patterns matched against base name and full path
- node_modules
- vendor
- ".cache"
# AI agent (requires --ai flag or enabled: true)
ai:
enabled: false
provider: openai # openai | ollama | anthropic
model: gpt-4o # empty → provider default (ollama: llama3.2, anthropic: claude-3-5-haiku-latest)
api_key_env: OPENAI_API_KEY
base_url: "" # override provider endpoint; empty → vendor default
execute: false # run confirmed AI commands (experimental, allowlist-guarded)
# Custom threshold rules
rules:
- name: stale-branch
field: behind # ahead | behind | stash | changes
op: ">=" # == | != | < | <= | > | >=
value: 10
label: stale
- name: dirty-workspace
field: changes
op: ">="
value: 1
label: dirty
# Webhooks (config-file-only, no CLI flags)
webhooks:
- name: team-slack
type: slack # slack | discord | generic
url: https://hooks.slack.com/services/T00/B00/TOKEN
on_change_only: true # skip when nothing changed
min_priority: 0 # 0 = fire on any change
rate_limit: 5m # minimum interval between fires
timeout: 10s # HTTP timeout per attempt
retry:
max_attempts: 3 # max 5
backoff: 2s # exponential backoff start
- name: ops-discord
type: discord
url: https://discord.com/api/webhooks/...
send_all: false # when true, include every repo, not just changed
- name: internal-dashboard
type: generic
url: https://dashboard.internal/api/gitflow
headers:
Authorization: Bearer ${DASHBOARD_TOKEN}
X-Source: gitflow
Environment variable mapping
| Config key | Env var |
|---|---|
dir |
GITFLOW_DIR |
interval |
GITFLOW_INTERVAL |
format |
GITFLOW_FORMAT |
color |
GITFLOW_COLOR |
theme |
GITFLOW_THEME |
notify |
GITFLOW_NOTIFY |
max_depth |
GITFLOW_MAX_DEPTH |
workers |
GITFLOW_WORKERS |
exclude |
GITFLOW_EXCLUDE (comma-separated) |
ai.enabled |
GITFLOW_AI_ENABLED |
ai.provider |
GITFLOW_AI_PROVIDER |
ai.model |
GITFLOW_AI_MODEL |
ai.api_key_env |
GITFLOW_AI_API_KEY_ENV |
ai.base_url |
GITFLOW_AI_BASE_URL |
ai.execute |
GITFLOW_AI_EXECUTE |
Output formats
Table (default)
REPOSITORY BRANCH STATUS AHEAD/BEHIND CHANGES STASH
~/projects/api main clean - - -
~/projects/web feat/x modified 3/0 2M 1U -
~/projects/lib main behind 0/5 - 2
5 repos | 2 clean | 3 need attention | 0 errors
Colorised per status class (--color=auto respected, NO_COLOR honoured).
Path prefix $HOME collapsed to ~.
JSON
{
"scanned_at": "2026-08-02T10:00:00+02:00",
"parent_dir": "/home/user/projects",
"repos": [
{
"path": "/home/user/projects/api",
"name": "api",
"branch": "main",
"status": "clean"
}
],
"summary": {
"total": 5,
"clean": 2,
"attention": 3,
"errored": 0,
"staged": 0,
"modified": 2,
"untracked": 1
}
}
Status fields marshal as string labels ("clean", "modified", …) and
unmarshal back from both strings and integers.
Compact
✓ ~/projects/api (main)
✗ ~/projects/web (feat/x) +3/-0 2 change(s)
↓ ~/projects/lib (main) +0/-5
Status symbols: ✓ ✗ ↑ ↓ ⇄ ◉ ▢ !.
AI agent
Enable with --ai (or ai.enabled: true in config):
# OpenAI (requires OPENAI_API_KEY exported or ai.api_key_env set)
gitflow scan -d ~/projects --ai
# Local Ollama
gitflow scan -d ~/projects --ai --ai-provider ollama --ai-model llama3.2
# Anthropic
gitflow scan -d ~/projects --ai --ai-provider anthropic --ai-model claude-3-5-haiku-latest
Suggestions render below the scan table, colour-coded by priority:
AI SUGGESTIONS
REPOSITORY ACTION PRIORITY MESSAGE COMMAND
~/projects/web commit high commit the new file git add -A && git commit -m wip
~/projects/lib pull medium pull 5 commits from origin git pull
In JSON mode AI is skipped (machine-readable streams stay clean). In watch mode AI is queried only on the first frame and when something changed.
--ai-execute (experimental)
Runs suggested commands after explicit per-command y/N confirmation,
only for actions on an allowlist (commit, push, pull, stash,
checkout). LLM output can never run arbitrary shell commands.
Webhooks
Webhooks fire in watch mode when webhooks: is present in the config file.
They run in a background goroutine and never block the scan interval.
Each webhook has independent rate limiting and retry with exponential backoff. Transient errors (5xx, timeouts) are retried; 4xx client errors fail immediately.
Slack Block Kit payload:
- Header: "gitflow: scan results"
- Section: summary line with repo counts
- Divider + changed-repo list (when changes exist)
- Context footer: timestamp + "gitflow"
Discord embed payload:
- Sidebar colour: green (all clean), yellow (attention), red (errors)
- Title: "gitflow scan"
- Markdown body with summary + changed repos
- Footer and timestamp
Generic payload:
- Raw JSON POST of the
webhook.Payloadstruct (timestamp,parent_dir,summary,changed,all) to any URL with custom headers.
Test suite
make test # all packages, no race detection
go test -race ./... # with data-race detector (13 packages)
go test -count=1 -race ./internal/ai ./internal/webhook # specific packages
Coverage (latest run):
| Package | Coverage |
|---|---|
internal/ai |
84.9% |
internal/app |
76.9% |
internal/config |
90.9% |
internal/git |
79.6% |
internal/httpclient |
89.1% |
internal/presenter |
86.6% |
internal/rules |
83.3% |
internal/scanner |
88.2% |
internal/scheduler |
88.9% |
internal/webhook |
91.7% |
internal/tui |
89.4% |
pkg/status |
91.1% |
Project layout
gitflow/
├── cmd/gitflow/ CLI commands (cobra)
│ ├── main.go entry point
│ ├── root.go root command + version + completion
│ ├── scan.go gitflow scan
│ ├── watch.go gitflow watch
│ ├── tui.go gitflow tui
│ └── prompt.go TTY helpers (dir prompt, y/N confirmation)
├── internal/
│ ├── ai/ AI providers (OpenAI, Ollama, Anthropic) + prompt
│ ├── app/ orchestration: discovery → scan → result
│ ├── config/ flags, env, config-file resolution (viper)
│ ├── git/ porcelain v2 git wrapper (executor pattern)
│ ├── httpclient/ shared JSON HTTP client (timeout, bounded reads)
│ ├── notify/ desktop notifications (notify-send / osascript)
│ ├── presenter/ table / json / compact output + colors + themes
│ ├── rules/ user-defined threshold rules
│ ├── scanner/ repository discovery + concurrent status scanning
│ ├── scheduler/ interval loop with graceful shutdown
│ ├── tui/ interactive terminal UI (bubbletea + lipgloss)
│ ├── version/ ldflags-injectable version
│ └── webhook/ Slack, Discord, Generic webhook senders + dispatcher
├── pkg/status/ shared domain model (RepoInfo, RepoStatus, ScanResult)
├── go.mod / go.sum
├── Makefile
├── .golangci.yml
├── .github/workflows/ci.yml
├── readme.md user-facing README
├── implementation.md original development plan + progress
├── tui.md TUI & webhooks plan + milestone status
└── buildAndRun.md this file
CI
GitHub Actions workflow (.github/workflows/ci.yml):
jobs:
test (matrix: go 1.24, 1.26)
- go build ./...
- go vet ./...
- go test -race ./...
lint
- golangci-lint
No remote pushes were made during development — branches were committed
and merged locally with --no-ff.
Troubleshooting
"not a git repository" when scanning a directory that IS a repo
The repo's .git directory may be missing or the path might be a bare
.git file pointing at a non-existent gitdir. Check git status manually
in that directory.
"no repositories found"
The directory contains no .git entries. If you expected repos deeper in
the tree, make sure --max-depth isn't set too low and no --exclude
pattern shadows the subtree.
"ai: OPENAI_API_KEY is not set"
Export the key (or the env var name in ai.api_key_env) before running:
export OPENAI_API_KEY=sk-...
gitflow scan --ai
TUI doesn't launch / exits immediately
The TUI requires a terminal that supports the alternate screen buffer. If
you are piping or redirecting output, use scan or watch instead.
Webhook not firing
- Check
gitflow configshows thewebhooks:section. - Webhooks only fire in
watchmode, not inscan. - If
on_change_only: true, the webhook skips when nothing changed between frames. - If
rate_limitis set, the webhook won't fire until the cooldown elapses.
Shell completion "command not found" after eval
Make sure gitflow is on your PATH (go install into $GOBIN):
go install ./cmd/gitflow
# Or
sudo cp gitflow /usr/local/bin/
Cross-compilation
GOOS=linux GOARCH=amd64 go build -o gitflow-linux ./cmd/gitflow
GOOS=darwin GOARCH=arm64 go build -o gitflow-darwin ./cmd/gitflow
GOOS=windows GOARCH=amd64 go build -o gitflow.exe ./cmd/gitflow