Implement the webhook dispatch subsystem so watch sessions can relay scan
results to external services when repositories change.
Webhook package (internal/webhook):
- Sender interface (Name + Send(ctx, Payload)) with three concrete
implementations:
- SlackSender: Block Kit message (header + mrkdwn section with summary
and change list + context footer), emoji status markers
- DiscordSender: single rich embed with color-coded sidebar (green=
clean, yellow=attention, red=errors) and markdown description
- GenericSender: raw JSON POST of the Payload struct, with custom
headers from config
- Config struct per destination: type, URL, send_all, on_change_only,
min_priority, rate_limit, timeout, custom headers, retry (max_attempts
+ backoff); TimeoutOrDefault helper
- NewSender factory switches on type; unknown types rejected
- Dispatcher: NewDispatcher builds a senderHandle per config (sender +
rate-limit state + its own http.Client with per-config timeout);
Dispatch() fans out with per-sender guards: on_change_only skips when
changedCount==0, rate_limit skips when too soon (lastSent check)
- senderHandle.sendWithRetry: retries with exponential backoff on
transient errors (5xx/timeout); 4xx errors fail immediately;
isRetryable helper
HTTP client (internal/httpclient):
- PostJSON now skips json.Unmarshal when out==nil, matching the common
webhook pattern where the response body is irrelevant
Testing:
- All three sender types round-trip through httptest (request payload
decoded and asserted)
- Discord embed colour: yellow for attention, red for errors
- Generic sender payload integrity check
- Dispatcher: OnChangeOnly guard skips when changedCount==0; multiple
senders all fire; HTTP errors surfaced (500)
- Retry: 504 Gateway Timeout retried 3× before succeeding via the
dispatcher's sendWithRetry
- NewSender rejects unknown types, requires a name for identification
Verified: go build, go vet, go test -race (13 packages), gofmt clean.
|
||
|---|---|---|
| .github/workflows | ||
| cmd/gitflow | ||
| internal | ||
| pkg/status | ||
| .gitignore | ||
| .golangci.yml | ||
| go.mod | ||
| go.sum | ||
| implementation.md | ||
| Makefile | ||
| readme.md | ||
| tui.md | ||
gitflow
gitflow is a CLI tool written in Go that explores every Git repository under a parent directory, scans each one's status, and presents the findings. It can rescan on a schedule — flagging repositories as they change — and, when enabled, uses an integrated AI agent to suggest the next actions for repositories that need attention.
Features
- Repo discovery — walks a directory tree and finds working trees,
linked worktrees/submodule checkouts, and bare repositories, without
descending into
.gitinternals - Status scanning — parses
git status --porcelain=v2for each repo: branch, detached HEAD, staged/modified/untracked files, ahead/behind counts, stash count, and remote URL; scans run concurrently with a bounded worker pool - Three output formats — aligned colorized table (default), indented JSON for scripting, and a compact one-line-per-repo view
- Watch mode — rescan on an interval with change detection, desktop notifications, and graceful Ctrl-C shutdown
- AI agent — OpenAI, Ollama (local), or Anthropic providers suggest
concrete next steps (
commit,push,pull, …) for repositories that need attention - Custom rules — user-defined threshold rules flag repositories (e.g.
"behind ≥ 10 commits" ⇒
stale) - Light/dark themes,
NO_COLORsupport, and shell completions
Installation
Requires Go 1.24+ and git on the PATH.
go install gitea.oblak.solutions/dimitar/gitFlow/cmd/gitflow@latest
# or build from a checkout:
make build
Usage
gitflow scan [flags] Scan repositories under a directory once
gitflow watch [flags] Repeatedly scan on an interval
gitflow config Show the effective configuration
gitflow completion [shell] Generate shell completions (bash/zsh/fish/powershell)
gitflow version Print version information
When no --dir is given and stdin is a terminal, gitflow asks for the
parent directory to scan, per the README's original design.
Examples
# One-shot scan of ~/projects (prompts for the directory if omitted)
gitflow scan
# JSON output for scripting
gitflow scan -d ~/projects -f json
# Skip dependency directories and cap traversal depth
gitflow scan -d ~ --exclude node_modules --exclude vendor --max-depth 3
# Watch every 30 seconds, notifying when repositories change
gitflow watch -d ~/projects -i 30s --notify
# AI suggestions via OpenAI (exports OPENAI_API_KEY or ai.api_key_env)
gitflow scan -d ~/projects --ai
# AI suggestions via a local Ollama server
gitflow scan -d ~/projects --ai --ai-provider ollama --ai-model llama3.2
# Custom rules
gitflow scan -d ~/projects # with rules: in ~/.gitflow.yaml
# Shell completion
eval "$(gitflow completion bash)"
Flags
| Flag | Default | Description |
|---|---|---|
-d, --dir |
. |
Parent directory to scan |
-i, --interval |
0 |
Rescan interval (e.g. 30s, 5m); 0 runs once |
-f, --format |
table |
Output format: table, json, or compact |
--color |
auto |
Color output: auto, always, or never |
--theme |
dark |
Color theme: dark or light |
--exclude |
– | Glob patterns of directories to skip (repeatable) |
--max-depth |
0 |
Maximum directory depth to scan (0 = unlimited) |
--workers |
8 |
Number of concurrent git scans |
--notify |
false |
Desktop notifications on watch changes |
--ai |
false |
Enable AI suggestions |
--ai-provider |
openai |
openai, ollama, or anthropic |
--ai-model |
gpt-4o |
Model name (provider default when empty) |
--ai-execute |
false |
Run confirmed AI-suggested commands (experimental) |
Status classes
clean · modified · ahead · behind · diverged · detached ·
bare · error
Configuration
Settings are resolved with the precedence flags > environment >
~/.gitflow.yaml > defaults. Environment variables use a GITFLOW_
prefix with dots as underscores (e.g. GITFLOW_FORMAT=json,
GITFLOW_AI_PROVIDER=ollama). Use GITFLOW_CONFIG=/path/to/file.yaml to
point at a specific config file.
# ~/.gitflow.yaml
dir: ~/projects
interval: 5m
format: table
color: auto
theme: dark
notify: true
max_depth: 3
workers: 8
exclude:
- node_modules
- vendor
ai:
enabled: true
provider: openai # openai | ollama | anthropic
model: gpt-4o
api_key_env: OPENAI_API_KEY
base_url: "" # provider endpoint override
execute: false # run confirmed AI-suggested commands
rules:
- name: stale
field: behind # ahead | behind | stash | changes
op: ">=" # == | != | < | <= | > | >=
value: 10
label: stale
AI agent
With --ai (or ai.enabled), gitflow renders a summary of the scan to the
configured provider and displays a AI SUGGESTIONS section below the
table, color-coded by priority:
AI SUGGESTIONS
REPOSITORY ACTION PRIORITY MESSAGE COMMAND
~/projects/web commit high commit the untracked file git add -A && git commit -m wip
Suggestions include repo_path, action, message, a concrete command
when one is safe, and a priority (low/medium/high). AI failures degrade
to a warning — a scan result is always shown. In watch mode the agent is
consulted only on the first frame and when something changed, so the
provider is not called on every interval.
--ai-execute (experimental)
Runs the commands of AI suggestions only after explicit per-command
confirmation (y/N) and only for actions on an allowlist
(commit, push, pull, stash, checkout) — LLM output can never run
arbitrary shell commands. Treat this feature as experimental.
Development
make build # build the binary (VERSION=... to stamp the version)
make test # run the full test suite
make lint # golangci-lint
Layout:
cmd/gitflow/ CLI commands (scan, watch, config, version, completion)
internal/ai/ AI providers (OpenAI, Ollama, Anthropic) + prompt + execution
internal/app/ orchestration: discovery → scan → result
internal/config/ flags, env, and config-file resolution
internal/git/ porcelain v2 git wrapper
internal/notify/ desktop notifications (Linux/macOS; Windows no-op)
internal/presenter/ table / json / compact output + colors + themes
internal/rules/ user-defined threshold rules
internal/scanner/ repository discovery + concurrent status scanning
internal/scheduler/ interval loop with graceful shutdown
internal/version/ ldflags-injectable version
pkg/status/ shared domain model (RepoInfo, RepoStatus, ScanResult)
CI runs build, vet, and tests (with -race) on Go 1.24/1.26 plus
golangci-lint.
Future work
- Interactive TUI (bubbletea) for navigating repositories and triggering AI suggestions
- Webhook alerts (Slack/Discord) instead of desktop notifications
ai_executehardening and a wider command allowlist- Fetch-history tracking to populate per-repo
last_fetchmetadata