From f6d2870635f1dfa355c00045a98c42a1a1a506be Mon Sep 17 00:00:00 2001 From: Toni Ramiro Date: Sun, 18 Oct 2026 14:03:22 +0200 Subject: [PATCH] feat: modelo por defecto por proyecto con selector y orden /modelo-proyecto --- CHANGELOG.md | 24 +++ INSTALL.md | 27 ++- README.md | 26 ++- TODO.md | 6 +- extensions/modelo-proyecto.ts | 199 ++++++++++++++++++++ package.json | 7 +- skills/new-project/SKILL.md | 29 ++- skills/new-project/references/plantillas.md | 13 +- skills/new-project/scripts/new-project.sh | 35 +++- skills/new-project/templates/AGENTS.md.tmpl | 21 ++- skills/new-project/templates/README.md.tmpl | 4 +- skills/new-project/templates/TODO.md.tmpl | 3 +- 12 files changed, 372 insertions(+), 22 deletions(-) create mode 100644 extensions/modelo-proyecto.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4300479..0b722cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,30 @@ y el proyecto usa [Versionado Semántico](https://semver.org/lang/es/). - Fijar las reglas de modelo y de funcionamiento que la skill escribirá en el `AGENTS.md` de cada proyecto generado (hoy quedan como `PENDIENTE`). +## [0.2.0] - 2026-10-18 + +### Añadido + +- Extensión `extensions/modelo-proyecto.ts`: + - orden `/modelo-proyecto [proveedor/id]`: elige, de entre los modelos + disponibles, el modelo por defecto del proyecto actual, lo guarda en + `.pi/settings.json` (fusionando con el resto de ajustes) y lo activa; + - herramienta `elegir_modelo_proyecto`: muestra el selector al crear un + proyecto y guarda la elección en `/.pi/settings.json`. +- `new-project.sh --modelo proveedor/id` para fijar el modelo sin pi. +- Paso obligatorio en la skill: el usuario elige el modelo del proyecto. + +### Cambiado + +- `AGENTS.md` generado: la sección *Reglas de modelo* documenta el modelo por + defecto y cómo cambiarlo; el criterio por tarea sigue `PENDIENTE`. +- Plantillas de `README.md` y `TODO.md` mencionan `.pi/settings.json`. + +### Corregido + +- `new-project.sh`: una opción sin valor (`--nombre` al final) terminaba sin + mensaje; ahora informa del error. + ## [0.1.0] - 2026-10-18 ### Añadido diff --git a/INSTALL.md b/INSTALL.md index 504fe31..4182e4c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -26,7 +26,8 @@ O desde git/npm cuando se publique: pi install git:github.com//pi-new-project@v0.1.0 ``` -Comprueba con `pi list` y arranca pi: la skill aparecerá como `new-project`. +Comprueba con `pi list` y arranca pi: la skill aparecerá como `new-project` y +estará disponible la orden `/modelo-proyecto`. Para una prueba puntual sin instalarlo: `pi -e ./pi-new-project`. ## 3. Opción B — copiar la skill @@ -46,6 +47,15 @@ ln -s "$PWD/pi-new-project/skills/new-project" ~/.agents/skills/new-project Rutas equivalentes: `~/.pi/agent/skills/` y, por proyecto, `.agents/skills/`. +La skill sola no incluye la orden `/modelo-proyecto` ni la herramienta +`elegir_modelo_proyecto`; cópialas también (o la skill usará el plan B, +`pi --list-models` + `--modelo`): + +```bash +mkdir -p ~/.pi/agent/extensions +cp pi-new-project/extensions/modelo-proyecto.ts ~/.pi/agent/extensions/ +``` + ## 4. Opción C — exportar a otro ordenador No hay más que copiar el directorio; no tiene dependencias. @@ -76,6 +86,17 @@ ls -A /tmp/demo Deben aparecer `AGENTS.md`, `README.md`, `CHANGELOG.md`, `TODO.md`, `INSTALL.md` y `.gitignore`, sin marcadores `{{...}}` pendientes. +Modelo por defecto: + +```bash +pi-new-project/skills/new-project/scripts/new-project.sh \ + --nombre Demo --modelo anthropic/claude-sonnet-4-5 /tmp/demo2 +cat /tmp/demo2/.pi/settings.json +``` + +En pi, dentro de `/tmp/demo2`, `/modelo-proyecto` debe mostrar el selector de +modelos y actualizar `.pi/settings.json`. + ## 6. Actualización ```bash @@ -88,6 +109,7 @@ pi update --extensions # si se instaló como paquete ```bash pi remove ./pi-new-project # si se instaló como paquete rm -rf ~/.agents/skills/new-project # si se copió o enlazó +rm -f ~/.pi/agent/extensions/modelo-proyecto.ts ``` La skill no deja estado propio: no hay caché ni configuración que limpiar. @@ -100,3 +122,6 @@ La skill no deja estado propio: no hay caché ni configuración que limpiar. | `no encuentro las plantillas` | Falta `templates/` | Reinstala el paquete completo, no solo el script. | | `no puedo deducir el nombre` | Destino `.` o `/` | Pasa `--nombre`. | | No sobreescribe mis ficheros | Protección por defecto | Usa `--force` tras revisar `--dry-run`. | +| El proyecto no arranca con su modelo | Proyecto no confiado: pi ignora `.pi/settings.json` | Ejecuta `/trust` en el proyecto. | +| `/modelo-proyecto` no existe | Solo se copió la skill | Instala el paquete o copia `extensions/modelo-proyecto.ts`. | +| El selector sale vacío | Ningún proveedor con credenciales | `/login` o configura la API key del proveedor. | diff --git a/README.md b/README.md index a507f48..e733965 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ Paquete de [pi](https://pi.dev) que genera la base documental de un proyecto nuevo: `AGENTS.md`, `README.md`, `CHANGELOG.md`, `TODO.md`, `INSTALL.md` y `.gitignore`, listos para versionar y con las secciones de reglas reservadas -para acordarlas después. +para acordarlas después. Al crear el proyecto, el usuario elige el **modelo por +defecto** de entre los disponibles; se guarda en `.pi/settings.json` y se puede +cambiar en cualquier momento con la orden `/modelo-proyecto`. ## Contenido @@ -14,6 +16,8 @@ pi-new-project/ ├── INSTALL.md # instalación en otro ordenador ├── CHANGELOG.md ├── TODO.md +├── extensions/ +│ └── modelo-proyecto.ts # orden /modelo-proyecto y herramienta elegir_modelo_proyecto └── skills/ └── new-project/ ├── SKILL.md # instrucciones para el agente @@ -41,6 +45,20 @@ skills/new-project/scripts/new-project.sh \ --git ~/src/gestor-gastos ``` +### Modelo por defecto del proyecto + +- **Al crear el proyecto**: la skill llama a la herramienta + `elegir_modelo_proyecto`, que muestra los modelos disponibles (los que tienen + credenciales en pi) para que elijas uno. +- **Dónde se guarda**: en `/.pi/settings.json` (`defaultProvider` y + `defaultModel`). pi lo aplica en cada sesión nueva iniciada en ese + directorio, siempre que el proyecto sea de confianza (`/trust`). +- **Para cambiarlo**: dentro del proyecto, `/modelo-proyecto` abre el selector; + `/modelo-proyecto proveedor/id` lo fija directamente (con autocompletado). + Conserva los demás ajustes del fichero y activa el modelo en la sesión actual. +- **Sin pi**: `new-project.sh --modelo proveedor/id destino` (lista con + `pi --list-models`). + El script nunca sobreescribe ficheros existentes salvo con `--force`, y admite `--dry-run` para revisar antes. @@ -48,8 +66,12 @@ El script nunca sobreescribe ficheros existentes salvo con `--force`, y admite Ver [`INSTALL.md`](INSTALL.md). Resumen: copia (o enlaza) el directorio en `~/.pi/agent/skills/` o instálalo como paquete con `pi install ./pi-new-project`. +La orden `/modelo-proyecto` solo está disponible al instalarlo como paquete (o +copiando también `extensions/`). ## Requisitos +- pi 1.3.0+ (la extensión usa la API de extensiones y `typebox`, incluidos en pi). - `bash` 4+ y `git` (opcional, solo para `--git` y el autor por defecto). -- No usa Python, Node ni dependencias externas: es `bash` con biblioteca estándar. +- El script no usa Python, Node ni dependencias externas: es `bash` con + biblioteca estándar. diff --git a/TODO.md b/TODO.md index 8fbe87e..db6a45e 100644 --- a/TODO.md +++ b/TODO.md @@ -6,8 +6,8 @@ ## Pendiente -- [ ] Definir las **reglas de modelo** (modelo, contexto, criterio de selección) - y volcarlas en `templates/AGENTS.md.tmpl`. +- [ ] Definir el resto de **reglas de modelo** (contexto, criterio de selección + por tarea) y volcarlas en `templates/AGENTS.md.tmpl`. - [ ] Definir las **reglas de funcionamiento** comunes y volcarlas en `templates/AGENTS.md.tmpl`. - [ ] Decidir si las reglas se configuran por parámetros del script @@ -20,5 +20,7 @@ ## Hecho +- [x] 2026-10-18 — modelo por defecto por proyecto: selector al crear y orden + `/modelo-proyecto` para cambiarlo. - [x] 2026-10-18 — estructura del paquete, skill, script y plantillas. - [x] 2026-10-18 — `INSTALL.md` con exportación a otro ordenador. diff --git a/extensions/modelo-proyecto.ts b/extensions/modelo-proyecto.ts new file mode 100644 index 0000000..11dee1e --- /dev/null +++ b/extensions/modelo-proyecto.ts @@ -0,0 +1,199 @@ +/** + * modelo-proyecto — modelo por defecto de un proyecto. + * + * Guarda el modelo elegido en `/.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 { Type } from "typebox"; + +const SETTINGS_REL = join(".pi", "settings.json"); + +function clave(m: Model): 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, 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[] { + return [...ctx.modelRegistry.getAvailable()].sort((a, b) => clave(a).localeCompare(clave(b))); +} + +function leerSettings(proyecto: string): Record { + 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; +} + +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): 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 | 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 | 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 opciones = lista.map((m) => etiqueta(m, actual)); + const elegido = await ctx.ui.select(`Modelo por defecto del proyecto (${proyecto})`, opciones); + if (!elegido) return undefined; + return lista[opciones.indexOf(elegido)]; +} + +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 | 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 /.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 | 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 }, + }; + }, + }); +} diff --git a/package.json b/package.json index 73982ed..8ae1a25 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,15 @@ { "name": "pi-new-project", - "version": "0.1.0", + "version": "0.2.0", "private": true, - "description": "Paquete de pi con la skill new-project: genera la base documental de un proyecto (AGENTS.md, README, CHANGELOG, TODO, INSTALL).", + "description": "Paquete de pi con la skill new-project (base documental de un proyecto) y la orden /modelo-proyecto (modelo por defecto del proyecto).", "keywords": [ "pi-package" ], "pi": { + "extensions": [ + "./extensions" + ], "skills": [ "./skills" ] diff --git a/skills/new-project/SKILL.md b/skills/new-project/SKILL.md index d345b2d..a2e4dc3 100644 --- a/skills/new-project/SKILL.md +++ b/skills/new-project/SKILL.md @@ -1,6 +1,6 @@ --- 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. Úsala cuando el usuario pida crear un proyecto, repositorio o directorio nuevo, inicializar su documentación o "montar el esqueleto" de un repo. +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 --- @@ -28,22 +28,37 @@ Genera el esqueleto documental de un proyecto. Todas las plantillas están en El script no sobreescribe ficheros existentes salvo con `--force` y muestra un resumen de lo creado. Usa `--dry-run` para revisar antes. -3. **Revisa el resultado**: abre `README.md` y `AGENTS.md` del proyecto generado +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 `/.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 ` (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. -4. **Ajusta** `INSTALL.md` con los requisitos y pasos reales del stack elegido. -5. **Commit inicial** si se creó repo git: +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" ``` -6. Informa al usuario de la ruta creada, los ficheros y del commit. +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` y `## Reglas de funcionamiento` de +- 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. @@ -52,6 +67,8 @@ Genera el esqueleto documental de un proyecto. Todas las plantillas están en ## 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. diff --git a/skills/new-project/references/plantillas.md b/skills/new-project/references/plantillas.md index b2c882b..3126018 100644 --- a/skills/new-project/references/plantillas.md +++ b/skills/new-project/references/plantillas.md @@ -10,6 +10,7 @@ Qué genera `new-project.sh` y cuándo hay que tocar cada documento. | `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 @@ -26,7 +27,17 @@ El script sustituye estos marcadores en todas las plantillas: `AGENTS.md` incluye a propósito dos secciones con `PENDIENTE`: -- `## Reglas de modelo` — modelo de pi, contexto y criterio de selección. +- `## 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 `/.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. diff --git a/skills/new-project/scripts/new-project.sh b/skills/new-project/scripts/new-project.sh index eab632b..658b07f 100755 --- a/skills/new-project/scripts/new-project.sh +++ b/skills/new-project/scripts/new-project.sh @@ -13,6 +13,7 @@ PROYECTO="" DESCRIPCION="" AUTOR="" DESTINO="" +MODELO="" INIT_GIT=0 FORCE=0 DRY_RUN=0 @@ -28,6 +29,10 @@ 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. @@ -37,12 +42,14 @@ 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) PROYECTO="${2:-}"; shift 2 ;; - -d | --descripcion) DESCRIPCION="${2:-}"; shift 2 ;; - -a | --autor) AUTOR="${2:-}"; shift 2 ;; + -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 ;; @@ -74,6 +81,11 @@ if [[ -z "$AUTOR" ]]; then [[ -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() { @@ -109,6 +121,22 @@ for fichero in "${FICHEROS[@]}"; do 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" @@ -120,6 +148,7 @@ 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' diff --git a/skills/new-project/templates/AGENTS.md.tmpl b/skills/new-project/templates/AGENTS.md.tmpl index 58c534c..3c46095 100644 --- a/skills/new-project/templates/AGENTS.md.tmpl +++ b/skills/new-project/templates/AGENTS.md.tmpl @@ -16,13 +16,28 @@ Léela antes de tocar cualquier fichero y respétala en cada cambio. | `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 -PENDIENTE: definir qué modelo de pi se usa en este proyecto, con qué contexto y -con qué criterio de selección (coste, VRAM, ventana, velocidad). Se acuerda -tarea por tarea; hasta entonces, no fijar valores aquí. +### 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 diff --git a/skills/new-project/templates/README.md.tmpl b/skills/new-project/templates/README.md.tmpl index 1a05b62..8f13bdd 100644 --- a/skills/new-project/templates/README.md.tmpl +++ b/skills/new-project/templates/README.md.tmpl @@ -39,7 +39,9 @@ PENDIENTE: ejemplo mínimo de uso o de arranque. ├── README.md # este documento ├── INSTALL.md # instalación y dependencias ├── CHANGELOG.md # historial de cambios -└── TODO.md # tareas pendientes +├── TODO.md # tareas pendientes +└── .pi/ + └── settings.json # modelo por defecto de pi (cambiar con /modelo-proyecto) ``` ## Documentación diff --git a/skills/new-project/templates/TODO.md.tmpl b/skills/new-project/templates/TODO.md.tmpl index 99e2e61..66fb9cf 100644 --- a/skills/new-project/templates/TODO.md.tmpl +++ b/skills/new-project/templates/TODO.md.tmpl @@ -9,7 +9,8 @@ anota el cambio en `CHANGELOG.md`. ## Pendiente -- [ ] Definir las **reglas de modelo** del proyecto en `AGENTS.md`. +- [ ] 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`.