summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-10-31 20:13:32 +0200
committerPaul Buetow <paul@buetow.org>2025-10-31 20:13:32 +0200
commit11eea6a82cbfdde40ec1457c6ea080da4da6b7dc (patch)
tree8026068f6a3beb3ee02c45f06f4487f4b89caaf1
parent5c3e0b5cf99d028c4f06be7a825388b296e37a22 (diff)
feat: implement amp AI tool support and replace Taskfile with Magev0.10.0
- Add amp as default AI tool for release notes and showcase generation - Fallback chain: amp → hexai → claude → aichat - Replace Taskfile.yaml with magefile.go for build automation - Update all documentation (README.md, AGENTS.md, doc/development.md) - Update version to 0.10.0 Amp-Thread-ID: https://ampcode.com/threads/T-735ba1e2-0255-4b43-8ed1-6c0d2f78301b Co-authored-by: Amp <amp@ampcode.com>
-rw-r--r--AGENTS.md14
-rw-r--r--README.md18
-rw-r--r--Taskfile.yaml94
-rw-r--r--cmd/gitsyncer/main.go3
-rw-r--r--doc/development.md38
-rw-r--r--gitsyncer.example.json24
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--internal/cli/description_cache.go51
-rw-r--r--internal/cli/description_sync.go185
-rw-r--r--internal/cli/flags.go72
-rw-r--r--internal/cli/handlers.go46
-rw-r--r--internal/cli/release.go270
-rw-r--r--internal/cli/showcase_handler.go10
-rw-r--r--internal/cli/showcase_only_handler.go152
-rw-r--r--internal/cli/sync_handlers.go256
-rw-r--r--internal/cmd/list.go4
-rw-r--r--internal/cmd/manage.go30
-rw-r--r--internal/cmd/release.go26
-rw-r--r--internal/cmd/root.go21
-rw-r--r--internal/cmd/showcase.go65
-rw-r--r--internal/cmd/sync.go58
-rw-r--r--internal/cmd/test.go12
-rw-r--r--internal/codeberg/codeberg.go124
-rw-r--r--internal/config/config.go44
-rw-r--r--internal/github/github.go128
-rw-r--r--internal/release/release.go411
-rw-r--r--internal/showcase/ai_context.go338
-rw-r--r--internal/showcase/code_extractor.go228
-rw-r--r--internal/showcase/images.go78
-rw-r--r--internal/showcase/language_detector.go214
-rw-r--r--internal/showcase/metadata.go33
-rw-r--r--internal/showcase/showcase.go311
-rw-r--r--internal/state/state.go2
-rw-r--r--internal/sync/branch_analyzer.go94
-rw-r--r--internal/sync/branch_filter.go8
-rw-r--r--internal/sync/branch_sync.go12
-rw-r--r--internal/sync/git_operations.go22
-rw-r--r--internal/sync/repository_setup.go14
-rw-r--r--internal/sync/sync.go40
-rw-r--r--internal/version/version.go2
-rw-r--r--magefile.go139
42 files changed, 1904 insertions, 1790 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 81f789a..354820a 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -7,13 +7,13 @@
- `doc/`: architecture, API, and development docs. `dist/`: build artifacts.
## Build, Test, and Development Commands
-- Build (current platform): `task build` or `go build -o gitsyncer ./cmd/gitsyncer`.
-- Cross-compile: `task build-all` (linux/darwin/windows variants also available).
-- Run binary: `./gitsyncer --help` or `task run -- --version`.
-- Unit tests (when present): `task test` or `go test ./...`.
+- Build (current platform): `mage build` or `go build -o gitsyncer ./cmd/gitsyncer`.
+- Cross-compile: `mage buildAll` (linux/darwin/windows variants also available).
+- Run binary: `./gitsyncer --help` or `mage run`.
+- Unit tests (when present): `mage test` or `go test ./...`.
- Integration tests: `cd test && ./run_integration_tests.sh`.
-- Format/lint: `task fmt`, `task vet`, `task lint` (golangci-lint).
-- Tidy modules: `task mod-tidy`.
+- Format/lint: `mage fmt`, `mage vet`, `mage lint` (golangci-lint).
+- Tidy modules: `mage modTidy`.
## Coding Style & Naming Conventions
- Go formatting: enforce `gofmt`/`go fmt`; prefer `goimports` in your editor.
@@ -31,7 +31,7 @@
- Commits: Conventional Commits format — `type(scope): summary`.
- Examples: `feat(sync): add bidirectional mode`, `fix(config): validate organizations`.
- PRs: clear description, linked issues, testing notes (unit + integration), and updated docs (`doc/` or `README.md`) when user-visible.
-- Keep changes focused; run `task fmt vet lint` before submitting.
+- Keep changes focused; run `mage fmt vet lint` before submitting.
## Security & Configuration Tips
- Configuration lives at `~/.config/gitsyncer/config.json` (see `gitsyncer.example.json`). Do not commit secrets.
diff --git a/README.md b/README.md
index 9feb08d..38c9e85 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
GitSyncer is a tool for synchronizing git repositories between multiple organizations (e.g., GitHub and Codeberg). It automatically keeps all branches in sync across different git hosting platforms.
-It has been vibe coded mainly vibe coded using Claude Code CLI.
+It has been vibe coded mainly using AI tools (Claude Code CLI and amp).
## Documentation
@@ -173,16 +173,16 @@ gitsyncer release create --update-existing
# Create for specific repository without AI
gitsyncer release create myproject --no-ai-notes
-# Use aichat instead of claude for AI release notes
-gitsyncer release create --ai-tool aichat
+# Use amp for AI release notes (default)
+gitsyncer release create --ai-tool amp
```
#### AI Release Notes Engines
-- Default flow: tries `hexai` first by piping the generated commit/diff payload to stdin and passing an instruction prompt as the sole argument (equivalent to `cat SOMETEXT.txt | hexai PROMPT`).
-- Fallback: if `hexai` is not available or fails, falls back to `claude --model sonnet`, then to `aichat`.
-- Explicit tool: `--ai-tool claude` or `--ai-tool aichat` influences the fallback preference, but `hexai` is still attempted first when available.
-- Requirements: ensure `hexai`, `claude`, or `aichat` are installed and available in `PATH`.
+- Default flow: tries `amp` first by piping the generated commit/diff payload to stdin and passing an instruction prompt via `--execute` (equivalent to `echo SOMETEXT | amp --execute 'PROMPT'`).
+- Fallback: if `amp` is not available or fails, falls back to `hexai`, then `claude --model sonnet`, then to `aichat`.
+- Explicit tool: `--ai-tool claude` or `--ai-tool aichat` influences the fallback preference, but `amp` is still attempted first when available.
+- Requirements: ensure `amp`, `hexai`, `claude`, or `aichat` are installed and available in `PATH`.
### Project Showcase
@@ -369,7 +369,7 @@ The backup location path format is: `user@host:path/REPONAME.git`
## Project Showcase Generation
-GitSyncer can generate a comprehensive showcase of all your projects using Claude AI. This feature creates a formatted document with project summaries, statistics, and code snippets.
+GitSyncer can generate a comprehensive showcase of all your projects using AI (amp by default). This feature creates a formatted document with project summaries, statistics, and code snippets.
### How it works
@@ -381,7 +381,7 @@ GitSyncer can generate a comprehensive showcase of all your projects using Claud
- Latest release version and date
- AI-assistance detection (looks for CLAUDE.md, GEMINI.md files)
-2. **AI-Powered Summaries**: Uses Claude AI to generate concise project descriptions that explain:
+2. **AI-Powered Summaries**: Uses AI (amp, hexai, claude, or aichat) to generate concise project descriptions that explain:
- What the project does
- Why it's useful
- How it's implemented
diff --git a/Taskfile.yaml b/Taskfile.yaml
deleted file mode 100644
index 94a968b..0000000
--- a/Taskfile.yaml
+++ /dev/null
@@ -1,94 +0,0 @@
-version: '3'
-
-vars:
- BINARY_NAME: gitsyncer
- BUILD_DIR: .
- CMD_PATH: ./cmd/gitsyncer
- LDFLAGS: -s -w
-
-tasks:
- default:
- desc: Build the gitsyncer binary
- cmds:
- - go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}} {{.CMD_PATH}}
-
- build:
- desc: Build the gitsyncer binary
- cmds:
- - go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}} {{.CMD_PATH}}
-
- build-all:
- desc: Build for all supported platforms
- cmds:
- - task: build-linux
- - task: build-darwin
- - task: build-windows
-
- build-linux:
- desc: Build for Linux
- cmds:
- - GOOS=linux GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-linux-amd64 {{.CMD_PATH}}
-
- build-darwin:
- desc: Build for macOS
- cmds:
- - GOOS=darwin GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-darwin-amd64 {{.CMD_PATH}}
- - GOOS=darwin GOARCH=arm64 go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-darwin-arm64 {{.CMD_PATH}}
-
- build-windows:
- desc: Build for Windows
- cmds:
- - GOOS=windows GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-windows-amd64.exe {{.CMD_PATH}}
-
- run:
- desc: Build and run the gitsyncer binary
- cmds:
- - task: build
- - ./{{.BINARY_NAME}}
-
- test:
- desc: Run tests
- cmds:
- - go test ./...
-
- test-verbose:
- desc: Run tests with verbose output
- cmds:
- - go test -v ./...
-
- clean:
- desc: Clean build artifacts
- cmds:
- - rm -f {{.BINARY_NAME}}
- - rm -f {{.BINARY_NAME}}-*
-
- mod-tidy:
- desc: Tidy go modules
- cmds:
- - go mod tidy
-
- fmt:
- desc: Format Go code
- cmds:
- - go fmt ./...
-
- vet:
- desc: Run go vet
- cmds:
- - go vet ./...
-
- lint:
- desc: Run golangci-lint
- cmds:
- - golangci-lint run
-
- install:
- desc: Install gitsyncer to $GOPATH/bin
- cmds:
- - go install {{.CMD_PATH}}
-
- version:
- desc: Show version
- deps: [build]
- cmds:
- - ./{{.BINARY_NAME}} --version \ No newline at end of file
diff --git a/cmd/gitsyncer/main.go b/cmd/gitsyncer/main.go
index e656e7b..4882b34 100644
--- a/cmd/gitsyncer/main.go
+++ b/cmd/gitsyncer/main.go
@@ -4,8 +4,7 @@ import (
"codeberg.org/snonux/gitsyncer/internal/cmd"
)
-
func main() {
// Execute the cobra command
cmd.Execute()
-} \ No newline at end of file
+}
diff --git a/doc/development.md b/doc/development.md
index b22fbd4..75274f0 100644
--- a/doc/development.md
+++ b/doc/development.md
@@ -18,8 +18,7 @@ This guide is for contributors who want to help develop GitSyncer.
- Go 1.21 or later
- Git
-- Make (optional, for Makefile)
-- Task (optional, for Taskfile)
+- Mage (optional, for magefile.go)
### Clone the Repository
@@ -46,12 +45,8 @@ go mod verify
### Install Development Tools
```bash
-# Install Task runner (optional)
-# macOS
-brew install go-task
-
-# Linux
-sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+# Install Mage (build tool)
+go install github.com/magefile/mage@latest
# Install golangci-lint for linting
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
@@ -91,26 +86,29 @@ gitsyncer/
├── go.sum # Go module checksums
├── LICENSE # BSD 2-Clause License
├── README.md # Project README
-├── CLAUDE.md # AI assistant hints
-└── Taskfile.yaml # Task automation
+├── AGENTS.md # AI assistant hints
+└── magefile.go # Mage build automation
```
## Building
-### Using Task (Recommended)
+### Using Mage (Recommended)
```bash
+# List available targets
+mage -l
+
# Build for current platform
-task build
+mage build
# Build for all platforms
-task build-all
+mage buildAll
# Run directly
-task run
+mage run
-# Run with arguments
-task run -- --version
+# Show version
+mage version
```
### Using Go Directly
@@ -202,7 +200,7 @@ Follow standard Go conventions:
1. **Formatting**: Use `gofmt` or `goimports`
```bash
# Format all files
- task fmt
+ mage fmt
# or
gofmt -w .
```
@@ -364,7 +362,7 @@ test: add integration tests for branch filtering
2. **Format code**:
```bash
- task fmt
+ mage fmt
```
3. **Update documentation**:
@@ -372,7 +370,7 @@ test: add integration tests for branch filtering
- Update README if adding user-facing features
- Add examples to `doc/examples.md`
-4. **Update CLAUDE.md** if adding development commands
+4. **Update AGENTS.md** if adding development commands
### Pull Request Process
@@ -442,6 +440,6 @@ GITSYNCER_DEBUG=1 gitsyncer --sync test-repo
```
4. Build releases:
```bash
- task build-all
+ mage buildAll
```
5. Create GitHub/Codeberg release with binaries \ No newline at end of file
diff --git a/gitsyncer.example.json b/gitsyncer.example.json
deleted file mode 100644
index 39984b4..0000000
--- a/gitsyncer.example.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "organizations": [
- {
- "host": "git@codeberg.org",
- "name": "snonux"
- },
- {
- "host": "git@github.com",
- "name": "snonux"
- },
- {
- "host": "paul@t450:git",
- "backupLocation": true
- }
- ],
- "repositories": [
- "gitsyncer",
- "another-repo",
- "yet-another-repo"
- ],
- "skip_releases": {
- "fapi": ["0.0.1"]
- }
-}
diff --git a/go.mod b/go.mod
index e53cf63..86f282a 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.24.3
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/magefile/mage v1.15.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
)
diff --git a/go.sum b/go.sum
index ffae55e..e6f25e1 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
+github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
diff --git a/internal/cli/description_cache.go b/internal/cli/description_cache.go
index 1cfc951..a2ce9ab 100644
--- a/internal/cli/description_cache.go
+++ b/internal/cli/description_cache.go
@@ -1,38 +1,37 @@
package cli
import (
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
)
// loadDescriptionCache loads the per-repo canonical description cache
func loadDescriptionCache(workDir string) map[string]string {
- cache := make(map[string]string)
- cacheFile := filepath.Join(workDir, ".gitsyncer-descriptions-cache.json")
- data, err := os.ReadFile(cacheFile)
- if err != nil {
- return cache
- }
- if err := json.Unmarshal(data, &cache); err != nil {
- fmt.Printf("Warning: Failed to parse descriptions cache: %v\n", err)
- return make(map[string]string)
- }
- fmt.Printf("Loaded descriptions cache with %d entries\n", len(cache))
- return cache
+ cache := make(map[string]string)
+ cacheFile := filepath.Join(workDir, ".gitsyncer-descriptions-cache.json")
+ data, err := os.ReadFile(cacheFile)
+ if err != nil {
+ return cache
+ }
+ if err := json.Unmarshal(data, &cache); err != nil {
+ fmt.Printf("Warning: Failed to parse descriptions cache: %v\n", err)
+ return make(map[string]string)
+ }
+ fmt.Printf("Loaded descriptions cache with %d entries\n", len(cache))
+ return cache
}
// saveDescriptionCache saves the per-repo canonical description cache
func saveDescriptionCache(workDir string, cache map[string]string) error {
- cacheFile := filepath.Join(workDir, ".gitsyncer-descriptions-cache.json")
- data, err := json.MarshalIndent(cache, "", " ")
- if err != nil {
- return fmt.Errorf("failed to marshal descriptions cache: %w", err)
- }
- if err := os.WriteFile(cacheFile, data, 0644); err != nil {
- return fmt.Errorf("failed to write descriptions cache: %w", err)
- }
- return nil
+ cacheFile := filepath.Join(workDir, ".gitsyncer-descriptions-cache.json")
+ data, err := json.MarshalIndent(cache, "", " ")
+ if err != nil {
+ return fmt.Errorf("failed to marshal descriptions cache: %w", err)
+ }
+ if err := os.WriteFile(cacheFile, data, 0644); err != nil {
+ return fmt.Errorf("failed to write descriptions cache: %w", err)
+ }
+ return nil
}
-
diff --git a/internal/cli/description_sync.go b/internal/cli/description_sync.go
index ae275ce..4904b56 100644
--- a/internal/cli/description_sync.go
+++ b/internal/cli/description_sync.go
@@ -1,113 +1,112 @@
package cli
import (
- "fmt"
- "strings"
+ "fmt"
+ "strings"
- "codeberg.org/snonux/gitsyncer/internal/codeberg"
- "codeberg.org/snonux/gitsyncer/internal/config"
- "codeberg.org/snonux/gitsyncer/internal/github"
+ "codeberg.org/snonux/gitsyncer/internal/codeberg"
+ "codeberg.org/snonux/gitsyncer/internal/config"
+ "codeberg.org/snonux/gitsyncer/internal/github"
)
// syncRepoDescriptions ensures both platforms have the canonical description
// Precedence: Codeberg > GitHub; if Codeberg empty and GitHub has one, use GitHub.
// knownCBDesc and knownGHDesc can be empty; the function fetches as needed.
func syncRepoDescriptions(cfg *config.Config, dryRun bool, repoName, knownCBDesc, knownGHDesc string, cache map[string]string) {
- // Load orgs
- ghOrg := cfg.FindGitHubOrg()
- cbOrg := cfg.FindCodebergOrg()
+ // Load orgs
+ ghOrg := cfg.FindGitHubOrg()
+ cbOrg := cfg.FindCodebergOrg()
- var ghClient *github.Client
- var cbClient *codeberg.Client
- if ghOrg != nil {
- c := github.NewClient(ghOrg.GitHubToken, ghOrg.Name)
- ghClient = &c
- }
- if cbOrg != nil {
- c := codeberg.NewClient(cbOrg.Name, cbOrg.CodebergToken)
- cbClient = &c
- }
+ var ghClient *github.Client
+ var cbClient *codeberg.Client
+ if ghOrg != nil {
+ c := github.NewClient(ghOrg.GitHubToken, ghOrg.Name)
+ ghClient = &c
+ }
+ if cbOrg != nil {
+ c := codeberg.NewClient(cbOrg.Name, cbOrg.CodebergToken)
+ cbClient = &c
+ }
- // Get current descriptions (use known if provided)
- cbDesc := strings.TrimSpace(knownCBDesc)
- ghDesc := strings.TrimSpace(knownGHDesc)
- var cbExists, ghExists bool
+ // Get current descriptions (use known if provided)
+ cbDesc := strings.TrimSpace(knownCBDesc)
+ ghDesc := strings.TrimSpace(knownGHDesc)
+ var cbExists, ghExists bool
- if cbDesc == "" && cbClient != nil {
- if repo, exists, err := cbClient.GetRepo(repoName); err == nil {
- cbExists = exists
- if exists {
- cbDesc = strings.TrimSpace(repo.Description)
- }
- } else {
- fmt.Printf(" Warning: Codeberg repo lookup failed: %v\n", err)
- }
- } else if cbClient != nil {
- cbExists = true
- }
+ if cbDesc == "" && cbClient != nil {
+ if repo, exists, err := cbClient.GetRepo(repoName); err == nil {
+ cbExists = exists
+ if exists {
+ cbDesc = strings.TrimSpace(repo.Description)
+ }
+ } else {
+ fmt.Printf(" Warning: Codeberg repo lookup failed: %v\n", err)
+ }
+ } else if cbClient != nil {
+ cbExists = true
+ }
- if ghClient != nil {
- if ghDesc == "" || !ghExists {
- if repo, exists, err := ghClient.GetRepo(repoName); err == nil {
- ghExists = exists
- if exists {
- ghDesc = strings.TrimSpace(repo.Description)
- }
- } else {
- fmt.Printf(" Warning: GitHub repo lookup failed: %v\n", err)
- }
- }
- }
+ if ghClient != nil {
+ if ghDesc == "" || !ghExists {
+ if repo, exists, err := ghClient.GetRepo(repoName); err == nil {
+ ghExists = exists
+ if exists {
+ ghDesc = strings.TrimSpace(repo.Description)
+ }
+ } else {
+ fmt.Printf(" Warning: GitHub repo lookup failed: %v\n", err)
+ }
+ }
+ }
- // Determine canonical description
- canonical := cbDesc
- if canonical == "" {
- canonical = ghDesc
- }
- canonical = strings.TrimSpace(canonical)
+ // Determine canonical description
+ canonical := cbDesc
+ if canonical == "" {
+ canonical = ghDesc
+ }
+ canonical = strings.TrimSpace(canonical)
- // If nothing to sync, bail
- if canonical == "" {
- return
- }
+ // If nothing to sync, bail
+ if canonical == "" {
+ return
+ }
- // Update Codeberg if needed
- if cbClient != nil && cbExists {
- if cbDesc != canonical {
- if dryRun {
- fmt.Printf(" [DRY RUN] Would update Codeberg description for %s -> %q\n", repoName, canonical)
- } else if cbClient.HasToken() {
- if err := cbClient.UpdateRepoDescription(repoName, canonical); err != nil {
- fmt.Printf(" Warning: Failed to update Codeberg description: %v\n", err)
- } else {
- fmt.Printf(" Updated Codeberg description for %s\n", repoName)
- }
- } else {
- fmt.Println(" Warning: No Codeberg token; cannot update description")
- }
- }
- }
+ // Update Codeberg if needed
+ if cbClient != nil && cbExists {
+ if cbDesc != canonical {
+ if dryRun {
+ fmt.Printf(" [DRY RUN] Would update Codeberg description for %s -> %q\n", repoName, canonical)
+ } else if cbClient.HasToken() {
+ if err := cbClient.UpdateRepoDescription(repoName, canonical); err != nil {
+ fmt.Printf(" Warning: Failed to update Codeberg description: %v\n", err)
+ } else {
+ fmt.Printf(" Updated Codeberg description for %s\n", repoName)
+ }
+ } else {
+ fmt.Println(" Warning: No Codeberg token; cannot update description")
+ }
+ }
+ }
- // Update GitHub if needed
- if ghClient != nil && ghExists {
- if ghDesc != canonical {
- if dryRun {
- fmt.Printf(" [DRY RUN] Would update GitHub description for %s -> %q\n", repoName, canonical)
- } else if ghClient.HasToken() {
- if err := ghClient.UpdateRepoDescription(repoName, canonical); err != nil {
- fmt.Printf(" Warning: Failed to update GitHub description: %v\n", err)
- } else {
- fmt.Printf(" Updated GitHub description for %s\n", repoName)
- }
- } else {
- fmt.Println(" Warning: No GitHub token; cannot update description")
- }
- }
- }
+ // Update GitHub if needed
+ if ghClient != nil && ghExists {
+ if ghDesc != canonical {
+ if dryRun {
+ fmt.Printf(" [DRY RUN] Would update GitHub description for %s -> %q\n", repoName, canonical)
+ } else if ghClient.HasToken() {
+ if err := ghClient.UpdateRepoDescription(repoName, canonical); err != nil {
+ fmt.Printf(" Warning: Failed to update GitHub description: %v\n", err)
+ } else {
+ fmt.Printf(" Updated GitHub description for %s\n", repoName)
+ }
+ } else {
+ fmt.Println(" Warning: No GitHub token; cannot update description")
+ }
+ }
+ }
- // Update cache
- if cache != nil {
- cache[repoName] = canonical
- }
+ // Update cache
+ if cache != nil {
+ cache[repoName] = canonical
+ }
}
-
diff --git a/internal/cli/flags.go b/internal/cli/flags.go
index 43d9fa3..5c6914c 100644
--- a/internal/cli/flags.go
+++ b/internal/cli/flags.go
@@ -4,39 +4,39 @@ import (
"flag"
"os"
"path/filepath"
-
+
"codeberg.org/snonux/gitsyncer/internal/state"
)
// Flags holds all command-line flag values
type Flags struct {
- VersionFlag bool
- ConfigPath string
- ListOrgs bool
- ListRepos bool
- SyncRepo string
- SyncAll bool
- SyncCodebergPublic bool
- SyncGitHubPublic bool
- FullSync bool
- CreateGitHubRepos bool
+ VersionFlag bool
+ ConfigPath string
+ ListOrgs bool
+ ListRepos bool
+ SyncRepo string
+ SyncAll bool
+ SyncCodebergPublic bool
+ SyncGitHubPublic bool
+ FullSync bool
+ CreateGitHubRepos bool
CreateCodebergRepos bool
- DryRun bool
- WorkDir string
- TestGitHubToken bool
- Clean bool
- DeleteRepo string
- Backup bool
- Showcase bool
- Force bool
- BatchRun bool
- CheckReleases bool
- NoCheckReleases bool
- AutoCreateReleases bool
- AIReleaseNotes bool
- UpdateReleases bool
- AITool string
-
+ DryRun bool
+ WorkDir string
+ TestGitHubToken bool
+ Clean bool
+ DeleteRepo string
+ Backup bool
+ Showcase bool
+ Force bool
+ BatchRun bool
+ CheckReleases bool
+ NoCheckReleases bool
+ AutoCreateReleases bool
+ AIReleaseNotes bool
+ UpdateReleases bool
+ AITool string
+
// Internal fields for batch run state management (not set by flags)
BatchRunStateManager *state.Manager
BatchRunState *state.State
@@ -45,7 +45,7 @@ type Flags struct {
// ParseFlags parses command-line flags and returns the flags struct
func ParseFlags() *Flags {
f := &Flags{}
-
+
flag.BoolVar(&f.VersionFlag, "version", false, "print version information")
flag.BoolVar(&f.VersionFlag, "v", false, "print version information (short)")
flag.StringVar(&f.ConfigPath, "config", "", "path to configuration file")
@@ -65,17 +65,17 @@ func ParseFlags() *Flags {
flag.BoolVar(&f.Clean, "clean", false, "delete all repositories in work directory (with confirmation)")
flag.StringVar(&f.DeleteRepo, "delete-repo", "", "delete specified repository from all configured organizations (with confirmation)")
flag.BoolVar(&f.Backup, "backup", false, "enable syncing to backup locations")
- flag.BoolVar(&f.Showcase, "showcase", false, "generate project showcase using Claude after syncing")
+ flag.BoolVar(&f.Showcase, "showcase", false, "generate project showcase using AI (amp by default) after syncing")
flag.BoolVar(&f.Force, "force", false, "force regeneration of cached data")
flag.BoolVar(&f.BatchRun, "batch-run", false, "enable --full and --showcase (runs only once per week)")
flag.BoolVar(&f.CheckReleases, "check-releases", false, "manually check for version tags without releases and create them (with confirmation)")
flag.BoolVar(&f.NoCheckReleases, "no-check-releases", false, "disable automatic release checking after sync operations")