release v0.3.0 (origen c00f020)

This commit is contained in:
sargatxet committed 2026-10-18 18:58:51 +02:00
commit 2df23d5b33
13 files changed
+874

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Toni Ramiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+17
View File
@@ -0,0 +1,17 @@
# pi-new-project (distribución)
Versión instalable de **pi-new-project** v0.3.0: solo los ficheros que pi
necesita para usar el paquete (skill `new-project` y orden `/modelo-proyecto`).
```bash
pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist
# versión fija:
pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist@v0.3.0
```
Documentación, cambios e incidencias: <https://git.sargatxet.cloud/sargatxet/pi-new-project>
> Repositorio generado automáticamente desde https://git.sargatxet.cloud/sargatxet/pi-new-project
> (commit `c00f020`). No hagas cambios aquí: se perderán en la próxima publicación.
Licencia MIT, ver [`LICENSE`](LICENSE).
+244
View File
@@ -0,0 +1,244 @@
/**
* modelo-proyecto — modelo por defecto de un proyecto.
*
* Guarda el modelo elegido en `<proyecto>/.pi/settings.json` (`defaultProvider`
* y `defaultModel`), que pi aplica al iniciar cualquier sesión nueva en ese
* proyecto (una vez concedida la confianza del proyecto).
*
* - Orden `/modelo-proyecto [proveedor/modelo]`: muestra los modelos
* disponibles para elegir (o usa el indicado), lo guarda en el proyecto del
* directorio actual y lo activa en la sesión en curso.
* - Herramienta `elegir_modelo_proyecto`: la usa la skill `new-project` para
* que el usuario elija el modelo al crear un proyecto en otra ruta.
*/
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import type { Api, Model } from "@earendil-works/pi-ai";
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
import { DynamicBorder } from "@earendil-works/pi-coding-agent";
import { Container, type SelectItem, SelectList, Text } from "@earendil-works/pi-tui";
import { Type } from "typebox";
const SETTINGS_REL = join(".pi", "settings.json");
/** Máximo de modelos visibles a la vez en el selector (la lista se desplaza). */
const MAX_VISIBLES = 15;
function clave(m: Model<Api>): string {
return `${m.provider}/${m.id}`;
}
function kilos(n: number | undefined): string {
if (!n) return "?";
return n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` : `${Math.round(n / 1000)}K`;
}
function etiqueta(m: Model<Api>, actual?: string): string {
const marca = actual === clave(m) ? " ← actual del proyecto" : "";
return `${clave(m)} (${m.name ?? m.id}, ctx ${kilos(m.contextWindow)})${marca}`;
}
function disponibles(ctx: ExtensionContext): Model<Api>[] {
return [...ctx.modelRegistry.getAvailable()].sort((a, b) => clave(a).localeCompare(clave(b)));
}
function leerSettings(proyecto: string): Record<string, unknown> {
const ruta = join(proyecto, SETTINGS_REL);
if (!existsSync(ruta)) return {};
const texto = readFileSync(ruta, "utf-8").trim();
if (!texto) return {};
const datos = JSON.parse(texto);
if (typeof datos !== "object" || datos === null || Array.isArray(datos)) {
throw new Error(`${ruta} no contiene un objeto JSON`);
}
return datos as Record<string, unknown>;
}
export function modeloDelProyecto(proyecto: string): string | undefined {
try {
const s = leerSettings(proyecto);
if (typeof s.defaultProvider === "string" && typeof s.defaultModel === "string") {
return `${s.defaultProvider}/${s.defaultModel}`;
}
} catch {
// settings ilegible: se trata como sin modelo
}
return undefined;
}
/** Fusiona defaultProvider/defaultModel en .pi/settings.json sin tocar el resto. */
function guardarModelo(proyecto: string, m: Model<Api>): string {
const ruta = join(proyecto, SETTINGS_REL);
const datos = leerSettings(proyecto);
datos.defaultProvider = m.provider;
datos.defaultModel = m.id;
mkdirSync(join(proyecto, ".pi"), { recursive: true });
const tmp = `${ruta}.tmp`;
writeFileSync(tmp, `${JSON.stringify(datos, null, 2)}\n`, "utf-8");
renameSync(tmp, ruta);
return ruta;
}
function buscar(ctx: ExtensionContext, texto: string): Model<Api> | undefined {
const t = texto.trim();
const lista = disponibles(ctx);
return (
lista.find((m) => clave(m) === t) ??
(t.includes("/") ? undefined : lista.filter((m) => m.id === t).at(0))
);
}
async function elegir(ctx: ExtensionContext, proyecto: string): Promise<Model<Api> | undefined> {
const lista = disponibles(ctx);
if (lista.length === 0) throw new Error("no hay modelos disponibles (¿falta /login o credenciales?)");
const actual = modeloDelProyecto(proyecto);
const titulo = `Modelo por defecto del proyecto (${proyecto})`;
// Fuera de la TUI (p. ej. RPC) no hay componentes propios: diálogo estándar.
if (ctx.mode !== "tui") {
const opciones = lista.map((m) => etiqueta(m, actual));
const elegido = await ctx.ui.select(titulo, opciones);
if (!elegido) return undefined;
return lista[opciones.indexOf(elegido)];
}
const items: SelectItem[] = lista.map((m) => ({ value: clave(m), label: etiqueta(m, actual) }));
const valor = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
const contenedor = new Container();
contenedor.addChild(new DynamicBorder((s) => theme.fg("accent", s)));
contenedor.addChild(new Text(theme.fg("accent", theme.bold(titulo))));
// Ventana de MAX_VISIBLES elementos que se desplaza con el cursor;
// ↑ en el primero salta al último y ↓ en el último vuelve al primero.
const selector = new SelectList(items, Math.min(items.length, MAX_VISIBLES), {
selectedPrefix: (t) => theme.fg("accent", t),
selectedText: (t) => theme.fg("accent", t),
description: (t) => theme.fg("muted", t),
scrollInfo: (t) => theme.fg("dim", t),
noMatch: (t) => theme.fg("warning", t),
});
const inicial = actual ? items.findIndex((i) => i.value === actual) : -1;
if (inicial >= 0) selector.setSelectedIndex(inicial);
selector.onSelect = (item) => done(item.value);
selector.onCancel = () => done(null);
contenedor.addChild(selector);
contenedor.addChild(new Text(theme.fg("dim", "↑↓ moverse • enter elegir • esc cancelar")));
contenedor.addChild(new DynamicBorder((s) => theme.fg("accent", s)));
return {
render: (width: number) => contenedor.render(width),
invalidate: () => contenedor.invalidate(),
handleInput: (data: string) => {
selector.handleInput(data);
tui.requestRender();
},
};
});
if (!valor) return undefined;
return lista.find((m) => clave(m) === valor);
}
export default function (pi: ExtensionAPI) {
// Lista para autocompletar (getArgumentCompletions no recibe ctx).
let cache: string[] = [];
pi.on("session_start", (_event, ctx) => {
try {
cache = disponibles(ctx).map(clave);
} catch {
cache = [];
}
});
pi.registerCommand("modelo-proyecto", {
description: "Elige el modelo por defecto de este proyecto (se guarda en .pi/settings.json)",
getArgumentCompletions: (prefijo) => {
const p = prefijo.trim().toLowerCase();
const items = cache.filter((k) => k.toLowerCase().includes(p)).map((k) => ({ value: k, label: k }));
return items.length ? items : null;
},
handler: async (args, ctx) => {
const proyecto = ctx.cwd;
let modelo: Model<Api> | undefined;
try {
if (args.trim()) {
modelo = buscar(ctx, args);
if (!modelo) {
ctx.ui.notify(`Modelo "${args.trim()}" no disponible. Usa /modelo-proyecto sin argumentos para elegir.`, "error");
return;
}
} else {
if (!ctx.hasUI) {
ctx.ui.notify("Sin interfaz: indica el modelo, /modelo-proyecto proveedor/modelo", "error");
return;
}
modelo = await elegir(ctx, proyecto);
if (!modelo) {
ctx.ui.notify("Sin cambios: no se eligió modelo.", "info");
return;
}
}
const ruta = guardarModelo(proyecto, modelo);
const activo = await pi.setModel(modelo);
ctx.ui.notify(
`Modelo del proyecto: ${clave(modelo)} (guardado en ${ruta})` +
(activo ? "; activado en esta sesión." : "; no se pudo activar ahora (¿credenciales?)."),
activo ? "info" : "warning",
);
} catch (err) {
ctx.ui.notify(`modelo-proyecto: ${(err as Error).message}`, "error");
}
},
});
pi.registerTool({
name: "elegir_modelo_proyecto",
label: "Elegir modelo del proyecto",
description:
"Muestra al usuario la lista de modelos disponibles para que elija el modelo por defecto de un proyecto " +
"y lo guarda en <ruta>/.pi/settings.json (defaultProvider/defaultModel), que pi usará en las nuevas sesiones " +
"de ese proyecto. Úsala al crear un proyecto. No elijas tú el modelo: deja que el usuario lo seleccione. " +
"Pasa 'modelo' (proveedor/id) solo si el usuario ya lo ha dicho explícitamente.",
parameters: Type.Object({
ruta: Type.String({ description: "Directorio raíz del proyecto" }),
modelo: Type.Optional(
Type.String({ description: "proveedor/id ya indicado por el usuario; si falta se muestra el selector" }),
),
}),
async execute(_id, params, _signal, _onUpdate, ctx) {
const proyecto = resolve(ctx.cwd, params.ruta.replace(/^~(?=$|\/)/, process.env.HOME ?? "~"));
let modelo: Model<Api> | undefined;
if (params.modelo) {
modelo = buscar(ctx, params.modelo);
if (!modelo) {
const lista = disponibles(ctx).map(clave).join(", ");
throw new Error(`modelo "${params.modelo}" no disponible. Disponibles: ${lista}`);
}
} else {
if (!ctx.hasUI) {
const lista = disponibles(ctx).map(clave).join("\n");
throw new Error(
`sin interfaz para mostrar el selector. Pregunta al usuario y vuelve a llamar con 'modelo'. Disponibles:\n${lista}`,
);
}
modelo = await elegir(ctx, proyecto);
if (!modelo) {
return {
content: [{ type: "text", text: "El usuario canceló la elección; no se guardó modelo. Pregúntale si quiere elegir uno." }],
details: { proyecto, modelo: null },
};
}
}
const ruta = guardarModelo(proyecto, modelo);
return {
content: [
{
type: "text",
text: `Modelo por defecto del proyecto: ${clave(modelo)}. Guardado en ${ruta}. Se cambia con /modelo-proyecto.`,
},
],
details: { proyecto, modelo: clave(modelo), ruta },
};
},
});
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "pi-new-project",
"version": "0.3.0",
"private": true,
"license": "MIT",
"description": "Paquete de pi con la skill new-project (base documental de un proyecto) y la orden /modelo-proyecto (modelo por defecto del proyecto).",
"homepage": "https://git.sargatxet.cloud/sargatxet/pi-new-project",
"repository": {
"type": "git",
"url": "git+https://git.sargatxet.cloud/sargatxet/pi-new-project.git"
},
"keywords": [
"pi-package"
],
"pi": {
"extensions": [
"./extensions"
],
"skills": [
"./skills"
]
}
}
+74
View File
@@ -0,0 +1,74 @@
---
name: new-project
description: Genera la base documental de un proyecto nuevo (AGENTS.md, README.md, CHANGELOG.md, TODO.md, INSTALL.md y .gitignore) a partir de plantillas, y deja que el usuario elija el modelo por defecto del proyecto. Úsala cuando el usuario pida crear un proyecto, repositorio o directorio nuevo, inicializar su documentación o "montar el esqueleto" de un repo.
license: MIT
---
# new-project
Genera el esqueleto documental de un proyecto. Todas las plantillas están en
`templates/` y el andamiaje lo hace `scripts/new-project.sh`.
## Procedimiento
1. **Recoge los datos** que falten antes de ejecutar nada:
- ruta de destino (obligatoria);
- nombre del proyecto (por defecto, el nombre del directorio destino);
- descripción en una frase (obligatoria: se usa en README y AGENTS);
- autor (opcional; por defecto `git config user.name`);
- si hay que inicializar un repositorio git (`--git`).
No inventes la descripción: si el usuario no la da, pregúntala o propón una
y confírmala.
2. **Ejecuta el script** desde este directorio de skill:
```bash
scripts/new-project.sh --nombre "Mi Proyecto" --descripcion "Qué hace" \
--git /ruta/al/proyecto
```
El script no sobreescribe ficheros existentes salvo con `--force` y muestra
un resumen de lo creado. Usa `--dry-run` para revisar antes.
3. **Modelo por defecto del proyecto** (obligatorio): el usuario elige el modelo
de entre los existentes; tú no lo eliges.
- Llama a la herramienta `elegir_modelo_proyecto` con `ruta` = destino del
proyecto. Muestra al usuario el selector con los modelos disponibles y
guarda su elección en `<ruta>/.pi/settings.json` (`defaultProvider` y
`defaultModel`). pi lo usará en todas las sesiones nuevas del proyecto.
- Si la herramienta no existe o no hay interfaz: ejecuta `pi --list-models`,
enséñale la lista al usuario, pregúntale cuál quiere y guárdalo con
`scripts/new-project.sh --modelo proveedor/id <destino>` (solo crea
`.pi/settings.json` si no existe).
- Si el usuario cancela, déjalo sin fijar e indícale que puede hacerlo más
tarde con `/modelo-proyecto`.
4. **Revisa el resultado**: abre `README.md` y `AGENTS.md` del proyecto generado
y completa lo que quede como `PENDIENTE` (objetivos, stack, comandos de
verificación reales). No dejes marcadores de plantilla ni secciones vacías.
5. **Ajusta** `INSTALL.md` con los requisitos y pasos reales del stack elegido.
6. **Commit inicial** si se creó repo git (incluye `.pi/settings.json`):
```bash
git add -A && git commit -m "docs: base documental inicial"
```
7. Informa al usuario de la ruta creada, los ficheros, el modelo elegido, el
commit y de que puede cambiar el modelo en cualquier momento con
`/modelo-proyecto` dentro del proyecto.
## Reglas
- No ejecutes el script sobre un directorio que ya tiene `AGENTS.md` o `README.md`
sin avisar y confirmar (usaría `--force`).
- Las secciones `## Reglas de modelo` (salvo el modelo por defecto, que gestiona
`/modelo-proyecto`) y `## Reglas de funcionamiento` de
`AGENTS.md` se dejan como `PENDIENTE` a propósito: se acuerdan después, por
proyecto. No las rellenes con suposiciones.
- Idioma de los documentos generados: español.
- Rutas relativas siempre desde este directorio de skill.
## Referencia
- `scripts/new-project.sh --help` — todas las opciones.
- `/modelo-proyecto [proveedor/id]` — cambia el modelo por defecto del proyecto
del directorio actual (sin argumento, muestra el selector).
- `templates/` — plantillas con marcadores `{{PROYECTO}}`, `{{DESCRIPCION}}`,
`{{AUTOR}}`, `{{FECHA}}`, `{{FECHA_ISO}}`.
- `references/plantillas.md` — qué contiene cada documento y cuándo tocarlo.
@@ -0,0 +1,43 @@
# Plantillas de la base documental
Qué genera `new-project.sh` y cuándo hay que tocar cada documento.
| Documento | Para qué sirve | Cuándo actualizarlo |
| --- | --- | --- |
| `AGENTS.md` | Reglas de trabajo para agentes y colaboradores: flujo, commits, verificación y las futuras reglas de modelo y de funcionamiento. | Al cambiar el flujo, las reglas, los comandos de verificación o al fijar las reglas de modelo. |
| `README.md` | Puerta de entrada: qué es el proyecto, estado, instalación rápida, uso y estructura. | Al cambiar el propósito, el uso o la estructura. |
| `INSTALL.md` | Instalación reproducible: requisitos, dependencias, configuración, verificación, arranque, actualización y desinstalación. | Al añadir dependencias, variables de entorno o cambiar pasos de instalación. |
| `CHANGELOG.md` | Historial *Keep a Changelog*: `Añadido` / `Cambiado` / `Corregido` / `Eliminado`. | En cada cambio, sin excepción. |
| `TODO.md` | Tareas en curso, pendientes y hechas, incluida la deuda técnica. | Al abrir o cerrar tareas. |
| `.gitignore` | Exclusiones básicas: secretos, dependencias descargadas, artefactos de build, logs. | Al detectar ficheros que no deben versionarse. |
| `.pi/settings.json` | Modelo por defecto de pi para el proyecto (`defaultProvider`, `defaultModel`). Lo crea `elegir_modelo_proyecto` o `--modelo`. | Solo con `/modelo-proyecto`, a petición del usuario. |
## Marcadores
El script sustituye estos marcadores en todas las plantillas:
| Marcador | Valor |
| --- | --- |
| `{{PROYECTO}}` | `--nombre` o, si falta, el nombre del directorio destino. |
| `{{DESCRIPCION}}` | `--descripcion` o un `PENDIENTE` explícito. |
| `{{AUTOR}}` | `--autor` o `git config user.name`. |
| `{{FECHA}}` y `{{FECHA_ISO}}` | Fecha de ejecución en formato `AAAA-MM-DD`. |
## Secciones reservadas
`AGENTS.md` incluye a propósito dos secciones con `PENDIENTE`:
- `## Reglas de modelo` — explica dónde está el modelo por defecto y cómo
cambiarlo; el criterio de selección por tarea queda `PENDIENTE`.
- `## Reglas de funcionamiento` — estándares, ramas, revisión y automatización.
No se rellenan al generar el proyecto: se acuerdan después, proyecto a proyecto.
## Modelo por defecto
- Al crear el proyecto, el usuario elige el modelo en el selector de la
herramienta `elegir_modelo_proyecto` (alternativa: `--modelo proveedor/id`).
- Se guarda en `<proyecto>/.pi/settings.json`; pi lo aplica en cada sesión nueva
del proyecto si éste es de confianza.
- Se cambia en cualquier momento con `/modelo-proyecto`, que fusiona el valor con
el resto de ajustes del fichero y activa el modelo en la sesión actual.
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env bash
# Genera la base documental de un proyecto a partir de las plantillas de la skill.
set -euo pipefail
# bash >= 5.2 sustituye '&' del reemplazo por el patrón encontrado; lo desactivamos
# para que los valores con '&' o '\' se inserten literales.
shopt -u patsub_replacement 2>/dev/null || true
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATES_DIR="$SCRIPT_DIR/../templates"
PROYECTO=""
DESCRIPCION=""
AUTOR=""
DESTINO=""
MODELO=""
INIT_GIT=0
FORCE=0
DRY_RUN=0
FECHA_ISO="$(date +%Y-%m-%d)"
uso() {
cat <<'EOF'
Uso: new-project.sh [opciones] <directorio-destino>
Genera AGENTS.md, README.md, CHANGELOG.md, TODO.md, INSTALL.md y .gitignore.
Opciones:
-n, --nombre NOMBRE Nombre del proyecto (por defecto: nombre del directorio).
-d, --descripcion TEXTO Descripción en una frase (por defecto: PENDIENTE).
-a, --autor NOMBRE Autor (por defecto: git config user.name).
-m, --modelo PROV/ID Modelo por defecto del proyecto; se guarda en
.pi/settings.json (lista: pi --list-models).
En pi es preferible la herramienta
elegir_modelo_proyecto o la orden /modelo-proyecto.
-g, --git Inicializa un repositorio git en el destino.
-f, --force Sobreescribe ficheros que ya existan.
--dry-run Muestra lo que haría sin escribir nada.
-h, --help Muestra esta ayuda.
EOF
}
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
aviso() { printf 'aviso: %s\n' "$*" >&2; }
requiere() { [[ $# -ge 2 && -n "$2" && "$2" != -* ]] || die "$1 necesita un valor"; }
while [[ $# -gt 0 ]]; do
case "$1" in
-n | --nombre) requiere "$@"; PROYECTO="$2"; shift 2 ;;
-d | --descripcion) requiere "$@"; DESCRIPCION="$2"; shift 2 ;;
-a | --autor) requiere "$@"; AUTOR="$2"; shift 2 ;;
-m | --modelo) requiere "$@"; MODELO="$2"; shift 2 ;;
-g | --git) INIT_GIT=1; shift ;;
-f | --force) FORCE=1; shift ;;
--dry-run) DRY_RUN=1; shift ;;
-h | --help) uso; exit 0 ;;
--) shift; break ;;
-*) die "opción desconocida: $1 (usa --help)" ;;
*) break ;;
esac
done
[[ $# -ge 1 ]] || { uso >&2; exit 2; }
DESTINO="$1"
[[ $# -eq 1 ]] || die "solo se admite un directorio destino"
[[ -d "$TEMPLATES_DIR" ]] || die "no encuentro las plantillas en $TEMPLATES_DIR"
if [[ -z "$PROYECTO" ]]; then
PROYECTO="$(basename -- "${DESTINO%/}")"
fi
[[ -n "$PROYECTO" ]] || die "no puedo deducir el nombre del proyecto; usa --nombre"
if [[ -z "$DESCRIPCION" ]]; then
DESCRIPCION="PENDIENTE: describir $PROYECTO en una frase."
aviso "sin --descripcion; se deja un PENDIENTE en README.md y AGENTS.md"
fi
if [[ -z "$AUTOR" ]]; then
AUTOR="$(git config user.name 2>/dev/null || true)"
[[ -n "$AUTOR" ]] || AUTOR="PENDIENTE"
fi
if [[ -n "$MODELO" ]]; then
[[ "$MODELO" =~ ^[^/[:space:]\"]+/[^[:space:]\"]+$ ]] \
|| die "--modelo debe tener la forma proveedor/id (p. ej. anthropic/claude-sonnet-4-5)"
fi
FICHEROS=(AGENTS.md README.md CHANGELOG.md TODO.md INSTALL.md .gitignore)
render() {
local plantilla="$1" salida="$2" contenido
contenido="$(<"$plantilla")"
contenido="${contenido//'{{PROYECTO}}'/$PROYECTO}"
contenido="${contenido//'{{DESCRIPCION}}'/$DESCRIPCION}"
contenido="${contenido//'{{AUTOR}}'/$AUTOR}"
contenido="${contenido//'{{FECHA}}'/$FECHA_ISO}"
contenido="${contenido//'{{FECHA_ISO}}'/$FECHA_ISO}"
printf '%s\n' "$contenido" > "$salida"
}
destino_abs="$(cd -- "$DESTINO" 2>/dev/null && pwd || true)"
[[ -n "$destino_abs" ]] || destino_abs="$DESTINO"
creados=()
omitidos=()
for fichero in "${FICHEROS[@]}"; do
ruta="$DESTINO/$fichero"
if [[ -e "$ruta" && $FORCE -eq 0 ]]; then
omitidos+=("$fichero")
aviso "$fichero ya existe; se omite (usa --force para sobreescribir)"
continue
fi
if [[ $DRY_RUN -eq 1 ]]; then
printf 'dry-run: escribiría %s\n' "$ruta"
else
mkdir -p -- "$DESTINO"
render "$TEMPLATES_DIR/$fichero.tmpl" "$ruta"
fi
creados+=("$fichero")
done
if [[ -n "$MODELO" ]]; then
settings="$DESTINO/.pi/settings.json"
if [[ -e "$settings" && $FORCE -eq 0 ]]; then
omitidos+=(".pi/settings.json")
aviso ".pi/settings.json ya existe; se omite (usa /modelo-proyecto en pi para cambiar el modelo sin perder otros ajustes)"
elif [[ $DRY_RUN -eq 1 ]]; then
printf 'dry-run: escribiría %s (modelo %s)\n' "$settings" "$MODELO"
creados+=(".pi/settings.json")
else
mkdir -p -- "$DESTINO/.pi"
printf '{\n "defaultProvider": "%s",\n "defaultModel": "%s"\n}\n' \
"${MODELO%%/*}" "${MODELO#*/}" > "$settings"
creados+=(".pi/settings.json")
fi
fi
if [[ $INIT_GIT -eq 1 && $DRY_RUN -eq 0 ]]; then
if [[ -d "$DESTINO/.git" ]]; then
aviso "$DESTINO ya es un repositorio git"
else
git -C "$DESTINO" init -q
printf 'git: repositorio inicializado en %s\n' "$destino_abs"
fi
fi
printf '\nproyecto: %s\n' "$PROYECTO"
printf 'destino: %s\n' "$destino_abs"
printf 'modelo: %s\n' "${MODELO:-sin fijar (elígelo con /modelo-proyecto)}"
printf 'creados: %s\n' "${creados[*]:-ninguno}"
[[ ${#omitidos[@]} -eq 0 ]] || printf 'omitidos: %s\n' "${omitidos[*]}"
[[ $DRY_RUN -eq 1 ]] || printf 'siguiente: revisa README.md, INSTALL.md y las secciones PENDIENTE de AGENTS.md\n'
@@ -0,0 +1,27 @@
# Secretos y configuración local
.env
.env.*
!.env.example
# Dependencias descargadas
node_modules/
vendor/
venv/
.venv/
__pycache__/
*.py[cod]
# Artefactos de build
dist/
build/
target/
out/
*.log
logs/
# Sistema operativo y editores
.DS_Store
Thumbs.db
.idea/
.vscode/
*.swp
@@ -0,0 +1,88 @@
# AGENTS.md
Guía para agentes y colaboradores que trabajen en **{{PROYECTO}}**.
Léela antes de tocar cualquier fichero y respétala en cada cambio.
## Propósito del proyecto
{{DESCRIPCION}}
## Contenido del repositorio
| Ruta | Descripción |
| --- | --- |
| `AGENTS.md` | Este documento: reglas de trabajo. |
| `README.md` | Visión general y puesta en marcha. |
| `INSTALL.md` | Instalación detallada y dependencias. |
| `CHANGELOG.md` | Historial de cambios (*Keep a Changelog*). |
| `TODO.md` | Tareas pendientes y deuda técnica. |
| `.pi/settings.json` | Ajustes de pi del proyecto: modelo por defecto. |
| _(completar)_ | Añade aquí el código y los recursos del proyecto. |
## Reglas de modelo
### Modelo por defecto
El modelo por defecto del proyecto se guarda en `.pi/settings.json`
(`defaultProvider` y `defaultModel`) y pi lo aplica al iniciar cada sesión nueva
en este directorio (requiere haber confiado en el proyecto: `/trust`).
- Para cambiarlo: `/modelo-proyecto` (muestra los modelos disponibles) o
`/modelo-proyecto proveedor/id`.
- No edites a mano ni cambies ese modelo sin que lo pida el usuario.
- `.pi/settings.json` se versiona; un cambio de modelo se commitea como
cualquier otro cambio.
### Otras reglas
PENDIENTE: definir el contexto y el criterio de selección de modelos por tarea
(coste, VRAM, ventana, velocidad). Se acuerda tarea por tarea; hasta entonces,
no fijar valores aquí.
## Reglas de funcionamiento
PENDIENTE: definir flujo de trabajo, estándares de código, comandos obligatorios,
política de ramas y de revisión, y cualquier automatización propia del proyecto.
## Reglas obligatorias
1. **Todo cambio se documenta y se commitea.** Cada vez que se modifique
cualquier fichero del repositorio:
- Añadir una entrada en `CHANGELOG.md` (fecha, versión y tipo:
`Añadido` / `Cambiado` / `Corregido` / `Eliminado`).
- Actualizar `TODO.md` si el cambio abre, cierra o modifica tareas.
- Actualizar `README.md`, `INSTALL.md` o los documentos que correspondan si
cambia el comportamiento, la instalación o el uso.
- Crear **un commit de git** con mensaje claro en imperativo.
2. **Nunca dejar documentación desfasada.** Si el código y los documentos no
coinciden, el cambio está incompleto.
3. **No commitear** secretos, binarios, artefactos de build, logs ni dependencias
descargadas (`.gitignore` cubre lo básico; amplíalo según el stack).
4. **Idioma: español** para documentación, mensajes de commit y comentarios.
## Verificación antes de commitear
PENDIENTE: sustituir por los comandos reales del stack (formato, tipos, tests,
lint). Ningún commit sin verificación en verde.
```bash
# ejemplo
# make check
```
## Flujo de trabajo
1. Editar los ficheros necesarios.
2. Ejecutar las verificaciones anteriores.
3. Actualizar `CHANGELOG.md`, `TODO.md` y la documentación afectada.
4. `git add -A && git commit -m "tipo(alcance): descripción breve"`.
5. Informar del cambio y del hash del commit.
## Convención de mensajes de commit
`tipo(alcance): resumen` — tipos: `feat`, `fix`, `docs`, `chore`, `refactor`,
`test`, `build`, `perf`.
Ejemplos:
- `docs: documentar instalación y puesta en marcha`
- `feat(api): añadir búsqueda paginada`
@@ -0,0 +1,24 @@
# Changelog
Todos los cambios relevantes de este proyecto se documentan en este fichero.
El formato sigue [*Keep a Changelog*](https://keepachangelog.com/es-ES/1.1.0/)
y el proyecto usa [Versionado Semántico](https://semver.org/lang/es/).
## [No publicado]
### Añadido
- Base documental inicial del proyecto ({{FECHA}}): `AGENTS.md`, `README.md`,
`INSTALL.md`, `CHANGELOG.md`, `TODO.md` y `.gitignore`.
<!--
Plantilla de entrada:
## [0.1.0] - AAAA-MM-DD
### Añadido
### Cambiado
### Corregido
### Eliminado
-->
@@ -0,0 +1,79 @@
# INSTALL — {{PROYECTO}}
Instalación, configuración y puesta en marcha de **{{PROYECTO}}**.
{{DESCRIPCION}}
## 1. Requisitos
PENDIENTE: completar con el stack real.
| Componente | Versión mínima | Notas |
| --- | --- | --- |
| Sistema operativo | PENDIENTE | |
| Lenguaje / runtime | PENDIENTE | |
| Gestor de paquetes | PENDIENTE | |
| Otros | PENDIENTE | |
Comprueba lo instalado:
```bash
# PENDIENTE: comandos de comprobación (p. ej. python3 --version)
```
## 2. Obtener el código
```bash
git clone <url-del-repositorio>
cd {{PROYECTO}}
```
## 3. Dependencias
```bash
# PENDIENTE: instalación de dependencias
```
## 4. Configuración
PENDIENTE: variables de entorno, ficheros de configuración y secretos. Indica
qué ficheros hay que crear y de dónde copiarlos (`.env.example`, etc.).
```bash
# PENDIENTE: cp .env.example .env y editar
```
| Variable | Obligatoria | Descripción |
| --- | --- | --- |
| PENDIENTE | | |
## 5. Verificación de la instalación
```bash
# PENDIENTE: comando de humo que demuestre que funciona
```
## 6. Arranque
```bash
# PENDIENTE: comando de arranque en desarrollo y en producción
```
## 7. Actualización
```bash
git pull
# PENDIENTE: reinstalar dependencias y aplicar migraciones si las hay
```
## 8. Desinstalación
PENDIENTE: qué borrar y qué dejar.
## 9. Resolución de problemas
| Síntoma | Causa probable | Solución |
| --- | --- | --- |
| PENDIENTE | | |
Documento creado el {{FECHA}} por {{AUTOR}}.
@@ -0,0 +1,58 @@
# {{PROYECTO}}
{{DESCRIPCION}}
## Estado
- Versión: `0.1.0`
- Fecha de creación: {{FECHA}}
- Autor: {{AUTOR}}
- Estado: en desarrollo
## Requisitos
PENDIENTE: lenguajes, versiones y dependencias del sistema. Ver `INSTALL.md`.
## Instalación rápida
```bash
# PENDIENTE: sustituir por los comandos reales
git clone <url-del-repositorio>
cd {{PROYECTO}}
```
Instrucciones completas en [`INSTALL.md`](INSTALL.md).
## Uso
PENDIENTE: ejemplo mínimo de uso o de arranque.
```bash
# PENDIENTE
```
## Estructura
```text
{{PROYECTO}}/
├── AGENTS.md # reglas de trabajo para agentes y colaboradores
├── README.md # este documento
├── INSTALL.md # instalación y dependencias
├── CHANGELOG.md # historial de cambios
├── TODO.md # tareas pendientes
└── .pi/
└── settings.json # modelo por defecto de pi (cambiar con /modelo-proyecto)
```
## Documentación
| Documento | Contenido |
| --- | --- |
| [`AGENTS.md`](AGENTS.md) | Reglas, flujo de trabajo y convenciones. |
| [`INSTALL.md`](INSTALL.md) | Instalación, configuración y desinstalación. |
| [`CHANGELOG.md`](CHANGELOG.md) | Qué cambió y cuándo. |
| [`TODO.md`](TODO.md) | Qué falta y qué está en curso. |
## Licencia
PENDIENTE: indicar licencia del proyecto.
+22
View File
@@ -0,0 +1,22 @@
# TODO — {{PROYECTO}}
Tareas pendientes y deuda técnica. Al cerrar una tarea, muévela a *Hecho* y
anota el cambio en `CHANGELOG.md`.
## En curso
- [ ] _(nada en curso)_
## Pendiente
- [ ] Definir las **reglas de modelo** del proyecto en `AGENTS.md` (criterio por
tarea; el modelo por defecto se cambia con `/modelo-proyecto`).
- [ ] Definir las **reglas de funcionamiento** del proyecto en `AGENTS.md`.
- [ ] Completar los `PENDIENTE` de `README.md` e `INSTALL.md`.
- [ ] Fijar los comandos de verificación reales en `AGENTS.md`.
- [ ] Elegir licencia.
## Hecho
- [x] {{FECHA}} — base documental inicial (`AGENTS.md`, `README.md`, `INSTALL.md`,
`CHANGELOG.md`, `TODO.md`, `.gitignore`).