Compare commits

..
4 Commits
10 changed files with 358 additions and 27 deletions

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
# Ficheros innecesarios para instalar el paquete (pi install / copia).
# Lo que se necesita: package.json, extensions/, skills/ (y LICENSE).
# Configuración local de pi en este directorio (modelo por defecto, confianza…)
.pi/
# Dependencias y artefactos de empaquetado
node_modules/
*.tgz
*.tar.gz
*.zip
# Registros y temporales
*.log
*.tmp
*.bak
*~
*.swp
*.swo
# Editores y sistema
.vscode/
.idea/
.DS_Store
Thumbs.db
# Variables de entorno / secretos
.env
.env.*
+4
View File
@@ -0,0 +1,4 @@
{
"defaultProvider": "github-copilot",
"defaultModel": "claude-opus-5.5"
}
+28
View File
@@ -12,6 +12,34 @@ 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.3.0] - 2026-10-18
### Añadido
- Repositorio de distribución
<https://git.sargatxet.cloud/sargatxet/pi-new-project-dist> con solo los
ficheros que necesita pi (`package.json`, `LICENSE`, `extensions/`, `skills/`
y un `README.md` mínimo), y script `scripts/publicar-dist.sh` para publicar
cada versión en él (commit + etiqueta `vX.Y.Z`, con `--dry-run`).
- `.gitignore` del repositorio (configuración local de pi, temporales, editores).
- Licencia MIT: fichero `LICENSE` y campo `license` en `package.json`.
- Documentada la descarga e instalación desde el repositorio
<https://git.sargatxet.cloud/sargatxet/pi-new-project>
(`pi install git:git.sargatxet.cloud/sargatxet/pi-new-project`) en
`README.md` e `INSTALL.md`, con actualización y desinstalación; campos
`homepage` y `repository` en `package.json`.
### Cambiado
- La instalación recomendada pasa a ser
`pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist`;
`README.md` e `INSTALL.md` actualizados (actualización, desinstalación y
publicación de versiones).
- El selector de modelo (`/modelo-proyecto` y `elegir_modelo_proyecto`) muestra
como máximo 15 modelos a la vez en una ventana que se desplaza con el cursor;
↑/↓ dan la vuelta de un extremo a otro y el cursor parte del modelo actual
del proyecto. Fuera de la TUI se mantiene el diálogo estándar.
## [0.2.0] - 2026-10-18 ## [0.2.0] - 2026-10-18
### Añadido ### Añadido
+71 -17
View File
@@ -3,6 +3,13 @@
Instalación del paquete/skill `new-project` en pi, en este ordenador o en Instalación del paquete/skill `new-project` en pi, en este ordenador o en
cualquier otro. cualquier otro.
Repositorios:
- **Distribución** (para usarlo): <https://git.sargatxet.cloud/sargatxet/pi-new-project-dist>.
Solo contiene lo que pi necesita: `package.json`, `LICENSE`, `extensions/`,
`skills/` y un `README.md` mínimo. Se genera con `scripts/publicar-dist.sh`.
- **Desarrollo** (para modificarlo): <https://git.sargatxet.cloud/sargatxet/pi-new-project>.
## 1. Requisitos ## 1. Requisitos
| Componente | Versión mínima | Notas | | Componente | Versión mínima | Notas |
@@ -12,23 +19,42 @@ cualquier otro.
| git | cualquiera | Opcional: `--git` y autor por defecto. | | git | cualquiera | Opcional: `--git` y autor por defecto. |
| shellcheck | opcional | Para validar el script (`shellcheck new-project.sh`). | | shellcheck | opcional | Para validar el script (`shellcheck new-project.sh`). |
## 2. Opción A — instalarlo como paquete de pi (recomendado) ## 2. Opción A — instalarlo como paquete de pi desde git (recomendado)
Desde el directorio que contiene `pi-new-project/`: pi clona el repositorio de distribución y lo instala directamente:
```bash ```bash
pi install ./pi-new-project pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist
# equivalente, con la URL completa:
pi install https://git.sargatxet.cloud/sargatxet/pi-new-project-dist
``` ```
O desde git/npm cuando se publique: Para fijar una versión concreta, añade una etiqueta (`v0.3.0`, …) o un commit:
`pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist@<etiqueta-o-commit>`.
```bash Añade `-l` (`--local`) para declararlo solo en el proyecto actual
pi install git:github.com/<usuario>/pi-new-project@v0.1.0 (`.pi/settings.json`) en lugar de en `~/.pi/agent/settings.json`.
```
Comprueba con `pi list` y arranca pi: la skill aparecerá como `new-project` y Comprueba con `pi list` y arranca pi: la skill aparecerá como `new-project` y
estará disponible la orden `/modelo-proyecto`. 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 git:git.sargatxet.cloud/sargatxet/pi-new-project-dist`.
> Si ya lo tenías instalado desde el repositorio de desarrollo
> (`git:git.sargatxet.cloud/sargatxet/pi-new-project`), pi lo considera otro
> paquete: quítalo antes con `pi remove git:git.sargatxet.cloud/sargatxet/pi-new-project`
> para que la skill no se cargue dos veces.
### Desde una copia local del repositorio de desarrollo
Si vas a revisarlo o modificarlo:
```bash
git clone https://git.sargatxet.cloud/sargatxet/pi-new-project.git
pi install ./pi-new-project
```
Una instalación local se carga desde ese directorio sin copiarlo: los cambios
(o un `git pull`) se aplican en la siguiente sesión de pi.
## 3. Opción B — copiar la skill ## 3. Opción B — copiar la skill
@@ -47,6 +73,10 @@ 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/`.
En estos ejemplos, `pi-new-project/` es una copia del repositorio obtenida con
`git clone https://git.sargatxet.cloud/sargatxet/pi-new-project-dist.git pi-new-project`
(o del de desarrollo, `.../pi-new-project.git`; ambos tienen `skills/` y `extensions/`).
La skill sola no incluye la orden `/modelo-proyecto` ni la herramienta 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, `elegir_modelo_proyecto`; cópialas también (o la skill usará el plan B,
`pi --list-models` + `--modelo`): `pi --list-models` + `--modelo`):
@@ -56,20 +86,22 @@ mkdir -p ~/.pi/agent/extensions
cp pi-new-project/extensions/modelo-proyecto.ts ~/.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 sin acceso al repositorio
No hay más que copiar el directorio; no tiene dependencias. Si el ordenador destino tiene acceso a
<https://git.sargatxet.cloud/sargatxet/pi-new-project-dist>, basta con la opción A.
Si no, copia el directorio (mejor el de distribución, que solo trae lo necesario);
no tiene dependencias.
```bash ```bash
# en el ordenador origen # en el ordenador origen
tar -czf pi-new-project-0.1.0.tar.gz pi-new-project/ tar -czf pi-new-project-0.3.0.tar.gz pi-new-project/
# en el ordenador destino # en el ordenador destino
tar -xzf pi-new-project-0.1.0.tar.gz tar -xzf pi-new-project-0.3.0.tar.gz
pi install ./pi-new-project # o la opción B pi install ./pi-new-project # o la opción B
``` ```
Alternativa sin `tar`: publicar el repositorio y usar `pi install <url-de-git>`.
## 5. Verificación ## 5. Verificación
@@ -100,20 +132,42 @@ modelos y actualizar `.pi/settings.json`.
## 6. Actualización ## 6. Actualización
```bash ```bash
git pull pi update --extensions # instalado desde git: actualiza el clon gestionado por pi
pi update --extensions # si se instaló como paquete
# instalado desde una copia local o copiado (opción B):
cd pi-new-project && git pull
``` ```
Si se instaló con una etiqueta o un commit fijo (`@<ref>`), `pi update` no lo
mueve: reinstala con la nueva referencia.
## 7. Desinstalación ## 7. Desinstalación
```bash ```bash
pi remove ./pi-new-project # si se instaló como paquete pi remove git:git.sargatxet.cloud/sargatxet/pi-new-project-dist # paquete desde git
pi remove git:git.sargatxet.cloud/sargatxet/pi-new-project # instalaciones antiguas
pi remove ./pi-new-project # paquete desde una copia local
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 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.
## Publicar una versión (mantenedores)
El repositorio de distribución se genera desde el de desarrollo; no se edita a
mano. Con los cambios ya commiteados y la versión subida en `package.json` y
`CHANGELOG.md`:
```bash
scripts/publicar-dist.sh --dry-run # prepara y muestra el contenido sin subir nada
scripts/publicar-dist.sh # commit + etiqueta vX.Y.Z en pi-new-project-dist
```
El script toma el contenido de `HEAD` (no del directorio de trabajo), se niega a
publicar si hay cambios sin commitear en lo que se publica o si la etiqueta ya
existe, y necesita permiso de escritura en el repositorio de distribución.
## 8. Resolución de problemas ## 8. Resolución de problemas
| Síntoma | Causa probable | Solución | | Síntoma | Causa probable | Solución |
+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.
+29 -4
View File
@@ -16,6 +16,9 @@ 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
├── LICENSE # licencia MIT
├── scripts/
│ └── publicar-dist.sh # publica la versión instalable en pi-new-project-dist
├── extensions/ ├── extensions/
│ └── modelo-proyecto.ts # orden /modelo-proyecto y herramienta elegir_modelo_proyecto │ └── modelo-proyecto.ts # orden /modelo-proyecto y herramienta elegir_modelo_proyecto
└── skills/ └── skills/
@@ -64,10 +67,28 @@ El script nunca sobreescribe ficheros existentes salvo con `--force`, y admite
## Instalación ## Instalación
Ver [`INSTALL.md`](INSTALL.md). Resumen: copia (o enlaza) el directorio en Para usarlo, instala la **versión de distribución**
`~/.pi/agent/skills/` o instálalo como paquete con `pi install ./pi-new-project`. (<https://git.sargatxet.cloud/sargatxet/pi-new-project-dist>), que contiene solo
La orden `/modelo-proyecto` solo está disponible al instalarlo como paquete (o los ficheros que necesita pi (`package.json`, `LICENSE`, `extensions/` y `skills/`):
copiando también `extensions/`).
```bash
pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist
# o una versión fija:
pi install git:git.sargatxet.cloud/sargatxet/pi-new-project-dist@v0.3.0
```
Este repositorio (<https://git.sargatxet.cloud/sargatxet/pi-new-project>) es el
de desarrollo. Para modificar el paquete, clónalo e instala la copia local:
```bash
git clone https://git.sargatxet.cloud/sargatxet/pi-new-project.git
pi install ./pi-new-project
```
Ver [`INSTALL.md`](INSTALL.md) para el resto de opciones (copiar solo la skill,
exportar sin acceso al repositorio, actualizar y desinstalar). La orden
`/modelo-proyecto` solo está disponible al instalarlo como paquete (o copiando
también `extensions/`).
## Requisitos ## Requisitos
@@ -75,3 +96,7 @@ copiando también `extensions/`).
- `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).
- El script no usa Python, Node ni dependencias externas: es `bash` con - El script no usa Python, Node ni dependencias externas: es `bash` con
biblioteca estándar. biblioteca estándar.
## Licencia
Distribuido bajo la licencia MIT. Ver [`LICENSE`](LICENSE).
+10 -1
View File
@@ -15,11 +15,20 @@
- [ ] Detectar y completar el stack automáticamente (¿`--stack python|node|rust`?) - [ ] Detectar y completar el stack automáticamente (¿`--stack python|node|rust`?)
para prellenar `INSTALL.md` y los comandos de verificación. para prellenar `INSTALL.md` y los comandos de verificación.
- [ ] Valorar soporte multiidioma (`--lang es|en`) en plantillas y script. - [ ] Valorar soporte multiidioma (`--lang es|en`) en plantillas y script.
- [ ] Publicar el paquete (git o npm) para instalarlo con `pi install <url>`. - [ ] Valorar automatizar la publicación con Gitea Actions al subir una
etiqueta `v*` (requiere runner y un token con escritura en `-dist`).
- [ ] Valorar publicarlo también en npm (galería de paquetes de pi).
- [ ] Prueba automática del script (golden test de los ficheros generados). - [ ] Prueba automática del script (golden test de los ficheros generados).
## Hecho ## Hecho
- [x] 2026-10-18 — publicada `v0.3.0` en `pi-new-project-dist`.
- [x] 2026-10-18 — repositorio de distribución `pi-new-project-dist` y script
`scripts/publicar-dist.sh`.
- [x] 2026-10-18 — licencia MIT (`LICENSE`).
- [x] 2026-10-18 — paquete publicado en git
(<https://git.sargatxet.cloud/sargatxet/pi-new-project>) e instalación con
`pi install` documentada.
- [x] 2026-10-18 — modelo por defecto por proyecto: selector al crear y orden - [x] 2026-10-18 — modelo por defecto por proyecto: selector al crear y orden
`/modelo-proyecto` para cambiarlo. `/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.
+49 -4
View File
@@ -16,9 +16,13 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "
import { join, resolve } from "node:path"; import { join, resolve } from "node:path";
import type { Api, Model } from "@earendil-works/pi-ai"; import type { Api, Model } from "@earendil-works/pi-ai";
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; 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"; import { Type } from "typebox";
const SETTINGS_REL = join(".pi", "settings.json"); 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 { function clave(m: Model<Api>): string {
return `${m.provider}/${m.id}`; return `${m.provider}/${m.id}`;
@@ -88,10 +92,51 @@ async function elegir(ctx: ExtensionContext, proyecto: string): Promise<Model<Ap
const lista = disponibles(ctx); const lista = disponibles(ctx);
if (lista.length === 0) throw new Error("no hay modelos disponibles (¿falta /login o credenciales?)"); if (lista.length === 0) throw new Error("no hay modelos disponibles (¿falta /login o credenciales?)");
const actual = modeloDelProyecto(proyecto); const actual = modeloDelProyecto(proyecto);
const opciones = lista.map((m) => etiqueta(m, actual)); const titulo = `Modelo por defecto del proyecto (${proyecto})`;
const elegido = await ctx.ui.select(`Modelo por defecto del proyecto (${proyecto})`, opciones);
if (!elegido) return undefined; // Fuera de la TUI (p. ej. RPC) no hay componentes propios: diálogo estándar.
return lista[opciones.indexOf(elegido)]; 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) { export default function (pi: ExtensionAPI) {
+7 -1
View File
@@ -1,8 +1,14 @@
{ {
"name": "pi-new-project", "name": "pi-new-project",
"version": "0.2.0", "version": "0.3.0",
"private": true, "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).", "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": [ "keywords": [
"pi-package" "pi-package"
], ],
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env bash
# publicar-dist.sh — publica en el repositorio de distribución
# (pi-new-project-dist) solo los ficheros que necesita pi para usar el paquete.
#
# Toma el contenido del commit actual (HEAD), no del directorio de trabajo.
# Cada publicación añade un commit y una etiqueta vX.Y.Z (versión de
# package.json) en el repositorio de distribución.
set -euo pipefail
REMOTO_DEF="https://git.sargatxet.cloud/sargatxet/pi-new-project-dist.git"
DESARROLLO="https://git.sargatxet.cloud/sargatxet/pi-new-project"
INSTALAR="git:git.sargatxet.cloud/sargatxet/pi-new-project-dist"
# Lo único que se publica (README.md se genera aparte).
RUTAS=(package.json LICENSE extensions skills)
uso() {
cat <<EOF
Uso: $(basename "$0") [opciones]
Publica en el repositorio de distribución los ficheros necesarios para usar
el paquete: ${RUTAS[*]} y un README.md mínimo.
-n, --dry-run Prepara la publicación sin subir nada y muestra el contenido.
-r, --remoto URL Repositorio de distribución (por defecto: $REMOTO_DEF).
-h, --help Muestra esta ayuda.
EOF
}
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
info() { printf '%s\n' "$*"; }
DRY_RUN=0
REMOTO="$REMOTO_DEF"
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--dry-run) DRY_RUN=1 ;;
-r|--remoto) [[ $# -ge 2 ]] || die "falta el valor de $1"; REMOTO="$2"; shift ;;
-h|--help) uso; exit 0 ;;
*) die "opción desconocida: $1 (usa --help)" ;;
esac
shift
done
RAIZ="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)" || die "no estoy dentro del repositorio"
cd "$RAIZ"
# 1. Comprobaciones
[[ -z "$(git status --porcelain -- "${RUTAS[@]}")" ]] \
|| die "hay cambios sin commitear en: ${RUTAS[*]}"
VERSION="$(sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -n1)"
[[ -n "$VERSION" ]] || die "no encuentro la versión en package.json"
ETIQUETA="v$VERSION"
ORIGEN="$(git rev-parse --short HEAD)"
if [[ -n "$(git ls-remote --tags "$REMOTO" "refs/tags/$ETIQUETA")" ]]; then
die "$ETIQUETA ya está publicada en $REMOTO; sube la versión en package.json"
fi
if git rev-parse -q --verify '@{u}' >/dev/null && [[ -n "$(git log --oneline '@{u}..HEAD')" ]]; then
info "aviso: HEAD ($ORIGEN) tiene commits sin subir al repositorio de desarrollo"
fi
# 2. Preparar el contenido sobre el último estado del repositorio de distribución
TMP="$(mktemp -d)"
if [[ $DRY_RUN -eq 0 ]]; then trap 'rm -rf "$TMP"' EXIT; fi
DIST="$TMP/dist"
git clone -q "$REMOTO" "$DIST" 2>/dev/null || die "no puedo clonar $REMOTO"
if ! git -C "$DIST" rev-parse -q --verify HEAD >/dev/null; then
git -C "$DIST" symbolic-ref HEAD refs/heads/main # repositorio vacío
fi
find "$DIST" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
git archive HEAD "${RUTAS[@]}" | tar -x -C "$DIST"
cat >"$DIST/README.md" <<EOF
# pi-new-project (distribución)
Versión instalable de **pi-new-project** $ETIQUETA: solo los ficheros que pi
necesita para usar el paquete (skill \`new-project\` y orden \`/modelo-proyecto\`).
\`\`\`bash
pi install $INSTALAR
# versión fija:
pi install $INSTALAR@$ETIQUETA
\`\`\`
Documentación, cambios e incidencias: <$DESARROLLO>
> Repositorio generado automáticamente desde $DESARROLLO
> (commit \`$ORIGEN\`). No hagas cambios aquí: se perderán en la próxima publicación.
Licencia MIT, ver [\`LICENSE\`](LICENSE).
EOF
git -C "$DIST" add -A
if git -C "$DIST" diff --cached --quiet; then
info "sin cambios respecto a la última publicación; no hay nada que publicar"
exit 0
fi
git -C "$DIST" commit -q -m "release $ETIQUETA (origen $ORIGEN)"
git -C "$DIST" tag -a "$ETIQUETA" -m "pi-new-project $ETIQUETA (origen $ORIGEN)"
info "contenido de $ETIQUETA:"
git -C "$DIST" ls-tree -r --name-only HEAD | sed 's/^/ /'
# 3. Publicar
if [[ $DRY_RUN -eq 1 ]]; then
info "dry-run: no se ha subido nada; revisa $DIST"
exit 0
fi
git -C "$DIST" push -q origin HEAD:refs/heads/main "refs/tags/$ETIQUETA"
info "publicado $ETIQUETA en $REMOTO"
info "instalar: pi install $INSTALAR"