Konzept nachschaerfen: Rueckmeldung statt nur annehmen-oder-wegwerfen

Nach dem Lesen eines Konzepts gab es bisher nur zwei Wege: "Gefaellt mir" oder
archivieren. Der haeufige Fall fehlte komplett — EIN Punkt passt nicht und der
Commander hat einen besseren Vorschlag.

- POST /api/ideen/nachschaerfen -> konzept_ueberarbeiten(): legt eine neue
  Idee-Karte an (kein Bau), verkettet an die Quell-Karte, mit dem Wortlaut der
  Rueckmeldung. Auftrag: bestehendes Konzept lesen, genannte Punkte UND deren
  Folgewirkungen aendern, Rest stehen lassen, kurzer Haertetest nur auf die
  geaenderten Teile. Schafft der Vorschlag ein Problem: einbauen UND die Folge
  sichtbar in die Risiken schreiben — nicht uebergehen, nicht schoenreden.
  Die neue Fassung geht an DIESELBE Stelle (Repo-Commit bzw. Datei + .bak), so
  zeigt "Konzept" ueberall den frischen Stand. Beliebig oft wiederholbar, weil
  die Ueberarbeitung selbst wieder eine Idee-Karte ist.
- UI: Rueckmeldefeld direkt unter dem gelesenen Konzept, ueber dem
  Gefaellt-mir-Weg (der haeufigere Fall gehoert nach oben und offen sichtbar).
- SOUL: neuer Sonderfall UEBERARBEITUNG (Schritt 2 entfaellt, kein zweites Repo).

Zwei Fehler nebenbei gefunden und behoben:
- _wo_liegt(): der Auftrag verwechselte "Repo bekannt" mit "Konzept kommt aus
  dem Repo". Faellt MC2 auf die lokale Datei zurueck, stand vorher "liegt im
  Repo als <lokaler Dateiname>" im Auftrag — eine Datei, die es dort nie gab.
- SOUL-Regel 4: der Push gilt erst als erledigt, wenn `git ls-remote` ihn
  BEWEIST. Ein Worker hat am 21.07. genau hier abgekuerzt, Erfolg gemeldet, und
  das Repo war nachher nicht auffindbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-21 09:05:34 +02:00
parent 94dc3fba9b
commit 0cea01c699
29 changed files with 286 additions and 118 deletions
+14
View File
@@ -93,6 +93,20 @@ def weiterfuehren(body: WeiterIn) -> dict:
return res
class NachschaerfenIn(BaseModel):
id: str
hinweis: str
@router.post("/ideen/nachschaerfen")
def nachschaerfen(body: NachschaerfenIn) -> dict:
"""„Punkt X passt nicht" → Überarbeitungs-Runde am bestehenden Konzept."""
res = ideen.konzept_ueberarbeiten(body.id, body.hinweis)
if not res.get("ok"):
raise HTTPException(400, res.get("error", "Überarbeitung konnte nicht gestartet werden."))
return res
@router.post("/ideen/archivieren")
def archive(body: TaskIn) -> dict:
res = ideen.archive_task(body.id)
+136 -27
View File
@@ -885,6 +885,52 @@ _AUS_IDEE_FUSS = (
"· Uebergabe wie ueblich: Repo-URL nennen, damit der Commander in Zed loslegen kann.")
def _wo_liegt(konz: dict) -> str:
"""Eine Zeile: WO das Konzept wirklich liegt — für den Auftrag an den Worker.
Wichtig ist der Unterschied zwischen „Repo bekannt" und „Konzept KOMMT aus dem Repo":
findet die Repo-Datei nicht statt (Push verpufft, Datei anders benannt), liest MC2 die
lokale Fassung. Wer dann trotzdem „liegt im Repo als <lokaler Dateiname>" in den Auftrag
schreibt, schickt den Worker auf eine Datei, die es dort nie gab.
"""
repo, pfad = konz.get("repo"), konz.get("pfad")
url = konz.get("clone_url") or (f"https://{_GITEA_HOST}/{repo}.git" if repo else "")
if konz.get("quelle") == "repo":
return (f"Das gepruefte Konzept liegt im Gitea-Repo `{repo}` ({url}) "
f"als `{konz.get('datei') or 'KONZEPT.md'}`.")
if repo:
return (f"Das gepruefte Konzept liegt als Datei `{pfad}` auf dieser Box. Zur Idee gehoert "
f"bereits das Gitea-Repo `{repo}` ({url}) — dort ist das Konzept aber NICHT (mehr) "
"abrufbar. Pruefe das Repo, bevor du etwas anlegst.")
return f"Das gepruefte Konzept liegt als Datei `{pfad}` auf dieser Box."
def _konzept_und_name(task_id: str) -> tuple:
"""(Konzept-Daten, kurzer Anzeige-Name, Fehlertext) einer Idee-Karte.
Der Kartentitel ist der ROHE Zuruf des Commanders („Recherchiere zu … Lass ma…") —
als Name unbrauchbar. Die Ueberschrift des Konzepts ist die aufgeraeumte Fassung
derselben Sache; nur wenn es keine gibt, den Zuruf kappen.
"""
konz = konzept_of(task_id)
if not konz.get("konzept"):
return {}, "", (konz.get("error")
or "Zu dieser Idee gibt es noch kein Konzept — erst durchdenken lassen.")
try:
r = _hermes(["show", task_id, "--json"], timeout=30)
d = _parse_json(r.stdout)
except Exception as exc:
return {}, "", f"Karte nicht lesbar: {exc}"
t = (d.get("task") if isinstance(d, dict) and isinstance(d.get("task"), dict)
else d if isinstance(d, dict) else {})
quell_titel = _TITEL_PRAEFIX_RX.sub("", str(t.get("title") or "").strip())
if not quell_titel:
return {}, "", "Quell-Karte hat keinen Titel."
h1 = re.search(r"^#\s+(.+)$", konz["konzept"], re.M)
kurz = h1.group(1).strip() if h1 else re.split(r"(?<=[.!?])\s", quell_titel)[0]
return konz, _kurz(kurz, 85), ""
def projekt_aus_idee(task_id: str, ziel: str = "", tiefe: str = "") -> dict:
"""Aus einer fertig durchdachten Idee eine IDE-Projekt-Karte machen.
@@ -897,39 +943,20 @@ def projekt_aus_idee(task_id: str, ziel: str = "", tiefe: str = "") -> dict:
if not _TASK_ID_RX.match(task_id or ""):
return {"ok": False, "error": f"Keine gültige Aufgaben-Nummer: {task_id!r}"}
konz = konzept_of(task_id)
if not konz.get("konzept"):
return {"ok": False, "error": konz.get("error")
or "Zu dieser Idee gibt es noch kein Konzept — erst durchdenken lassen."}
try:
r = _hermes(["show", task_id, "--json"], timeout=30)
d = _parse_json(r.stdout)
except Exception as exc:
return {"ok": False, "error": f"Karte nicht lesbar: {exc}"}
t = (d.get("task") if isinstance(d, dict) and isinstance(d.get("task"), dict)
else d if isinstance(d, dict) else {})
quell_titel = re.sub(r"^\s*Idee\s*[:\-]\s*", "", str(t.get("title") or "").strip())
if not quell_titel:
return {"ok": False, "error": "Quell-Karte hat keinen Titel."}
# Der Kartentitel ist der ROHE Zuruf des Commanders („Recherchiere zu … Lass ma…") —
# als Projektname unbrauchbar. Die Ueberschrift des Konzepts ist die aufgeraeumte
# Fassung derselben Sache; nur wenn es keine gibt, den Zuruf kappen.
h1 = re.search(r"^#\s+(.+)$", konz["konzept"], re.M)
kurz = h1.group(1).strip() if h1 else re.split(r"(?<=[.!?])\s", quell_titel)[0]
kurz = (kurz[:85].rstrip() + "") if len(kurz) > 86 else kurz
konz, kurz, fehler = _konzept_und_name(task_id)
if fehler:
return {"ok": False, "error": fehler}
titel = f"Projekt: {kurz}"[:200]
if konz.get("repo"):
# Liegt das Konzept schon in einem Gitea-Repo, ist DAS das Projekt-Repo —
# ein zweites anzulegen wuerde den Commander nur mit Dubletten beschenken.
quelle = (f"· Das gepruefte Konzept liegt bereits im Gitea-Repo `{konz['repo']}` "
f"({konz.get('clone_url') or ''}) als `{konz.get('datei') or 'KONZEPT.md'}`. "
"NUTZE GENAU DIESES REPO als Projekt-Repo — lege KEIN neues an (Schritt 2 "
quelle = (f"· {_wo_liegt(konz)}\n"
f"· NUTZE GENAU DIESES REPO als Projekt-Repo — lege KEIN neues an (Schritt 2 "
"entfaellt). Klone es voll, ergaenze die fehlenden Doku-Dateien und pushe auf main.")
else:
quelle = (f"· Das gepruefte Konzept liegt als Datei `{konz.get('pfad')}` auf dieser Box. "
"Lies sie vollstaendig, lege dann wie in Schritt 2 ein frisches Repo an und "
quelle = (f"· {_wo_liegt(konz)}\n"
"· Lies es vollstaendig, lege dann wie in Schritt 2 ein frisches Repo an und "
"uebernimm den Inhalt als `KONZEPT.md`.")
zielwahl = _ZIEL.get(str(ziel).lower())
@@ -955,7 +982,7 @@ def projekt_aus_idee(task_id: str, ziel: str = "", tiefe: str = "") -> dict:
_list_cache["ts"] = 0.0
try:
from services import announce
announce.add(f"Aus der Idee „{_kurz(quell_titel, 60)}“ wird ein Projekt: {task['id']} — die Box "
announce.add(f"Aus der Idee „{_kurz(kurz, 60)}“ wird ein Projekt: {task['id']} — die Box "
"bereitet das Repo vor, gebaut wird in Zed.",
"[Ideen-Queue]", "ideen-queue", "silent")
except Exception:
@@ -963,6 +990,88 @@ def projekt_aus_idee(task_id: str, ziel: str = "", tiefe: str = "") -> dict:
return {"ok": True, "id": task["id"], "titel": titel}
# ── „So nicht" — Konzept mit Rueckmeldung ueberarbeiten lassen (21.07.) ──────
# Vorher gab es nach dem Lesen nur annehmen oder wegwerfen. Ein Konzept wird aber
# selten beim ersten Wurf richtig: der Commander sieht EINEN Punkt, der nicht passt.
# Die Ueberarbeitung ist wieder eine Idee-Karte (kein Bau) und schreibt die neue
# Fassung an DIESELBE Stelle — so zeigt „Konzept" ueberall den frischen Stand.
_UEBERARBEITEN_KOPF = (
"AUFTRAGS-ART: IDEE PRUEFEN — UEBERARBEITUNG. Es gibt bereits ein durchdachtes, "
"gehaertetes Konzept. Der Commander hat es GELESEN und will gezielte Aenderungen. "
"KEIN Projektstart, KEIN Bau, KEIN Geruest.")
_UEBERARBEITEN_FUSS = (
"· Arbeite die Rueckmeldung EIN: lies das bestehende Konzept vollstaendig, aendere die "
"genannten Punkte und alles, was dadurch nicht mehr stimmt (Folgewirkungen!). Denk das "
"Ganze NICHT von vorn — was er nicht angesprochen hat, bleibt.\n"
"· Nimm seinen Vorschlag ernst, aber sei ehrlich: schafft er ein Problem, dann bau ihn ein "
"UND schreib die Folge sichtbar ins Konzept (Abschnitt Risiken/offene Punkte). Setz dich "
"nicht stillschweigend darueber hinweg und rede es auch nicht schoen.\n"
"· Ein KURZER Haertetest (Advocatus Diaboli) NUR auf die geaenderten Teile reicht — die "
"volle Kaskade ist Verschwendung, der Rest ist bereits geprueft.\n"
"· Uebergabe: was du geaendert hast, was daraus folgte und was du bewusst gelassen hast.")
def konzept_ueberarbeiten(task_id: str, hinweis: str) -> dict:
"""Rueckmeldung zum Konzept → neue Idee-Karte, die das Konzept ueberarbeitet.
Haengt als Kind an der Quell-Karte (Kette im Auftragsbuch) und schreibt die neue
Fassung an dieselbe Stelle wie die alte (Repo bzw. Datei).
"""
if not _available():
return {"ok": False, "error": "Die Ideen-Queue lebt auf der Box."}
if not _TASK_ID_RX.match(task_id or ""):
return {"ok": False, "error": f"Keine gültige Aufgaben-Nummer: {task_id!r}"}
hinweis = (hinweis or "").strip()
if not (3 <= len(hinweis) <= 2000):
return {"ok": False, "error": "Bitte schreib zwischen 3 und 2000 Zeichen, was anders werden soll."}
konz, kurz, fehler = _konzept_und_name(task_id)
if fehler:
return {"ok": False, "error": fehler}
if konz.get("quelle") == "repo":
quelle = (f"· {_wo_liegt(konz)}\n"
"· Die NEUE FASSUNG gehoert an dieselbe Stelle: Repo voll klonen, die Datei "
"ueberschreiben, committen und auf main pushen (Push mit `git ls-remote origin main` "
"BEWEISEN, nicht annehmen). KEIN neues Repo anlegen — der Commander vergleicht die "
"Fassungen ueber die Git-Historie.")
else:
quelle = (f"· {_wo_liegt(konz)}\n"
"· Die NEUE FASSUNG gehoert an dieselbe Stelle. Sichere die alte Fassung vorher "
"als `<datei>.bak`, dann schreib die ueberarbeitete Fassung in die Originaldatei."
+ (" Gibt es das genannte Repo wirklich, gehoert die neue Fassung ZUSAETZLICH "
"als `KONZEPT.md` hinein (klonen, schreiben, pushen, Push beweisen)."
if konz.get("repo") else ""))
teile = [f"{_UEBERARBEITEN_KOPF}\n{quelle}\n"
f"· SO SOLL ES ANDERS WERDEN — Wortlaut des Commanders:\n{hinweis}\n"
f"{_UEBERARBEITEN_FUSS}",
_INFRA_LXC]
titel = f"Konzept nachschärfen: {kurz}"[:200]
args = ["create", titel, "--body", "\n\n".join(teile)[:4000], "--assignee", "projektstart",
"--parent", task_id, "--created-by", "mc2-ui", "--json"]
try:
r = _hermes(args)
except Exception as exc:
return {"ok": False, "error": f"Queue nicht erreichbar: {exc}"}
task = _parse_json(r.stdout)
if r.returncode != 0 or not isinstance(task, dict) or not task.get("id"):
return {"ok": False,
"error": (r.stderr or r.stdout or "kanban create fehlgeschlagen").strip()[:300]}
# Die alte Fassung wird ueberschrieben → gecachtes Konzept der Quell-Karte verwerfen,
# sonst zeigt die alte Karte nach der Ueberarbeitung noch tagelang den alten Stand.
_konzept_cache.pop(task_id, None)
_list_cache["ts"] = 0.0
try:
from services import announce
announce.add(f"Konzept „{_kurz(kurz, 60)}“ wird nachgeschärft ({task['id']}): "
f"{_kurz(hinweis, 120)}",
"[Ideen-Queue]", "ideen-queue", "silent")
except Exception:
pass
return {"ok": True, "id": task["id"], "titel": titel}
def log_of(task_id: str) -> dict:
"""Worker-Log einer Idee (hermes kanban log <id>), ANSI entfernt, letzte ~120 Zeilen."""
if not _available():
+18
View File
@@ -81,6 +81,19 @@ DURCHDACHT haben — er hat sich NICHT entschieden, sie zu bauen. Dann gilt abwe
- Schritt 4 (Uebergabe) NICHT als Bau-Aufforderung: sag, was die Idee waere, was sie braeuchte und
was dagegen spricht — und dass die Entscheidung bei ihm liegt. Kein „oeffne in Zed und bau".
## Sonderfall: „UEBERARBEITUNG" (der Commander hat das Konzept gelesen)
Steht im Auftrag **`AUFTRAGS-ART: IDEE PRUEFEN — UEBERARBEITUNG`**, existiert das Konzept schon
und der Commander will gezielte Änderungen. Dann gilt abweichend:
- **Schritt 2 entfällt — lege KEIN neues Repo an.** Der Auftrag nennt die Stelle, an der das
Konzept liegt (Repo oder Datei); die neue Fassung gehört an **genau dieselbe Stelle**. Ein
zweites Repo für dieselbe Idee ist ein Fehlschlag.
- Schritt 1 schrumpft: lies das bestehende Konzept, ändere die genannten Punkte **und deren
Folgewirkungen**, lass den Rest stehen. Ein kurzer Härtetest nur auf die geänderten Teile.
- Sein Vorschlag hat Vorrang, aber nicht blind: schafft er ein Problem, bau ihn ein **und**
schreib die Folge sichtbar in den Abschnitt Risiken/offene Punkte. Nicht stillschweigend
übergehen, nicht schönreden.
- Übergabe: was geändert wurde, was daraus folgte, was du bewusst gelassen hast.
## Eiserne Regeln (nicht verhandelbar)
1. **Du baust KEINEN Code.** Kein Gerüst, keine `package.json`, keine Quelldateien. Nur die
Doku-Dateien aus Schritt 3. Wer Code schreibt, hat die Aufgabe verfehlt.
@@ -89,6 +102,11 @@ DURCHDACHT haben — er hat sich NICHT entschieden, sie zu bauen. Dann gilt abwe
Auftrag ein bestehendes Repo als Vorbild genannt ist, nur LESEN, nie hineinschreiben.)
3. **Jede Rollen-Stimme des Konzepts kommt aus `worker.sh` an heavy** — nie selbst ausgedacht.
4. **Kein `git init`, kein Shallow-Clone.** Immer voll klonen (Credentials in `~/.git-credentials`).
**Der Push gilt erst als erledigt, wenn `git ls-remote origin main` ihn BEWEIST** — ein Exit-Code
0 und „main -> main" auf dem Bildschirm reichen nicht. Ohne diesen Beleg kein `kanban_complete`;
melde stattdessen ehrlich, dass der Push unbestätigt ist. (Am 21.07. hat ein Worker genau hier
abgekürzt, „Push erfolgreich" gemeldet — und das Repo war nachher nicht auffindbar. Der
Commander stand vor einer Übergabe ins Leere.)
5. **TABU-Zonen:** approvals/Tokens/ufw/Security-Configs, `~/.hermes/config.yaml`, systemd-Units.
Braucht der Auftrag so etwas → `kanban_block` mit Begründung.
6. **Festfahren-Stopp:** Bleibt das Konzept nach den Runden mit echten Blockern offen (widersprüchliche
@@ -1,4 +1,4 @@
import{c as A,Y as P,l as B,b as M,u as D,r as z,j as e,Z as T,e as a,$ as g,a0 as b,a1 as W,x as f,a2 as j,W as G,X as I,C as N,g as y,q as i}from"./index-BIxEIVk4.js";/**
import{c as A,Y as P,l as B,b as M,u as D,r as z,j as e,Z as T,e as a,$ as g,a0 as b,a1 as W,x as f,a2 as j,W as G,X as I,C as N,g as y,q as i}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as m,ag as S,r as c,j as e,ah as z,L as g,U as M,ai as C,ad as D,aj as A,ak as B,al as Z,e as E,am as L,u as R,b as q,g as k,q as T}from"./index-BIxEIVk4.js";import{R as j}from"./rotate-ccw-BUzBp8lK.js";/**
import{c as m,ag as S,r as c,j as e,ah as z,L as g,U as M,ai as C,ad as D,aj as A,ak as B,al as Z,e as E,am as L,u as R,b as q,g as k,q as T}from"./index-DZ34eLRy.js";import{R as j}from"./rotate-ccw-DRxIaGHA.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as v,r as c,I,J as A,j as e,K as h,x as B,B as H,D as E,M as z,e as M,L as T,N as F,C as K}from"./index-BIxEIVk4.js";import{F as O}from"./folder-open-BNUNFCZC.js";import{C as R}from"./circle-x-CoImx1bc.js";import{C as W}from"./copy-KeB1XKMX.js";/**
import{c as v,r as c,I,J as A,j as e,K as h,x as B,B as H,D as E,M as z,e as M,L as T,N as F,C as K}from"./index-DZ34eLRy.js";import{F as O}from"./folder-open-Bb3HvkQd.js";import{C as R}from"./circle-x-Cw3-1mKp.js";import{C as W}from"./copy-aWlMWseD.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as h,an as b,r as i,j as e,L as g,ao as f,a2 as j,U as k,e as o,D as N,M as v,N as w,g as y}from"./index-BIxEIVk4.js";import{B as S}from"./book-open-ypkLMas8.js";import{C as z}from"./circle-x-CoImx1bc.js";/**
import{c as h,an as b,r as i,j as e,L as g,ao as f,a2 as j,U as k,e as o,D as N,M as v,N as w,g as y}from"./index-DZ34eLRy.js";import{B as S}from"./book-open-C27V5HRN.js";import{C as z}from"./circle-x-Cw3-1mKp.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as p,j as e,r as M,x as C,a6 as z,a7 as k,a8 as y,a0 as v,W as L,a9 as G,a2 as T,e as f,U as D}from"./index-BIxEIVk4.js";import{B as w}from"./book-open-ypkLMas8.js";import{a as N,L as B,C as P,Z as E}from"./zap-Yspf4acb.js";import{L as R}from"./layers-DKhggjLr.js";import{L as I}from"./lightbulb-DoLJF3FP.js";/**
import{c as p,j as e,r as M,x as C,a6 as z,a7 as k,a8 as y,a0 as v,W as L,a9 as G,a2 as T,e as f,U as D}from"./index-DZ34eLRy.js";import{B as w}from"./book-open-C27V5HRN.js";import{a as N,L as B,C as P,Z as E}from"./zap-BXgwt99q.js";import{L as R}from"./layers-a89ag0RB.js";import{L as I}from"./lightbulb-CUD1a5ov.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1 +1 @@
import{Y as b,u as f,$ as h,r as d,j as e,e as c,Z as g,T as p,a3 as j,a4 as N,g as v,a5 as w,q as x}from"./index-BIxEIVk4.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
import{Y as b,u as f,$ as h,r as d,j as e,e as c,Z as g,T as p,a3 as j,a4 as N,g as v,a5 as w,q as x}from"./index-DZ34eLRy.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
@@ -1,5 +1,5 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/GraphView-WCSGAm2M.js","assets/index-BIxEIVk4.js","assets/index-DvvQb_PK.css"])))=>i.map(i=>d[i]);
var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,configurable:!0,writable:!0,value:c}):d[i]=c;var J=(d,i,c)=>ke(d,typeof i!="symbol"?i+"":i,c);import{c as h,r as s,j as e,u as ve,b as Ne,O as W,Q as Se,C as X,R as Ce,S as Ee,P as Me,U as O,e as x,V as ze,W as De,X as Z,A as Y,_ as Oe,g as j}from"./index-BIxEIVk4.js";import{C as ee}from"./copy-KeB1XKMX.js";import{S as _e}from"./send-Dl36sWcQ.js";import{B as Ge}from"./book-open-ypkLMas8.js";/**
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/GraphView-CfWXNn-Z.js","assets/index-DZ34eLRy.js","assets/index-CYYdYGeg.css"])))=>i.map(i=>d[i]);
var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,configurable:!0,writable:!0,value:c}):d[i]=c;var J=(d,i,c)=>ke(d,typeof i!="symbol"?i+"":i,c);import{c as h,r as s,j as e,u as ve,b as Ne,O as W,Q as Se,C as X,R as Ce,S as Ee,P as Me,U as O,e as x,V as ze,W as De,X as Z,A as Y,_ as Oe,g as j}from"./index-DZ34eLRy.js";import{C as ee}from"./copy-aWlMWseD.js";import{S as _e}from"./send-DrbIxJOY.js";import{B as Ge}from"./book-open-C27V5HRN.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -34,7 +34,7 @@ var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,config
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Pe=h("User",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);class te extends s.Component{constructor(){super(...arguments);J(this,"state",{error:null})}static getDerivedStateFromError(c){return{error:c}}render(){return this.state.error?e.jsxs("div",{className:"h-[480px] rounded-2xl border border-red-500/20 bg-red-500/5 p-6 text-xs text-red-400 overflow-auto",children:[e.jsx("div",{className:"font-semibold mb-2",children:"Graph konnte nicht gerendert werden"}),e.jsx("pre",{className:"whitespace-pre-wrap break-words text-[11px] leading-relaxed",children:this.state.error.message})]}):this.props.children}}const se=s.lazy(()=>Oe(()=>import("./GraphView-WCSGAm2M.js"),__vite__mapDeps([0,1,2])).then(d=>({default:d.GraphView}))),k=["identity","knowledge","rules","events"],v=new Set(["auto","agent","hermes"]),N={identity:{label:"Identität",icon:Pe,bg:"bg-cyan-500/10",text:"text-cyan-400"},knowledge:{label:"Wissen",icon:De,bg:"bg-indigo-500/10",text:"text-indigo-400"},rules:{label:"Regeln",icon:Le,bg:"bg-violet-500/10",text:"text-violet-400"},events:{label:"Ereignisse",icon:ze,bg:"bg-amber-500/10",text:"text-amber-400"}},ne={label:"Gedächtnis",icon:Ge,text:"text-muted-foreground"},Re={identity:"border-l-cyan-500/80",knowledge:"border-l-indigo-500/80",rules:"border-l-violet-500/80",events:"border-l-amber-500/80"},_={identity:"#00f5ff",knowledge:"#3b82f6",rules:"#d946ef",events:"#fbbf24"},ae=`Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu:
*/const Pe=h("User",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);class te extends s.Component{constructor(){super(...arguments);J(this,"state",{error:null})}static getDerivedStateFromError(c){return{error:c}}render(){return this.state.error?e.jsxs("div",{className:"h-[480px] rounded-2xl border border-red-500/20 bg-red-500/5 p-6 text-xs text-red-400 overflow-auto",children:[e.jsx("div",{className:"font-semibold mb-2",children:"Graph konnte nicht gerendert werden"}),e.jsx("pre",{className:"whitespace-pre-wrap break-words text-[11px] leading-relaxed",children:this.state.error.message})]}):this.props.children}}const se=s.lazy(()=>Oe(()=>import("./GraphView-CfWXNn-Z.js"),__vite__mapDeps([0,1,2])).then(d=>({default:d.GraphView}))),k=["identity","knowledge","rules","events"],v=new Set(["auto","agent","hermes"]),N={identity:{label:"Identität",icon:Pe,bg:"bg-cyan-500/10",text:"text-cyan-400"},knowledge:{label:"Wissen",icon:De,bg:"bg-indigo-500/10",text:"text-indigo-400"},rules:{label:"Regeln",icon:Le,bg:"bg-violet-500/10",text:"text-violet-400"},events:{label:"Ereignisse",icon:ze,bg:"bg-amber-500/10",text:"text-amber-400"}},ne={label:"Gedächtnis",icon:Ge,text:"text-muted-foreground"},Re={identity:"border-l-cyan-500/80",knowledge:"border-l-indigo-500/80",rules:"border-l-violet-500/80",events:"border-l-amber-500/80"},_={identity:"#00f5ff",knowledge:"#3b82f6",rules:"#d946ef",events:"#fbbf24"},ae=`Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu:
1) wer ich bin und woran ich gerade arbeite,
2) wie ich angesprochen werden möchte,
3) meine bevorzugten Tools, Sprachen und Arbeitsweise,
@@ -1,4 +1,4 @@
import{c as E,u as oe,a as Fe,b as ge,j as e,f as ue,d as Re,e as w,g as K,q as P,h as Ie,r as N,C as Y,L as Oe,i as Pe,B as le,k as Te,E as Be,l as de,m as Ge,n as $e,o as He,p as ee,X as pe,P as Qe,H as fe,T as re,s as Ue,t as Ve,v as V,w as We,S as ye,x as Ze,y as Je,z as Xe,A as Ye,D as De,F as et,G as tt}from"./index-BIxEIVk4.js";import{C as rt}from"./code-xml-CGKbbFs2.js";import{R as st}from"./rotate-ccw-BUzBp8lK.js";import{C as he,Z as ie,L as nt,a as at}from"./zap-Yspf4acb.js";import{L as ot}from"./layers-DKhggjLr.js";/**
import{c as E,u as oe,a as Fe,b as ge,j as e,f as ue,d as Re,e as w,g as K,q as P,h as Ie,r as N,C as Y,L as Oe,i as Pe,B as le,k as Te,E as Be,l as de,m as Ge,n as $e,o as He,p as ee,X as pe,P as Qe,H as fe,T as re,s as Ue,t as Ve,v as V,w as We,S as ye,x as Ze,y as Je,z as Xe,A as Ye,D as De,F as et,G as tt}from"./index-DZ34eLRy.js";import{C as rt}from"./code-xml-CCSjVAVc.js";import{R as st}from"./rotate-ccw-DRxIaGHA.js";import{C as he,Z as ie,L as nt,a as at}from"./zap-BXgwt99q.js";import{L as ot}from"./layers-a89ag0RB.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{ap as y,r as x,g as L,j as e,L as v,S,ae as W,U as C,e as w,aq as E}from"./index-BIxEIVk4.js";import{F as M}from"./folder-open-BNUNFCZC.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(`
import{ap as y,r as x,g as L,j as e,L as v,S,ae as W,U as C,e as w,aq as E}from"./index-DZ34eLRy.js";import{F as M}from"./folder-open-Bb3HvkQd.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(`
`),d=[];let r=[],u=null;const i=a=>{r.length&&(d.push(e.jsx("ul",{className:"mb-3 ml-4 list-disc space-y-1",children:r},a)),r=[])};return f.forEach((a,p)=>{const t=`l${p}`;if(u!==null){a.trimEnd()==="```"?(d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85",children:u.join(`
`)},t)),u=null):u.push(a);return}if(a.trimStart().startsWith("```")){i(t),u=[];return}const m=a.trimEnd();if(!m.trim()){i(t);return}const g=m.match(/^(#{1,4})\s+(.*)$/);if(g){i(t);const j=g[1].length,s=j===1?"text-lg font-bold mt-1 mb-3":j===2?"text-base font-bold mt-4 mb-2":"text-sm font-bold mt-3 mb-1.5";d.push(e.jsx("p",{className:w(s,"font-space text-foreground"),children:N(g[2],h,l,t)},t));return}const b=m.match(/^\s*[-*•]\s+(.*)$/);if(b){r.push(e.jsx("li",{className:"text-xs leading-relaxed text-foreground/85",children:N(b[1],h,l,t)},t));return}i(t),d.push(e.jsx("p",{className:"mb-2 text-xs leading-relaxed text-foreground/85",children:N(m,h,l,t)},t))}),i("end"),u!==null&&d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]",children:u.join(`
`)},"code-end")),e.jsx("div",{className:"max-w-3xl",children:d})}function N(o,h,l,f){return o.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g).map((r,u)=>{const i=`${f}-${u}`,a=r.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/);if(a){const p=a[1].trim(),t=(a[2]??a[1]).trim();return l.has(p.toLowerCase())?e.jsx("button",{onClick:()=>h(p),className:"rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer",children:t},i):e.jsx("span",{className:"rounded bg-background/40 px-1 text-muted-foreground",title:"Notiz existiert (noch) nicht",children:t},i)}return r.startsWith("**")&&r.endsWith("**")?e.jsx("b",{className:"font-semibold text-foreground",children:r.slice(2,-2)},i):r.startsWith("*")&&r.endsWith("*")&&r.length>2?e.jsx("i",{children:r.slice(1,-1)},i):r.startsWith("`")&&r.endsWith("`")?e.jsx("code",{className:"rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300",children:r.slice(1,-1)},i):e.jsx("span",{children:r},i)})}export{T as WissenView};
@@ -1,4 +1,4 @@
import{c as a}from"./index-BIxEIVk4.js";/**
import{c as a}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c}from"./index-BIxEIVk4.js";/**
import{c}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as e}from"./index-BIxEIVk4.js";/**
import{c as e}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c}from"./index-BIxEIVk4.js";/**
import{c}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-BIxEIVk4.js";/**
import{c as a}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as a}from"./index-BIxEIVk4.js";/**
import{c as a}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as t}from"./index-BIxEIVk4.js";/**
import{c as t}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as t}from"./index-BIxEIVk4.js";/**
import{c as t}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-BIxEIVk4.js";/**
import{c as a}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-BIxEIVk4.js";/**
import{c as a}from"./index-DZ34eLRy.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
+2 -2
View File
@@ -7,8 +7,8 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-BIxEIVk4.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DvvQb_PK.css">
<script type="module" crossorigin src="/assets/index-DZ34eLRy.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CYYdYGeg.css">
</head>
<body>
<div id="root"></div>
+42 -15
View File
@@ -734,22 +734,20 @@ function KonzeptPanel({ id, istIdee, onWeiter }: {
)
}
// „Gefällt mir" — aus der geprüften Idee ein IDE-Projekt machen. Ohne das war die Idee
// eine Sackgasse (Titel neu eintippen, Konzept von vorn denken lassen). Die neue Karte
// bekommt das fertige Konzept als Vorlage und hängt als Kette an der Idee.
// Was nach dem Lesen passieren kann. Zwei Wege, bewusst nebeneinander statt hintereinander:
// nachschärfen (ein Punkt passt nicht — häufiger Fall, darum oben und offen) oder annehmen.
function WeiterLeiste({ id, onFertig }: { id: string; onFertig?: () => void }) {
const [ziel, setZiel] = useState<"lxc" | "docker">("lxc")
const [tiefe, setTiefe] = useState<"simpel" | "gruendlich">("simpel")
const [busy, setBusy] = useState(false)
const [neu, setNeu] = useState<string | null>(null)
const [neu, setNeu] = useState<{ id: string; art: "projekt" | "runde" } | null>(null)
const [fehler, setFehler] = useState("")
const los = async () => {
const [hinweis, setHinweis] = useState("")
const ruf = async (pfad: string, rumpf: object, art: "projekt" | "runde") => {
setBusy(true); setFehler("")
try {
const r = await api<{ id: string }>("/api/ideen/weiterfuehren", {
method: "POST", body: JSON.stringify({ id, ziel, tiefe }),
})
setNeu(r.id)
const r = await api<{ id: string }>(pfad, { method: "POST", body: JSON.stringify(rumpf) })
setNeu({ id: r.id, art })
onFertig?.()
} catch (e: any) {
setFehler(String(e?.message || e))
@@ -760,16 +758,45 @@ function WeiterLeiste({ id, onFertig }: { id: string; onFertig?: () => void }) {
if (neu) {
return (
<p className="mt-3 border-t border-violet-500/20 pt-2 text-[11px] text-emerald-300">
Projekt-Karte <span className="font-mono">{neu}</span> ist angelegt die Box bereitet das
Repo vor (Konzept übernimmt sie, sie denkt es nicht neu). Danach öffnest du es in Zed.
{neu.art === "projekt" ? (
<>Projekt-Karte <span className="font-mono">{neu.id}</span> ist angelegt die Box bereitet
das Repo vor (Konzept übernimmt sie, sie denkt es nicht neu). Danach öffnest du es in Zed.</>
) : (
<>Überarbeitung <span className="font-mono">{neu.id}</span> läuft die Box arbeitet deine
Rückmeldung ein und schreibt die neue Fassung an dieselbe Stelle. Danach hier wieder
Konzept" öffnen.</>
)}
</p>
)
}
return (
<div className="mt-3 space-y-2 border-t border-violet-500/20 pt-2.5">
<div className="mt-3 space-y-3 border-t border-violet-500/20 pt-2.5">
<div className="space-y-1.5">
<p className="text-[11px] text-muted-foreground">
<b className="text-sky-300/90">Passt etwas nicht?</b> Schreib hin, was anders werden soll —
die Box arbeitet es ein und lässt den Rest stehen. Beliebig oft wiederholbar.
</p>
<div className="flex gap-2">
<input
value={hinweis}
onChange={(e) => setHinweis(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter" && hinweis.trim().length >= 3 && !busy) ruf("/api/ideen/nachschaerfen", { id, hinweis }, "runde") }}
placeholder="z. B. Punkt 3: kein WebSocket, das ist mir zu fragil nimm Polling"
maxLength={2000}
className="h-8 min-w-0 flex-1 rounded-lg border border-border/60 bg-background/60 px-2.5 text-xs text-foreground placeholder:text-muted-foreground/50 focus:border-sky-400/50 focus:outline-none"
/>
<button
onClick={() => ruf("/api/ideen/nachschaerfen", { id, hinweis }, "runde")}
disabled={busy || hinweis.trim().length < 3}
className="flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-sky-500/40 bg-sky-500/10 px-3 text-[10px] font-bold uppercase tracking-wide text-sky-300 transition-all hover:bg-sky-500/20 cursor-pointer disabled:opacity-50">
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <RefreshCw className="h-3.5 w-3.5" />}
Nachschärfen
</button>
</div>
</div>
<p className="text-[11px] text-muted-foreground">
Gefällt dir das? Dann macht die Box daraus ein <b className="text-violet-300/90">startklares
Repo</b> Konzept wird übernommen (nicht neu gedacht), dazu Roadmap, AGENTS.md &amp; Co.
<b className="text-violet-300/90">Passt es so?</b> Dann macht die Box daraus ein startklares
Repo — Konzept wird übernommen (nicht neu gedacht), dazu Roadmap, AGENTS.md &amp; Co.
Gebaut wird danach von dir in Zed.
</p>
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
@@ -798,7 +825,7 @@ function WeiterLeiste({ id, onFertig }: { id: string; onFertig?: () => void }) {
))}
</div>
<button
onClick={los}
onClick={() => ruf("/api/ideen/weiterfuehren", { id, ziel, tiefe }, "projekt")}
disabled={busy}
className="ml-auto flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-violet-500/40 bg-violet-500/10 px-3 text-[10px] font-bold uppercase tracking-wide text-violet-300 transition-all hover:bg-violet-500/20 cursor-pointer disabled:opacity-50">
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Code2 className="h-3.5 w-3.5" />}