First step of the model-agnostic image-generation framework. Lands the
plumbing other components (skill, ComfyUI/Replicate adapters, agents)
will plug into:
- internal/backend: Backend interface (Request/Result), thread-safe
Registry with init-time Register, plus a Mock reference adapter that
emits a deterministic gradient PNG for smoke tests.
- internal/config: YAML loader for ~/.config/imagen.yaml. Framework owns
default_backend + output settings + a per-backend block; each adapter
owns the schema below its own block via BackendSpec.Raw.
- internal/output: filename templating ({date}/{time}/{slug}/{seed}/
{backend}/{ext}), JSON metadata sidecar, --output override path.
- internal/prompt: embedded styles.yaml, style-preset suffix application.
- internal/server: 501 stub — HTTP surface lands in a follow-up issue.
- cmd/imagen: generate / backends / config (init|validate|path) / serve
/ version subcommands. Stdlib-only flag parsing with a small helper to
honour positional prompt args ahead of flags (matches the issue spec).
- Tests for output (slug, naming template, sidecar), backend (mock PNG
validity + determinism, registry build + duplicate panic), config
(round-trip + validation), prompt (style apply + unknown-style error).
- CLAUDE.md, README.md, docs/architecture.md, docs/usage.md, Makefile.
Acceptance criteria from #211:
1. go build ./... — clean
2. imagen backends — lists registered backends, exits 0
3. imagen generate "test prompt" --backend mock --output /tmp/x.png —
writes a 1024x1024 PNG plus an x.png.json sidecar
4. imagen config init | imagen config validate — round-trips cleanly
5. CLAUDE.md "Adding a new adapter" — six-step recipe
78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
// Command imagen is the model-agnostic image-generation CLI. It dispatches
|
|
// `generate`, `backends`, and `config` subcommands against backends that
|
|
// register themselves at package init time.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
_ "mgit.msbls.de/m/ImaGen/internal/backend"
|
|
)
|
|
|
|
const usage = `imagen — model-agnostic image generation
|
|
|
|
Usage:
|
|
imagen generate <prompt> [flags] generate one image
|
|
imagen backends list registered backend types
|
|
imagen config init print a sample imagen.yaml on stdout
|
|
imagen config validate validate the active config
|
|
imagen serve [--addr :8080] (stub) start the HTTP server
|
|
imagen version print version
|
|
imagen help show this help
|
|
|
|
Run "imagen <subcommand> --help" for subcommand-specific flags.
|
|
`
|
|
|
|
// Version is overridable at link time via -ldflags '-X main.Version=...'.
|
|
var Version = "dev"
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
fmt.Fprint(os.Stderr, usage)
|
|
os.Exit(2)
|
|
}
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer cancel()
|
|
|
|
args := os.Args[2:]
|
|
var err error
|
|
switch os.Args[1] {
|
|
case "generate":
|
|
err = runGenerate(ctx, args)
|
|
case "backends":
|
|
err = runBackends(args)
|
|
case "config":
|
|
err = runConfig(args)
|
|
case "serve":
|
|
err = runServe(args)
|
|
case "version", "-v", "--version":
|
|
fmt.Println(Version)
|
|
case "help", "-h", "--help":
|
|
fmt.Print(usage)
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "imagen: unknown subcommand %q\n\n%s", os.Args[1], usage)
|
|
os.Exit(2)
|
|
}
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "imagen:", err)
|
|
var u *userError
|
|
if errors.As(err, &u) {
|
|
os.Exit(2)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// userError signals "user did the wrong thing" so we exit 2 rather than 1.
|
|
type userError struct{ msg string }
|
|
|
|
func (u *userError) Error() string { return u.msg }
|
|
func userErr(format string, a ...any) error {
|
|
return &userError{msg: fmt.Sprintf(format, a...)}
|
|
}
|