This commit is contained in:
Team3
2026-06-07 11:29:04 +02:00
parent 6743b1234e
commit 58fb1b6a56
16 changed files with 225 additions and 60 deletions

View File

@@ -15,6 +15,7 @@ TIMEOUTS = {
"recherche": (1800, 0), # fix 30 min
"auswahl": (600, 10),
"auswahl_check": (300, 2),
"ergaenzung": (900, 0), # Themenfeld-Ergänzung bei Projekten (Web-Recherche)
"guide_auswahl": (300, 5), # pro Baustein im Inventar
"guide_check": (300, 2), # Auswahl-/Gliederungs-Prüfung (nur Titellisten)
"plan": (300, 5),

View File

@@ -2,6 +2,7 @@ import asyncio
import json
import math
import shutil
import subprocess
import re
import uuid
from datetime import datetime, timezone
@@ -26,7 +27,7 @@ async def cancel_guide(guide_id: str) -> bool:
_cancelled.add(guide_id)
kill_process(guide_id)
now = datetime.now(timezone.utc).isoformat()
await update_guide(guide_id, status="error", progress=None, error_msg="Abgebrochen", updated_at=now)
await update_guide(guide_id, status="error", progress=None, error_msg="Abgebrochen — Fortschritt bleibt erhalten", updated_at=now)
return True
@@ -201,6 +202,13 @@ BAUSTEINE_STEPS = ("Recherche", "Auswahl", "Prüfung")
_CATEGORIES = ("KERN", "WICHTIG", "REST") # nur noch für den Altformat-Reader
def _bausteine_steps(topic: str) -> tuple:
"""Projekte haben einen 4. Schritt: Themenfeld-Ergänzung per Web-Recherche."""
if project_dir(topic).is_dir():
return BAUSTEINE_STEPS + ("Ergänzung",)
return BAUSTEINE_STEPS
def _bausteine_files(topic: str) -> dict:
arbeit = arbeit_dir(topic)
return {
@@ -209,11 +217,12 @@ def _bausteine_files(topic: str) -> dict:
"recherche": [arbeit / f"recherche-{i}.md" for i in (1, 2, 3, 4)],
"auswahl": [arbeit / f"auswahl-{i}.md" for i in (1, 2)],
"auswahl_check": arbeit / "auswahl-check.json",
"ergaenzung": arbeit / "ergaenzung.json",
}
def _alle_slot_dateien(files: dict) -> list[Path]:
return [*files["recherche"], *files["auswahl"], files["auswahl_check"]]
return [*files["recherche"], *files["auswahl"], files["auswahl_check"], files["ergaenzung"]]
def cancel_bausteine(topic: str) -> bool:
@@ -233,10 +242,13 @@ def _resume_step(topic: str) -> int:
return 1
if not files["auswahl_check"].exists():
return 2
return 3
if project_dir(topic).is_dir() and not files["ergaenzung"].exists():
return 3
return len(_bausteine_steps(topic))
def bausteine_status(topic: str) -> dict:
steps = _bausteine_steps(topic)
ready = bausteine_path(topic).exists()
generating = topic in _bausteine_progress
partial = False
@@ -244,21 +256,21 @@ def bausteine_status(topic: str) -> dict:
current = _bausteine_step.get(topic)
states = [
"pending" if current is None else "done" if i < current else "active" if i == current else "pending"
for i in range(len(BAUSTEINE_STEPS))
for i in range(len(steps))
]
elif ready:
states = ["done"] * len(BAUSTEINE_STEPS)
states = ["done"] * len(steps)
else:
nxt = _resume_step(topic)
partial = nxt > 0
states = ["done" if i < nxt else "pending" for i in range(len(BAUSTEINE_STEPS))]
states = ["done" if i < nxt else "pending" for i in range(len(steps))]
return {
"ready": ready,
"generating": generating,
"progress": _bausteine_progress.get(topic),
"error": _bausteine_errors.get(topic),
"partial": partial,
"steps": [{"label": label, "state": s} for label, s in zip(BAUSTEINE_STEPS, states)],
"steps": [{"label": label, "state": s} for label, s in zip(steps, states)],
}
@@ -273,6 +285,41 @@ def reset_bausteine(topic: str) -> None:
_bausteine_errors.pop(topic, None)
def _ergaenzung_schema(data):
"""{"bausteine": [{"titel", "beschreibung"}]} → Liste (leer erlaubt) · sonst None."""
if not isinstance(data, dict) or not isinstance(data.get("bausteine"), list):
return None
out = []
for b in data["bausteine"]:
if not isinstance(b, dict) or not isinstance(b.get("titel"), str) or not isinstance(b.get("beschreibung"), str):
return None
titel, beschreibung = b["titel"].strip(), b["beschreibung"].strip()
if not titel:
return None
out.append((titel, beschreibung))
return out
def _pdfs_konvertieren(project: Path) -> None:
"""PDFs im Projekt in .txt wandeln (pdftotext) — Agenten lesen Text statt Seiten-Bildern.
Wird vor jeder Projekt-Generierung aufgerufen; konvertiert nur, wenn die
.txt fehlt oder älter als das PDF ist. Das Original bleibt unangetastet.
"""
if shutil.which("pdftotext") is None:
_log(project.name, "pdftotext nicht installiert — PDFs bleiben unkonvertiert")
return
for pdf in project.rglob("*.pdf"):
txt = pdf.with_suffix(".txt")
if txt.exists() and txt.stat().st_mtime >= pdf.stat().st_mtime:
continue
try:
subprocess.run(["pdftotext", "-layout", str(pdf), str(txt)], check=True, timeout=120)
_log(project.name, f"PDF konvertiert: {pdf.name}{txt.name}")
except Exception as e:
_log(project.name, f"PDF-Konvertierung fehlgeschlagen ({pdf.name}): {e}")
def _build_recherche_prompt(topic: str, out_path: Path, instructions: str = "", project: Path | None = None) -> str:
if project:
source = _prompt("Bausteine-Quelle-Projekt", project=project)
@@ -384,6 +431,8 @@ async def generate_bausteine(topic: str, instructions: str = "", provider: str =
try:
async with _semaphore:
files["arbeit"].mkdir(parents=True, exist_ok=True)
if project:
await asyncio.to_thread(_pdfs_konvertieren, project)
# „Neu erstellen": fertige Bausteine → kompletter Frischstart.
# Sonst sind Slot-Dateien Reste eines Abbruchs/Fehlers → Resume.
if final_path.exists():
@@ -486,6 +535,40 @@ async def generate_bausteine(topic: str, instructions: str = "", provider: str =
texts = [t for _, t in sorted(entries.items())] + list(patch["nachtraege"])
entries = {i: t for i, t in enumerate(texts, 1)}
# Schritt 4 (nur Projekte): Themenfeld-Ergänzung — Skript/Projekt ist ein Ausschnitt,
# ein Web-Agent ergänzt kanonisch fehlende Bausteine, markiert mit [Ergänzung].
if project:
set_p("Ergänze Themenfeld…", step=3)
erg_path = files["ergaenzung"]
ergaenzungen = _ergaenzung_schema(_json_datei(erg_path))
if ergaenzungen is None:
erg_path.unlink(missing_ok=True)
slots = [{
"key": f"bausteine-{topic}-ergaenzung-1",
"prompt": _prompt(
"Bausteine-Ergaenzung",
topic=topic, bausteine="\n".join(f"- {t}" for t in entries.values()),
out_path=erg_path, extra=_extra(instructions),
),
"role": "quick", "capabilities": "full",
"payload": (lambda result: _ergaenzung_schema(_json_datei(erg_path))),
}]
res = await _race(topic, "Ergänzung", slots, 1, _timeout("ergaenzung"), provider, cancelled=is_cancelled)
if is_cancelled():
abgebrochen()
return
if res is None:
_bausteine_errors[topic] = "Ergänzung fehlgeschlagen (kein gültiges Ergebnis)"
return
ergaenzungen = res[0]
idx = _titel_index(entries)
neu = [(t, b) for t, b in ergaenzungen if _titel_aufloesen(idx, t) is None]
if neu:
_log(topic, f"Ergänzung: {len(neu)} Baustein(e) aus dem Themenfeld ergänzt")
start = max(entries, default=0) + 1
for off, (t, b) in enumerate(neu):
entries[start + off] = f"{t}{b} [Ergänzung]"
# Titel eindeutig machen und unsortiertes Inventar schreiben
entries = _eindeutige_titel(entries)
final_path.write_text(
@@ -1192,6 +1275,9 @@ async def generate_guide(guide_id: str, topic: str, format_name: str, instructio
if guide_id in _cancelled:
return
if project:
await asyncio.to_thread(_pdfs_konvertieren, project)
# „Neu erstellen": fertiger Guide → kompletter Frischstart.
# Sonst sind Schritt-Dateien Reste eines Abbruchs/Fehlers → Resume.
if content_path.exists():

View File

@@ -169,8 +169,10 @@ async def create(req: GuideCreateRequest):
for g in await list_guides():
if g["topic"] == req.topic.strip() and g["format"] == req.format and g["status"] in ("queued", "generating"):
raise HTTPException(409, "Generierung läuft bereits")
# Lernschulden-Regel: neue Guides nur, wenn das Format weniger als 5 offene hat (erstellt, nicht absolviert)
if req.format != "OnePager" and not guide_content_path(req.topic.strip(), req.format).exists():
# Lernschulden-Regel: neue Guides nur, wenn das Format weniger als 5 offene hat (erstellt, nicht absolviert).
# Resume (Schritt-Dateien vorhanden) ist ausgenommen — der Guide wurde bereits angefangen.
content = guide_content_path(req.topic.strip(), req.format)
if req.format != "OnePager" and not content.exists() and not guide_slot_dateien(content):
stat = (await _formate_stats()).get(req.format, {"erstellt": 0, "absolviert": 0})
offen = stat["erstellt"] - stat["absolviert"]
if offen >= MAX_OFFENE_GUIDES:
@@ -240,16 +242,22 @@ async def cancel(guide_id: str):
@router.delete("/guides/{guide_id}")
async def remove(guide_id: str):
async def remove(guide_id: str, slots: bool = False):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
content = guide_content_path(guide["topic"], guide["format"])
for p in guide_slot_dateien(content):
p.unlink(missing_ok=True)
content.unlink(missing_ok=True)
await delete_progress(guide_id)
await delete_guide(guide_id)
# Content-/Schritt-Dateien teilen sich alle Läufe eines Thema+Formats — erst löschen,
# wenn kein Eintrag sie mehr braucht. Teilfortschritt (Schritt-Dateien ohne fertigen
# Content) bleibt fürs Resume erhalten, außer es wird explizit verlangt (slots=1).
rest = [g for g in await list_guides() if g["topic"] == guide["topic"] and g["format"] == guide["format"]]
if not rest:
content = guide_content_path(guide["topic"], guide["format"])
if slots or content.exists():
for p in guide_slot_dateien(content):
p.unlink(missing_ok=True)
content.unlink(missing_ok=True)
return {"ok": True}

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { fetchGuides, fetchTopics, createTopic as apiCreateTopic, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBausteineStatus, fetchActiveBausteine, createBausteine as apiCreateBausteine, cancelBausteine as apiCancelBausteine, deleteBausteine as apiDeleteBausteine, fetchProjects, deleteProject as apiDeleteProject, fetchProviders, fetchStats } from './api.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'
@@ -118,9 +118,22 @@ async function loadTopics() {
}
}
// Fehlermeldungen verhalten sich wie Flash-Messages: × blendet aus,
// beim Reload sind Alt-Fehler von vornherein ausgeblendet.
const dismissedErrors = ref(new Set())
let errorsInitialized = false
function handleDismissError(guideId) {
dismissedErrors.value = new Set([...dismissedErrors.value, guideId])
}
async function loadGuides() {
try {
guides.value = await fetchGuides()
if (!errorsInitialized) {
errorsInitialized = true
dismissedErrors.value = new Set(guides.value.filter((g) => g.status === 'error').map((g) => g.id))
}
loadStats()
} catch (e) {
console.error('Fehler beim Laden:', e)
@@ -166,10 +179,16 @@ function selectTopic(topic) {
selectedTopic.value = topic
previewGuide.value = null
sidebarSticky.value = false
localStorage.setItem('lastTopic', topic)
loadBausteine()
nextTick(autoPreview)
}
// Beim Reload dort landen, wo man vorher war (Thema + Format)
watch(previewGuide, (g) => {
if (g) localStorage.setItem('lastFormat', g.format)
})
async function createTopic(topic) {
await apiCreateTopic(topic)
await loadTopics()
@@ -223,8 +242,8 @@ function handlePreview(guide) {
previewGuide.value = guide
}
async function handleDeleteGuide(guideId) {
await deleteGuide(guideId)
async function handleDeleteGuide(guideId, slots = false) {
await deleteGuide(guideId, slots)
if (previewGuide.value?.id === guideId) {
previewGuide.value = null
}
@@ -278,7 +297,14 @@ function onVisibility() {
onMounted(async () => {
await Promise.all([loadGuides(), loadTopics(), loadProjects(), loadProviders()])
if (!selectedTopic.value && topics.value.length) {
const savedTopic = localStorage.getItem('lastTopic')
const savedFormat = localStorage.getItem('lastFormat')
if (savedTopic && [...topics.value, ...projectNames.value].includes(savedTopic)) {
selectTopic(savedTopic)
await nextTick()
const g = doneByFormat.value[savedFormat]
if (g) previewGuide.value = g
} else if (!selectedTopic.value && topics.value.length) {
selectTopic(topics.value[0])
}
document.addEventListener('visibilitychange', onVisibility)
@@ -302,6 +328,7 @@ onUnmounted(() => {
:doneByFormat="doneByFormat"
:latestByFormat="latestByFormat"
:allGuides="guides"
:dismissedErrors="dismissedErrors"
:bausteine="bausteine"
:activeBausteine="activeBausteine"
:pinned="sidebarPinned"
@@ -320,6 +347,7 @@ onUnmounted(() => {
@deleteProject="handleDeleteProject"
@cancelGuide="handleCancel"
@deleteGuide="handleDeleteGuide"
@dismissError="handleDismissError"
@preview="handlePreview"
@togglePin="toggleSidebarPin"
@sidebarLeave="onSidebarLeave"

View File

@@ -64,8 +64,8 @@ export async function cancelGuide(id) {
await fetch(`${BASE}/guides/${id}/cancel`, { method: 'POST' })
}
export async function deleteGuide(id) {
await fetch(`${BASE}/guides/${id}`, { method: 'DELETE' })
export async function deleteGuide(id, slots = false) {
await fetch(`${BASE}/guides/${id}${slots ? '?slots=1' : ''}`, { method: 'DELETE' })
}
export async function fetchGuideContent(id) {

View File

@@ -48,6 +48,7 @@ const CH_COLORS = ['#3b82f6', '#8b5cf6', '#14b8a6', '#f59e0b', '#22c55e', '#6366
// --- Inhalt laden ---
const content = ref(null)
const loadError = ref(null)
const doneChapters = ref(new Set())
const scrollEl = ref(null)
@@ -55,6 +56,7 @@ watch(() => props.previewGuide?.id, loadContent, { immediate: true })
async function loadContent() {
content.value = null
loadError.value = null
doneChapters.value = new Set()
const g = props.previewGuide
if (!g || g.status !== 'done') return
@@ -62,6 +64,7 @@ async function loadContent() {
content.value = await fetchGuideContent(g.id)
} catch (e) {
console.error('Fehler beim Laden des Guides:', e)
loadError.value = 'Inhalt nicht verfügbar — die Datei fehlt. Guide neu generieren (▶).'
return
}
try {
@@ -236,7 +239,7 @@ async function send() {
</div>
<div v-else-if="previewGuide" class="empty-preview">
<p>Lade Inhalt</p>
<p>{{ loadError || 'Lade Inhalt…' }}</p>
</div>
<div class="empty-preview" v-else>

View File

@@ -9,6 +9,7 @@ const props = defineProps({
doneByFormat: { type: Object, default: () => ({}) },
latestByFormat: { type: Object, default: () => ({}) },
allGuides: { type: Array, default: () => [] },
dismissedErrors: { type: Object, default: () => new Set() },
bausteine: { type: Object, default: () => ({ ready: false, generating: false, progress: null, error: null }) },
activeBausteine: { type: Array, default: () => [] },
pinned: { type: Boolean, default: true },
@@ -17,7 +18,7 @@ const props = defineProps({
providers: { type: Array, default: () => [] },
})
const emit = defineEmits(['select', 'create', 'formatClick', 'bausteineClick', 'cancelBausteine', 'resetBausteine', 'deleteTopic', 'deleteProject', 'cancelGuide', 'deleteGuide', 'preview', 'togglePin', 'sidebarLeave', 'toggleDark', 'setProvider'])
const emit = defineEmits(['select', 'create', 'formatClick', 'bausteineClick', 'cancelBausteine', 'resetBausteine', 'deleteTopic', 'deleteProject', 'cancelGuide', 'deleteGuide', 'dismissError', 'preview', 'togglePin', 'sidebarLeave', 'toggleDark', 'setProvider'])
function providerAvailable(id) {
const p = props.providers.find((x) => x.id === id)
@@ -104,21 +105,38 @@ function guideStatus(format) {
const GUIDE_STEPS = ['Auswahl', 'Auswahl-Prüfung', 'Gliederung', 'Gliederungs-Prüfung', 'Schreiben', 'Lese-Prüfung']
const ONEPAGER_STEPS = ['Recherche', 'Recherche-Prüfung', 'Bauen', 'Prüfung']
// Kugeln werden wie bei den Bausteinen immer angezeigt:
// fertig = alle grün, laufend = live, abgebrochen = Teilfortschritt, sonst grau
function guideSteps(format) {
const st = guideStatus(format)
if (st !== 'generating' && st !== 'queued') return []
const labels = format === 'OnePager' ? ONEPAGER_STEPS : GUIDE_STEPS
const step = props.latestByFormat[format]?.step ?? -1
return labels.map((label, i) => ({
label,
state: i < step ? 'done' : i === step ? 'active' : 'pending',
}))
const st = guideStatus(format)
if (st === 'generating' || st === 'queued') {
const step = props.latestByFormat[format]?.step ?? -1
return labels.map((label, i) => ({
label,
state: i < step ? 'done' : i === step ? 'active' : 'pending',
}))
}
if (props.doneByFormat[format]) {
return labels.map((label) => ({ label, state: 'done' }))
}
if (abgebrochen(format)) {
const step = props.latestByFormat[format]?.step ?? 0
return labels.map((label, i) => ({ label, state: i < step ? 'done' : 'pending' }))
}
return labels.map((label) => ({ label, state: 'pending' }))
}
function errorMsg(format) {
const latest = props.latestByFormat[format]
if (latest?.status === 'error') return latest.error_msg || 'Fehler bei der Generierung'
return ''
if (latest?.status !== 'error' || props.dismissedErrors.has(latest.id)) return ''
return latest.error_msg || 'Fehler bei der Generierung'
}
// Abgebrochener Lauf = Teilfortschritt vorhanden: ▶ setzt fort, ✕ löscht den Fortschritt
function abgebrochen(format) {
const latest = props.latestByFormat[format]
return latest?.status === 'error' && (latest.error_msg || '').startsWith('Abgebrochen')
}
function handleFormatClick(format) {
@@ -150,11 +168,10 @@ function handlePlay(format) {
emit('formatClick', { format, instructions: '' })
}
// Flash-Message-Verhalten: × blendet nur aus, nichts wird gelöscht
function dismissError(format) {
const latest = props.latestByFormat[format]
if (latest?.status === 'error') {
emit('deleteGuide', latest.id)
}
if (latest?.status === 'error') emit('dismissError', latest.id)
}
function handleDelete(format) {
@@ -297,7 +314,7 @@ function confirmDeleteProject(name) {
<template v-if="guideStatus(f.key) !== 'generating' && guideStatus(f.key) !== 'queued'">
<button
class="action-btn play"
:title="playLock(f.key) || 'Generieren'"
:title="playLock(f.key) || (abgebrochen(f.key) ? 'Fortsetzen' : 'Generieren')"
:disabled="!!playLock(f.key)"
@click="handlePlay(f.key)"
></button>
@@ -306,7 +323,7 @@ function confirmDeleteProject(name) {
</div>
<div v-if="errorMsg(f.key)" class="format-error">
<span class="format-error-text">{{ errorMsg(f.key) }}</span>
<button class="format-error-x" title="Fehler entfernen" @click="dismissError(f.key)">&times;</button>
<button class="format-error-x" title="Ausblenden" @click="dismissError(f.key)">×</button>
</div>
</div>
</div>
@@ -664,6 +681,7 @@ function confirmDeleteProject(name) {
}
.format-x.armed,
.format-error-x.armed,
.delete-topic.armed {
display: inline-block;
font-size: 0.7rem;

View File

@@ -0,0 +1,21 @@
Prüfe das Baustein-Inventar zum Thema "{topic}" auf Vollständigkeit gegenüber dem Themenfeld.
Das Inventar stammt aus einem Projekt/Skript — es kann Bausteine geben, die fachlich zum Thema gehören, dort aber nicht behandelt werden. Finde genau diese Lücken.
VORHANDENE BAUSTEINE:
{bausteine}
Regeln:
- Recherchiere das Themenfeld (Lehrbücher, Standardreferenzen) und ergänze NUR Bausteine, die kanonisch dazugehören und im Inventar fehlen.
- Ein Baustein löst GENAU EIN PROBLEM und ist ATOMAR — gleiche Maßstäbe wie im Inventar.
- KEINE Varianten, Umformulierungen oder Vertiefungen vorhandener Bausteine — nur echte Lücken.
- Erfinde nichts: nur Bausteine, die du in der Recherche belegt hast.
- Titel und Beschreibung auf DEUTSCH (Fachbegriffe bleiben original), Beschreibung maximal ~12 Wörter.
- Gibt es keine Lücken, liefere eine leere Liste — das ist ein gültiges Ergebnis.
Schreibe NUR die JSON-Datei nach: {out_path}
Format:
{{"bausteine": [{{"titel": "…", "beschreibung": "…"}}]}}
Keine Lücken: {{"bausteine": []}}
{extra}

View File

@@ -1 +1 @@
Das Thema ist das Projekt unter {project}. Verschaffe dir mit Bash (ls/find) einen Überblick und lies README, Doku-Ordner und den relevanten Quellcode mit dem Read-Tool. Die Bausteine müssen das echte Projekt widerspiegeln, nichts Erfundenes.
Das Thema ist das Projekt unter {project}. Verschaffe dir mit Bash (ls/find) einen Überblick und lies README, Doku-Ordner und den relevanten Quellcode mit dem Read-Tool. PDFs liegen als gleichnamige .txt-Dateien vor — lies IMMER die .txt, nie das PDF. Die Bausteine müssen das echte Projekt widerspiegeln, nichts Erfundenes.

View File

@@ -1 +1 @@
Die Fakten stammen aus dem Projekt unter {project} — lies bei Bedarf Dateien mit Read/Bash nach.
Die Fakten stammen aus dem Projekt unter {project} — lies bei Bedarf Dateien mit Read/Bash nach. PDFs liegen als gleichnamige .txt-Dateien vor — lies IMMER die .txt, nie das PDF.

View File

@@ -5,12 +5,12 @@ FAKTENBASIS (alleinige Quelle, nichts hinzuerfinden):
Erstelle GENAU diese 7 Karten (JSON-Schlüssel exakt so):
- "info" — Titel: "{topic}". Kurzbeschreibung in 12 Sätzen, darunter technische Daten als Stichpunkte (Art/Typ, Version/Stand, Lizenz/Kosten).
- "eigenschaften" — Titel: "Kerneigenschaften". Die 47 prägenden Eigenschaften des Systems als Stichpunkte.
- "eigenschaften" — Titel: "Kerneigenschaften". Was einen IM Thema erwartet: kleine Übersicht der Inhalte/Teilgebiete.
- "beispiel" — Titel: "Beispiel". EIN anschauliches, typisches Codebeispiel (Markdown-Codeblock) mit einem Satz Erklärung.
- "zusammenhaenge" — Titel: "Zusammenhänge". Mit welchen Systemen/Themen es zusammenhängt — Stichpunkte mit je einem halben Satz.
- "voraussetzungen" — Titel: "Voraussetzungen". Was man vorher können oder haben muss.
- "modern" — Titel: "Moderne Features". Was aktuell ist und heute verwendet wird.
- "veraltet" — Titel: "Veraltete Features". Was es noch gibt, aber nicht mehr verwendet werden sollte. Gibt es nichts Veraltetes: ehrlich "Keine." mit einem Satz Begründung — nichts erfinden.
- "zusammenhaenge" — Titel: "Zusammenhänge". Mit welchen ANDEREN Themen es zusammenhängt — Nachbarthemen außerhalb dieses Themas, keine Inhalte des Themas selbst.
- "voraussetzungen" — Titel: "Voraussetzungen". Welche Themen man vorher bearbeitet haben sollte, um hier klarzukommen.
- "modern" — Titel: "Moderne Features". NUR was in den letzten Jahren neu dazugekommen ist. Gibt es nichts Neues: ehrlich "Keine." mit einem Satz Begründung.
- "veraltet" — Titel: "Veraltete Features". Was nicht mehr verwendet wird. Gibt es nichts Veraltetes: ehrlich "Keine." mit einem Satz Begründung — nichts erfinden.
KOMPAKTHEIT — der OnePager muss OHNE Scrollen auf eine Bildschirmseite passen:
- Maximal 5 Stichpunkte pro Karte, je maximal ~8 Wörter (Schlagwort + halber Satz).

View File

@@ -1 +1 @@
Das Thema ist das Projekt unter {project}. Verschaffe dir mit Bash (ls/find) einen Überblick und lies README, Doku und den relevanten Quellcode mit dem Read-Tool. Erfasse Zweck, Architektur und die wichtigsten Konzepte — nichts Erfundenes.
Das Thema ist das Projekt unter {project}. Verschaffe dir mit Bash (ls/find) einen Überblick und lies README, Doku und den relevanten Quellcode mit dem Read-Tool. PDFs liegen als gleichnamige .txt-Dateien vor — lies IMMER die .txt, nie das PDF. Erfasse Zweck, Architektur und die wichtigsten Konzepte — nichts Erfundenes.

View File

@@ -6,11 +6,11 @@ FAKTENBASIS:
Sie muss diese Dimensionen abdecken:
1. Kurzbeschreibung (Art des Projekts, Gegenstand)
2. Technische Daten (Technologie/Format, Umfang, Stand/Aktualität)
3. Kerneigenschaften (prägende Konzepte, Komponenten oder Inhalte)
3. Inhaltsübersicht (was einen im Projekt erwartet)
4. Ein typisches Beispiel aus dem Projekt
5. Zusammenhänge (Umfeld, Abhängigkeiten, angrenzende Systeme/Themen)
6. Voraussetzungen
7. Moderne vs. veraltete Teile (oder die ausdrückliche Feststellung, dass nichts veraltet ist)
5. Zusammenhänge mit ANDEREN Themen (Nachbarthemen außerhalb des Projektinhalts)
6. Voraussetzungen (vorher zu bearbeitende Themen)
7. Neuerungen der letzten Jahre vs. nicht mehr Verwendetes (oder die ausdrückliche Feststellung, dass es jeweils nichts gibt)
Prüfe:
1. Ist jede Dimension mit konkreten Fakten aus den Projektdateien belegt (Namen, Zahlen — nicht vage)?

View File

@@ -6,11 +6,11 @@ FAKTENBASIS:
Sie muss diese Dimensionen abdecken:
1. Kurzbeschreibung (12 Sätze)
2. Technische Daten (Art/Typ, Version/Stand, Lizenz/Kosten)
3. Kerneigenschaften des Systems
3. Inhaltsübersicht (was einen im Thema erwartet)
4. Ein typisches Beispiel
5. Zusammenhänge mit anderen Systemen/Themen
6. Voraussetzungen
7. Moderne vs. veraltete Features (oder die ausdrückliche Feststellung, dass nichts veraltet ist)
5. Zusammenhänge mit ANDEREN Themen (Nachbarthemen, nicht Inhalte des Themas selbst)
6. Voraussetzungen (vorher zu bearbeitende Themen)
7. Neuerungen der letzten Jahre vs. nicht mehr Verwendetes (oder die ausdrückliche Feststellung, dass es jeweils nichts gibt)
Prüfe:
1. Ist jede Dimension mit konkreten Fakten belegt (Namen, Versionen, Zahlen — nicht vage)?

View File

@@ -5,11 +5,11 @@ Sammle die Faktenbasis für einen OnePager — ein Übersichtsblatt auf einer Se
Erfasse gezielt diese Dimensionen:
1. Kurzbeschreibung: Was ist "{topic}" in 12 Sätzen (Art des Projekts, Gegenstand)?
2. Technische Daten: Technologie/Format, Umfang (Dateien/Seiten/Module), Stand/Aktualität.
3. Kerneigenschaften: die prägenden Konzepte, Komponenten oder Inhalte des Projekts.
3. Inhaltsübersicht: Was erwartet einen — die wichtigsten Inhalte/Teilgebiete des Projekts.
4. Beispiel: ein typisches, konkretes Beispiel aus dem Projekt (zentraler Code-Flow bzw. Kerninhalt).
5. Zusammenhänge: in welchem Umfeld es steht (Abhängigkeiten, angrenzende Systeme/Themen).
6. Voraussetzungen: was man können oder haben muss, um es zu nutzen bzw. zu verstehen.
7. Moderne vs. veraltete Teile: was aktueller Stand ist — und was als Altlast gilt (falls nichts veraltet ist, ausdrücklich notieren).
5. Zusammenhänge: mit welchen ANDEREN Themen es zusammenhängt (Nachbarthemen außerhalb des Projektinhalts).
6. Voraussetzungen: welche Themen man vorher bearbeitet haben sollte, um das Projekt zu verstehen.
7. Neuerungen vs. Veraltetes: was im Themenfeld in den letzten Jahren neu dazugekommen ist — und was nicht mehr verwendet wird (falls es nichts gibt, jeweils ausdrücklich notieren).
Schreibe NUR die Markdown-Datei nach: {out_path}

View File

@@ -5,11 +5,11 @@ Sammle die Faktenbasis für einen OnePager — ein Übersichtsblatt auf einer Se
Recherchiere gezielt diese Dimensionen:
1. Kurzbeschreibung: Was ist "{topic}" in 12 Sätzen?
2. Technische Daten: Art/Typ, aktuelle Version/Stand, Lizenz/Kosten, Verbreitung.
3. Kerneigenschaften: die prägenden Eigenschaften und Merkmale des Systems.
3. Inhaltsübersicht: Was erwartet einen im Thema — die wichtigsten Inhalte/Teilgebiete.
4. Beispiel: ein minimales, typisches Code-/Anwendungsbeispiel.
5. Zusammenhänge: mit welchen Systemen/Themen es zusammenhängt (Ökosystem, Nachbarn, typische Kombinationen).
6. Voraussetzungen: was man vorher können oder haben muss.
7. Moderne vs. veraltete Features: was heute verwendet wird — und was es noch gibt, aber nicht mehr verwendet werden sollte (falls nichts veraltet ist, ausdrücklich notieren).
5. Zusammenhänge: mit welchen ANDEREN Themen es zusammenhängt (Nachbarthemen außerhalb von "{topic}").
6. Voraussetzungen: welche Themen man vorher bearbeitet haben sollte.
7. Neuerungen vs. Veraltetes: was in den letzten Jahren neu dazugekommen ist — und was nicht mehr verwendet wird (falls es nichts gibt, jeweils ausdrücklich notieren).
Schreibe NUR die Markdown-Datei nach: {out_path}