Skills als eigene Bereiche dokumentieren und das ZIP über ein Gitea-Release ausliefern.
This commit is contained in:
1 parent
b57c458919
commit
55e93e7660
17 files changed
+436
-215
No files matched your search
@@ -1,43 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Skills pruefen
|
|
||||||
run: bash scripts/validate.sh
|
|
||||||
|
|
||||||
- name: README-Tabelle aktuell?
|
|
||||||
run: bash scripts/update-readme.sh --check
|
|
||||||
|
|
||||||
- name: PowerShell-Skripte nur ASCII?
|
|
||||||
run: |
|
|
||||||
if grep -nP '[^\x00-\x7F]' scripts/*.ps1; then
|
|
||||||
echo "::error::.ps1-Dateien duerfen nur ASCII enthalten (Windows PowerShell 5.1)."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: ZIPs bauen
|
|
||||||
run: bash scripts/package.sh
|
|
||||||
|
|
||||||
check-windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Skills pruefen und verpacken (Windows PowerShell 5.1)
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
.\scripts\validate.ps1
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
||||||
.\scripts\update-readme.ps1 -Check
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
||||||
.\scripts\package.ps1
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
# Ausloeser: Tag im Format <skill-name>/v<x.y.z>, z. B. wordcraft-mcp/v0.1.0
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags: ['*/v*']
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Tag zerlegen
|
|
||||||
id: tag
|
|
||||||
run: |
|
|
||||||
TAG="$GITHUB_REF_NAME"
|
|
||||||
SKILL="${TAG%%/v*}"
|
|
||||||
VERSION="${TAG#*/v}"
|
|
||||||
if [ ! -d "skills/$SKILL" ]; then
|
|
||||||
echo "::error::Skill '$SKILL' aus Tag '$TAG' existiert nicht unter skills/."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "skill=$SKILL" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Version gegen CHANGELOG pruefen
|
|
||||||
env:
|
|
||||||
SKILL: ${{ steps.tag.outputs.skill }}
|
|
||||||
VERSION: ${{ steps.tag.outputs.version }}
|
|
||||||
run: |
|
|
||||||
source scripts/lib.sh
|
|
||||||
CL="$(skill_version "skills/$SKILL")"
|
|
||||||
if [ "$CL" != "$VERSION" ]; then
|
|
||||||
echo "::error::Tag-Version $VERSION passt nicht zur CHANGELOG-Version '$CL' von $SKILL."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Verpacken
|
|
||||||
run: bash scripts/package.sh "${{ steps.tag.outputs.skill }}"
|
|
||||||
|
|
||||||
- name: Release-Text aus CHANGELOG
|
|
||||||
env:
|
|
||||||
SKILL: ${{ steps.tag.outputs.skill }}
|
|
||||||
VERSION: ${{ steps.tag.outputs.version }}
|
|
||||||
run: |
|
|
||||||
awk -v v="$VERSION" '
|
|
||||||
index($0, "## " v " ") == 1 { on = 1; next }
|
|
||||||
/^## / && on { exit }
|
|
||||||
on { print }' "skills/$SKILL/CHANGELOG.md" > notes.md
|
|
||||||
cat notes.md
|
|
||||||
|
|
||||||
- name: Release anlegen
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
SKILL: ${{ steps.tag.outputs.skill }}
|
|
||||||
VERSION: ${{ steps.tag.outputs.version }}
|
|
||||||
run: |
|
|
||||||
gh release create "$GITHUB_REF_NAME" "dist/$SKILL-$VERSION.zip" \
|
|
||||||
--title "$SKILL $VERSION" \
|
|
||||||
--notes-file notes.md
|
|
||||||
+68
-14
@@ -1,16 +1,21 @@
|
|||||||
# Mitarbeiten
|
# Mitarbeiten
|
||||||
|
|
||||||
|
Kollegen installieren einen Skill ohne Git: ZIP aus der Tabelle in der [README](README.md) laden und in Claude hochladen. Dieser Text ist für alle, die einen Skill anlegen oder aktualisieren.
|
||||||
|
|
||||||
## Aufbau eines Skills
|
## Aufbau eines Skills
|
||||||
|
|
||||||
```
|
```
|
||||||
skills/<name>/
|
skills/<name>/
|
||||||
├── SKILL.md Pflicht – Anweisungen für Claude
|
├── SKILL.md Pflicht – Anweisungen für Claude (liegt im ZIP)
|
||||||
├── CHANGELOG.md Pflicht – Versionen (wird nicht ins ZIP gepackt)
|
├── README.md Pflicht – Doku für Menschen (nicht im ZIP)
|
||||||
|
├── CHANGELOG.md Pflicht – Versionen (nicht im ZIP)
|
||||||
├── scripts/ optional – Hilfsskripte, die Claude ausführen kann
|
├── scripts/ optional – Hilfsskripte, die Claude ausführen kann
|
||||||
├── references/ optional – Nachschlagematerial, das Claude bei Bedarf liest
|
├── references/ optional – Nachschlagematerial, das Claude bei Bedarf liest
|
||||||
└── assets/ optional – Vorlagen, Bilder usw.
|
└── assets/ optional – Vorlagen, Bilder usw.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`README.md` hat dieselben Abschnitte wie [templates/skill/README.md](templates/skill/README.md): Zweck, Voraussetzungen, Installation, Einrichtung, Stolpersteine, Test, Deinstallation/Update, Link zum Changelog.
|
||||||
|
|
||||||
## Regeln
|
## Regeln
|
||||||
|
|
||||||
- **Name:** kebab-case, nur `a-z`, `0-9` und einzelne Bindestriche, höchstens 64 Zeichen, ohne „claude“ oder „anthropic“. Der Ordnername ist der Skill-Name.
|
- **Name:** kebab-case, nur `a-z`, `0-9` und einzelne Bindestriche, höchstens 64 Zeichen, ohne „claude“ oder „anthropic“. Der Ordnername ist der Skill-Name.
|
||||||
@@ -23,39 +28,88 @@ skills/<name>/
|
|||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
`name` muss dem Ordnernamen entsprechen. `description` ist Pflicht, höchstens 1024 Zeichen, ohne spitze Klammern. Sie entscheidet, wann Claude den Skill benutzt – also beschreiben, *wann* er gebraucht wird, nicht nur *was* er ist. Enthält sie „: “, in Anführungszeichen setzen.
|
`name` muss dem Ordnernamen entsprechen. `description` ist Pflicht, höchstens 1024 Zeichen, ohne spitze Klammern. Sie entscheidet, wann Claude den Skill benutzt – also beschreiben, *wann* er gebraucht wird, nicht nur *was* er ist. Enthält sie „: “, in Anführungszeichen setzen. Dieselbe `description` erscheint als Kurzbeschreibung in der Tabelle der Wurzel-README.
|
||||||
- **Kodierung:** UTF-8 ohne BOM, Zeilenenden LF (regelt `.gitattributes`).
|
- **Kodierung:** UTF-8 ohne BOM, Zeilenenden LF (regelt `.gitattributes`).
|
||||||
- **CHANGELOG.md:** Einträge als `## x.y.z (YYYY-MM-DD)`, neuester oben. Der oberste Eintrag ist die aktuelle Version.
|
- **CHANGELOG.md:** Einträge als `## x.y.z (YYYY-MM-DD)`, neuester oben. Der oberste Eintrag ist die aktuelle Version.
|
||||||
|
|
||||||
## Ablauf
|
## Neuer Skill
|
||||||
|
|
||||||
1. Skill anlegen:
|
1. Ordner aus der Vorlage anlegen:
|
||||||
`scripts/new-skill.sh <name>` bzw. `scripts\new-skill.ps1 -Name <name>`
|
`scripts/new-skill.sh <name>` bzw. `scripts\new-skill.ps1 -Name <name>`
|
||||||
2. `SKILL.md` ausfüllen.
|
2. `SKILL.md` und `README.md` ausfüllen.
|
||||||
3. Prüfen:
|
3. Prüfen:
|
||||||
`scripts/validate.sh <name>` bzw. `scripts\validate.ps1 -Name <name>`
|
`scripts/validate.sh <name>` bzw. `scripts\validate.ps1 -Name <name>`
|
||||||
4. Version im `CHANGELOG.md` des Skills erhöhen und Änderungen beschreiben.
|
4. Tabelle in der Wurzel-README neu erzeugen:
|
||||||
5. Skill-Tabelle in der README aktualisieren:
|
`scripts/update-index.sh` bzw. `scripts\update-index.ps1`
|
||||||
`scripts/update-readme.sh` bzw. `scripts\update-readme.ps1`
|
5. Committen und nach `main` pushen.
|
||||||
6. Committen.
|
6. Tag setzen und pushen. Bestehende Tags nicht verschieben und nicht löschen. Der Push allein legt kein Release an.
|
||||||
7. Tag setzen und pushen – das Release entsteht automatisch:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git tag <name>/v<version>
|
git tag <name>/v<version>
|
||||||
git push origin main <name>/v<version>
|
git push origin main <name>/v<version>
|
||||||
```
|
```
|
||||||
|
|
||||||
Die GitHub Action prüft, dass die Tag-Version zur obersten CHANGELOG-Version passt, baut das ZIP und hängt es an ein Release mit dem CHANGELOG-Abschnitt als Text.
|
7. Release vom eigenen Rechner anlegen (siehe unten). Auf git.gundel.dev läuft kein Actions-Runner.
|
||||||
|
|
||||||
|
## Update eines Skills
|
||||||
|
|
||||||
|
1. `SKILL.md` und `README.md` ändern.
|
||||||
|
2. Im `CHANGELOG.md` einen neuen Eintrag `## x.y.z (YYYY-MM-DD)` oben einfügen.
|
||||||
|
3. Prüfen (`validate`), dann `update-index` ausführen.
|
||||||
|
4. Committen und nach `main` pushen.
|
||||||
|
5. Neuen Tag `<name>/v<version>` setzen und pushen. Der Tag muss exakt zur obersten CHANGELOG-Version passen. Danach das Release vom eigenen Rechner anlegen.
|
||||||
|
|
||||||
## Versionen
|
## Versionen
|
||||||
|
|
||||||
- **Patch** (0.1.0 → 0.1.1): Korrekturen, Tippfehler, präzisere Formulierungen
|
- **Patch** (0.1.0 → 0.1.1): Korrekturen, Tippfehler, präzisere Formulierungen, Doku
|
||||||
- **Minor** (0.1.0 → 0.2.0): neue Abschnitte, neue bekannte Fallen, neue Beispiele
|
- **Minor** (0.1.0 → 0.2.0): neue Abschnitte, neue bekannte Fallen, neue Beispiele
|
||||||
- **Major** (0.x → 1.0.0): geänderte Voraussetzungen oder Arbeitsweise, die bestehende Einrichtungen betreffen
|
- **Major** (0.x → 1.0.0): geänderte Voraussetzungen oder Arbeitsweise, die bestehende Einrichtungen betreffen
|
||||||
|
|
||||||
|
## Release
|
||||||
|
|
||||||
|
Ein Paket pro Skill, Dateiname `dist/<name>-v<version>.zip`. Im ZIP liegt der Ordner `<name>/` mit `SKILL.md` auf oberster Ebene. `README.md` und `CHANGELOG.md` werden nicht eingepackt.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/package.sh # alle Skills
|
||||||
|
scripts/package.sh <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\package.ps1
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\package.ps1 -Name <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vom eigenen Rechner
|
||||||
|
|
||||||
|
Auf git.gundel.dev ist kein Actions-Runner registriert. Das Release legt deshalb ein Skript auf dem Rechner an, der das ZIP schon gebaut hat. Es spricht die Gitea-API an ([Release anlegen](https://docs.gitea.com/enterprise/api/21/operations/repo-create-release/), [Anhang hochladen](https://docs.gitea.com/api/operations/repo-create-release-attachment/)):
|
||||||
|
|
||||||
|
- `POST /api/v1/repos/dg/claude-skills/releases`
|
||||||
|
- `POST /api/v1/repos/dg/claude-skills/releases/{id}/assets`
|
||||||
|
|
||||||
|
Dafür braucht es ein persönliches Zugangs-Token mit Schreibrecht auf dieses Repository (`write:repository`). Anlegen unter Profil → Einstellungen → Applications. Authentifizierung: [`Authorization: token …`](https://docs.gitea.com/development/api-usage/). Das Token nur in die Umgebung legen, nicht ins Repository.
|
||||||
|
|
||||||
|
Der Tag `<name>/v<version>` muss lokal existieren und schon auf dem Server liegen. Das Skript verschiebt keine Tags und pusht nichts.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:GITEA_TOKEN = '<Token>'
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\release.ps1 -Name <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
GITEA_TOKEN='<Token>' scripts/release.sh <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manuell, ohne Token
|
||||||
|
|
||||||
|
1. Paket bauen (Befehle oben). Prüfen, dass `dist/<name>-v<version>.zip` die Einträge `<name>/` und `<name>/SKILL.md` enthält und weder `README.md` noch `CHANGELOG.md`.
|
||||||
|
2. Unter [Releases](https://git.gundel.dev/dg/claude-skills/releases) ein neues Release anlegen.
|
||||||
|
3. Tag: `<name>/v<version>` (der Tag muss schon auf dem Server liegen). Titel: `<name> <version>`.
|
||||||
|
4. Als Text den Abschnitt dieser Version aus `skills/<name>/CHANGELOG.md` einfügen.
|
||||||
|
5. `dist/<name>-v<version>.zip` als Anhang hochladen.
|
||||||
|
|
||||||
## Hinweise zu den Skripten
|
## Hinweise zu den Skripten
|
||||||
|
|
||||||
- Für jedes Skript gibt es eine Bash- (`.sh`) und eine PowerShell-Variante (`.ps1`). Die Bash-Skripte brauchen `zip` und `unzip`, die PowerShell-Skripte laufen ohne Zusatztools unter Windows PowerShell 5.1 und PowerShell 7.
|
- Für jedes Skript gibt es eine Bash- (`.sh`) und eine PowerShell-Variante (`.ps1`). Die Bash-Skripte brauchen `zip` und `unzip`, die PowerShell-Skripte laufen ohne Zusatztools unter Windows PowerShell 5.1 und PowerShell 7. `release.sh` braucht zusätzlich `curl` und `python3`; unter Windows genügt `release.ps1`.
|
||||||
- `.ps1`-Dateien enthalten nur ASCII-Zeichen. Windows PowerShell 5.1 liest UTF-8 ohne BOM als ANSI; Umlaute oder Gedankenstriche können ein Skript dort unbrauchbar machen.
|
- `.ps1`-Dateien enthalten nur ASCII-Zeichen. Windows PowerShell 5.1 liest UTF-8 ohne BOM als ANSI; Umlaute oder Gedankenstriche können ein Skript dort unbrauchbar machen.
|
||||||
- `package.ps1` baut ZIPs mit `System.IO.Compression` statt `Compress-Archive`, weil `Compress-Archive` unter 5.1 Pfade mit `\` schreibt.
|
- `package.ps1` baut ZIPs mit `System.IO.Compression` statt `Compress-Archive`, weil `Compress-Archive` unter 5.1 Pfade mit `\` schreibt.
|
||||||
- Unter Windows bei gesperrter Ausführungsrichtlinie: `powershell -ExecutionPolicy Bypass -File scripts\<skript>.ps1`
|
- Unter Windows bei gesperrter Ausführungsrichtlinie: `powershell -ExecutionPolicy Bypass -File scripts\<skript>.ps1`
|
||||||
@@ -1,84 +1,24 @@
|
|||||||
# claude-skills
|
# claude-skills
|
||||||
|
|
||||||
Sammlung von Skills für Claude – geprüft, versioniert und als ZIP zum Hochladen.
|
Sammlung von Skills für Claude. Jeder Skill ist ein abgeschlossener Ordner: `SKILL.md` für Claude, `README.md` für Menschen. Die fertigen Pakete liegen als ZIP in den [Releases](https://git.gundel.dev/dg/claude-skills/releases) auf [git.gundel.dev/dg/claude-skills](https://git.gundel.dev/dg/claude-skills).
|
||||||
|
|
||||||
[](LICENSE)
|
|
||||||
[](https://git.gundel.dev/dg/claude-skills/releases)
|
|
||||||
|
|
||||||
Ein Skill ist ein Ordner mit einer `SKILL.md`. Sie sagt Claude, wann die Aufgabe ansteht und wie sie zuverlässig erledigt wird. Quelle und Releases: [git.gundel.dev/dg/claude-skills](https://git.gundel.dev/dg/claude-skills).
|
|
||||||
|
|
||||||
| | |
|
|
||||||
|---|---|
|
|
||||||
| **Holen** | ZIP unter [Releases](https://git.gundel.dev/dg/claude-skills/releases) laden |
|
|
||||||
| **Einspielen** | In Claude: Skills → **„+“ → „Create skill“ → „Upload a skill“** |
|
|
||||||
| **Pflegen** | Skill anlegen, prüfen, Tag setzen – das Release entsteht automatisch |
|
|
||||||
|
|
||||||
## Skills
|
## Skills
|
||||||
|
|
||||||
<!-- skills:start -->
|
<!-- skills:start -->
|
||||||
| Skill | Beschreibung | Version |
|
| Skill | Kurzbeschreibung | Version | Doku | Download |
|
||||||
|---|---|---|
|
|---|---|---|---|---|
|
||||||
| [wordcraft-mcp](skills/wordcraft-mcp/) | Word-Dokumente (.docx, .odt, .md, PDF) über den WordCraft-MCP-Server erstellen, bearbeiten und prüfen, inkl. Nachverfolgung – mit erprobten Fallen aus WordCraft 0.8.0; statt wordcraft-dokumente verwenden. | 0.1.0 |
|
| [wordcraft-mcp](skills/wordcraft-mcp/) | Word-Dokumente (.docx, .odt, .md, PDF) über den WordCraft-MCP-Server erstellen, bearbeiten und prüfen, inkl. Nachverfolgung – mit erprobten Fallen aus WordCraft 0.8.0; statt wordcraft-dokumente verwenden. | 0.1.1 | [README](skills/wordcraft-mcp/README.md) | [wordcraft-mcp-v0.1.1.zip](https://git.gundel.dev/dg/claude-skills/releases/tag/wordcraft-mcp/v0.1.1) |
|
||||||
<!-- skills:end -->
|
<!-- skills:end -->
|
||||||
|
|
||||||
## Installation
|
## Skill installieren
|
||||||
|
|
||||||
1. Das ZIP des gewünschten Skills unter [Releases](https://git.gundel.dev/dg/claude-skills/releases) herunterladen – oder selbst bauen (siehe unten).
|
1. In der Tabelle das ZIP des Skills laden.
|
||||||
2. In Claude die Skills öffnen, **„+“ → „Create skill“ → „Upload a skill“** wählen und das ZIP hochladen.
|
2. In Claude **„+“ → „Skill erstellen“ → „Skill hochladen“** wählen und das ZIP hochladen.
|
||||||
|
|
||||||
Die Pakete heißen `<name>-<version>.zip`.
|
|
||||||
|
|
||||||
### Selbst bauen
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Linux / macOS / Git Bash
|
|
||||||
scripts/package.sh # alle Skills
|
|
||||||
scripts/package.sh wordcraft-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Windows PowerShell
|
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\package.ps1
|
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\package.ps1 -Name wordcraft-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Die ZIPs landen in `dist/`.
|
|
||||||
|
|
||||||
## wordcraft-mcp
|
|
||||||
|
|
||||||
Erstellt, bearbeitet und prüft Word-Dokumente über den MCP-Server von WordCraft – inklusive Nachverfolgung von Änderungen und der Fallen, die beim Praxistest mit WordCraft 0.8.0 aufgefallen sind.
|
|
||||||
|
|
||||||
Voraussetzungen:
|
|
||||||
|
|
||||||
- WordCraft mit `wordcraft-cli` ist installiert.
|
|
||||||
- Der MCP-Server ist in der `claude_desktop_config.json` eingetragen, auf Ordner beschränkt:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"wordcraft": {
|
|
||||||
"command": "C:\\Pfad\\zu\\WordCraft\\wordcraft-cli.exe",
|
|
||||||
"args": [
|
|
||||||
"mcp",
|
|
||||||
"--author", "Claude",
|
|
||||||
"--automation-read-root", "C:\\Pfad\\zu\\Dokumente",
|
|
||||||
"--automation-write-root", "C:\\Pfad\\zu\\Dokumente\\claude_out"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Falls der Plugin-Skill `wordcraft-dokumente` installiert ist: deaktivieren, damit Claude `wordcraft-mcp` verwendet.
|
|
||||||
|
|
||||||
## Mitarbeiten
|
## Mitarbeiten
|
||||||
|
|
||||||
Neue Skills anlegen, prüfen und veröffentlichen: siehe [CONTRIBUTING.md](CONTRIBUTING.md).
|
Neue Skills und Updates: [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://git.gundel.dev/dg/claude-skills.git
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
[MIT](LICENSE) · Copyright (c) 2026 Dominic Gundel
|
[MIT](LICENSE)
|
||||||
@@ -18,13 +18,13 @@ if (Test-Path $dir) {
|
|||||||
New-Item -ItemType Directory -Force -Path $SkillsDir | Out-Null
|
New-Item -ItemType Directory -Force -Path $SkillsDir | Out-Null
|
||||||
Copy-Item -Path (Join-Path $Root 'templates/skill') -Destination $dir -Recurse
|
Copy-Item -Path (Join-Path $Root 'templates/skill') -Destination $dir -Recurse
|
||||||
$today = Get-Date -Format 'yyyy-MM-dd'
|
$today = Get-Date -Format 'yyyy-MM-dd'
|
||||||
foreach ($f in @('SKILL.md', 'CHANGELOG.md')) {
|
foreach ($f in @('SKILL.md', 'README.md', 'CHANGELOG.md')) {
|
||||||
$p = Join-Path $dir $f
|
$p = Join-Path $dir $f
|
||||||
$t = (Read-Text $p).Replace('SKILL_NAME', $Name).Replace('DATUM', $today)
|
$t = (Read-Text $p).Replace('SKILL_NAME', $Name).Replace('DATUM', $today)
|
||||||
Write-Text $p $t
|
Write-Text $p $t
|
||||||
}
|
}
|
||||||
|
|
||||||
& (Join-Path $PSScriptRoot 'update-readme.ps1')
|
& (Join-Path $PSScriptRoot 'update-index.ps1')
|
||||||
|
|
||||||
Write-Host "Angelegt: skills/$Name"
|
Write-Host "Angelegt: skills/$Name"
|
||||||
Write-Host "Weiter: SKILL.md ausfuellen, dann scripts\validate.ps1 -Name $Name"
|
Write-Host "Weiter: SKILL.md und README.md ausfuellen, dann scripts\validate.ps1 -Name $Name"
|
||||||
@@ -25,12 +25,12 @@ fi
|
|||||||
mkdir -p "$SKILLS_DIR"
|
mkdir -p "$SKILLS_DIR"
|
||||||
cp -R "$ROOT/templates/skill" "$dir"
|
cp -R "$ROOT/templates/skill" "$dir"
|
||||||
today="$(date +%Y-%m-%d)"
|
today="$(date +%Y-%m-%d)"
|
||||||
for f in "$dir/SKILL.md" "$dir/CHANGELOG.md"; do
|
for f in "$dir/SKILL.md" "$dir/README.md" "$dir/CHANGELOG.md"; do
|
||||||
sed -e "s/SKILL_NAME/$name/g" -e "s/DATUM/$today/g" "$f" > "$f.tmp"
|
sed -e "s/SKILL_NAME/$name/g" -e "s/DATUM/$today/g" "$f" > "$f.tmp"
|
||||||
mv "$f.tmp" "$f"
|
mv "$f.tmp" "$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
bash "$(dirname "$0")/update-readme.sh"
|
bash "$(dirname "$0")/update-index.sh"
|
||||||
|
|
||||||
echo "Angelegt: skills/$name"
|
echo "Angelegt: skills/$name"
|
||||||
echo "Weiter: SKILL.md ausfüllen, dann scripts/validate.sh $name"
|
echo "Weiter: SKILL.md und README.md ausfüllen, dann scripts/validate.sh $name"
|
||||||
+6
-5
@@ -1,5 +1,6 @@
|
|||||||
# Prueft und verpackt alle Skills (oder nur den angegebenen) als dist\<name>-<version>.zip.
|
# Prueft und verpackt alle Skills (oder nur den angegebenen) als dist\<name>-v<version>.zip.
|
||||||
# Im ZIP liegt der Ordner <name>/ auf oberster Ebene, CHANGELOG.md wird nicht mitverpackt.
|
# Im ZIP liegt der Ordner <name>/ auf oberster Ebene.
|
||||||
|
# README.md und CHANGELOG.md werden nicht mitverpackt.
|
||||||
# Pfade im ZIP immer mit "/" (Compress-Archive in Windows PowerShell 5.1 schreibt "\",
|
# Pfade im ZIP immer mit "/" (Compress-Archive in Windows PowerShell 5.1 schreibt "\",
|
||||||
# das bricht den Upload). Deshalb System.IO.Compression direkt.
|
# das bricht den Upload). Deshalb System.IO.Compression direkt.
|
||||||
# Aufruf: scripts\package.ps1 [-Name <skill-name>]
|
# Aufruf: scripts\package.ps1 [-Name <skill-name>]
|
||||||
@@ -14,12 +15,12 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|||||||
|
|
||||||
if ($Name) { $names = @($Name) } else { $names = @(Get-SkillNames) }
|
if ($Name) { $names = @($Name) } else { $names = @(Get-SkillNames) }
|
||||||
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
|
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
|
||||||
$exclude = @('CHANGELOG.md', '.DS_Store', 'Thumbs.db')
|
$exclude = @('CHANGELOG.md', 'README.md', '.DS_Store', 'Thumbs.db')
|
||||||
|
|
||||||
foreach ($n in $names) {
|
foreach ($n in $names) {
|
||||||
$src = (Get-Item (Join-Path $SkillsDir $n)).FullName
|
$src = (Get-Item (Join-Path $SkillsDir $n)).FullName
|
||||||
$version = Get-SkillVersion $src
|
$version = Get-SkillVersion $src
|
||||||
$zipPath = Join-Path $DistDir "$n-$version.zip"
|
$zipPath = Join-Path $DistDir "$n-v$version.zip"
|
||||||
if (Test-Path $zipPath) { Remove-Item $zipPath }
|
if (Test-Path $zipPath) { Remove-Item $zipPath }
|
||||||
|
|
||||||
$files = @(Get-ChildItem -Path $src -Recurse -File -Force |
|
$files = @(Get-ChildItem -Path $src -Recurse -File -Force |
|
||||||
@@ -38,7 +39,7 @@ foreach ($n in $names) {
|
|||||||
finally { $zip.Dispose() }
|
finally { $zip.Dispose() }
|
||||||
|
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host "dist/$n-$version.zip"
|
Write-Host "dist/$n-v$version.zip"
|
||||||
$read = [IO.Compression.ZipFile]::OpenRead($zipPath)
|
$read = [IO.Compression.ZipFile]::OpenRead($zipPath)
|
||||||
try {
|
try {
|
||||||
foreach ($e in $read.Entries) { Write-Host ('{0,9} {1}' -f $e.Length, $e.FullName) }
|
foreach ($e in $read.Entries) { Write-Host ('{0,9} {1}' -f $e.Length, $e.FullName) }
|
||||||
|
|||||||
+6
-5
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Prüft und verpackt alle Skills (oder nur den angegebenen) als dist/<name>-<version>.zip.
|
# Prüft und verpackt alle Skills (oder nur den angegebenen) als dist/<name>-v<version>.zip.
|
||||||
# Im ZIP liegt der Ordner <name>/ auf oberster Ebene, CHANGELOG.md wird nicht mitverpackt.
|
# Im ZIP liegt der Ordner <name>/ auf oberster Ebene.
|
||||||
|
# README.md und CHANGELOG.md werden nicht mitverpackt.
|
||||||
# Aufruf: scripts/package.sh [<skill-name>]
|
# Aufruf: scripts/package.sh [<skill-name>]
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
source "$(dirname "$0")/lib.sh"
|
source "$(dirname "$0")/lib.sh"
|
||||||
@@ -17,17 +18,17 @@ mkdir -p "$DIST_DIR"
|
|||||||
|
|
||||||
for name in "${names[@]}"; do
|
for name in "${names[@]}"; do
|
||||||
version="$(skill_version "$SKILLS_DIR/$name")"
|
version="$(skill_version "$SKILLS_DIR/$name")"
|
||||||
zip_path="$DIST_DIR/$name-$version.zip"
|
zip_path="$DIST_DIR/$name-v$version.zip"
|
||||||
rm -f "$zip_path"
|
rm -f "$zip_path"
|
||||||
|
|
||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
mkdir "$tmp/$name"
|
mkdir "$tmp/$name"
|
||||||
(cd "$SKILLS_DIR/$name" && tar cf - --exclude=CHANGELOG.md --exclude=.DS_Store --exclude=Thumbs.db .) \
|
(cd "$SKILLS_DIR/$name" && tar cf - --exclude=CHANGELOG.md --exclude=README.md --exclude=.DS_Store --exclude=Thumbs.db .) \
|
||||||
| (cd "$tmp/$name" && tar xf -)
|
| (cd "$tmp/$name" && tar xf -)
|
||||||
(cd "$tmp" && zip -qrX "$zip_path" "$name")
|
(cd "$tmp" && zip -qrX "$zip_path" "$name")
|
||||||
rm -rf "$tmp"
|
rm -rf "$tmp"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "dist/$name-$version.zip"
|
echo "dist/$name-v$version.zip"
|
||||||
unzip -l "$zip_path"
|
unzip -l "$zip_path"
|
||||||
done
|
done
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Baut das ZIP eines Skills und legt damit ein Gitea-Release an.
|
||||||
|
# Kein Actions-Runner. Der Tag <name>/v<version> muss lokal und auf dem Server liegen.
|
||||||
|
# Token: Umgebungsvariable GITEA_TOKEN (persoenliches Zugangs-Token, nicht ins Repo).
|
||||||
|
# Aufruf: scripts\release.ps1 -Name <skill-name>
|
||||||
|
param([Parameter(Mandatory = $true)][string]$Name)
|
||||||
|
. (Join-Path $PSScriptRoot 'lib.ps1')
|
||||||
|
|
||||||
|
$Token = $env:GITEA_TOKEN
|
||||||
|
if (-not $Token) {
|
||||||
|
Write-Host 'GITEA_TOKEN fehlt. Persoenliches Zugangs-Token mit Schreibrecht (write:repository), nur in der Umgebung.'
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$dir = Join-Path $SkillsDir $Name
|
||||||
|
if (-not (Test-Path $dir -PathType Container)) {
|
||||||
|
Write-Host "Ordner skills/$Name existiert nicht."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$ver = Get-SkillVersion $dir
|
||||||
|
if (-not $ver) {
|
||||||
|
Write-Host "CHANGELOG von $Name hat keine Version '## x.y.z (YYYY-MM-DD)'."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag = "$Name/v$ver"
|
||||||
|
git -C $Root rev-parse --verify --quiet "refs/tags/$tag" | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "Tag $tag fehlt lokal. Setzen, nicht verschieben: git tag $tag"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
& (Join-Path $PSScriptRoot 'package.ps1') -Name $Name
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
$zipName = "$Name-v$ver.zip"
|
||||||
|
$zipPath = Join-Path $DistDir $zipName
|
||||||
|
if (-not (Test-Path $zipPath)) {
|
||||||
|
Write-Host "ZIP fehlt: dist/$zipName"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$on = $false
|
||||||
|
$acc = New-Object System.Collections.Generic.List[string]
|
||||||
|
foreach ($l in ((Read-Text (Join-Path $dir 'CHANGELOG.md')) -split "`n")) {
|
||||||
|
if (-not $on) {
|
||||||
|
if ($l.StartsWith("## $ver ")) { $on = $true }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ($l.StartsWith('## ')) { break }
|
||||||
|
$acc.Add($l)
|
||||||
|
}
|
||||||
|
$notes = (($acc -join "`n").Trim())
|
||||||
|
|
||||||
|
$api = 'https://git.gundel.dev/api/v1/repos/dg/claude-skills'
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
|
||||||
|
function Invoke-Gitea([string]$Method, [string]$Url, [byte[]]$Body, [string]$ContentType) {
|
||||||
|
$req = [Net.HttpWebRequest]::Create($Url)
|
||||||
|
$req.Method = $Method
|
||||||
|
$req.Headers.Add('Authorization', "token $Token")
|
||||||
|
$req.Accept = 'application/json'
|
||||||
|
if ($null -ne $Body) {
|
||||||
|
$req.ContentType = $ContentType
|
||||||
|
$req.ContentLength = $Body.LongLength
|
||||||
|
$stream = $req.GetRequestStream()
|
||||||
|
$stream.Write($Body, 0, $Body.Length)
|
||||||
|
$stream.Close()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$resp = $req.GetResponse()
|
||||||
|
}
|
||||||
|
catch [System.Net.WebException] {
|
||||||
|
$errResp = $_.Exception.Response
|
||||||
|
$detail = ''
|
||||||
|
if ($errResp) {
|
||||||
|
$reader = New-Object IO.StreamReader($errResp.GetResponseStream())
|
||||||
|
$detail = $reader.ReadToEnd()
|
||||||
|
$reader.Close()
|
||||||
|
}
|
||||||
|
throw "API-Fehler $Url : $detail"
|
||||||
|
}
|
||||||
|
$reader = New-Object IO.StreamReader($resp.GetResponseStream(), $Utf8NoBom)
|
||||||
|
$text = $reader.ReadToEnd()
|
||||||
|
$reader.Close()
|
||||||
|
$resp.Close()
|
||||||
|
return $text
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = @{
|
||||||
|
tag_name = $tag
|
||||||
|
name = "$Name $ver"
|
||||||
|
body = $notes
|
||||||
|
} | ConvertTo-Json -Compress
|
||||||
|
try {
|
||||||
|
$created = Invoke-Gitea 'POST' "$api/releases" $Utf8NoBom.GetBytes($payload) 'application/json; charset=utf-8' | ConvertFrom-Json
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host $_
|
||||||
|
Write-Host "Tag auf dem Server? git push origin $tag"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$assetUrl = "$api/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($zipName))"
|
||||||
|
$asset = Invoke-Gitea 'POST' $assetUrl ([IO.File]::ReadAllBytes($zipPath)) 'application/octet-stream' | ConvertFrom-Json
|
||||||
|
|
||||||
|
if ($created.html_url) { Write-Host $created.html_url }
|
||||||
|
if ($asset.browser_download_url) { Write-Host $asset.browser_download_url }
|
||||||
|
Write-Host "Release $tag angelegt."
|
||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Baut das ZIP eines Skills und legt damit ein Gitea-Release an.
|
||||||
|
# Kein Actions-Runner. Der Tag <name>/v<version> muss lokal und auf dem Server liegen.
|
||||||
|
# Token: Umgebungsvariable GITEA_TOKEN (persönliches Zugangs-Token, nicht ins Repo).
|
||||||
|
# Aufruf: scripts/release.sh <skill-name>
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/lib.sh"
|
||||||
|
|
||||||
|
name="${1:-}"
|
||||||
|
if [[ -z "$name" ]]; then
|
||||||
|
echo "Aufruf: scripts/release.sh <skill-name>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||||
|
echo "GITEA_TOKEN fehlt. Persönliches Zugangs-Token mit Schreibrecht (write:repository), nur in der Umgebung." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! command -v python3 >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "release.sh braucht curl und python3. Unter Windows: scripts\\release.ps1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir="$SKILLS_DIR/$name"
|
||||||
|
version="$(skill_version "$dir")"
|
||||||
|
if [[ -z "$version" ]]; then
|
||||||
|
echo "CHANGELOG von $name hat keine Version '## x.y.z (YYYY-MM-DD)'." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="$name/v$version"
|
||||||
|
if ! git -C "$ROOT" rev-parse --verify --quiet "refs/tags/$tag" >/dev/null; then
|
||||||
|
echo "Tag $tag fehlt lokal. Setzen, nicht verschieben: git tag $tag" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash "$(dirname "$0")/package.sh" "$name"
|
||||||
|
|
||||||
|
zip_name="$name-v$version.zip"
|
||||||
|
zip_path="$DIST_DIR/$zip_name"
|
||||||
|
[[ -f "$zip_path" ]] || { echo "ZIP fehlt: dist/$zip_name" >&2; exit 1; }
|
||||||
|
|
||||||
|
notes="$(mktemp)"
|
||||||
|
payload="$(mktemp)"
|
||||||
|
trap 'rm -f "$notes" "$payload"' EXIT
|
||||||
|
awk -v v="$version" '
|
||||||
|
index($0, "## " v " ") == 1 { on = 1; next }
|
||||||
|
/^## / && on { exit }
|
||||||
|
on { print }' "$dir/CHANGELOG.md" > "$notes"
|
||||||
|
|
||||||
|
TAG="$tag" TITLE="$name $version" NOTES="$notes" python3 -c '
|
||||||
|
import json, os
|
||||||
|
print(json.dumps({
|
||||||
|
"tag_name": os.environ["TAG"],
|
||||||
|
"name": os.environ["TITLE"],
|
||||||
|
"body": open(os.environ["NOTES"], encoding="utf-8").read().strip(),
|
||||||
|
}))' > "$payload"
|
||||||
|
|
||||||
|
api="https://git.gundel.dev/api/v1/repos/dg/claude-skills"
|
||||||
|
resp="$(mktemp)"
|
||||||
|
code="$(curl -sS -o "$resp" -w '%{http_code}' -X POST "$api/releases" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @"$payload")"
|
||||||
|
if [[ "$code" != "201" ]]; then
|
||||||
|
echo "API $code" >&2
|
||||||
|
cat "$resp" >&2
|
||||||
|
echo "Tag auf dem Server? git push origin $tag" >&2
|
||||||
|
rm -f "$resp"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
created="$(cat "$resp")"
|
||||||
|
rm -f "$resp"
|
||||||
|
rel_id="$(CREATED="$created" python3 -c 'import json,os; print(json.loads(os.environ["CREATED"])["id"])')"
|
||||||
|
html="$(CREATED="$created" python3 -c 'import json,os; print(json.loads(os.environ["CREATED"]).get("html_url",""))')"
|
||||||
|
|
||||||
|
asset_resp="$(mktemp)"
|
||||||
|
code="$(curl -sS -o "$asset_resp" -w '%{http_code}' -X POST "$api/releases/$rel_id/assets?name=$zip_name" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"$zip_path")"
|
||||||
|
if [[ "$code" != "201" ]]; then
|
||||||
|
echo "API $code" >&2
|
||||||
|
cat "$asset_resp" >&2
|
||||||
|
rm -f "$asset_resp"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
asset="$(cat "$asset_resp")"
|
||||||
|
rm -f "$asset_resp"
|
||||||
|
download="$(ASSET="$asset" python3 -c 'import json,os; print(json.loads(os.environ["ASSET"]).get("browser_download_url",""))')"
|
||||||
|
|
||||||
|
[[ -n "$html" ]] && echo "$html"
|
||||||
|
[[ -n "$download" ]] && echo "$download"
|
||||||
|
echo "Release $tag angelegt."
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
# Erzeugt die Skill-Tabelle in README.md zwischen <!-- skills:start --> und <!-- skills:end -->
|
# Erzeugt die Skill-Tabelle in README.md zwischen <!-- skills:start --> und <!-- skills:end -->
|
||||||
# neu. Mit -Check wird nur geprueft, ob die Tabelle aktuell ist (Exit-Code 1, wenn nicht).
|
# neu. Mit -Check wird nur geprueft, ob die Tabelle aktuell ist (Exit-Code 1, wenn nicht).
|
||||||
# Aufruf: scripts\update-readme.ps1 [-Check]
|
# Aufruf: scripts\update-index.ps1 [-Check]
|
||||||
param([switch]$Check)
|
param([switch]$Check)
|
||||||
. (Join-Path $PSScriptRoot 'lib.ps1')
|
. (Join-Path $PSScriptRoot 'lib.ps1')
|
||||||
|
|
||||||
|
$RepoWeb = 'https://git.gundel.dev/dg/claude-skills'
|
||||||
$readme = Join-Path $Root 'README.md'
|
$readme = Join-Path $Root 'README.md'
|
||||||
$current = Read-Text $readme
|
$current = Read-Text $readme
|
||||||
$lines = $current -split "`n"
|
$lines = $current -split "`n"
|
||||||
@@ -14,7 +15,7 @@ if ($start -lt 0 -or $end -lt $start) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$table = @('| Skill | Beschreibung | Version |', '|---|---|---|')
|
$table = @('| Skill | Kurzbeschreibung | Version | Doku | Download |', '|---|---|---|---|---|')
|
||||||
foreach ($n in (Get-SkillNames)) {
|
foreach ($n in (Get-SkillNames)) {
|
||||||
$dir = Join-Path $SkillsDir $n
|
$dir = Join-Path $SkillsDir $n
|
||||||
$desc = ''
|
$desc = ''
|
||||||
@@ -27,7 +28,8 @@ foreach ($n in (Get-SkillNames)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$desc = $desc.Replace('|', '\|')
|
$desc = $desc.Replace('|', '\|')
|
||||||
$table += ('| [{0}](skills/{0}/) | {1} | {2} |' -f $n, $desc, (Get-SkillVersion $dir))
|
$ver = Get-SkillVersion $dir
|
||||||
|
$table += ('| [{0}](skills/{0}/) | {1} | {2} | [README](skills/{0}/README.md) | [{0}-v{2}.zip]({3}/releases/tag/{0}/v{2}) |' -f $n, $desc, $ver, $RepoWeb)
|
||||||
}
|
}
|
||||||
|
|
||||||
$newLines = @()
|
$newLines = @()
|
||||||
@@ -38,7 +40,7 @@ $new = $newLines -join "`n"
|
|||||||
|
|
||||||
if ($Check) {
|
if ($Check) {
|
||||||
if ($new -cne $current) {
|
if ($new -cne $current) {
|
||||||
Write-Host 'README.md ist nicht aktuell - scripts\update-readme.ps1 ausfuehren und committen.'
|
Write-Host 'README.md ist nicht aktuell - scripts\update-index.ps1 ausfuehren und committen.'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
Write-Host 'README.md ist aktuell.'
|
Write-Host 'README.md ist aktuell.'
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Erzeugt die Skill-Tabelle in README.md zwischen <!-- skills:start --> und <!-- skills:end -->
|
# Erzeugt die Skill-Tabelle in README.md zwischen <!-- skills:start --> und <!-- skills:end -->
|
||||||
# neu. Mit --check wird nur geprüft, ob die Tabelle aktuell ist (Exit-Code 1, wenn nicht).
|
# neu. Mit --check wird nur geprüft, ob die Tabelle aktuell ist (Exit-Code 1, wenn nicht).
|
||||||
# Aufruf: scripts/update-readme.sh [--check]
|
# Aufruf: scripts/update-index.sh [--check]
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
source "$(dirname "$0")/lib.sh"
|
source "$(dirname "$0")/lib.sh"
|
||||||
|
|
||||||
|
REPO_WEB="https://git.gundel.dev/dg/claude-skills"
|
||||||
readme="$ROOT/README.md"
|
readme="$ROOT/README.md"
|
||||||
check=0
|
check=0
|
||||||
[[ "${1:-}" == "--check" ]] && check=1
|
[[ "${1:-}" == "--check" ]] && check=1
|
||||||
@@ -17,16 +18,18 @@ fi
|
|||||||
table="$(mktemp)"
|
table="$(mktemp)"
|
||||||
trap 'rm -f "$table"' EXIT
|
trap 'rm -f "$table"' EXIT
|
||||||
{
|
{
|
||||||
echo "| Skill | Beschreibung | Version |"
|
echo "| Skill | Kurzbeschreibung | Version | Doku | Download |"
|
||||||
echo "|---|---|---|"
|
echo "|---|---|---|---|---|"
|
||||||
while IFS= read -r name; do
|
while IFS= read -r name; do
|
||||||
[[ -z "$name" ]] && continue
|
[[ -z "$name" ]] && continue
|
||||||
desc=""
|
desc=""
|
||||||
|
ver="$(skill_version "$SKILLS_DIR/$name")"
|
||||||
if fm="$(frontmatter "$SKILLS_DIR/$name/SKILL.md" 2>/dev/null)"; then
|
if fm="$(frontmatter "$SKILLS_DIR/$name/SKILL.md" 2>/dev/null)"; then
|
||||||
desc="$(fm_value "$fm" description || true)"
|
desc="$(fm_value "$fm" description || true)"
|
||||||
fi
|
fi
|
||||||
desc="${desc//|/\\|}"
|
desc="${desc//|/\\|}"
|
||||||
printf '| [%s](skills/%s/) | %s | %s |\n' "$name" "$name" "$desc" "$(skill_version "$SKILLS_DIR/$name")"
|
printf '| [%s](skills/%s/) | %s | %s | [README](skills/%s/README.md) | [%s-v%s.zip](%s/releases/tag/%s/v%s) |\n' \
|
||||||
|
"$name" "$name" "$desc" "$ver" "$name" "$name" "$ver" "$REPO_WEB" "$name" "$ver"
|
||||||
done < <(skill_names)
|
done < <(skill_names)
|
||||||
} > "$table"
|
} > "$table"
|
||||||
|
|
||||||
@@ -37,7 +40,7 @@ new="$(tr -d '\r' < "$readme" | awk -v t="$table" '
|
|||||||
|
|
||||||
if [[ $check -eq 1 ]]; then
|
if [[ $check -eq 1 ]]; then
|
||||||
if [[ "$new" != "$(tr -d '\r' < "$readme")" ]]; then
|
if [[ "$new" != "$(tr -d '\r' < "$readme")" ]]; then
|
||||||
echo "README.md ist nicht aktuell – scripts/update-readme.sh ausführen und committen." >&2
|
echo "README.md ist nicht aktuell – scripts/update-index.sh ausführen und committen." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "README.md ist aktuell."
|
echo "README.md ist aktuell."
|
||||||
@@ -41,6 +41,10 @@ function Test-Skill([string]$N) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $dir 'README.md'))) {
|
||||||
|
$errs += 'README.md fehlt'
|
||||||
|
}
|
||||||
|
|
||||||
if (-not (Test-Path (Join-Path $dir 'CHANGELOG.md'))) {
|
if (-not (Test-Path (Join-Path $dir 'CHANGELOG.md'))) {
|
||||||
$errs += 'CHANGELOG.md fehlt'
|
$errs += 'CHANGELOG.md fehlt'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ validate_skill() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$dir/README.md" ]]; then
|
||||||
|
errs+=("README.md fehlt")
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$dir/CHANGELOG.md" ]]; then
|
if [[ ! -f "$dir/CHANGELOG.md" ]]; then
|
||||||
errs+=("CHANGELOG.md fehlt")
|
errs+=("CHANGELOG.md fehlt")
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Changelog – wordcraft-mcp
|
# Changelog – wordcraft-mcp
|
||||||
|
|
||||||
|
## 0.1.1 (2026-10-19)
|
||||||
|
|
||||||
|
- Doku: eigene README mit Windows-Stolpersteinen; ZIP über Release.
|
||||||
|
|
||||||
## 0.1.0 (2026-10-19)
|
## 0.1.0 (2026-10-19)
|
||||||
|
|
||||||
- Erste Version, getestet mit WordCraft 0.8.0 (headless, `--automation-read-root` / `--automation-write-root`).
|
- Erste Version, getestet mit WordCraft 0.8.0 (headless, `--automation-read-root` / `--automation-write-root`).
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# wordcraft-mcp
|
||||||
|
|
||||||
|
[Alle Skills](../../README.md)
|
||||||
|
|
||||||
|
## Zweck
|
||||||
|
|
||||||
|
Erstellt, bearbeitet und prüft Word-Dokumente über den MCP-Server von WordCraft – inklusive Nachverfolgung von Änderungen und der Fallen, die beim Praxistest mit WordCraft 0.8.0 aufgefallen sind. Die Anweisungen für Claude stehen in [SKILL.md](SKILL.md).
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- WordCraft mit `wordcraft-cli` ist installiert.
|
||||||
|
- Der MCP-Server ist in der `claude_desktop_config.json` eingetragen und über `--automation-read-root` und `--automation-write-root` auf Ordner beschränkt.
|
||||||
|
- Der Ordner aus `--automation-write-root` existiert bereits.
|
||||||
|
- Falls der Plugin-Skill `wordcraft-dokumente` installiert ist: deaktivieren, damit Claude `wordcraft-mcp` verwendet.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Git ist nicht nötig.
|
||||||
|
|
||||||
|
1. Das ZIP [wordcraft-mcp-v0.1.1.zip](https://git.gundel.dev/dg/claude-skills/releases/tag/wordcraft-mcp/v0.1.1) aus dem Release laden.
|
||||||
|
2. In Claude **„+“ → „Skill erstellen“ → „Skill hochladen“** wählen und das ZIP hochladen.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
In die bestehende `claude_desktop_config.json` nur den Block `mcpServers` einfügen. Platzhalter durch die eigenen Pfade ersetzen. Beispiel für den vollen Pfad nach der Installation durch den ArtCraft-Launcher: `C:\\Program Files\\WordCraft\\wordcraft-cli.exe`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"wordcraft": {
|
||||||
|
"command": "C:\\Pfad\\zu\\WordCraft\\wordcraft-cli.exe",
|
||||||
|
"args": [
|
||||||
|
"mcp",
|
||||||
|
"--author", "Claude",
|
||||||
|
"--automation-read-root", "C:\\Pfad\\zu\\Dokumente",
|
||||||
|
"--automation-write-root", "C:\\Pfad\\zu\\Dokumente\\claude_out"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Einrichtung unter Windows – Stolpersteine
|
||||||
|
|
||||||
|
- Der ArtCraft-Launcher installiert nach `C:\Program Files\WordCraft\` (`wordcraft.exe` und `wordcraft-cli.exe`) und trägt nichts in den PATH ein. `Get-Command wordcraft-cli` findet die Datei daher nicht; in die Config den vollen Pfad schreiben. Auffinden mit:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem "$env:ProgramFiles","$env:LOCALAPPDATA" -Recurse -Filter "wordcraft-cli.exe" -ErrorAction SilentlyContinue
|
||||||
|
```
|
||||||
|
|
||||||
|
- Ort der Config:
|
||||||
|
- Claude-Installer: `%APPDATA%\Claude\claude_desktop_config.json`
|
||||||
|
- Microsoft-Store-App: `%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\claude_desktop_config.json` (existiert `%APPDATA%\Claude` nicht, ist es die Store-Version)
|
||||||
|
- Die Config enthält bereits Einstellungen der App. Nur den Block `"mcpServers"` ergänzen, sonst nichts verändern; auf gültiges JSON achten (Kommas, doppelte Backslashes).
|
||||||
|
- Der Ordner aus `--automation-write-root` muss vorher existieren, sonst startet der Server nicht.
|
||||||
|
- Die Claude-App speichert Einstellungen aus ihrem Arbeitsspeicher zurück und löscht dabei frisch eingetragene `mcpServers` (passiert zum Beispiel, wenn man im laufenden Betrieb einen Ordnerzugriff erteilt). Deshalb: eintragen, speichern, **sofort** Claude komplett beenden (auch im Tray) und neu starten, danach den Eintrag kontrollieren.
|
||||||
|
- Test: neuen Chat öffnen und fragen „Sind die WordCraft-Tools verfügbar?“
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
Neuen Chat öffnen und fragen: „Sind die WordCraft-Tools verfügbar?“
|
||||||
|
|
||||||
|
Fehlen die Tools, zuerst den Eintrag in der Config prüfen. Nicht auf ein anderes Programm ausweichen.
|
||||||
|
|
||||||
|
## Deinstallation/Update
|
||||||
|
|
||||||
|
Deinstallation: In Claude den Skill aus der Liste entfernen. Den Block `wordcraft` unter `mcpServers` nur löschen, wenn WordCraft nicht mehr benutzt wird. Andere Einträge in der Config nicht anfassen.
|
||||||
|
|
||||||
|
Update: das ZIP der neuen Version aus dem Release laden und wie bei der Installation hochladen. Liegt die alte Fassung noch in Claude, diese vorher entfernen.
|
||||||
|
|
||||||
|
## Änderungen
|
||||||
|
|
||||||
|
[CHANGELOG.md](CHANGELOG.md)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# SKILL_NAME
|
||||||
|
|
||||||
|
[Alle Skills](../../README.md)
|
||||||
|
|
||||||
|
## Zweck
|
||||||
|
|
||||||
|
Wofür ist dieser Skill da, und was kann Claude damit, was es ohne ihn nicht zuverlässig kann?
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
Was muss installiert, verbunden oder freigegeben sein?
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Git ist nicht nötig.
|
||||||
|
|
||||||
|
1. Das ZIP aus dem Release in der [Übersicht](../../README.md) laden.
|
||||||
|
2. In Claude **„+“ → „Skill erstellen“ → „Skill hochladen“** wählen und das ZIP hochladen.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
Welche Config, Umgebungsvariablen oder Ordner nötig sind. Konkrete Pfade und Beispiele, keine allgemeinen Ratschläge.
|
||||||
|
|
||||||
|
## Stolpersteine
|
||||||
|
|
||||||
|
Was in der Praxis schiefgegangen ist: Symptom und was man stattdessen tut.
|
||||||
|
|
||||||
|
- …
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
Woran man erkennt, dass der Skill und seine Voraussetzungen greifen.
|
||||||
|
|
||||||
|
## Deinstallation/Update
|
||||||
|
|
||||||
|
Wie man den Skill in Claude entfernt und wie eine neue ZIP-Version eingespielt wird.
|
||||||
|
|
||||||
|
## Änderungen
|
||||||
|
|
||||||
|
[CHANGELOG.md](CHANGELOG.md)
|
||||||
Reference in new issue
Block a user