Skip to content

Latest commit

 

History

History
1188 lines (1038 loc) · 91.4 KB

File metadata and controls

1188 lines (1038 loc) · 91.4 KB

MODIFICATIONS

Dziennik odstępstw tego prywatnego forka od upstreamu wavetermdev/waveterm. Cel: każda zmiana w izolowanych plikach, minimalne dotknięcia kodu upstreamu → łatwy rebase. Każdy punkt: co, gdzie, dlaczego.


Pkt 10 — Branche sesji agentów, gęstsza historia, poprawka fałszywych linków

Zestaw zmian w kokpicie agentów zgłoszony po pracy na żywo.

T29 — Branche sesji Claude (fork) w historii + akcja „Branch"

Cel: móc rozgałęzić (fork) sesję do NOWEGO terminala oraz odróżnić brancze w historii. Fork Claude Code = claude --resume <parent> --fork-session --session-id <new> (flaga udokumentowana; zweryfikowane empirycznie, że --session-id jest respektowane razem z --fork-session → nowa sesja <new>.jsonl z polem forkedFrom, rodzic nietknięty). W pełni config-driven (żadnej listy agentów w kodzie): zdolność forka to launch:forkargs w agents.json — ma ją tylko Claude.

Detekcja branchy (nowe pole forkedfrom w podsumowaniu sesji):

  • pkg/agentsession/agentsession_list.gosummarizeTranscriptscanTranscriptHead czyta w JEDNYM przejściu głowy transkryptu i snippet, i forkedFrom.sessionId (parser claudeForkedFrom, tylko dla Claude). Nowe pole SessionSummary.ForkedFrom.
  • pkg/wshrpc/wshrpctypes.goAgentSessionSummary.ForkedFrom; AgentDef.ForkArgs.
  • pkg/wconfig/settingsconfig.goAgentConfigType.ForkArgs (launch:forkargs).
  • pkg/wshrpc/wshserver/wshserver.go — przekazanie ForkArgs (build+toRpc) i ForkedFrom w konwersji.
  • pkg/wconfig/defaultconfig/agents.json"launch:forkargs": ["--fork-session"] dla agent@claude.

Wykonanie forka (jednorazowy marker meta):

  • pkg/agentsession/agentsession.goAgentDef.ForkArgs, CanFork() (wymaga forkargs + createargs dla deterministycznego id), ResolveFork() (kolejność: autonomy + resume(parent) + fork + create(new)).
  • pkg/waveobj/wtypemeta.go — meta agent:forkfrom (parent id, jednorazowy).
  • cmd/wsh/cmd/wshcmd-agentresume.go — gdy agent:forkfrom ustawione i brak agent:sessionid i agent CanFork(): ResolveFork, zapis nowego id na blok, wyczyszczenie agent:forkfrom (restart wznawia fork normalnie, bez ponownego forkowania). Banner „⎇ Branching…".

Frontend:

  • frontend/app/agent/agentsessionsmodel.tsagentCanFork() (czyta launch:forkargs), forkSessionIntoBlock() (nowy blok z agent:forkfrom) — współdzielone.
  • frontend/app/agent/agentsessionspanel.tsx — badge „branch" + ikona przy tytule dla sesji z forkedfrom; ikonowy przycisk Branch (obok Resume) tylko dla agentów agentCanFork.
  • frontend/app/agent/agentsessions.scss — style .ap-branch/.ap-branchmark/.ap-branchdot/.ap-row--branch.
  • frontend/app/view/term/term-model.ts (upstream, minimalnie) — pozycja context-menu „Branch This Session" dla bloku z uruchomionym, forkowalnym agentem.

T30 — Gęstsza lista historii agentów (mniejsza czcionka)

frontend/app/agent/agentsessions.scss — tytuł 12.5→11px, meta 9.5→9px, ciaśniejsze paddingi/gap i kompaktowy przycisk Resume, żeby zmieściło się więcej sesji i więcej tekstu na wiersz (łatwiejsza identyfikacja sesji do wznowienia).

T31 — Poprawka fałszywych linków w terminalu (ratio typu 0k/1000k)

frontend/app/view/term/termlinks.tsisPathLike odrzuca teraz tokeny-ułamki/ratio/daty ze slashem, gdzie KAŻDY segment to liczba z opcjonalną krótką jednostką (NumericRatioRegex): 0k/1000k, 5/10, 50%/100%, 2026/07/03. Wymóg wiodącej cyfry w każdym segmencie chroni realne ścieżki względne (pkg/agentsession). Czysto frontendowe.


Pkt 9 — Okno powitalne (onboarding): pełny rebranding na „Terminal Cloud"

Cel: okno pokazywane przy PIERWSZYM uruchomieniu (NewInstallOnboardingModal, bramka !clientData.tosagreed) to teraz brandowany ekran forka — nie „Welcome to Wave Terminal". Design: greeting/MOTD terminala (sygnatura), spójny z redesignem (mint, ostre krawędzie, mono). Atrybucja Wave w wierszu „built on" spełnia wymóg NOTICE/Apache-2.0.

Nowe (izolowane) pliki:

  • frontend/app/onboarding/terminalcloudgreeting.tsx + terminalcloudgreeting.scss — komponent TerminalCloudGreeting. Titlebar (znak >_ + terminal-cloud + status ● first run), prompt ~ $ welcome z żywym mint kursorem, mono headline „Welcome to Terminal Cloud", tagline, oraz „status readout" kredytów (developed by / sponsored by / built on / contact) i mint Continue. Loga = jedyne plamy koloru.
  • frontend/app/onboarding/statxplorer-logo.png — oryginalne logo StatXplorer (z ich strony, 260×144, czarny wordmark + zielona kropka #0dd14a), na jasnym chipie.
  • Logo CloudBots = inline 3 heksagony (#A9F5D6/#84DCF1/#84F1F1) + wordmark „CLOUDBOTS" w prawdziwym foncie brandu Exo 2 (z C:\Projekty\Strona Firmowa\svgs\cloudbots-logo-*.svg).

Font: frontend/util/fontutil.ts — dobundlowany Exo 2 600 (latin), public/fonts/exo2-latin-600.woff2 (Google Fonts). Mono = JetBrains Mono (już w bundlu).

Dotknięty upstream (minimalnie):

  • frontend/app/onboarding/onboarding.tsxInitPage odchudzony do <TerminalCloudGreeting>; Continue = AgreeTos() (raz) + zamknięcie modala (retired: telemetry/GitHub-star/features flow). Modal dla strony init renderuje sam panel (!p-0 !rounded-none !border-0 !bg-transparent, bez OnboardingGradientBg). Usunięty auto-advance do „features" (żeby preview pokazywał greeting). Nowy eksport showOnboardingGreeting() (ustawia pageNameAtom="init" + modalsModel.newInstallOnboardingOpen=true) — czysto frontendowy re-show, bez ruszania flagi.
  • cloudbotsbranding.tsx — nieużywany (zastąpiony przez greeting); zostaje w repo.

Podgląd (sposób resetu, wybór usera): pozycja „Welcome Screen" w menu koła zębatego (widgets.tsx SettingsFloatingWindow) woła showOnboardingGreeting() — re-show onboardingu nawet po zaakceptowaniu ToS, bez dotykania tosagreed (frontend, repeatowalne).

Wszystko frontendowe → działa po Ctrl+Shift+R (bez restartu backendu).


Pkt 8 — UX agentów/folderów: resolucja cwd, uruchamianie w istniejącym terminalu, autonomia, toggle widgetów

Zestaw poprawek zgłoszonych po pierwszym uruchomieniu dev (foldery + zachowanie agentów/widgetów).

T1 — Foldero-zależne widgety (Git/Files/Agents) startują w folderze WYBRANEGO terminala

Problem: widget „git"/Source Control otwierał się w katalogu procesu wavesrv (C:/Projekty/new Wave), a nie w folderze terminala, w którym pracuje użytkownik — bo resolucja brała tylko getFocusedBlockId(), które przy fokusie na bloku nie-terminalowym (np. panelu Source Control) zwracało zły/pusty cwd. Rozwiązanie: resolucja idzie teraz po historii fokusu do najświehigher terminala.

  • frontend/layout/lib/layoutModel.ts (upstream, minimalnie) — nowy publiczny getFocusedBlockIdStack() mapujący prywatny focusedNodeIdStack (MRU) na blockIds. Klik w pasek widgetów nie rusza tego stosu.
  • frontend/app/store/global.ts — nowy getFocusedBlockIdStack() (wrapper) + resolveDefaultTermCwd przepięte na przejście stosu (pomija bloki nie-terminalowe).
  • frontend/app/agent/agentsessionsmodel.tsresolveAgentCwd() analogicznie przez stos fokusu.

T4 — Klik agenta w terminalu bez agenta uruchamia go w TYM terminalu (nie nowy blok)

Problem: start agenta z przycisku agenta w nagłówku terminala zawsze tworzył drugi terminal. Rozwiązanie: gdy panel jest scope'owany do bloku (ctx.blockId) i blok to wolny lokalny shell bez działającego agenta, uzbrajamy meta bloku i wpisujemy wsh agentresume do istniejącej powłoki (ta sama ścieżka resolucji co initscript).

  • frontend/app/agent/agentsessionsmodel.tscanLaunchAgentInBlock(blockId) (term + shell + brak connection + brak activeAgentAtom).
  • frontend/app/agent/agentsessionspanel.tsxrunAgentInBlock() (SetMeta + sendDataToController), launchNew/launchResume przyjmują targetBlockId; blockId wątkowany przez AgentView/SessionRow.

T5 — Stopień autonomii agenta (config-driven, dropdown + zapamiętany domyślny)

Cel: wybór poziomu uprawnień per agent (np. „Skip permissions" = --dangerously-skip-permissions), z możliwością ustawienia domyślnego. W pełni w agents.json (poziomy + flagi + domyślny).

  • pkg/wconfig/defaultconfig/agents.jsonautonomy:levels ({key,label,args}) + autonomy:default dla claude/codex/cursor (opencode/antigravity: tylko „default" — flagi skip do potwierdzenia).
  • pkg/wconfig/settingsconfig.goAgentConfigType.AutonomyLevels/AutonomyDefault + typ AgentAutonomyLevel.
  • pkg/wshrpc/wshrpctypes.goAgentDef.AutonomyLevels/AutonomyDefault + typ AutonomyLevel.
  • pkg/agentsession/agentsession.goAgentDef.AutonomyLevels/AutonomyDefault, autonomyArgs(), ResolveLaunchWithAutonomy() (flagi autonomii prependowane przed argsy agenta); ResolveLaunch zachowane.
  • pkg/wshrpc/wshserver/wshserver.go — mapowanie autonomii w buildAgentDef/toRpcAgentDef (+ helpery).
  • cmd/wsh/cmd/wshcmd-agentresume.go — flaga --autonomy, odczyt agent:autonomy z meta, ResolveLaunchWithAutonomy, agentDefFromRpc mapuje poziomy.
  • pkg/waveobj/wtypemeta.go — meta agent:autonomy (→ task generate).
  • frontend/app/agent/agentsessionsmodel.tsagentAutonomyLevels() / agentDefaultAutonomy() (z configu).
  • frontend/app/agent/agentsessionspanel.tsx + agentsessions.scss — dropdown „Autonomy" (widoczny gdy >1 poziom), wybór zapamiętany w localStorage per agent = domyślny; wpinany w launch (meta agent:autonomy).

T6 — Przyciski widgetów jako toggle (open / re-click close / Ctrl+Click nowy)

  • frontend/app/workspace/widgets.tsx — mapa widgetOpenBlocks (per tab+widget) + widgetToggleKey; handleWidgetSelect(widget, env, newWindow): powtórny klik zamyka blok (layoutModel.closeNode), Ctrl/Cmd+Click zawsze otwiera nowy. Reszta logiki cwd (files/gitscm) bez zmian.

T2 — Restyle ikon nagłówka bloku (czysty UI)

  • frontend/app/block/block.scss — pod .block-frame-end-icons: gap: 2px + align-items: center (był brak marginesu), przyciski .wave-iconbutton:not(.wave-agent-headerbutton) = jednolite 22×22, wycentrowane, border-radius: 0, hover mint (--accent-color) z wygaszeniem dla .disabled/.no-action; ikona magnify (SVG) też mint na hover. Pill agenta (.wave-agent-headerbutton) i globalny .iconbutton nietknięte.

T3 — Restyle prawego paska widgetów (mniejszy, czysty UI)

  • frontend/app/workspace/widgets.tsx — tylko klasy: py-1.5→py-1, usunięte rounded-sm (ostre krawędzie), ikony text-base→text-sm / supercompact text-sm→text-xs, kontener w-10→w-9, badge DEV 18px→15px. Mirror pomiarowy (measurementRef) zmieniony 1:1, więc tryby normal/compact/supercompact nadal działają. Logika toggle (T6) nietknięta.

T7 — Własne menu kontekstowe (zastąpienie natywnego Electrona)

Natywne menu (Electron Menu.buildFromTemplate) są nieostylowywalne CSS-em → zastąpione własnym komponentem React, żeby pasowały do forka (Inter, ostre krawędzie, jaśniejsza powierzchnia, mint hover).

  • frontend/app/element/wavecontextmenu.tsx (NOWY) — WaveContextMenu + showWaveContextMenu() (baza z preview/preview-contextmenu.tsx): floating-ui, submenu, separatory, checkbox/radio, disabled, sublabel, dismiss (klik poza / Esc / blur / resize / scroll). Role (copy/cut/paste/selectAll/ undo/redo/delete) reimplementowane w performRole (execCommand + clipboard API dla paste), a panele robią onMouseDown → preventDefault, więc pod menu zostaje fokus/zaznaczenie i role działają.
  • frontend/app/element/wavecontextmenu.scss (NOWY) — styl (--ap-*: Inter, #1e2223, mint), max-height+scroll.
  • frontend/app/store/contextmenu.tsshowContextMenu przekierowany na showWaveContextMenu (opts onSelect/onCancel/onClose zachowane). Stary elektronowy path (_convertAndRegisterMenu) zostawiony martwy dla łatwego revertu. Wszystkie menu idą przez jedno wejście → 100% pokrycia (0 wywołań natywnych).
  • frontend/app/app.tsx — montaż <WaveContextMenu /> w AppInner.

T8 — „Split with Agent" w menu bloku

  • frontend/app/view/term/term-model.ts (getSettingsMenuItems) — po Split Horizontally/Vertically dwa submenu „Split Horizontally/Vertically with Agent" listujące agentów (z configuredAgents). Wybór dzieli blok i startuje agenta w nowym panelu (dziedziczy folder resolveTermWorkingDir + zapamiętaną autonomię getStoredAutonomy, launch przez wsh agentresume).
  • frontend/app/agent/agentsessionsmodel.tsgetStoredAutonomy/setStoredAutonomy przeniesione tu (z panelu) jako współdzielone; panel importuje je z modelu.

Korekty autonomii (T5) po weryfikacji flag u źródła (--help realnych CLI)

Flagi sprawdzone empirycznie (claude/codex/cursor/opencode zainstalowane) + docs (antigravity):

  • Claude: poziomy przez --permission-mode {plan|acceptEdits} + --dangerously-skip-permissions.
  • Codex: POPRAWKA — usunięty nieistniejący --full-auto; „auto" = --ask-for-approval never --sandbox workspace-write, „full" = --dangerously-bypass-approvals-and-sandbox.
  • Cursor: --mode plan + --force.
  • OpenCode: dodany --auto (auto-approve).
  • Antigravity: dodany --dangerously-skip-permissions (docs — CLI to agy, nie antigravity; detect:pattern rozszerzony o agy; cmd:bin zostawiony — do potwierdzenia po instalacji).

T9 — Poprawki po testach na żywo (round 2)

  • canLaunchAgentInBlock utwardzone (frontend/app/agent/agentsessionsmodel.ts): odrzuca też bloki z ustawionym agent:type (terminal agenta), niezmontowany model, oraz gdy shell integration ≠ „ready". Naprawia bug: klik „New" wstrzykiwał wsh agentresume do terminala z DZIAŁAJĄCYM agentem (Codex).
  • Menu kontekstowe — submenu portalowane (frontend/app/element/wavecontextmenu.tsx): każdy panel (root + submenu) w osobnym FloatingPortal. overflow-y:auto na panelu z transform (floating-ui) przycinał submenu position:fixed → „Split with Agent" (selektor agenta) było niewidoczne. Dismiss przez closest('.wave-contextmenu') (obejmuje portalowane submenu).
  • Pasek widgetów icon-only (frontend/app/workspace/widgets.tsx): usunięte etykiety (koniec ucinania „ter…"/„sysi…"), pełne nazwy w tooltipach; mirror pomiarowy zsynchronizowany 1:1; toggle (T6) nietknięty.
  • Własny dropdown (frontend/app/element/wavedropdown.tsx + .scss, NOWE): zastępuje natywne <select> w panelu agenta (selektor agenta + autonomii) — mint highlight zamiast systemowego niebieskiego, Inter, ostre krawędzie, size-middleware = szerokość triggera. Portal jako React-descendant opener'a → panel się nie zamyka przy wyborze opcji. Usunięte martwe .ap-select* w agentsessions.scss.
  • Mockup designu: C:/Projekty/new Wave/design-reference.html (poza repo) — pełny wizualny spec forka (mint, ostre krawędzie, Inter) jako referencja do ujednolicania UI.

Poprawki z code review (Codex, read-only)

  • HIGH canLaunchAgentInBlock: reuse tylko gdy shellIntegrationStatusAtom === "ready" (brak integracji / status nieznany → nowy blok; bez integracji activeAgentAtom też bywa pusty).
  • HIGH menu z termlinks (stub-event bez współrzędnych) trafiało na (0,0) → contextmenu.ts śledzi ostatnią pozycję kursora (pointermove) i używa jej jako fallback (ev.clientX ?? lastPointer.x).
  • MED role-only items ({role:"copy"} w AI panelu) renderowały pusty wiersz → itemLabel() humanizuje rolę (Copy/Paste/…) dla etykiety i data-context-menu-item.
  • MED scroll wewnątrz długiego menu zamykał je → scroll-listener ignoruje target w .wave-contextmenu.
  • MED contextmenu.test.ts przepisany pod nową ścieżkę (mock wavecontextmenu, cykl onClose) — 3/3 zielone.
  • LOW prawy-klik wewnątrz menu nie propaguje do app-level onContextMenu (nie otwiera drugiego menu).
  • Odłożone (LOW): pełna nawigacja klawiaturą w menu (strzałki/Enter/typeahead) — obecnie tylko Esc.
  • Codex potwierdził brak aliasing-bugu w ResolveLaunchWithAutonomy (Go).

T10 — Reuse terminala oparty na REALNYM procesie (nie na statusie shell-integration) + UI paska

Problem: decyzja „uruchom agenta w tym terminalu vs nowe okno" (T4/T9) opierała się na shellIntegrationStatusAtom === "ready" (OSC-driven). Gdy integracja powłoki nie jest zainstalowana / status nieznany, warunek nigdy nie był spełniony → agent ZAWSZE otwierał się w nowym oknie („nie działa"). Rozwiązanie: reuse decyduje się teraz po tym, czy w powłoce bloku realnie działa proces pierwszoplanowy (dowolny — agent, edytor, build), sprawdzany po drzewie procesów-dzieci powłoki na backendzie.

  • pkg/shellexec/shellpid.go (NOWY) — (*ShellProc).Pid() (pid lokalnej powłoki CmdWrap; 0 dla ssh).
  • pkg/blockcontroller/foregroundproc.go (NOWY) — GetBlockForegroundRunning(blockId): getController*ShellControllerGetShellPid()gopsutil/process.Children(); ignoruje helpery ConPTY (conhost.exe/OpenConsole.exe). Zwraca (running, childName).
  • pkg/wshrpc/wshrpctypes.go + wshserver/wshserver.go — nowy RPC BlockForegroundStatusCommand(blockId)BlockForegroundStatusData{running, childname} (+ task generate).
  • frontend/app/agent/agentsessionsmodel.tscanLaunchAgentInBlock async: bramka strukturalna (isReusableShellBlock: term + shell + brak connection) + zapytanie do RPC; usunięta zależność od activeAgentAtom/shellIntegrationStatusAtom. Świadomie NIE odrzucamy bloku z ustawionym agent:type — terminal, w którym agent padł/wyszedł (np. antigravity „executable not found"), stoi bezczynnie na prompcie i ma być reużywalny; o zajętości decyduje wyłącznie realny proces.
  • frontend/app/agent/agentsessionspanel.tsxlaunchNew/launchResume async (await canLaunch…), wywołania owinięte w fireAndForget.

T11 — UI paska widgetów: tło przycisków + toggle Agents

  • frontend/app/workspace/widgets.tsx — każdy przycisk widgetu ma subtelne tło (bg-white/[0.05]) + odstęp (mb-0.5) → segmentowany, wyróżniony wygląd (spójny z radius-0). AgentWidget toggle: ponowny klik zamyka panel (via isOpenAtom), aktywny stan podświetlony (bg-hoverbg).

T12 — UX startu agenta: submit CR, baner „Launching", ukrycie echa komendy

Problem: przy reuse injekcja wysyłała \n → PowerShell utykał na >> (PSReadLine: \n=Ctrl+J=nowa linia, \r=Enter). Dodatkowo widoczne było surowe wsh agentresume, a nowy blok agenta nie pokazywał żadnej informacji.

  • frontend/app/agent/agentsessionspanel.tsx (runAgentInBlock) + frontend/app/view/term/term-model.ts („Resume agent here"): submit przez \r (nie \n) + flaga --clearcmd.
  • cmd/wsh/cmd/wshcmd-agentresume.gozawsze baner z etykietą agenta: „▶ Launching …" (świeży) / „⟳ Resuming …" (wznowienie), z def.Label zamiast surowego typu. Odpala się w OBU ścieżkach (nowy blok przez initscript + reuse). Ukryta flaga --clearcmd: czyści echo wpisanej komendy (\x1b[1A\x1b[2K\r) przy injekcji w interaktywny prompt (zakłada 1-liniowy prompt).

T13 — Nie zamykaj aplikacji po usunięciu wszystkich terminali (pusty canvas / launcher)

Problem: zamknięcie ostatniego bloku → DeleteBlock kasuje pusty tab → DeleteTab przy ostatnim tabie wołał CloseWindowwindow-all-closed → quit aplikacji.

  • pkg/wcore/workspace.go (DeleteTab) — gdy nie zostaje żaden tab, zamiast CloseWindow tworzy świeży tab (CreateTab) i zwraca jego id. WorkspaceService.CloseTab widzi newActiveTabId != ""CloseWindow=false, electron przełącza na nowy tab. Pokrywa obie ścieżki (zamknięcie bloku i taba); okno zostaje otwarte, aplikacja nie znika.
  • Aktualizacja (runda 2 / T20): zastępczy tab seeduje teraz launcher (ekran startowy / widget picker), a nie auto-terminal. CreateTab(..., isInitialLaunch=true) pomija domyślny layout terminala, po czym ApplyPortableLayout(newTabId, GetEmptyTabLayout(), true) wstawia blok view:"launcher". Nowy GetEmptyTabLayout() w pkg/wcore/layout.go. Odpowiedź na „czy nie da się aby nie było terminala" — po zamknięciu wszystkiego zostaje ekran startowy, nie kolejna powłoka.
  • Hardening kolejności (fix z review 2026-07): zastępczy tab powstaje przed utrwaleniem usunięcia ostatniego taba. CreateTab re-czyta i re-zapisuje workspace, więc po jego sukcesie DeleteTab przeładowuje ws z DB i wycina stary tab; gdy CreateTab padnie, zwraca błąd z workspace nadal wskazującym stary (już bezblokowy) tab — zamiast utrwalonego stanu zero-tab (okno bez niczego). Wcześniej DBUpdate(ws) z pustymi TabIds/ActiveTabId="" szedł przed CreateTab.

Szlify UI — runda 2 (T14–T21)

T14 — Domyślna strona przeglądarki = StatXplorer

  • pkg/wconfig/defaultconfig/settings.jsonweb:defaulturl z repo Wave → https://statxplorer.com (spójne ze sponsorem w stopce launchera). Default embedowany (go:embed), scalany pod override użytkownika → wymaga rebuildu wavesrv, nie nadpisuje konfiguracji usera.

T15 — Kursor terminala w kolorze mint (domyślnie)

  • pkg/wconfig/defaultconfig/termthemes.json — motyw default-dark: "cursor": """#00ced1" (mint = akcent forka). Wszystkie terminale na default-dark mają teraz mint cursor. Styl kursora (block/bar/underline) bez zmian — to tylko kolor.

T16 — Ostre ikony agentów w headerze terminala

  • frontend/app/block/block.scss.wave-agent-headerbutton border-radius: 5px0.
  • frontend/app/agent/agentheaderbutton.tsx — monogram fallbacku rounded-[4px]rounded-none. (Logo brandów Claude/Codex/Cursor to znaki firmowe — bez zmian.)

T17 — Kompaktowe menu kontekstowe (in-app WaveContextMenu)

  • frontend/app/element/wavecontextmenu.scssmin-width 190→168, font-size 12→11.5, item min-height 27→23 + ciaśniejszy padding, separatory margin 4→3. Menu (Image #9/#14) mniejsze.

T18 — Podgląd kierunku splitu jako ikonka w menu

  • frontend/types/custom.d.tsContextMenuItem + opcjonalne icon?: React.ReactNode (renderuje tylko in-app WaveContextMenu; natywna ścieżka electrona je ignoruje).
  • frontend/app/element/wavecontextmenu.tsx + .scss — slot .wave-contextmenu-icon (dziedziczy kolor hover, opacity 0.55).
  • frontend/app/view/term/term-model.ts — helper splitPreviewIcon(dir) rysuje 2-komórkowy diagram (obok siebie = horizontal, jeden nad drugim = vertical) i podpina go do „Split Horizontally/Vertically" oraz obu „…with Agent".

T19 — „•••" (menu aplikacji) → czytelny hamburger + tooltip

  • frontend/app/tab/tabbar.tsx — przycisk App Menu (Windows, showWorkspaceAppMenu) z fa-ellipsis (26px, niejasne) → Tooltip content="Menu" + fa-bars (18px). divRef/divStyle/divOnClick zachowują pomiar szerokości, WebkitAppRegion:no-drag i akcję.

T21 — Panel sesji agenta: paginacja + stabilna wysokość

  • frontend/app/agent/agentsessionspanel.tsx — render kapowany do AgentPageSize=20; search/sort nadal po całym zbiorze (wyszukiwanie widzi wszystkie sesje), przycisk „Show N more · X/Y" dokłada kolejne 20; showCount resetuje się przy zmianie query/agenta/trybu.
  • frontend/app/agent/agentsessions.scss — stany .ap-loading/.ap-empty mają min-height:180px (loading nie zwija popovera → floating-ui nie przerzuca placementu → kontrolki nad listą nie skaczą); styl .ap-more.

Szlify UI — runda 3 (T22, po feedbacku + konsultacji UI z Codexem)

T22 — Poprawki spójności/pozycji + rekomendacje Codexa

  • Ikony splitu (poprawka T18): teraz po prawej od tekstu (.wave-contextmenu-icon margin-left:auto, render po labels w wavecontextmenu.tsx) i tylko kontursplitPreviewIcon rysuje ramkę + jedną linię podziału (bez wypełnienia / „kropkowanego" tła).
  • Agent na górze paska widgetów: frontend/app/workspace/widgets.tsx<AgentWidget> przeniesiony przed widgets.map w obu trybach (normal + supercompact). Wcześniej był na dole listy.
  • Ikona workspace: frontend/app/tab/workspaceswitcher.tsx — domyślna (niezapisany workspace) z WorkspaceSVG (2×2 kratka) → fa-sharp fa-solid fa-layer-group; usunięty nieużywany import SVG.
  • Hamburger wyżej: tabbar.tsx — glif fa-bars z relative top-[-2px] (był odrobinę za nisko).
  • Mniejsze przyciski headera bloku: block.scss .block-frame-end-icons .wave-iconbutton 22→20px
    • font-size:13px; gap 2→4px (oddech).
  • Spójność czcionek: agentsessions.scss --ap-mono JetBrains Mono → Hack (jedyny mono w forku = Hack, jak reszta appki). Główne źródło „różnych czcionek".
  • Lżejsza typografia nagłówków (Codex #4): theme.scss --header-font 700 11px600 11px.
  • Restraint akcentu na badge agenta (Codex #1/#8): block.scss .wave-agent-branded tło 0.14→0.10, border 0.4→0.35, hover 0.24→0.20.
  • Wyrównanie hamburgera z ikoną workspace: .tab-bar-wrapper ma align-items:end + workspace ma margin-bottom:4px, więc hamburger (bez struktury wysokości) siedział niżej. tabbar.tsx — kontener hamburgera dostał h-[22px] mb-1 + glif 16px (bez ad-hoc nudge) → identyczny środek pionowy jak workspace.
  • Wskaźniki aktywności (Codex #6/#7, wybór usera): tab.scss aktywny tab = mint górna linia (box-shadow: inset 0 2px 0 var(--accent-color), bez przesunięcia layoutu); widgets.tsx otwarty widget Agent = mint lewa linia (shadow-[inset_2px_0_0_0_#00ced1]). (Zwykłe widgety to launchery jednorazowe — nie mają trwałego stanu „aktywny", więc wskaźnik tylko dla taba i otwartego panelu Agent.)
  • Normalizacja ikon (Codex #2, wybór usera): tab-bar = 16px (hamburger + .workspace-icon 15→16px), pasek widgetów = text-[15px] (było text-sm/14px), header bloku = 13px/20px (z rundy T22 wyżej).

Konsultacja UI z Codexem (gpt-5.5, xhigh): pełna lista 10 rekomendacji zapisana; wdrożono bezpieczny podzbiór (typografia, sizing/gap headera, restraint akcentu, spójność mono). Większe zmiany motywu (globalne tiery kontrastu tła/tekstu, restyl aktywnego taba, wskaźnik zaznaczenia na pasku widgetów, normalizacja rozmiarów ikon tab-bar/widget-bar) czekają na decyzję użytkownika (mogą regresować istniejący wygląd) — patrz pytanie opcji.

T23 — Menu ustawień (floating) + zmniejszenie selektora agenta

  • Menu Settings/Tips/Secrets/Release Notes/Help (SettingsFloatingWindow w widgets.tsx) dopasowane do ostrego designu: kontener rounded-lg→**rounded-none, p-2p-1 + min-w-[168px]; itemy gap-3gap-2.5, px-3 py-2px-2.5 py-1.5, roundedrounded-none; label text-smtext-[12px]**, ikona text-basetext-[13px] i mint na hover (group-hover:text-accent). Spójne z restylowanym WaveContextMenu.
  • Selektor agenta mniejszy (Image #20): wavedropdown.scss — nowy wariant .wave-dropdown--sm .wave-dropdown-trigger (padding:5px 9px, font-size:11.5px). agentsessionspanel.tsx — dropdown agenta dostał prop sm (Autonomy już go miał → oba triggery teraz kompaktowe). agentsessions.scss .ap-btn (przycisk „New") height 32→28px, font-size 12→11.5, padding 0 13px0 12px — wyrównany do niższego selektora. (.ap-pane-actions .ap-btn = 34px zostaje — to osobny obszar akcji w bloku, nie rząd selektora.)

T24 — Doszlifowanie nagłówka terminala + kompaktowy modal „Working folder" (feedback usera)

  • Ikona folderu w nagłówku (złoto → mint): frontend/app/view/term/term-model.ts — przycisk „Working folder" w viewText miał iconColor: var(--warning-color) (krzykliwe złoto) → var(--accent-color) (mint, spójne z akcentem forka; usuwa off-brandowy kolor).
  • Ikona lokalnej maszyny (laptop) cieńsza: frontend/app/block/connectionbutton.tsxmakeIconClass("laptop", false) (solid) → makeIconClass("regular@laptop", false) (fa-sharp fa-regular, lżejszy outline; kolor bez zmian = --color-secondary). Wariant regular/sharp jest już używany w forku (np. regular@thumbs-up), więc font jest dostępny.
  • Mniejsza czcionka tekstu nagłówka (11 → 10px): frontend/app/block/block.scss.block-frame-text font-size 11→10px (ścieżka robocza + cmdText typu „Claude Code v2.1"); .connection-button dostał font-size: 10px (nazwa hosta „krzys@DESKTOP…"). Terminal = 12px (--fixed-font), więc nagłówek jest teraz wyraźnie mniejszy (był praktycznie równy). Zmiana dotyczy nagłówków wszystkich bloków (współdzielony komponent) — świadomie, spójnie.
  • Kompaktowy modal „Working folder" (variant="folder"): frontend/app/agent/agentsessionspanel.tsx — etykieta przycisku „Set from terminal window" → „From terminal" (oba przyciski luźno w jednej linii). agentsessions.scss.ap-pane padding 12px 13px 13px→**9px 12px 11px; .ap-pathcard padding 10px 11px7px 10px, margin-bottom 12→8px**; heksagon .ap-pathcard-ic 26→22px (glif 11→10px); .ap-pane-actions .ap-btn height 34→28px (zastępuje notkę z T23, że 34px zostaje).

T25 — Lepsze ikony menu koła + „Welcome Screen" + widok Agents na ostro (feedback usera)

  • Ikony menu ustawień cieńsze/sharp (Image #21): widgets.tsx SettingsFloatingWindow — glify solid → regular@… (fa-sharp fa-regular, lżejszy kontur): Settings regular@gear, Tips regular@lightbulb, Secrets lock→**regular@key, Release Notes book-openregular@rocket**, Help regular@circle-question.
  • Nowa pozycja „Welcome Screen" (regular@terminal) w tym menu — woła showOnboardingGreeting() (pkt 9): re-show onboardingu do podglądu, bez ruszania tosagreed.
  • Agents: usunięta zakładka „Raw JSON" (Image #22): waveconfig-model.ts — wpis agents.json hasJsonView: true→**false** (jak Secrets). Pasek zakładek Visual/Raw JSON renderuje się tylko przy hasJsonView, więc znika tylko dla agentów; inne configi bez zmian. Nieaktualne zdanie o „Raw JSON tab" w agentsconfig.tsx usunięte.
  • Agents: ostre krawędzie boxów (Image #22): agentsconfig.tsx — karty agentów, formularz edycji, box „How detection works", box ostrzeżenia built-in, box „No custom agents", inputy i przyciski akcji: usunięte rounded-lg/rounded-md/rounded → radius 0 (spójne z redesignem). Pille/kropki (rounded-full) zostają.

T26 — Poprawka wbudowanego agenta Antigravity (weryfikacja agy --help na maszynie usera)

Definicja agent@antigravity w pkg/wconfig/defaultconfig/agents.json powstała „na docs" (T5, cmd:bin do potwierdzenia po instalacji). Po instalacji zweryfikowano realia CLI: agy 1.0.16 (%LOCALAPPDATA%\agy\bin\agy.exe, jest w trwałym user-PATH → exec.LookPath OK), backend = Antigravity.exe/language_server.exe w trybie CLI (product=antigravity), dane w ~/.gemini/antigravity-cli/ (współdzieli dom ~/.gemini z osobnym Gemini CLI — to NIE ten sam agent). Poprawki z agy --help:

  • cmd:bin "antigravity""agy" (realna komenda; antigravity nie jest binarką).
  • detect:pattern^(agy|antigravity)\b (agy pierwsze — to faktyczny launcher).
  • launch:resumeargs []["--conversation", "{id}"] (agy resumuje konwersację po ID; mapuje się 1:1 na {id} forka — inaczej niż Gemini CLI, który resumuje po indeksie). -c/--continue = ostatnia.
  • store:roots []["~/.gemini/antigravity-cli/conversations"] (sesje CLI: conversations/<UUID>; sesje IDE-Cascade są osobno w ~/.gemini/antigravity/conversations). store:driver zostaje "" (parser proto+SQLite „trajectory store" niepisany — resume-by-ID z panelu wymaga przyszłego drivera „antigravity").
  • autonomy: --dangerously-skip-permissions potwierdzony (był poprawny); dodany poziom sandbox = --sandbox. Domyślny request-review zgadza się z toolPermission=request-review w logu CLI.

Nie rusza Go — plik konfiguracyjny (bez task generate). Konfig referencyjny w pamięci agenta (antigravity-gemini-cli-config.md).

T27 — Przycisk „About Terminal Cloud" w pasku zakładek (re-show welcome screen)

Greeting pierwszego uruchomienia pokazuje się tylko przy tosagreed=false; po akceptacji znikał, a jedyny re-show był ukryty w menu ••• („Welcome Screen", T25). Na prośbę usera dodany widoczny przycisk w prawym górnym rogu.

  • frontend/app/tab/tabbar.tsx — w rightContainerRef (klaster prawego rogu, obok UpdateStatusBanner i przycisków okna) nowy <button> „About Terminal Cloud" (ikona fa-sharp fa-regular fa-circle-info, hover na mint), onClickshowOnboardingGreeting() (ten sam seam co pozycja menu). WebkitAppRegion: no-drag żeby był klikalny mimo drag-regionu paska. Import showOnboardingGreeting z @/app/onboarding/onboarding.
  • Szerokość przycisku wchodzi automatycznie do rightContainerWidth w setSizeAndPosition (pomiar getBoundingClientRect), więc obliczenia szerokości zakładek same to uwzględniają — bez dodatkowego refa.

T28 — Powiadomienia agentów: który agent + która zakładka, mintowa kropka, własny dźwięk (feedback z testu na żywo)

Test na żywo (Claude, Windows) ujawnił trzy braki: powiadomienie miało tytuł „Terminal agent" (bez nazwy agenta), nie mówiło z której zakładki, na zakładce nie było żadnego wskaźnika, a dźwięk był taki sam jak zwykłego powiadomienia. Do tego user chciał mintową kropkę zamiast żółtego dzwonka.

  • frontend/app/view/term/agentnotify.ts (przebudowa mostu):
    • Który agent (tytuł). Stary resolveAgentLabel czytał tylko activeAgentAtom, ustawiany wyłącznie z detekcji shell-integration (OSC 133;C) — na Windows/PowerShell i przy resume to nie strzela → fallback „Terminal agent". Teraz resolveAgentType ma łańcuch fallbacków: activeAgentAtom → trwała meta bloku agent:type (widget/wsh agentresume) → wyłuskanie z komunikatu hooka (agentLabelFromMessage: „Claude - …" → „Claude", bounded + tylko separator -, żeby nie brać zdania za etykietę).
    • Która zakładka (tytuł). resolveTabName czyta Tab po termWrap.tabId; tytuł = "<Agent> · <tab>" (np. „Claude · T1"). stripAgentPrefix usuwa z treści zdublowany prefiks „ - ", więc body = samo zdarzenie („waiting for your input"). Fallbacki treści przeredagowane na samodzielne („Waiting for your input", „Needs your approval", …).
    • Mintowa kropka (zakładka). notifyAgentEvent woła teraz setBadge(blockId, {icon:"circle", color:"#00ced1", priority:2}) — istniejący system badge'ów agreguje kropki bloków na zakładce i sam je czyści na focus (badge nie-pidlinked). Kropka ustawiana przed throttlem powiadomienia, żeby kolejne zdarzenia utrzymywały oznaczenie. maybeNotifyOnBell używa tego samego resolveAgentType, więc dzwonek w bloku-agencie (bez żywej detekcji, ale z meta) też powiadamia.
  • frontend/app/view/term/agentsound.tsNOWY (izolowany). Odrębny chime agenta (Web Audio, bez assetu), inny per typ: waiting = pojedynczy miękki ton, approval = wznoszący (bardziej natarczywy), finished = opadająca kadencja. Gra z renderera (działa przy nieaktywnym oknie — AudioContext odblokowany wcześniejszą interakcją). playAgentChime zwraca false gdy audio niedostępne → wtedy powiadomienie OS wraca do systemowego dźwięku; w przeciwnym razie OS-notyfikacja jest silent:true (nasz chime zamiast „dinga").
  • frontend/app/view/term/termwrap.ts — badge dzwonka (term:bellindicator) zmieniony z żółtego dzwonka (icon:"bell", #fbbf24) na mintową kropkę (icon:"circle", #00ced1), spójnie ze wskaźnikiem agenta.
  • Zero zmian Go / configu → bez task generate i bez restartu dev (renderer, Vite HMR; istniejące terminale łapią zamianę dzwonka po Ctrl+Shift+R lub w nowym bloku). npx tsc --noEmit ✅ = 0.

T37 — Pulsujący wskaźnik attention + auto-fokus na pierwszego czekającego agenta po powrocie do okna (feedback usera)

User: „kropka może pulsować i jak wrócę do okna, fokus niech przeskoczy na okno pierwszego agenta, który skończył pracę." Rozszerzenie T28 (mintowa kropka). Czysto renderer — bez Go / task generate / restartu dev.

Pulsowanie kropki:

  • frontend/app/store/badge.ts — stała AttentionBadgeColor (#00ced1) + helper isAttentionBadge(badge) (match po mintowym kolorze; Badge to typ upstreamu, nie forkujemy jego schematu dla jednego pola). Dedup koloru: agentnotify.ts i termwrap.ts używają tej stałej zamiast literału.
  • frontend/tailwindsetup.css — keyframe agent-badge-pulse (oddech: opacity + scale) + @media (prefers-reduced-motion: reduce) wyłącza animację.
  • frontend/app/block/blockframe-header.tsx i frontend/app/tab/tabbadges.tsx — klasa agent-badge-pulse na kropce gdy isAttentionBadge (nagłówek bloku i zakładka; kropka dzwonka też pulsuje — spójnie).

Auto-fokus po powrocie do okna:

  • frontend/app/view/term/agentattention.tsNOWY (izolowany). Kolejka waitingBlocks (Set, kolejność wstawienia = kto pierwszy zaczął czekać; idempotentne, blok już w kolejce zachowuje starszą pozycję). markAgentAttention wołane z agentnotify.ts po setBadge. Nasłuch window "focus" (lazy, dowiązany przy pierwszym marku): po powrocie do okna wybiera NAJSTARSZY wciąż czekający blok (prune po żywym stanie badge — isAttentionBadge, więc odwiedzone/wyczyszczone kropki wypadają), przełącza zakładkę jeśli blok jest na innej (getApi().setActiveTab + krótki polling aż layout docelowej zakładki ożyje) i ustawia fokus (refocusNode), po czym zdejmuje go z kolejki (kolejny powrót → następny agent). Guard document.hasFocus().
  • frontend/app/view/term/agentnotify.ts — import + wywołanie markAgentAttention; dedup koloru na AttentionBadgeColor.
  • npx tsc --noEmit ✅ = 0. Otwarte: brak przełącznika w configu (zachowanie domyślnie włączone, zgodnie z prośbą); ewentualny gate term:agentfocusonreturn w przyszłości wymagałby Go + task generate + restartu.

T32 — Logo Terminal Cloud w powiadomieniu i ikonie na pasku Windows (feedback usera: „nie logo Electrona")

W dev aplikacja działa jako electron.exe, więc Windows przypisywał powiadomienia i ikonę na pasku do „Electron" (nazwa + logo). Do tego winOpts.icon był ustawiany tylko dla Linuksa — win32/mac szły z ikony exe.

  • public/logos/terminal-cloud.{png,ico}NOWE. Zrasteryzowany mark „Terminal Cloud" (mintowy heksagon + prompt >_ na ciemnym kafelku), wzięty z SVG z terminalcloudgreeting.tsx. Generator: scratchpad/gen-tc-icon.cjs (przez @resvg/resvg-js, już w node_modules); ICO = 16/24/32/48/64/128/256.
  • emain/tcicon.tsNOWE (izolowane). Ikona wbudowana jako base64 PNG → nativeImage (data-URL), bo w dev public/ NIE trafia na dysk (Vite serwuje je z pamięci) — ścieżka pliku by się nie rozwiązała. getTerminalCloudIcon() (cache) dla ikony okna i toasta. registerWindowsDevIdentity(dataDir) (dev/win32, best-effort): ustawia własny AppUserModelID + rejestruje jego DisplayName=„Terminal Cloud" i IconUri w HKCU\Software\Classes\AppUserModelId\… (przez reg add), żeby nagłówek toasta pokazywał naszą nazwę+logo zamiast „Electron"; zapisuje też PNG do data-dir na potrzeby IconUri. Błąd nigdy nie blokuje startu.
  • emain/emain-wsh.tshandle_notify dostał icon: getTerminalCloudIcon() (Windows: appLogoOverride — logo w toaście).
  • emain/emain-window.ts — w gałęzi win32 winOpts.icon = getTerminalCloudIcon() (ikona okna/paska; upstream to pomijał).
  • emain/emain-platform.ts — w dev woła registerWindowsDevIdentity(getWaveDataDir()) tuż po app.setName.
  • Zmiana procesu głównego → wymaga restartu task dev (emain nie ma HMR). npx tsc --noEmit (frontend+emain) ✅ = 0.
  • TODO (prod/packaging): pod produkcyjny build można podmienić build/icon.ico + build/icons/*.png na wariant TC.

T33 — Prawdziwe loga OpenCode i Antigravity w nagłówku agenta (feedback usera: generyczne kwadraty „O"/„A")

AgentLogo (frontend/app/agent/agentheaderbutton.tsx) renderował SVG tylko dla claude/cursor/codex, a reszta dostawała monogram (pierwsza litera w kwadracie brand-color). OpenCode i Antigravity miały więc tylko „O"/„A".

  • frontend/app/asset/opencode-color.svg, antigravity-color.svgNOWE. Realne marki z LobeHub icons (@lobehub/icons-static-svg, MIT) — to samo źródło i styl co istniejące codex/cursor (viewBox 24×24, fill="currentColor", jeden path). OpenCode = blokowa ramka „O"; Antigravity = bumerang. currentColor → biorą kolor agenta z agents.json (opencode bursztyn #e8a33d, antigravity mięta #00b4a6), jak codex/cursor.
  • agentheaderbutton.tsx — dwa nowe case'y (opencode, antigravity) w AgentLogo, wzorzec 1:1 jak codex/cursor ([&_svg]:w-[13px] [&_svg]:h-[13px], style={{ color }}) + dwa importy SVGR. Seam dokładnie taki, jak opisuje komentarz komponentu („Drop a <key>-color.svg into app/asset and special-case it here").
  • Renderer-only → Vite HMR (bez restartu). Atrybucja: LobeHub icons (MIT) — dodane do NOTICE. tsc ✅ = 0.

T35 — Loga agentów też w dropdownie selektora (feedback usera: „dodaj te loga do tego menu")

Selektor agentów w panelu sesji (WaveDropdown) pokazywał tylko kolorowe kropki. Teraz pokazuje te same marki co nagłówek terminala (claude/codex/cursor/opencode/antigravity), w triggerze i na liście.

  • frontend/app/agent/agentheaderbutton.tsxAgentLogo wyeksportowany (był lokalny), by dało się go użyć w dropdownie.
  • frontend/app/element/wavedropdown.tsxWaveDropdownOption dostał opcjonalne icon?: ReactNode; render (trigger + pozycje listy) ma pierwszeństwo dla icon, z fallbackiem na kropkę (color). Dzięki temu autonomia-dropdown i opcja „All agents" (bez ikony) działają jak dotąd.
  • frontend/app/element/wavedropdown.scss — nowy slot .wave-dropdown-icon (16×16, wyśrodkowany), żeby wiersze z logo i z kropką były wyrównane.
  • frontend/app/agent/agentsessionspanel.tsxagentOptions mapuje realnych agentów z icon: <AgentLogo … /> (dane z configuredAgentskey/label/color/icon); „All agents" zostaje z szarą kropką.
  • Renderer-only → Vite HMR (bez restartu). npx tsc --noEmit ✅ = 0.

T36 — Panel sesji: kolejność segmentów + spójny hint „pick a folder" (feedback usera)

Drobne poprawki w frontend/app/agent/agentsessionspanel.tsx:

  • Segmented control przełożony na Current → Recent → Folder (było Recent → Folder → Current); tylko kolejność renderu (["current","recent","folder"]), domyślny widok/logika bez zmian.
  • Split-button „+ New": pierwsza pozycja „New in another folder…" dostała ten sam sublabel „pick a folder" co „New in new tab" — obie i tak otwierają picker folderu, więc podpis jest teraz na obu (spójność).
  • Renderer-only → Vite HMR. npx tsc --noEmit ✅ = 0.

T30 — App menu (hamburger) rysowane in-app zamiast natywnego menu Windows (feedback usera: „ostre krawędzie")

Menu aplikacji (Wave/File/Edit/View/Workspace/Window spod hamburgera) było natywnym menu Electrona (electron.Menu.popup()), któremu Windows 11 rysuje zaokrąglone rogi — CSS ich nie ruszy. Przepięte na istniejący fork'owy silnik in-app (WaveContextMenu, T17): ostre krawędzie, mint, spójne z resztą.

  • emain/emain-menu.tsmakeFullAppMenu rozbite na makeFullAppMenuTemplate (zwraca szablon) + cienki wrapper. serializeMenuTemplate idzie bezpośrednio po szablonie (nie po zbudowanym Menu.items) — na Windows część ról app-menu (appMenu itd.) rozwiązuje się inaczej / wypada, co rozjechałoby walk po indeksach menu.items/szablon. Labelki ról z RoleLabelFallback (appMenuelectron.app.name), resztę rozwiązuje renderer. Akcyjnym liściom nadaje id i rejestruje w appMenuRegistry. IPC get-workspace-appmenu (handle) zwraca drzewo; workspace-appmenu-click (on) odbiera klik: oryginalny closure albo dispatchAppMenuRole (role Electrona ręcznie — edit-role na webContents, quit/close/minimize/zoom/front/togglefullscreen na oknie/app).
  • emain/preload.ts + frontend/types/custom.d.tsgetWorkspaceAppMenu (invoke) i workspaceAppMenuClick (send). frontend/app/tab/tabbarenv.ts — te dwie metody w zawężonym TabBarEnv.electron.
  • frontend/app/tab/tabbar.tsxonEllipsisClick pobiera serializowane menu i rysuje przez showWaveContextMenu zakotwiczone pod hamburgerem; toAppMenuItems konwertuje drzewo (klik liścia → id z powrotem do main). Fallback: gdy getWorkspaceAppMenu niedostępny (stary preload przed restartem) lub rzuci — wywołuje natywne showWorkspaceAppMenu, więc przycisk nigdy nie jest „martwy". Natywna ścieżka (workspace-appmenu-show) zostaje też dla builder windows.
  • frontend/preview/mock/preview-electron-api.ts — stuby nowych metod.
  • Zmiana Go/electron-main → wymaga restartu task dev. (Objaw „menu nie działa" po pierwszej implementacji = dev z 00:04 miał stary dist/main+dist/preload bez nowych handlerów; renderer dostał nowy tabbar.tsx przez HMR, ale electron-vite nie hot-reloaduje main/preload → getWorkspaceAppMenu undefined → wyjątek łykany przez fireAndForget. Stąd powyższy fallback.) tsc --noEmit (frontend + emain, jeden tsconfig) ✅ = 0. Znane pominięcie: akceleratory (skróty) nie są jeszcze rysowane w in-app menu — do dodania osobnym slotem w WaveContextMenu, jeśli potrzebne.

T29 — Kompaktowy typeahead połączeń (feedback usera: „menu za duże, czcionka większa niż w headerze")

Menu przełącznika połączeń (Connect to (username@host)…, otwierane z przycisku połączenia w nagłówku bloku) renderowało się większe niż sam nagłówek terminala: search-input 12px, itemy 11px vs 10px headera (.connection-button/.block-frame-text), do tego luźne odstępy.

  • frontend/app/modals/typeaheadmodal.scss (plik upstreamowy — zmiana minimalna, stąd notka tutaj): input 12px → 11px, .typeahead-item-name 11px → 10px (= skala headera) + line-height 14→13, offset ikony top 2→1, .suggestion-header 11px → 10px, padding itemu 6px 8px → 4px 8px, gap grup 10px → 7px.
  • Czysto renderer (SCSS) → bez task generate/restartu; podgląd po Ctrl+Shift+R. tsc --noEmit ✅ = 0.

T31 — Drobne poprawki UI (feedback usera: ostre krawędzie, wyśrodkowanie, „New in current folder")

Trzy niezależne, czysto rendererowe zmiany (podgląd po Ctrl+Shift+R, bez restartu):

  • Przycisk „+" (add tab)frontend/app/tab/tabbar.tsx: usunięte rounded-md z klasy przycisku dodawania karty → ostre krawędzie tła hovera, spójnie z redesignem radius-0.
  • Tekst zakładekfrontend/app/tab/tab.scss: .name przesunięte o 2px w dół (translate3d(-50%, calc(-50% + 2px), 0)) — mintowy keyline 2px na górze aktywnej karty zjadał górę, przez co tekst czytał się za wysoko; teraz optycznie wyśrodkowany.
  • Split-button „New" (jak „Open ▾" w Windows) — frontend/app/agent/agentsessionspanel.tsx + agentsessions.scss (.ap-new-split, .ap-btn--caret). Główny New zawsze startuje w bieżącym folderze (global: currentCwd/resolveAgentCwd, gdy brak → picker; tryb folder: blok terminala). Caret ▾ otwiera in-app menu (showWaveContextMenu pod splitem): „New in another folder…" (picker → nowy blok w bieżącej zakładce) i „New in new tab" (picker → nowa zakładka + agent w wskazanym folderze).
    • New in new tab jest cross-renderer-safe (każda zakładka = osobny renderer): WorkspaceService.CreateTab (zwraca tabId, aktywuje) → ObjectService.GetObject(tab) po domyślny blockids[0]CreateBlockCommand (wshrpc, tabid + targetaction:"replace") zastępuje domyślny terminal blokiem agenta backendowo (kolejka layoutu per-tab), więc nowa zakładka ma tylko agenta, bez wyścigu z frontendowym layout modelem. Fallback do insert, gdy domyślnego bloku nie da się odczytać. tsc --noEmit ✅ = 0.

T32 — Rebrand modala „About" na Terminal Cloud (feedback usera + zgodność licencji)

Modal „About" spod app-menu (Wave → About) pokazywał branding upstreamu (logo Wave, „Wave Terminal", GitHub/Website/Open Source/Sponsor → wavetermdev, © Command Line Inc.). Apache-2.0 nie wymaga utrzymywania tego okna, a §6 nie daje praw do znaku „Wave" — więc fork powinien mieć własną tożsamość.

  • frontend/app/modals/about.tsx — przebrandowane na Terminal Cloud (spójnie z terminalcloudgreeting.tsx): mintowy znak heksagonalny zamiast Logo Wave, nazwa „Terminal Cloud", tagline „The terminal built for coding agents.", zachowana wersja klienta + kanał. Linki: CloudBots (cloudbots.eu), StatXplorer (statxplorer.com), Built on Wave (waveterm.dev — atrybucja), Contact (mailto). Krawędzie linków ostre (usunięte rounded).
  • Atrybucja / licencja: stopka „Built on Wave Terminal — © Command Line Inc. · Apache-2.0" + zachowany nagłówek copyright pliku + nienaruszony LICENSE/NOTICE → spełnia wymogi Apache-2.0 §4 i NOTICE.
  • Czysto renderer → podgląd po Ctrl+Shift+R. tsc --noEmit ✅ = 0.

Pkt 7 — Powiadomienia o agentach (skończył / czeka / prosi o dostęp)

Cel: powiadomienie OS + dźwięk, gdy agent kodujący w terminalu (Claude Code, Codex, OpenCode, Cursor, …) skończy turę, czeka na odpowiedź albo prosi o zgodę — jak wbudowane powiadomienia Codexa, tylko dla wszystkich agentów i przez natywny mechanizm Wave.

Mechanizm (wybrany: „OSC 9 + bell"): wspólnym mianownikiem wszystkich agentów jest sekwencja ucieczki terminala. Agent emituje OSC 9 (ESC ] 9 ; <tekst> BEL, styl iTerm2/Codex/OpenCode) lub OSC 777 (ESC ] 777 ; notify ; <tytuł> ; <treść>), a gdy nie potrafi — dzwonek (\x07). Wave przechwytuje to w terminalu i podnosi powiadomienie OS przez istniejące NotifyCommandhandle_notify (Electron Notification). Zero nowego pluminingu powiadomień — tylko most.

Frontend NOWY (izolowany)

  • frontend/app/view/term/agentnotify.ts — logika mostu:
    • handleOsc9Notification / handleOsc777Notification — parsują payload, klasyfikują zdarzenie z tekstu (approval / waiting / finished), wołają NotifyCommand (route:"electron") z tytułem = etykieta aktywnego agenta (z agents.json), treść = tekst. OSC 9 z prefiksem numerycznym (^\d+;) jest ignorowane (progres ConEmu/Windows Terminal, nie powiadomienie).
    • maybeNotifyOnBell — dzwonek zamieniany na powiadomienie tylko gdy w bloku aktywny jest agent (activeAgentAtom), by nie hałasować przy zwykłym BEL powłoki.
    • Warunek unfocused (domyślny) / always, throttle 1.5 s per blok. Dźwięk = silent:!term:agentnotifysound.

Pliki ZMIENIONE w upstream (minimalnie)

  • frontend/app/view/term/termwrap.ts — import + rejestracja OSC 9 i OSC 777 (tuż za OSC 16162) + jedno wywołanie maybeNotifyOnBell w istniejącym onBell.
  • pkg/wconfig/settingsconfig.go + pkg/waveobj/wtypemeta.go — 4 klucze (skill add-config): term:agentnotify, term:agentnotifysound, term:agentnotifycondition (unfocused|always), term:agentnotifyautoinstall (ostatni tylko globalny). Defaulty w defaultconfig/settings.json, docs w docs/config.mdx. task generate dogenerował metaconsts.go (waveobj+wconfig) i gotypes.d.ts.
  • cmd/server/main-server.gogo agentnotify.EnsureAllOnStartup() po startConfigWatcher().

Backend NOWY (izolowany) — auto-instalacja configu agentów

  • pkg/agentnotify/ — przy starcie wavesrv iteruje agents.json; dla każdego agenta obecnego na dysku (exec.LookPath cmd:bin / discover:binpath) i jeszcze nie obsłużonego (plik stanu agentnotify-state.json w data-dir) idempotentnie dopisuje jego config powiadomień. Nowy agent zainstalowany po Wave dostaje config przy następnym starcie (binarka pojawia się → brak w stanie → jednorazowy provisioning). Bezpieczeństwo: backup <plik>.wave-bak przed pierwszą zmianą, zapis atomowy, znacznik wave-agent-notify w naszych wpisach (idempotentna podmiana bez ruszania wpisów użytkownika), gate term:agentnotifyautoinstall. Best-effort — błędy logowane, nigdy nie blokują startu.
    • provider_codex.go — konserwatywna chirurgia ~/.codex/config.toml: ustawia [tui] notification_method="osc9" (tryb auto Codexa spada do gołego BEL dla nieznanych terminali — Wave jest „nieznany"). Nie rusza, jeśli notification_method już jest; wstawia klucze pod istniejący [tui] (tylko brakujące) albo blok [tui] przed pierwszym [tui.*] (rodzic-przed-dzieckiem) / na końcu pliku.
    • provider_opencode.goattention {enabled,notifications,sound} w tui.json (OpenCode emituje OSC 9 sam). Ścieżka: $OPENCODE_TUI_CONFIG$XDG_CONFIG_HOME~/.config/opencode/tui.json.
    • provider_claude.go — hooki w ~/.claude/settings.json: Stop → „finished", Notification/idle_prompt → „waiting", Notification/permission_prompt → „approval". Komenda hooka drukuje terminalSequence JSON z OSC 9 (Claude sam pisze go do PTY — stdout hooka nie trafia do terminala). Wymaga Claude Code ≥ v2.1.141.
    • provider_cursor.go — hook stop w ~/.cursor/hooks.json (schema v1) piszący OSC 9 wprost do terminala. Tylko stop (hooki before* odpalają się przy KAŻDYM narzędziu, nie tylko przy realnej zgodzie → byłby spam).
    • helper.go — skrypt wave-agent-notify.{sh,cmd} w data-dir: <mode> <text> (claude = JSON z terminalSequence; inaczej = surowy OSC 9 na /dev/tty / CONOUT$). Centralizuje różnice platformowe zamiast wklejać kruche one-linery do configów. Hardening cytowania (fix z code review): agent:label jest user-controlled i trafia do komendy hooka, którą shell agenta re-parsuje → ryzyko wstrzyknięcia. shellQuote cytuje poprawnie per-OS (POSIX: idiom '\'' — dokładny; Windows: "..."), a sanitizeNotifyText (główna granica, wołana w agentLabel) usuwa znaki kontrolne oraz "/% (nieocytowalne bezpiecznie wewnątrz "..." w cmd.exe). Runda 2 (fix z review 2026-07): na Windows label jest wklejany jako %~2 jeszcze raz — wewnątrz single-quoted literału PowerShella (i JSON-a w trybie claude) w .cmd, czego shellQuote (cmd.exe) nie widzi; apostrof zamykał literał (cichy błąd parse → brak powiadomienia, a spreparowany label → wstrzyknięcie PS), \ psuł escape JSON. sanitizeNotifyText usuwa więc dodatkowo '/\ tylko na Windows (POSIX obsługuje je dokładnie, test idiomu '\'' zostaje). Testy: TestSanitizeNotifyText, TestShellQuoteNoBreakout (case'y '/\ gate'owane per-OS).

Macierz wsparcia (dla których agentów działa i jak)

Agent Kanał „skończył" „czeka" „prosi o dostęp" Pewność
Codex wbudowany OSC 9 (config.toml notification_method=osc9) ✅ (= turn) Wysoka (Codex emituje sam; bez osc9 i tak działa przez BEL)
OpenCode wbudowany attention OSC 9 (tui.json) ✅ (= idle) permission.asked Śr.-wysoka (do potwierdzenia ścieżka tui.json na nie-Linux)
Claude Code hook → terminalSequence OSC 9 Stop idle_prompt permission_prompt Średnia (wymaga v2.1.141+; do testu na żywo)
Cursor hook stop → OSC 9 na TTY stop ✅ (= stop) — (pominięte, byłby spam) Niska–średnia (Unix /dev/tty OK; Windows CONOUT$ do weryfikacji)

Weryfikacja

  • go vet ./pkg/agentnotify/ ✅ = 0. go test ./pkg/agentnotify/ ✅ (merge hooków idempotentny, chirurgia TOML Codexa wstawia [tui] przed [tui.*] i respektuje istniejący notification_method, hasTomlKey bez false-matchy, backup powstaje).
  • task generate ✅ (metaconsts + gotypes spójne). npx tsc --noEmit ✅ = 0 błędów.
  • go vet ./cmd/server/ ./pkg/waveobj/ ./pkg/wconfig/ ✅ = 0.

Otwarte / do dopracowania

  • Test na żywo w GUI wszystkich czterech ścieżek (jak reszta forka — kod gotowy, zweryfikowany offline). Priorytet: Claude (wersja hooka terminalSequence) i Cursor na Windows (CONOUT$).
  • OSC 9 niesie tylko tekst (brak maszynowego typu) → typ zdarzenia zgadywany z prefiksu; dla Codex/OpenCode tekst jest ich, dla Claude/Cursor kontrolujemy go w hooku.
  • OpenCode: potwierdzić lokalizację tui.json poza Linuxem. Cursor: brak czystego, nie-spamującego sygnału „approval" (hooki before* odpalają się zawsze) → pominięty.
  • Codex notify (zewn. program) tylko dla agent-turn-complete — nie użyty; OSC 9 pokrywa też approval/plan-mode.

Pkt 6 — Widget Git (Source Control jak VS Code) + widok diff

Cel: panel kontroli źródła sterowany folderem roboczym wybranego terminala, z detalami git jak w rozszerzeniu VS Code, oraz diff plików w Monaco.

Backend NOWY (izolowany)

  • pkg/git/git.go — cienki wrapper na binarkę git (exec.CommandContext, cmd.Dir=repoRoot, GIT_TERMINAL_PROMPT=0, GIT_OPTIONAL_LOCKS=0). Funkcje: Status (status --porcelain=v2 --branch -z → branch/upstream/ahead-behind + per-plik index/worktree/untracked/conflict + remote), Diff (index↔worktree / HEAD↔index), Action (stage/unstage/discard/commit/fetch/pull/push/checkout/ createbranch/stash), Log, ListBranches.
    • Bezpieczeństwo (z automatycznego security review): repoPath() blokuje path-traversal na readWorking/discard os.Remove/Diff; validRef() odrzuca ref-y z wiodącym -/białym znakiem (argv flag smuggling) w checkout/createbranch; remoteUrlToHttp (FE) wpuszcza tylko http/https.

RPC (skill add-rpc)

  • pkg/wshrpc/wshrpctypes.go — 5 metod Git{Status,Diff,Action,Log,ListBranches}Command
    • typy Command Git*Data / Git*Data. Handlery w wshserver.go (timeout 15s, 120s dla akcji sieciowych). task generate (klient TS + gotypes + wshclient.go).

Frontend NOWY (izolowany)

  • frontend/app/view/gitscm/gitscm.tsx — widok gitscm (panel SCM: branch + ahead/behind + remote/GitHub, commit box Commit/Commit All + Ctrl+Enter, sekcje Merge/Staged/Changes ze statusami i akcjami per plik, przełącznik brancha, menu „…", zakładka History). gitdiff.tsx — widok gitdiff (Monaco diff, reużywa DiffViewer). gitutil.ts, gitscm.scss.

Pliki ZMIENIONE (minimalnie)

  • frontend/app/block/blockregistry.ts — rejestracja gitscm/gitdiff.
  • pkg/wconfig/defaultconfig/widgets.json — widget git.
  • frontend/app/workspace/widgets.tsx — seed gitscm:cwd z aktywnego terminala.
  • pkg/waveobj/wtypemeta.go — klucze meta gitscm:*/gitdiff:* (→ MetaType).
  • frontend/app/tab/tabcontextmenu.ts — „Open Source Control" (natywne menu Electrona nie da się stylować CSS; zmiana treści).

Weryfikacja

  • Parser porcelain v2 -z sprawdzony na realnym repo (A./.M/?/branch.head). go vet = 0, tsc = 0, build wavesrv EXIT 0.

Pkt 5 — Edytor kodu: odblokowanie funkcji Monaco

Cel: zoom, zwijanie (JSON), i funkcje jak w edytorach — Wave ma już Monaco.

  • frontend/app/view/codeeditor/codeeditor.tsxdefaultEditorOptions(): folding, foldingStrategy:"auto", showFoldingControls:"always", foldingHighlight, mouseWheelZoom (Ctrl+scroll), bracketPairColorization, guides, renderLineHighlight, cursorBlinking:"smooth".
  • frontend/app/view/preview/preview-model.tsx — nagłówek bloku codeedit dostaje szybkie przełączniki Word Wrap + Minimap (stan w akcencie) + Refresh (wcześniej brak).
  • Diff plików dostarczony jako widok gitdiff (Pkt 6), bo potrzebuje danych z git.

Pkt 4 — Redesign globalny (mint + ostre krawędzie)

Cel: cała apka w nowym języku wizualnym (mint #00CED1, ostre krawędzie), przez zmienne CSS (mały blast-radius — ~90% czyta akcent przez zmienne).

  • frontend/tailwindsetup.css — skala --color-accent-* + --color-accent/accenthover/ accentbg na mint; --radius:0.
  • frontend/app/theme.scss--accent-color, --tab-green, --link-color, --button-green-bg/border na mint; --block-border-radius/--modal-border-radius:0.
  • frontend/app/element/button.scss + widoczny chrome (taby, inputy, iconbuttony, menu, modale, switcher) — radius 0 (pigułki 50%/999px zostają okrągłe).
  • Poprawki na żywo: pasek widgetów węższy (w-10, ikony base, DEV 18px); „apps" ukryte (SHOW_APPS_WIDGET=false); przycisk Wave AI ✨ ukryty domyślnie (app:hideaibutton ?? true w tabbar/vtabbar); ramka aktywnego bloku mint 0.6 alpha (mniej jaskrawa); caret-color: mint w polach edytowalnych.
  • emain/emain-window.ts — na win32 winOpts.roundedCorners = false: ostre rogi całego okna aplikacji (spójne z radius 0). Electron 41 mapuje to na DWMWA_WINDOW_CORNER_PREFERENCE = DWMWCP_DONOTROUND, więc bez FFI. Wymaga restartu Electrona (zmiana w main process).
  • Ikony prawego paska widgetów na styl Sharp (FA Pro 6.7.2, nowocześniejsze, ostre rogi): frontend/app/workspace/widgets.tsx — klasa wave-widgetbar na kontenerze paska; frontend/app/theme.scss.wave-widgetbar i.fa-solid { font-family:"Font Awesome 6 Sharp"; font-weight:400 } (400=Sharp Regular kontur; 900=Sharp Solid). Zascope'owane do paska, fa-brands (badge DEV) nietknięte. Zmiana tylko w rendererze — łapie ją hot-reload.

Pkt 3 — Redesign panelu sesji agentów (język „cloud-bench")

Cel: panel przeglądarki sesji (globalny „All agent sessions", per-terminal „Agent sessions", „Working folder") dostaje spójny, rozpoznawalny wygląd wzorowany na projekcie cloud-bench użytkownika — przeniesiony na ciemny motyw Wave.

Sygnatura designu (ciemna adaptacja): akcent mint #00CED1, ostre krawędzie (radius 0), nagłówki sekcji z mono-etykietą i gradientowym „tickiem" (mint→navy), heksagonalny mark, JetBrains Mono na metadanych, górna linia tożsamości + delikatny glow. „New" = jedyny pełny-mint akcent; aktywny segment stonowany (mint-tekst + tint + podkreślenie), by nie konkurował.

Pliki NOWE (izolowane)

  • frontend/app/agent/agentsessions.scss — cały system stylów panelu, scoped pod .wave-agentpanel (zero wycieku do reszty zaokrąglonego, zielonego UI Wave). Fonty z zestawu Wave (Inter + JetBrains Mono, ładowane przez fontutil.ts).

Pliki ZMIENIONE

  • frontend/app/agent/agentsessionspanel.tsx — markup przepisany na klasy ap-* (bez zmian w logice: te same handlery, stan, efekty i RPC AgentListSessionsCommand).

Weryfikacja

  • tsc --noEmit ✅ = 0 błędów. Backend nietknięty (zmiana czysto FE — Vite HMR).
  • Podgląd renderowany 1:1 (standalone HTML) + potwierdzony na żywo w apce (screenshoty od użytkownika): widok Recent, Folder (pogrupowany), Current, Working folder.

Pkt 2 — Auto-kontynuacja sesji agenta (Claude Code / Codex / Cursor)

Cel: terminal pamięta sesję agenta per okno/blok i przy restarcie ją wznawia.

  • Lokalnie = Wersja B (relaunch + --resume), bo lokalny PTY ginie z restartem (durable w Wave jest celowo remote-only — IsBlockTermDurable() zwraca false dla local/WSL).
  • Remote = Wersja A (durable reattach, już w Wave) automatycznie: gdy żywy PTY istnieje, init-script się NIE uruchamia (agent dalej działa); gdy PTY padł, zimny start odpala ten sam mechanizm co lokalnie.

Jeden mechanizm (cmd:initscriptwsh agentresume) obsługuje obie wersje.

Pliki NOWE (izolowane, zero konfliktu przy rebase)

  • pkg/agentsession/agentsession.go — rdzeń. ResolveLaunch(agentType, cwd, sessionId, extraArgs...) zwraca LaunchSpec{Cmd, Args, SessionId, Resumed, TranscriptPath}.
    • Claude (zweryfikowane empirycznie): --session-id <uuid> = create-only, --resume <uuid> = wznowienie. Jeśli transkrypt ~/.claude/projects/<enc-cwd>/<id>.jsonl istnieje → resume; inaczej → (re)use id jako create (stabilne id nawet gdy poprzedni start nie zdążył nic zapisać).
    • EncodeProjectDir: każdy znak spoza [A-Za-z0-9]- (jeden do jednego, bez zwijania). Zweryfikowane na ~40 realnych katalogach ~/.claude/projects.
    • Codex/Cursor: resume po id gdy zapisane; brak flagi create-with-id → świeży start (id do przechwycenia później; TODO w kodzie).
  • pkg/agentsession/agentsession_test.go — 6 testów (kodowanie, create/resume, reuse-id-bez-transkryptu, codex/cursor, nieobsługiwany typ). go test ./pkg/agentsession/ ✅.
  • cmd/wsh/cmd/wshcmd-agentresume.go — komenda wsh agentresume --type <claude|codex|cursor>. Czyta meta bloku (agent:type, agent:sessionid), woła rdzeń, persystuje agent:type/agent:sessionid/agent:transcriptpath przez SetMeta RPC, exec agenta. Flagi: --type, --print (podgląd komendy bez uruchomienia). Argumenty po -- przekazywane do agenta.
  • cmd/wsh/cmd/wshcmd-agentresume-unix.go (//go:build !windows) — syscall.Exec (przejęcie procesu → agent dostaje PTY i sygnały bez forwardingu).
  • cmd/wsh/cmd/wshcmd-agentresume-win.go (//go:build windows) — spawn+wait, signal.Ignore(os.Interrupt) żeby Ctrl+C trafiał do agenta a nie zabijał wsh.
    • UWAGA: claude na Windows bywa shimem claude.cmd (npm). exec.LookPath to znajduje; gdyby pojawiły się problemy z uruchamianiem .cmd, owinąć w cmd /c.

Pliki ZMIENIONE w upstream (minimalnie)

  • pkg/waveobj/wtypemeta.go — dodane do MetaTSType: agent:*, agent:type, agent:sessionid, agent:autoresume, agent:transcriptpath. (Blok ~5 linii, oznaczony komentarzem // coding-agent auto-resume.)
  • pkg/waveobj/metaconsts.gogenerowany z tagów JSON powyższego structa przez task generate (cmd/generatego). Stałe MetaKey_Agent* pojawiają się automatycznie.
  • pkg/wconfig/defaultconfig/widgets.json — dodany defwidget@claude (ikona robot, display:order -4.5). Tworzy terminal z agent:type=claude, cmd:initscript="wsh agentresume --type claude". Dla remote-workera (Wersja A) dorzucić do blockdef.meta "term:durable": true.
  • frontend/app/view/term/term-model.ts — w getContextMenuItems() pozycja "Show Agent Session History" (widoczna gdy agent:transcriptpath ustawione), otwiera podgląd transkryptu w bloku preview. (Label po angielsku dla spójności z resztą menu.)
  • frontend/types/gotypes.d.tsgenerowany (task generate).

Jak używać

  1. Kliknij widget claude w sidebarze (albo ustaw na dowolnym terminalu: wsh setmeta agent:type=claude cmd:initscript="wsh agentresume --type claude").
  2. Pierwszy start tworzy sesję i zapisuje jej id na bloku.
  3. Restart bloku/aplikacji → ta sama rozmowa wznowiona (--resume).
  4. Prawy klik → Show Agent Session History → podgląd <id>.jsonl.
  5. Remote-worker: ten sam blok + term:durable:true → żywy reattach, a po padzie PTY zimne wznowienie.

Weryfikacja

  • go test ./pkg/agentsession/ ✅ (6/6).
  • go build ✅ na windows + linux + darwin (cmd/wsh, pkg/agentsession, pkg/waveobj).
  • task generate ✅ (typy TS + metaconsts spójne).
  • Frontend typecheck (task check:ts) NIE uruchomiony — brak node_modules (wymaga yarn install). Zmiana TS to kopia sąsiedniego wzorca; typy z gotypes.d.ts potwierdzone ręcznie (agent:transcriptpath: string, file: string).

Poprawki środowiskowe / dev (przy pierwszym uruchomieniu task dev)

  • start-dev.cmd (root repo) — lokalny launcher: ustawia PATH (go+task+zig) i odpala task dev. Uruchamiać jako WŁASNY proces użytkownika (dwuklik / własny terminal), nie jako zadanie w tle agenta — tło agenta bywa reapowane i apka się zamyka.

  • Toolchain zig 0.16.0task dev na Windows buduje wavesrv z CGO przez CC="zig cc -target x86_64-windows-gnu" (sqlite). Bez zig build pada (cgo: C compiler "zig" not found). Zainstalowany portable w C:\Users\krzys\AppData\Local\Programs\zig\zig-x86_64-windows-0.16.0. To zależność builda (jest w BUILD.md), nie zmiana kodu.

  • emain/emain-window.tsfix błędu dev-only (nie związany z pkt 2, ale blokował uruchomienie). awaitWithDevTimeout() przy przekroczeniu DevInitTimeoutMs odrzucał promise, co przerywało initializeTab() PRZED addChildView(tabView) → warstwa taba nigdy nie była dołączana → trwale puste okno na wolnym pierwszym buildzie dev (init taba ~7–12 s > stare 5 s). Zmiana: timeout już nie odrzuca — tylko pokazuje okno+devtools jako pomoc debugową i dalej czeka na realny init (widok dołącza się gdy gotowy). Limit podbity 5000 → 30000 ms. Tylko ścieżka isDev, produkcja bez zmian.

Otwarte / do dopracowania

  • Codex/Cursor: przechwycenie nowego session-id po świeżym starcie (skan ~/.codex/sessions, SQLite ~/.cursor). Na razie tylko resume-po-id działa pełni.
  • Windows: drive-letter case (C: vs c:) w kodowaniu ścieżki — przy niezgodności z process.cwd() Claude'a transkrypt może się nie znaleźć (rzadki edge case).
  • Dedykowany renderer .jsonl (teraz transkrypt otwiera się jako surowy preview).

Pkt 1 — Klikalne ścieżki plików w terminalu

Cel: ścieżka pliku w outputcie terminala staje się klikalna (model jak VS Code).

  • Zwykły (lewy) klik → otwórz plik w bloku preview w Wave.
  • Ctrl/Cmd + lewy klik → otwórz w domyślnej aplikacji OS (openNativePath, tylko lokalnie).
  • Prawy klik zostawiony bez zmian → normalne menu kontekstowe Wave.

Historia gestu (zgłoszenia użytkownika): pierwotnie Ctrl+Shift → zmienione na Ctrl+prawy-klik (kolizja Shift) → finalnie odwrócone na zwykły klik = Wave, Ctrl = program OS (bez przechwytywania prawego kliku).

Wave ma WebLinksAddon, ale jego LinkComputer twardo filtruje każde trafienie przez isUrl() — linkuje wyłącznie URL-e http(s). Stąd osobny provider dla ścieżek.

Pliki NOWE (izolowane)

  • frontend/app/view/term/termlinks.ts — całość logiki:
    • installFilePathLinks(terminal, blockId) → rejestruje ILinkProvider; activate (lewy klik) rozróżnia modyfikator: bez Ctrl → preview, z Ctrl/Cmd → aplikacja OS. Zwraca IDisposable[]. (Prawy klik nie jest już przechwytywany.)
    • Wykrywanie dwuprzebiegowe (computePathLinks):
      • Pass 1 — ścieżki bezwzględne ze spacjami (AbsFilePathRegex): kotwica dysku/UNC/home (C:\, \\, ~/), potem leniwie znaki ścieżki wraz ze spacjami do pierwszego rozszerzenia .ext (1–8) + opcjonalny :linia:kol, zakończone granicą (biały znak/cudzysłów/nawias/koniec). Leniwy kwantyfikator *? (nie zachłanny) zapobiega pochłanianiu tekstu PO ścieżce — ...plik.md opis v1.2 podkreśla tylko plik.md. Negatywny lookahead (?![A-Za-z]:[\\/]) nie pozwala pochłonąć kolejnej ścieżki z nowym dyskiem (C:\a\one.md and C:\b\two.md → dwa linki). Rozwiązuje C:\Projekty\new Wave\...\plik.md (spacja w „new Wave" nie rozrywa tokenu).
      • Pass 2 — tokeny rozdzielone białym znakiem (PathRunRegex, jak wcześniej): ścieżki względne, nazwa.ext, ścieżki uniksowe; token to „ścieżka" gdy ma separator /\ albo nazwa.ext (ext 1–8 z min. jedną literą, więc 1.5 odpada). URL-e (://) i flagi (-…) pomijane, końcowe ./: obcinane. Zakresy nachodzące na Pass 1 są pomijane (bez podwójnego linkowania).
    • Rozwiązywanie: absolutne (POSIX /…, Windows C:\…, UNC \\…, ~/…) jak jest; względne → sklejone z cmd:cwd bloku; normalizacja ./.. do forward-slashy.
    • Walidacja istnienia przy kliknięciu przez FileInfoCommand (notfound → no-op); bez RPC na hover (decyzja użytkownika). Remote: formatRemoteUri(path, conn) + connection przekazywane do bloku preview; natywne otwarcie tylko lokalnie.
    • Sekcja „wrapped-line / mapStrIdx" zaadaptowana z @xterm/addon-web-links (MIT, autorzy xterm.js) — tylko matematyka mapowania indeks→komórka, bo oryginał jest zaszyty na isUrl(). Atrybucja w nagłówku sekcji.

Fallback „szukaj w projekcie" (model VS Code)

Gdy kliknięta ścieżka NIE istnieje na dysku, zamiast no-op robimy wyszukiwanie po nazwie pliku w projekcie (jak Ctrl+klik na nieistniejącą ścieżkę w VS Code):

  • 0 trafień → krótki komunikat w natywnym menu („Nie znaleziono w projekcie: …").
  • 1 trafienie → otwiera od razu (podgląd lub aplikacja OS, wg gestu).
  • ≥2 trafienia → natywne menu electrona w miejscu kursora (menu.popup() bez współrzędnych = przy myszy) z listą ścieżek względnych; klik = otwórz.

Rdzeń wyszukiwania — NOWY pakiet Go pkg/filesearch/filesearch.go:

  • FindProjectRoot(cwd) idzie w górę od cmd:cwd bloku szukając markera projektu (.git/.hg/.svn/go.mod/package.json/Cargo.toml/pyproject.toml/.claude); brak → sam cwd.
  • Indeks w pamięci (zamiast chodzić po FS przy każdym kliknięciu — jak „Go to File" w VS Code): getIndex(root) buduje raz listę wszystkich plików korzenia (filepath.WalkDir, rekurencyjnie — ZWERYFIKOWANE testem: z C:/Projekty/new Wave znajduje zagnieżdżony waveterm/frontend/app/view/term/termlinks.ts i 8× SKILL.md), cache per-korzeń z TTL 30 s, singleflight przeciw równoległym budowom. Gdy wynik = 0 i indeks starszy niż 1 s → wymuszony rebuild i ponowna próba (świeżo utworzony plik jest od razu znajdowany). Pomija ciężkie katalogi (node_modules, .git, dist, build, target, .venv, vendor, .idea, … — dotfoldery .claude/.kilocode NIE pomijane). Limity: 200 000 plików w indeksie, build-deadline 15 s.
  • SearchProject filtruje indeks po basename (case-insensitive, dokładna nazwa — „jeden z taką nazwą"); przy klikniętej ścieżce z podkatalogami trafienia z pasującym ogonem idą na górę (sort: suffix→głębokość→długość→leks.), cap 50 wyników.
  • Lokalnie (dla connection set = remote fallback pomijany, bo szuka po lokalnym FS).

Nowy RPC FileSearchCommand(CommandFileSearchData) → CommandFileSearchRtnData (typy w pkg/wshrpc/wshrpctypes.go, handler w wshserver.go deleguje do filesearch.SearchProject). task generate dogenerował klienta i typy TS. go vet ./pkg/filesearch/ = 0; go test ./pkg/filesearch/ (diag rekursji) PASS.

Pliki ZMIENIONE w upstream (minimalnie)

  • frontend/app/view/term/termwrap.ts — import makeFilePathLinkProvider + jedna rejestracja w konstruktorze (tuż za WebLinksAddon), wynik wrzucony do toDispose.
  • pkg/wshrpc/wshrpctypes.go — 1 metoda interfejsu + 3 typy (CommandFileSearchData, FileSearchMatch, CommandFileSearchRtnData).
  • pkg/wshrpc/wshserver/wshserver.go — import filesearch + handler FileSearchCommand.
  • Wygenerowane (przez task generate): frontend/app/store/wshclientapi.ts, frontend/types/gotypes.d.ts, pkg/wshrpc/wshclient/wshclient.go.

Weryfikacja

  • npx tsc --noEmit ✅ — zero błędów w termlinks.ts/termwrap.ts (17 błędów w repo to istniejący dryf typów w frontend/preview/mock/* i processviewer.preview.tsx, niezwiązane).
  • go vet ./pkg/filesearch/ ./pkg/wshrpc/wshserver/ ✅ = 0.
  • UWAGA: fallback wyszukiwania wymaga przebudowy backendu (nowy RPC) → restart task dev (Windows blokuje podmianę działającego wavesrv.x64.exe).
  • Test na żywo: wymaga świeżej instancji TermWrap (provider rejestruje się w konstruktorze) → pełny reload renderera (Ctrl+Shift+R) albo nowy blok terminala.

Otwarte / do dopracowania

  • Sufiks :linia:kol jest obcinany do rozwiązania pliku, ale numer linii nie jest przekazywany do preview (brak skoku do linii) — do dodania, jeśli preview to wspiera.
  • Ścieżki bezwzględne ze spacjami — ROZWIĄZANE (Pass 1, AbsFilePathRegex). Pozostaje: katalogi bezwzględne BEZ rozszerzenia oraz uniksowe ścieżki ze spacjami nadal dzielą się na spacji (Pass 1 wymaga .ext i kotwicy dysku/UNC/home) — rzadkie, poza zakresem.
  • Hover podświetla token zawsze (xterm dekoruje link pod kursorem); VS Code pokazuje podkreślenie dopiero z Ctrl — kosmetyczna różnica, do ewentualnego dostrojenia.

T34 — Powiadomienia agentów działają dla wszystkich agentów, nie tylko Claude (feedback usera + konsultacja z Codexem)

User: „działają tylko dla Claude-a, chciałbym dla wszystkich wspieranych agentów". Diagnoza empiryczna (realne pliki configów na dysku + stringi z binarek codex.exe/opencode.exe + źródło openai/codex + spec Kitty OSC 99) wykazała, że provisioning i odbiór (renderer) są OK — configi wszystkich 4 agentów były poprawnie zainstalowane (stan v1). Problem był w emisji per agent: renderer łapał tylko OSC 9/777/bell, a Claude działał, bo Wave sam autoruje jego escape (hook terminalSequence, bezwarunkowo). Dwa niezależne root-causy:

  • Codex (pkg/agentnotify/provider_codex.go): notification_method="osc9" był dobry, ale notification_condition="unfocused" zależy od raportowania focusu przez terminal (DEC mode 1004, FocusGained/FocusLost). Codex źródłowo: unfocused == !terminal_focused, flaga aktualizowana z crossterm po włączeniu focus-reportingu. Pod ConPTY/Wave przełączenie okna/zakładki nie robi DOM-blur na textarea xtermu → Codex nigdy nie dostaje FocusLost → sądzi, że ma focus → nie emituje nic. Fix: notification_condition = "always" (Codex emituje bezwarunkowo, focus gejtuje Wave — jak przy Claude). Emisja OSC 9 z własnego stdout Codeksa jest lepsza na Windows niż zewnętrzny notify = [program] (ten musiałby pisać do CONOUT$ z podprocesu). Migracja istniejących instalacji: migrateManagedCodex reconciluje 3 zarządzane klucze (notifications/notification_method/notification_condition) w obrębie bloku z markerem (ManagedMarker → do pustej linii / innego nagłówka niż [tui]); user-owned config bez markera nietknięty. Marker dopisywany też w ścieżce insert-pod-istniejące-[tui] (wcześniej go nie było). provisionVersion 1 → 2, żeby stan v1 wymusił re-run i migrację. Testy: v1 unfocusedalways, idempotencja, unmarked user config, scoping ([other] notifications = false nietknięte) — go test ./pkg/agentnotify/ ✅.
  • OpenCode (frontend/app/view/term/agentnotify.ts + termwrap.ts): attention.enabled domyślnie false (nasz config true poprawnie włącza), ale desktopowe powiadomienia OpenCode idą przez OSC 99 (protokół Kitty/opentui: ESC]99;i=opentui-notifications:p=<title|body>:e=1:d=<0|1>;<base64>ST), którego Wave nie rejestrował → przepadały. Fix: nowy handleOsc99Notification + rejestracja OSC 99 w termwrap.ts. Parser wg spec Kitty: metadane (klucze key=val po : przed pierwszym ;), payload base64 przy e=1 (dekoder UTF-8-safe base64ToString, nie atob), p domyślnie title, d domyślnie „done" (d=0 = kolejne chunki), grupowanie chunków po (blok, i), flush na końcu → wspólny lejek notifyAgentEvent (kropka + chime + OS-notyfikacja). Kontrolne p (p=? capability query, p=close, p=alive, nieznane) są ignorowane.
  • Korekta guardu OSC 9 (z konsultacji Codeksa): stary ^\d+(;|$) ucinał każdy payload zaczynający się cyfrą, a tekst turn-complete Codeksa to podgląd odpowiedzi asystenta i może zaczynać się od cyfr (np. „1."). Zawężono do samego kształtu progresu ConEmu/WT: ^4;\d (subkomenda 4).

Konsultacja z Codexem (agent codex:codex-rescue, read-only): plan CONFIRMED. Potwierdził semantykę unfocused/always, że osc9 emituje ESC]9;<msg>BEL i że jest lepszy niż notify=[program] na Windows; skorygował guard OSC 9 (leading-digit), domyślne p/d i grupowanie po i w OSC 99, oraz migrację (reconcile zarządzanych kluczy, nie tylko condition; zostaw user-owned). Highest-risk (odłożone do testu na żywo): OpenTUI sonduje możliwości terminala (batch OSC 99 p=? + iTerm2 OSC 1337;Capabilities + Kitty-graphics + DECRQM) i może nie emitować, jeśli terminal nie odpowie. Emulacji handshake nie robimy (złożona, ryzyko wysłania błędnej odpowiedzi > korzyść) — OpenTUI ma fallbacki (method_9), a Wave łapie teraz wszystkie trzy metody (9/99/777), więc niezależnie od wyboru metody escape zostaje przechwycony. Do weryfikacji na żywo; jeśli mimo to nie strzela, kolejny krok = odpowiedź na sondę p=? (jest już ścieżka zapisu do pty: termWrap.sendDataHandler).

Zakres: Cursor niezainstalowany (pominięty; ma znany haczyk CONOUT$-z-podprocesu na Windows). Antigravity/Gemini = launch-only, bez mechanizmu notify. Efekt/uruchomienie: zmiana Go (Codex) wymaga restartu task dev — na starcie wavesrv widzi stan v1 < v2 i automatycznie migruje ~/.codex/config.toml (unfocusedalways), bez ręcznej reinstalacji. Handler OSC 99 (renderer) łapie się po Ctrl+Shift+R (restart też obejmie). tsc ✅ = 0, go test ./pkg/agentnotify/ ✅.


T35 — Hotfix: crash procesu main na starcie („Cannot access 'WaveHomeVarName' before initialization")

Po restarcie task dev (wymuszonym przez zmianę Go z T34) aplikacja padała na starcie: ReferenceError: Cannot access 'WaveHomeVarName' before initialization w getWaveHomeDir (dist/main/index.js). Regresja z T32 (rebranding taskbara/toastów Windows): w emain/emain-platform.ts blok if (isDev) registerWindowsDevIdentity( getWaveDataDir()) był przed deklaracjami const WaveConfigHomeVarName/WaveDataHomeVarName/WaveHomeVarName. getWaveDataDir()getWaveHomeDir() czyta process.env[WaveHomeVarName], więc top-levelowe wywołanie łapało TDZ stałej (funkcje są hoistowane, const nie). Bomba uśpiona, bo zmiana w emain działa dopiero po pełnym restarcie task dev — dopiero ten restart ją odpalił. Fix: przeniesiono trzy const Wave*VarName przed blok if (isDev), z komentarzem-ostrzeżeniem o kolejności. tsc ✅ = 0. Wymaga pełnego restartu task dev (emain nie ma hot-reloadu).


T36 — Rebrand paczki na „Terminal Cloud" + odcięcie zależności od infrastruktury Wave (build standalone)

Przygotowanie samodzielnej aplikacji: nazwa/ikona Terminal Cloud zamiast Wave oraz — kluczowe — usunięcie wszystkich runtime'owych „phone-home" do serwerów Wave/Command Line Inc, żeby fork nie wysyłał danych ani nie podmienił się sam na upstream. Audyt zależności miał zrobić Codex, ale jego sandbox padł w tej sesji (CreateProcessAsUserW 1312 / brak sandboxPolicy) i słusznie odmówił zmyślania — audyt przeprowadzony ręcznie (grep całego repo).

Branding (nazwa + ikona + homepage):

  • package.json: productName „Wave" → „Terminal Cloud" (electron-builder: executableName/artifactNameTerminal Cloud.exe), build.appId dev.commandline.wavetermeu.cloudbots.terminalcloud (spójnie z dev TerminalCloudDevAppId z tcicon.ts), homepagehttps://terminal.cloudbots.eu, description → tagline forka.
  • build/icon.ico → podmienione na public/logos/terminal-cloud.ico (7 warstw, do 256×256); oryginał Wave zachowany jako build/icon.ico.wave-orig. (mac icon.icns zostaje — build macOS poza zakresem.)
  • emain/emain-platform.ts: app.setName(...) → „Terminal Cloud" / „Terminal Cloud (Dev)" (etykiety menu roli; linia 18 app.setName("waveterm/electron") zostaje — to ona wyznacza katalog danych electrona).
  • Widoczne stringi „Wave Terminal" w shippowanym emain/: emain-menu.ts „About Wave Terminal" → „About Terminal Cloud"; emain.ts dialog wyjścia „…quit Wave Terminal?" → „…quit Terminal Cloud?"; updater.ts tytuł/treść powiadomienia (nieaktywne, ale spójne).

P0 — odcięcie phone-home (audyt):

  • Auto-update → serwer Wave (KRYTYCZNE). Domyślnie WŁĄCZONY (autoupdate:enabled:true, installonquit:true, co 1h), feed dl.waveterm.dev/releases-w2 → fork sam zainstalowałby release Wave na wierzch. Fix: settings.json autoupdate:enabled/installonquitfalse; electron-builder.config.cjs publish.urlhttps://terminal.cloudbots.eu/releases (nawet ręczne „Check for updates" nie ściągnie Wave).
  • Telemetria → chmura Wave. pkg/wcloud wysyła usage/activity/TEvent do api.waveterm.dev/central + diagnostic ping do ping.waveterm.dev/central. telemetry:enabled domyślnie true, a SendDiagnosticPing odpala na starcie niezależnie od flagi. Fix dwutorowy: settings.json telemetry:enabledfalse ORAZ pkg/wcloud/wcloud.go stałe WCloudEndpoint/WCloudPingEndpoint"" (w buildzie prod GetEndpoint zwraca puste → każdy send to graceful no-op; dev niedotknięty — czyta WCLOUD_ENDPOINT z env).
  • Brak crashReporter, brak rejestracji schematu URL waveterm:// (setAsDefaultProtocolClient nie występuje) — nic do usuwania.

P1 (zanotowane, nie zmienione): domyślne tryby Wave AI (waveai.json, waveai:showcloudmodes:true, default waveai@balanced) routują przez proxy cfapi.waveterm.dev — ale to opt-in (tylko gdy user użyje panelu AI) i dla forka i tak nie zadziała (brak kredytów Wave); user może wpiąć własny ai:baseurl. Link FAQ ARM64 w emain-platform.tsdocs.waveterm.dev (ścieżka tylko przy translacji ARM64 — rzadka). Identyfikatory WaveAI* (klasy, komendy RPC GetWaveAIChatCommand…) to nazwy wewnętrzne — nie ruszane (inwazyjny refactor + codegen).

Build: task package -- --win dir (toolchain z start-dev.cmd, wavesrv z zig CC). Na Windows electron-builder zawsze rozpakowuje cache winCodeSign (nawet dla dir/bez podpisu), a jego archiwum ma dowiązania symboliczne (macowe .dylib) → 7za ... -snld pada „A required privilege is not held by the client" i electron-builder wychodzi z kodem 1 zanim zdąży odpalić rcedit (ikona + metadane exe). Katalog aplikacji jest już wtedy w pełni złożony (exe + app.asar + wavesrv.x64.exe + wsh), więc standalone działa — ale exe ma domyślną ikonę/metadane Electrona. Obejście (zastosowane): ręcznie odpalony node_modules/electron-winstaller/vendor/rcedit.exe na make/win-unpacked/ Terminal Cloud.exe--set-icon build/icon.ico, --set-version-string ProductName/FileDescription "Terminal Cloud", CompanyName "CloudBots" (to samo narzędzie, którego użyłby electron-builder). Po tym: ProductName/FileDescription = Terminal Cloud, ikona Terminal Cloud. Czysty build (auto-rcedit + instalator NSIS) wymaga włączenia Trybu programisty Windows (albo uruchomienia jako admin), co pozwala 7za tworzyć symlinki. Wynik: make/win-unpacked/ Terminal Cloud.exe — przenośny, samowystarczalny standalone z kompletem poprawek (T34 powiadomienia, T35 fix TDZ, T36 rebrand+de-Wave). tsc ✅ = 0.


T37 — Domyślna ikona workspace'a: logo Wave → znak Terminal Cloud (maska SVG, bez przebudowy webfonta)

User zauważył zielone logo Wave w przełączniku workspace'ów. To domyślna ikona workspace'a: pkg/wcore/workspace.go WorkspaceIcons[0] = "custom@wave-logo-solid", renderowana jako fa-kit fa-wave-logo-solid (glif z customowego webfonta public/fontawesome/webfonts/custom-icons.woff2), tintowana kolorem workspace'a (WorkspaceColors[0]=#58C142). Fix (renderer-only, bez ruszania .woff2 ani configu): nadpisanie w frontend/app/theme.scss — reguła .fa-kit.fa-wave-logo-solid::before { content:"" !important; background-color: currentColor; mask: url("data:image/svg+xml,…heks…") center/contain }. Technika mask + currentColor renderuje mintowy znak Terminal Cloud (heks + > + kreska) i zachowuje tintowanie kolorem workspace'a jak oryginalny glif; zmiana obejmuje wszystkie miejsca użycia (istniejące + nowe workspace'y), bo nadpisuje samą klasę glifu. Znak to ten sam mark co w emain/tcicon.ts/greetingu (viewBox 0 0 52 58). Weryfikacja przez Playwright: SVG się ładuje (naturalSize 134×150), maska maluje 2716 niepustych pikseli, ::before bg = kolor workspace'a. Renderer → w dev łapie się po Ctrl+Shift+R, w standalone wymaga przebudowy paczki. (Ikona jest jednokolorowa/tintowana — domyślny workspace pokazuje ją na zielono #58C142; stały mint = zmiana WorkspaceColors[0] lub hardcode koloru w regule.)

T38 — Publikacja forka: publiczne repo GitHub + atrybucja upstreamu + podmiana linków na stronie (prośba usera)

User: „trzeba zrobić repo dla tego projektu i połączyć że to fork dla Wave oraz dać to na stronę gdzie to można pobrać". Decyzje usera (AskUserQuestion): namespace futureofworktraining, widoczność public, powiązanie = standalone repo + atrybucja (nie GitHub-fork relationship).

Repo: https://github.com/futureofworktraining/terminal-cloud (public, default branch agent-sessions, homepage terminal.cloudbots.eu, topiki: terminal / wave-terminal / ai-agents / claude-code / electron / developer-tools).

  • Remote origin (był = upstream Wave) przemianowany na upstream (wavetermdev/waveterm); nowy origin = repo forka.
  • Repo lokalne było shallow (.git/shallow, 11 commitów) → pierwszy push padał (did not receive expected object, index-pack failed). Fix: git fetch --unshallow upstream (pełna historia, 2656 commitów), potem push agent-sessions. Nie pushowano tagów Wave (release'y upstreamu) — strona releases forka zostaje czysta na własne wydania.
  • Przed pushem: skan sekretów (0 realnych credentiali; publiczny klucz Algolia DocSearch odziedziczony z upstreamu, benign). Commit batcha ~97 plików (rebrand + powiadomienia + attention + context-menu + onboarding).

Atrybucja (żeby „było widać że to fork Wave"):

  • README.md — banner „Terminal Cloud" na górze (co dodaje fork + link do wavetermdev/waveterm, Apache-2.0, CloudBots, terminal.cloudbots.eu), pod nim --- i oryginalny README Wave; nagłówek # Wave Terminal## Wave Terminal (upstream base).
  • NOTICE — dopisana linijka: fork Wave Terminal na Apache-2.0, modyfikacje developed by CloudBots, patrz MODIFICATIONS.md.

Strona / docs (poza repo git — w root workspace'u): placeholder repo URL github.com/cloudbots/terminal-cloud (zmyślony, nieistniejący) → github.com/futureofworktraining/terminal-cloud w website/index.html (4×), website/README.md, docs/getting-started.md, docs/README.md. Sekcja Download zostaje „coming soon" + beta-mailto (brak wydanych binarek; publiczne repo = pobranie/build ze źródeł). Zob. [[website-and-docs]].

Deploy (Hetzner, [[terminal-cloud-deploy]]): wersja serwerowa index.html różni się od lokalnej — względne ../docs/... przepisane na absolutne linki GitHub do gałęzi agent-sessions (/blob|/tree/agent-sessions/docs/..., bo serwer serwuje sam index.html). Wgrane na /opt/terminal-cloud/site/index.html (backup index.html.bak-<ts>, nginx :ro, bez restartu). DNS już rozwiązuje (terminal.cloudbots.eu49.13.145.82, HTTP 200 przez HTTPS/Caddy) — rekord A dodany od czasu notatek. Live-check: 7× nowy URL, 0 starych placeholderów, repo publicznie HTTP 200 dla anonima.