feat: modelo por defecto por proyecto con selector y orden /modelo-proyecto
This commit is contained in:
1 parent
b35bc7a607
commit
f6d2870635
12 files changed
+372
-22
No files matched your search
@@ -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
|
- Fijar las reglas de modelo y de funcionamiento que la skill escribirá en el
|
||||||
`AGENTS.md` de cada proyecto generado (hoy quedan como `PENDIENTE`).
|
`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 `<ruta>/.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
|
## [0.1.0] - 2026-10-18
|
||||||
|
|
||||||
### Añadido
|
### Añadido
|
||||||
|
|||||||
+26
-1
@@ -26,7 +26,8 @@ O desde git/npm cuando se publique:
|
|||||||
pi install git:github.com/<usuario>/pi-new-project@v0.1.0
|
pi install git:github.com/<usuario>/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`.
|
Para una prueba puntual sin instalarlo: `pi -e ./pi-new-project`.
|
||||||
|
|
||||||
## 3. Opción B — copiar la skill
|
## 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/`.
|
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
|
## 4. Opción C — exportar a otro ordenador
|
||||||
|
|
||||||
No hay más que copiar el directorio; no tiene dependencias.
|
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`
|
Deben aparecer `AGENTS.md`, `README.md`, `CHANGELOG.md`, `TODO.md`, `INSTALL.md`
|
||||||
y `.gitignore`, sin marcadores `{{...}}` pendientes.
|
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
|
## 6. Actualización
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -88,6 +109,7 @@ pi update --extensions # si se instaló como paquete
|
|||||||
```bash
|
```bash
|
||||||
pi remove ./pi-new-project # si se instaló como paquete
|
pi remove ./pi-new-project # si se instaló como paquete
|
||||||
rm -rf ~/.agents/skills/new-project # si se copió o enlazó
|
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.
|
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 encuentro las plantillas` | Falta `templates/` | Reinstala el paquete completo, no solo el script. |
|
||||||
| `no puedo deducir el nombre` | Destino `.` o `/` | Pasa `--nombre`. |
|
| `no puedo deducir el nombre` | Destino `.` o `/` | Pasa `--nombre`. |
|
||||||
| No sobreescribe mis ficheros | Protección por defecto | Usa `--force` tras revisar `--dry-run`. |
|
| 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. |
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
Paquete de [pi](https://pi.dev) que genera la base documental de un proyecto
|
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
|
nuevo: `AGENTS.md`, `README.md`, `CHANGELOG.md`, `TODO.md`, `INSTALL.md` y
|
||||||
`.gitignore`, listos para versionar y con las secciones de reglas reservadas
|
`.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
|
## Contenido
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ pi-new-project/
|
|||||||
├── INSTALL.md # instalación en otro ordenador
|
├── INSTALL.md # instalación en otro ordenador
|
||||||
├── CHANGELOG.md
|
├── CHANGELOG.md
|
||||||
├── TODO.md
|
├── TODO.md
|
||||||
|
├── extensions/
|
||||||
|
│ └── modelo-proyecto.ts # orden /modelo-proyecto y herramienta elegir_modelo_proyecto
|
||||||
└── skills/
|
└── skills/
|
||||||
└── new-project/
|
└── new-project/
|
||||||
├── SKILL.md # instrucciones para el agente
|
├── SKILL.md # instrucciones para el agente
|
||||||
@@ -41,6 +45,20 @@ skills/new-project/scripts/new-project.sh \
|
|||||||
--git ~/src/gestor-gastos
|
--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 `<proyecto>/.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
|
El script nunca sobreescribe ficheros existentes salvo con `--force`, y admite
|
||||||
`--dry-run` para revisar antes.
|
`--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
|
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`.
|
`~/.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
|
## 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).
|
- `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.
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
## Pendiente
|
## Pendiente
|
||||||
|
|
||||||
- [ ] Definir las **reglas de modelo** (modelo, contexto, criterio de selección)
|
- [ ] Definir el resto de **reglas de modelo** (contexto, criterio de selección
|
||||||
y volcarlas en `templates/AGENTS.md.tmpl`.
|
por tarea) y volcarlas en `templates/AGENTS.md.tmpl`.
|
||||||
- [ ] Definir las **reglas de funcionamiento** comunes y volcarlas en
|
- [ ] Definir las **reglas de funcionamiento** comunes y volcarlas en
|
||||||
`templates/AGENTS.md.tmpl`.
|
`templates/AGENTS.md.tmpl`.
|
||||||
- [ ] Decidir si las reglas se configuran por parámetros del script
|
- [ ] Decidir si las reglas se configuran por parámetros del script
|
||||||
@@ -20,5 +20,7 @@
|
|||||||
|
|
||||||
## Hecho
|
## 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 — estructura del paquete, skill, script y plantillas.
|
||||||
- [x] 2026-10-18 — `INSTALL.md` con exportación a otro ordenador.
|
- [x] 2026-10-18 — `INSTALL.md` con exportación a otro ordenador.
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Type } from "typebox";
|
||||||
|
|
||||||
|
const SETTINGS_REL = join(".pi", "settings.json");
|
||||||
|
|
||||||
|
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 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<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 },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
+5
-2
@@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "pi-new-project",
|
"name": "pi-new-project",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"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": [
|
"keywords": [
|
||||||
"pi-package"
|
"pi-package"
|
||||||
],
|
],
|
||||||
"pi": {
|
"pi": {
|
||||||
|
"extensions": [
|
||||||
|
"./extensions"
|
||||||
|
],
|
||||||
"skills": [
|
"skills": [
|
||||||
"./skills"
|
"./skills"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: new-project
|
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
|
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
|
El script no sobreescribe ficheros existentes salvo con `--force` y muestra
|
||||||
un resumen de lo creado. Usa `--dry-run` para revisar antes.
|
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 `<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
|
y completa lo que quede como `PENDIENTE` (objetivos, stack, comandos de
|
||||||
verificación reales). No dejes marcadores de plantilla ni secciones vacías.
|
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. **Ajusta** `INSTALL.md` con los requisitos y pasos reales del stack elegido.
|
||||||
5. **Commit inicial** si se creó repo git:
|
6. **Commit inicial** si se creó repo git (incluye `.pi/settings.json`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add -A && git commit -m "docs: base documental inicial"
|
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
|
## Reglas
|
||||||
|
|
||||||
- No ejecutes el script sobre un directorio que ya tiene `AGENTS.md` o `README.md`
|
- No ejecutes el script sobre un directorio que ya tiene `AGENTS.md` o `README.md`
|
||||||
sin avisar y confirmar (usaría `--force`).
|
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
|
`AGENTS.md` se dejan como `PENDIENTE` a propósito: se acuerdan después, por
|
||||||
proyecto. No las rellenes con suposiciones.
|
proyecto. No las rellenes con suposiciones.
|
||||||
- Idioma de los documentos generados: español.
|
- 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
|
## Referencia
|
||||||
|
|
||||||
- `scripts/new-project.sh --help` — todas las opciones.
|
- `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}}`,
|
- `templates/` — plantillas con marcadores `{{PROYECTO}}`, `{{DESCRIPCION}}`,
|
||||||
`{{AUTOR}}`, `{{FECHA}}`, `{{FECHA_ISO}}`.
|
`{{AUTOR}}`, `{{FECHA}}`, `{{FECHA_ISO}}`.
|
||||||
- `references/plantillas.md` — qué contiene cada documento y cuándo tocarlo.
|
- `references/plantillas.md` — qué contiene cada documento y cuándo tocarlo.
|
||||||
@@ -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. |
|
| `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. |
|
| `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. |
|
| `.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
|
## Marcadores
|
||||||
|
|
||||||
@@ -26,7 +27,17 @@ El script sustituye estos marcadores en todas las plantillas:
|
|||||||
|
|
||||||
`AGENTS.md` incluye a propósito dos secciones con `PENDIENTE`:
|
`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.
|
- `## 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.
|
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.
|
||||||
@@ -13,6 +13,7 @@ PROYECTO=""
|
|||||||
DESCRIPCION=""
|
DESCRIPCION=""
|
||||||
AUTOR=""
|
AUTOR=""
|
||||||
DESTINO=""
|
DESTINO=""
|
||||||
|
MODELO=""
|
||||||
INIT_GIT=0
|
INIT_GIT=0
|
||||||
FORCE=0
|
FORCE=0
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
@@ -28,6 +29,10 @@ Opciones:
|
|||||||
-n, --nombre NOMBRE Nombre del proyecto (por defecto: nombre del directorio).
|
-n, --nombre NOMBRE Nombre del proyecto (por defecto: nombre del directorio).
|
||||||
-d, --descripcion TEXTO Descripción en una frase (por defecto: PENDIENTE).
|
-d, --descripcion TEXTO Descripción en una frase (por defecto: PENDIENTE).
|
||||||
-a, --autor NOMBRE Autor (por defecto: git config user.name).
|
-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.
|
-g, --git Inicializa un repositorio git en el destino.
|
||||||
-f, --force Sobreescribe ficheros que ya existan.
|
-f, --force Sobreescribe ficheros que ya existan.
|
||||||
--dry-run Muestra lo que haría sin escribir nada.
|
--dry-run Muestra lo que haría sin escribir nada.
|
||||||
@@ -37,12 +42,14 @@ EOF
|
|||||||
|
|
||||||
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
||||||
aviso() { printf 'aviso: %s\n' "$*" >&2; }
|
aviso() { printf 'aviso: %s\n' "$*" >&2; }
|
||||||
|
requiere() { [[ $# -ge 2 && -n "$2" && "$2" != -* ]] || die "$1 necesita un valor"; }
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-n | --nombre) PROYECTO="${2:-}"; shift 2 ;;
|
-n | --nombre) requiere "$@"; PROYECTO="$2"; shift 2 ;;
|
||||||
-d | --descripcion) DESCRIPCION="${2:-}"; shift 2 ;;
|
-d | --descripcion) requiere "$@"; DESCRIPCION="$2"; shift 2 ;;
|
||||||
-a | --autor) AUTOR="${2:-}"; shift 2 ;;
|
-a | --autor) requiere "$@"; AUTOR="$2"; shift 2 ;;
|
||||||
|
-m | --modelo) requiere "$@"; MODELO="$2"; shift 2 ;;
|
||||||
-g | --git) INIT_GIT=1; shift ;;
|
-g | --git) INIT_GIT=1; shift ;;
|
||||||
-f | --force) FORCE=1; shift ;;
|
-f | --force) FORCE=1; shift ;;
|
||||||
--dry-run) DRY_RUN=1; shift ;;
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
@@ -74,6 +81,11 @@ if [[ -z "$AUTOR" ]]; then
|
|||||||
[[ -n "$AUTOR" ]] || AUTOR="PENDIENTE"
|
[[ -n "$AUTOR" ]] || AUTOR="PENDIENTE"
|
||||||
fi
|
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)
|
FICHEROS=(AGENTS.md README.md CHANGELOG.md TODO.md INSTALL.md .gitignore)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -109,6 +121,22 @@ for fichero in "${FICHEROS[@]}"; do
|
|||||||
creados+=("$fichero")
|
creados+=("$fichero")
|
||||||
done
|
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 [[ $INIT_GIT -eq 1 && $DRY_RUN -eq 0 ]]; then
|
||||||
if [[ -d "$DESTINO/.git" ]]; then
|
if [[ -d "$DESTINO/.git" ]]; then
|
||||||
aviso "$DESTINO ya es un repositorio git"
|
aviso "$DESTINO ya es un repositorio git"
|
||||||
@@ -120,6 +148,7 @@ fi
|
|||||||
|
|
||||||
printf '\nproyecto: %s\n' "$PROYECTO"
|
printf '\nproyecto: %s\n' "$PROYECTO"
|
||||||
printf 'destino: %s\n' "$destino_abs"
|
printf 'destino: %s\n' "$destino_abs"
|
||||||
|
printf 'modelo: %s\n' "${MODELO:-sin fijar (elígelo con /modelo-proyecto)}"
|
||||||
printf 'creados: %s\n' "${creados[*]:-ninguno}"
|
printf 'creados: %s\n' "${creados[*]:-ninguno}"
|
||||||
[[ ${#omitidos[@]} -eq 0 ]] || printf 'omitidos: %s\n' "${omitidos[*]}"
|
[[ ${#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'
|
[[ $DRY_RUN -eq 1 ]] || printf 'siguiente: revisa README.md, INSTALL.md y las secciones PENDIENTE de AGENTS.md\n'
|
||||||
@@ -16,13 +16,28 @@ Léela antes de tocar cualquier fichero y respétala en cada cambio.
|
|||||||
| `INSTALL.md` | Instalación detallada y dependencias. |
|
| `INSTALL.md` | Instalación detallada y dependencias. |
|
||||||
| `CHANGELOG.md` | Historial de cambios (*Keep a Changelog*). |
|
| `CHANGELOG.md` | Historial de cambios (*Keep a Changelog*). |
|
||||||
| `TODO.md` | Tareas pendientes y deuda técnica. |
|
| `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. |
|
| _(completar)_ | Añade aquí el código y los recursos del proyecto. |
|
||||||
|
|
||||||
## Reglas de modelo
|
## Reglas de modelo
|
||||||
|
|
||||||
PENDIENTE: definir qué modelo de pi se usa en este proyecto, con qué contexto y
|
### Modelo por defecto
|
||||||
con qué criterio de selección (coste, VRAM, ventana, velocidad). Se acuerda
|
|
||||||
tarea por tarea; hasta entonces, no fijar valores aquí.
|
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
|
## Reglas de funcionamiento
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ PENDIENTE: ejemplo mínimo de uso o de arranque.
|
|||||||
├── README.md # este documento
|
├── README.md # este documento
|
||||||
├── INSTALL.md # instalación y dependencias
|
├── INSTALL.md # instalación y dependencias
|
||||||
├── CHANGELOG.md # historial de cambios
|
├── 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
|
## Documentación
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ anota el cambio en `CHANGELOG.md`.
|
|||||||
|
|
||||||
## Pendiente
|
## 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`.
|
- [ ] Definir las **reglas de funcionamiento** del proyecto en `AGENTS.md`.
|
||||||
- [ ] Completar los `PENDIENTE` de `README.md` e `INSTALL.md`.
|
- [ ] Completar los `PENDIENTE` de `README.md` e `INSTALL.md`.
|
||||||
- [ ] Fijar los comandos de verificación reales en `AGENTS.md`.
|
- [ ] Fijar los comandos de verificación reales en `AGENTS.md`.
|
||||||
|
|||||||
Reference in new issue
Block a user