auto(0129): agents_dashboard — secret_store_cpp_infra + CMakeLists register #4

Open
dataforge wants to merge 615 commits from auto/0129 into master
2 changed files with 570 additions and 0 deletions
Showing only changes of commit bcfe87af7f - Show all commits
+110
View File
@@ -0,0 +1,110 @@
---
name: init_cli_app
kind: pipeline
lang: bash
domain: pipelines
version: "1.0.0"
purity: impure
signature: "init_cli_app(nombre: string, [--with-tui]) -> void"
description: "Scaffold de Go CLI app con subcomandos (version/status/help) routed via os.Args. Con --with-tui genera model.go con un modelo Bubbletea fullscreen (lista filtrable + spinner + dark theme) y main.go arranca la TUI en modo fullscreen. Sin dependencias de cobra/urfave — consistente con las apps del registry."
tags: [init, scaffold, cli, tui, pipeline, bash, launcher]
uses_functions:
- assert_command_exists_bash_shell
- new_base_model_go_tui
- dark_styles_go_tui
- run_fullscreen_go_tui
- new_spinner_go_tui
- new_filtered_list_go_tui
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
- name: nombre
desc: "nombre de la CLI app (apps/{nombre}/); se usa como binario y como modulo Go"
- name: "--with-tui"
desc: "anade model.go con modelo Bubbletea (lista + spinner + dark theme) y arranca TUI fullscreen al invocar sin args"
output: "CLI app en apps/{nombre}/ con main.go (subcommand routing), cmd_version.go, cmd_status.go, Makefile con targets build/run/install/test/vet/clean. Con --with-tui anade model.go y el default sin args arranca TUI."
tested: false
tests: []
test_file_path: ""
example: "fn run init_cli_app my_cli --with-tui"
file_path: "bash/functions/pipelines/init_cli_app.sh"
---
## Sinopsis
```bash
fn run init_cli_app <nombre> [--with-tui]
```
## Ejemplo rapido
```bash
# CLI clasica
fn run init_cli_app mytool
cd apps/mytool
make build
./mytool version
./mytool status
# CLI + TUI fullscreen
fn run init_cli_app deploy_helper --with-tui
cd apps/deploy_helper
make build
./deploy_helper # arranca TUI
./deploy_helper version # subcomando
```
## Archivos generados
| Archivo | Descripcion |
|---------|-------------|
| `main.go` | Entry con `switch os.Args[1]`; subcomandos: version, status, help |
| `cmd_version.go` | Imprime nombre + version |
| `cmd_status.go` | Imprime app/version/go/os/arch |
| `go.mod` | Modulo Go con replace `fn-registry`; con `--with-tui` agrega bubbletea/bubbles/lipgloss |
| `Makefile` | Targets build, run (ARGS=...), install (~/.local/bin/), test, vet, clean |
| `.gitignore` | Binario + IDE files |
| `app.md` | Tag `cli` (o `cli,tui,bubbletea` con `--with-tui`) |
Con `--with-tui` anade:
- `model.go``Model` con spinner (spinner.Dot), lista Bubbletea con 4 items de ejemplo, dark theme (lipgloss con colores 7D56F4 / 06B6D4), keys enter/q/ctrl+c
- `main.go` arranca la TUI con `tea.NewProgram(model, tea.WithAltScreen())` si no hay args
## Flags
| Flag | Efecto |
|------|--------|
| `--with-tui` | Anade model.go con Bubbletea y TUI fullscreen como default |
## Post-setup
```bash
cd apps/{nombre}
make build # construye ./{nombre}
./{nombre} version
./{nombre} status
make install # copia a ~/.local/bin/{nombre}
make run ARGS="status" # build + run con argumentos
```
## Notas
Pipeline impuro: genera archivos, ejecuta `go mod tidy` y `go vet` al final.
Los ejemplos del modelo TUI (items "Deploy", "Status", "Logs", "Exit") son
placeholders — reemplazar con la logica real de la app. El modelo usa los
componentes estandar de bubbles (`list`, `spinner`) y lipgloss para estilos.
Las funciones del registry `new_base_model_go_tui`, `dark_styles_go_tui`,
`run_fullscreen_go_tui`, `new_spinner_go_tui`, `new_filtered_list_go_tui`
son referenciadas en el frontmatter como deps conceptuales aunque el
scaffold inline el codigo Bubbletea directamente (las funciones del registry
son stubs que delegan a devfactory/tui).
Abort si `apps/{nombre}/` ya existe.
El tag `launcher` permite que aparezca en el Pipeline Launcher TUI (aunque
una CLI con TUI interactiva normalmente no se lanza como subprocess).
+460
View File
@@ -0,0 +1,460 @@
#!/usr/bin/env bash
# init_cli_app
# ------------
# Scaffold de Go CLI app con subcomandos, opcionalmente con TUI Bubbletea.
#
# Genera main.go con routing de subcomandos (os.Args + switch), cmd_version.go,
# cmd_status.go, Makefile, .gitignore, go.mod y app.md.
#
# Con --with-tui genera ademas model.go con un modelo Bubbletea base y main.go
# arranca la TUI con tea.NewProgram().Run() en modo fullscreen.
#
# USO:
# ./init_cli_app.sh <nombre> [--with-tui]
#
# EJEMPLOS:
# ./init_cli_app.sh my_cli
# ./init_cli_app.sh deploy_helper --with-tui
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REGISTRY_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
source "$REGISTRY_ROOT/bash/functions/shell/assert_command_exists.sh"
NOMBRE=""
WITH_TUI="false"
while [ $# -gt 0 ]; do
case "$1" in
--with-tui) WITH_TUI="true"; shift ;;
-h|--help) grep "^#" "$0" | sed 's/^# \?//' ; exit 0 ;;
-*) echo "Flag desconocido: $1" >&2 ; exit 1 ;;
*)
if [ -z "$NOMBRE" ]; then NOMBRE="$1"
else echo "Argumento extra ignorado: $1" >&2
fi
shift ;;
esac
done
if [ -z "$NOMBRE" ]; then
echo "Uso: $0 <nombre> [--with-tui]" >&2
exit 1
fi
APP_DIR="${REGISTRY_ROOT}/apps/${NOMBRE}"
if [ -d "$APP_DIR" ]; then
echo "ERROR: ${APP_DIR} ya existe. Abortando." >&2
exit 1
fi
echo ""
echo "════════════════════════════════════════════════════════════"
echo " INIT CLI APP: ${NOMBRE}"
echo " Directorio: ${APP_DIR}"
echo " TUI: ${WITH_TUI}"
echo "════════════════════════════════════════════════════════════"
echo ""
# ── 1. Verificar Go ──────────────────────────────────────────
echo "[1/5] Verificando herramientas..."
assert_command_exists go
echo " Go: $(go version)"
# ── 2. Crear estructura ──────────────────────────────────────
echo "[2/5] Creando estructura..."
mkdir -p "$APP_DIR"
# go.mod
if [ "$WITH_TUI" = "true" ]; then
cat > "$APP_DIR/go.mod" <<EOF
module ${NOMBRE}
go 1.25.0
require (
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
)
require (
fn-registry v0.0.0-00010101000000-000000000000
)
replace fn-registry => ${REGISTRY_ROOT}
EOF
else
cat > "$APP_DIR/go.mod" <<EOF
module ${NOMBRE}
go 1.25.0
require (
fn-registry v0.0.0-00010101000000-000000000000
)
replace fn-registry => ${REGISTRY_ROOT}
EOF
fi
# ── 3. Archivos Go ───────────────────────────────────────────
echo "[3/5] Escribiendo archivos Go..."
# cmd_version.go — siempre existe
cat > "$APP_DIR/cmd_version.go" <<EOF
package main
import "fmt"
var version = "0.1.0"
func cmdVersion() {
fmt.Printf("${NOMBRE} %s\n", version)
}
EOF
# cmd_status.go — siempre existe
cat > "$APP_DIR/cmd_status.go" <<EOF
package main
import (
"fmt"
"runtime"
)
func cmdStatus() {
fmt.Printf("app: ${NOMBRE}\n")
fmt.Printf("version: %s\n", version)
fmt.Printf("go: %s\n", runtime.Version())
fmt.Printf("os/arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
}
EOF
if [ "$WITH_TUI" = "true" ]; then
# model.go — Bubbletea BaseModel con spinner y lista
cat > "$APP_DIR/model.go" <<EOF
package main
import (
"fmt"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
// --- estilos (dark theme) ---
var (
titleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#7D56F4"))
helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262"))
selectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#06B6D4"))
)
// --- item de la lista ---
type item struct {
title, desc string
}
func (i item) Title() string { return i.title }
func (i item) Description() string { return i.desc }
func (i item) FilterValue() string { return i.title }
// --- modelo raiz ---
type Model struct {
spinner spinner.Model
list list.Model
status string
quit bool
}
func NewModel() Model {
items := []list.Item{
item{title: "Deploy", desc: "Subir codigo al VPS"},
item{title: "Status", desc: "Ver estado de servicios"},
item{title: "Logs", desc: "Tail de logs en tiempo real"},
item{title: "Exit", desc: "Salir"},
}
l := list.New(items, list.NewDefaultDelegate(), 0, 0)
l.Title = "${NOMBRE} — elige una accion"
l.Styles.Title = titleStyle
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = selectedStyle
return Model{
spinner: s,
list: l,
status: "listo",
}
}
func (m Model) Init() tea.Cmd {
return tea.Batch(m.spinner.Tick)
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
m.quit = true
return m, tea.Quit
case "enter":
if it, ok := m.list.SelectedItem().(item); ok {
if it.title == "Exit" {
m.quit = true
return m, tea.Quit
}
m.status = fmt.Sprintf("seleccionado: %s", it.title)
}
}
case tea.WindowSizeMsg:
m.list.SetSize(msg.Width, msg.Height-4)
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
cmds = append(cmds, cmd)
}
var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
if m.quit {
return "adios!\n"
}
return fmt.Sprintf(
"%s\n\n%s %s\n%s",
m.list.View(),
m.spinner.View(),
m.status,
helpStyle.Render("enter: seleccionar · q: salir"),
)
}
EOF
# main.go con TUI
cat > "$APP_DIR/main.go" <<EOF
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
func main() {
if len(os.Args) < 2 {
runTUI()
return
}
switch os.Args[1] {
case "tui":
runTUI()
case "version":
cmdVersion()
case "status":
cmdStatus()
case "help", "-h", "--help":
printUsage()
default:
fmt.Fprintf(os.Stderr, "comando desconocido: %s\n", os.Args[1])
printUsage()
os.Exit(1)
}
}
func runTUI() {
p := tea.NewProgram(NewModel(), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "tui error: %v\n", err)
os.Exit(1)
}
}
func printUsage() {
fmt.Println(\`${NOMBRE} — CLI tool
Uso:
${NOMBRE} [comando]
Sin comando arranca la TUI fullscreen.
Comandos:
tui Arranca la TUI fullscreen (default)
version Imprime la version
status Muestra info del sistema
help Muestra esta ayuda\`)
}
EOF
else
# main.go sin TUI — subcommand routing clasico
cat > "$APP_DIR/main.go" <<EOF
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
switch os.Args[1] {
case "version":
cmdVersion()
case "status":
cmdStatus()
case "help", "-h", "--help":
printUsage()
default:
fmt.Fprintf(os.Stderr, "comando desconocido: %s\n", os.Args[1])
printUsage()
os.Exit(1)
}
}
func printUsage() {
fmt.Println(\`${NOMBRE} — CLI tool
Uso:
${NOMBRE} <comando>
Comandos:
version Imprime la version
status Muestra info del sistema
help Muestra esta ayuda\`)
}
EOF
fi
# ── 4. Makefile, .gitignore, app.md ─────────────────────────
echo "[4/5] Escribiendo Makefile, .gitignore, app.md..."
cat > "$APP_DIR/Makefile" <<EOF
.PHONY: build run install test vet clean
BIN=${NOMBRE}
build:
CGO_ENABLED=1 go build -tags fts5 -o \$(BIN) .
run: build
./\$(BIN) \$(ARGS)
install: build
install -m755 \$(BIN) \$(HOME)/.local/bin/\$(BIN)
@echo "instalado en \$(HOME)/.local/bin/\$(BIN)"
test:
CGO_ENABLED=1 go test -tags fts5 -v ./...
vet:
CGO_ENABLED=1 go vet -tags fts5 ./...
clean:
rm -f \$(BIN)
EOF
cat > "$APP_DIR/.gitignore" <<EOF
# Binario
${NOMBRE}
# IDE
.idea/
.vscode/
*.swp
EOF
if [ "$WITH_TUI" = "true" ]; then
FRAMEWORK="bubbletea"
USES_FUNCTIONS=' - new_base_model_go_tui
- dark_styles_go_tui
- run_fullscreen_go_tui
- new_spinner_go_tui
- new_filtered_list_go_tui'
TAGS='[cli, tui, bubbletea]'
else
FRAMEWORK=""
USES_FUNCTIONS=' []'
TAGS='[cli]'
fi
cat > "$APP_DIR/app.md" <<EOF
---
name: ${NOMBRE}
lang: go
domain: tools
description: "CLI app generada por init_cli_app."
tags: ${TAGS}
uses_functions:
${USES_FUNCTIONS}
uses_types: []
framework: "${FRAMEWORK}"
entry_point: "main.go"
dir_path: "apps/${NOMBRE}"
---
## Notas
CLI con routing de subcomandos (\`os.Args\` + switch). Sin cobra/urfave —
consistente con las apps del registry.
Ejecutar: \`make run ARGS="version"\` o \`./\${NOMBRE} status\`.
EOF
# ── 5. go mod tidy + go vet ─────────────────────────────────
echo "[5/5] Verificacion..."
(
cd "$APP_DIR"
if CGO_ENABLED=1 go mod tidy 2>&1 | tail -5; then
:
fi
if CGO_ENABLED=1 go vet -tags fts5 ./... 2>&1; then
echo " go vet OK"
else
echo " WARN: go vet fallo" >&2
fi
)
echo ""
echo "════════════════════════════════════════════════════════════"
echo " CLI APP '${NOMBRE}' LISTA"
echo "════════════════════════════════════════════════════════════"
echo ""
echo " Pasos siguientes:"
echo " cd apps/${NOMBRE}"
echo " make build"
if [ "$WITH_TUI" = "true" ]; then
echo " ./${NOMBRE} # arranca la TUI fullscreen"
echo " ./${NOMBRE} version # comando CLI"
else
echo " ./${NOMBRE} version"
echo " ./${NOMBRE} status"
fi
echo ""