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
52 lines
1.3 KiB
Go
52 lines
1.3 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"text/tabwriter"
|
|
|
|
"mgit.msbls.de/m/ImaGen/internal/backend"
|
|
"mgit.msbls.de/m/ImaGen/internal/config"
|
|
)
|
|
|
|
func runBackends(args []string) error {
|
|
fs := flag.NewFlagSet("backends", flag.ContinueOnError)
|
|
var configPath string
|
|
fs.StringVar(&configPath, "config", "", "config file path (default: ~/.config/imagen.yaml)")
|
|
if err := fs.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
|
|
cfg, cfgErr := config.Load(configPath)
|
|
if cfgErr != nil && !os.IsNotExist(cfgErr) {
|
|
return cfgErr
|
|
}
|
|
|
|
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(tw, "INSTANCE\tTYPE\tSTATUS")
|
|
if cfg != nil {
|
|
for name, spec := range cfg.Backends {
|
|
status := "registered"
|
|
if !backend.Default.Has(spec.Type) {
|
|
status = fmt.Sprintf("type %q not compiled in", spec.Type)
|
|
}
|
|
marker := ""
|
|
if name == cfg.DefaultBackend {
|
|
marker = " (default)"
|
|
}
|
|
fmt.Fprintf(tw, "%s%s\t%s\t%s\n", name, marker, spec.Type, status)
|
|
}
|
|
}
|
|
if cfg == nil {
|
|
for _, t := range backend.Default.Types() {
|
|
fmt.Fprintf(tw, "%s\t%s\t%s\n", t, t, "no config — type registered, no instance defined")
|
|
}
|
|
}
|
|
if err := tw.Flush(); err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintln(os.Stderr, "registered types:", backend.Default.Types())
|
|
return nil
|
|
}
|