From ac519c53497b2a4f3db9aa2502fc4ed6a6792dbf Mon Sep 17 00:00:00 2001 From: Armando Fernandez Date: Tue, 28 Jul 2026 12:54:21 -0700 Subject: [PATCH 1/2] Retarget MandoCode Desktop to .NET 10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The harness stays on net8.0 (no change needed there — a higher-TFM project referencing a lower one is standard and fully supported), but Desktop's own csproj was still net8.0-windows10.0.19041.0. Verified clean: a full rebuild, a forced restore, the 223-test suite, and a self-contained win-x64 Release publish all succeed against net10.0-windows10.0.19041.0 with the existing WindowsAppSDK 2.2.0 package. Also bumps both GitHub Actions workflows' pinned dotnet-version from 8.0.x to 10.0.x — left at 8.0.x, the release pipeline would have failed the moment this merged, since the runner would have no SDK able to build a net10.0 target. --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- src/MandoCode.Desktop/MandoCode.Desktop.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 178d85d..ab7004a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 10.0.x - name: Build run: dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj -c Release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1558fb..664557e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 10.0.x - name: Publish (self-contained win-x64) run: dotnet publish src/MandoCode.Desktop/MandoCode.Desktop.csproj -c Release -r win-x64 --self-contained -o publish diff --git a/src/MandoCode.Desktop/MandoCode.Desktop.csproj b/src/MandoCode.Desktop/MandoCode.Desktop.csproj index 9ab53c6..ca9514f 100644 --- a/src/MandoCode.Desktop/MandoCode.Desktop.csproj +++ b/src/MandoCode.Desktop/MandoCode.Desktop.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows10.0.19041.0 + net10.0-windows10.0.19041.0 10.0.19041.0 MandoCode.Desktop app.manifest From af6493304978a15463e1c9b21b6fbc8a9710dc55 Mon Sep 17 00:00:00 2001 From: Armando Fernandez Date: Tue, 28 Jul 2026 12:55:18 -0700 Subject: [PATCH 2/2] Rewrite the README to lead with the pitch instead of the internals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old README opened with submodule/harness plumbing before ever saying what the app does or why someone would want it. Restructured so a first- time visitor sees the value proposition, a screenshots section, and a benefit-driven feature list first; install/build instructions stay close to where they were. All of the deep internals that used to open the README — the harness pin and rolling instructions, the CLI/Desktop layer map, per-agent vs app-wide service tables, split view/snapshots/notes/history internals, why the tab strip isn't a TabView, and the full v0.1 feature inventory — move to a new docs/ARCHITECTURE.md wholesale. Nothing was cut, just relocated. docs/screenshots/README.md lists the exact filenames the new README's placeholders expect, so dropping images in that folder is the only step left to make them appear. --- README.md | 446 ++++++++++--------------------------- docs/ARCHITECTURE.md | 328 +++++++++++++++++++++++++++ docs/screenshots/README.md | 20 ++ 3 files changed, 460 insertions(+), 334 deletions(-) create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/screenshots/README.md diff --git a/README.md b/README.md index 72775fc..d199bc2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,97 @@ -# MandoCode Desktop (WinUI 3) - -The MandoCode AI coding agent with a native Windows desktop interface. Same engine -as the [MandoCode CLI](https://github.com/DevMando/MandoCode) — literally: the CLI -repo is pinned here as a **git submodule** (`/MandoCode`) and this app project-references -`MandoCode/src/MandoCode/MandoCode.csproj`, reusing the entire harness (AIService, -task planner, plugins, MCP, skills, config, approvals, token tracking). Only the -user interface is different: WinUI 3 instead of RazorConsole. +

+ MandoCode Desktop icon +

+ +

MandoCode Desktop

+

An AI coding assistant that lives on your machine, not someone else's server.

+ +

+ Latest release + License: MIT + .NET 10 + Platform: Ollama + Engine: MandoCode CLI +

+

Made with <3 by Mando

+ +--- + +Most AI coding tools assume you're always online, sending your code to someone else's servers, on +someone else's subscription. MandoCode doesn't have to. It runs entirely on your own machine through +[Ollama](https://ollama.com) — free, private, no API key, nothing metered per token — or, when you +want more horsepower, a cloud model when *you* choose to reach for one. Same assistant either way, +same conversation, your call. + +MandoCode Desktop takes that assistant and gives it an actual home on Windows, instead of a single +chat window bolted onto a terminal: several agents working in parallel, a real integrated shell, a +git-aware file browser, a notes pad, and enough personality in the theming that it doesn't have to +look like every other dev tool on your taskbar. + +It's built on the exact same engine as the [MandoCode CLI](https://github.com/DevMando/MandoCode) — +literally the same code, pinned in as a submodule — so nothing about how it thinks is different. +Only the interface is. + +## Screenshots + +*(Drop your images into `docs/screenshots/` with the filenames below and they'll show up here — +see `docs/screenshots/README.md` for the exact list.)* + +

+ Chat with multiple agent tabs +

+ + + + + + + + + + + + + + +
Split view with agents side by sideIntegrated terminal
Notes jot pad with AI assistGit-aware file explorer
Cathode Ray CRT themeWindows 98 theme
+ +## Why it exists + +Two problems, one app: + +- **You shouldn't need a cloud subscription to have an AI pair programmer.** MandoCode is + local-first — point it at Ollama and it runs on your own hardware, on your own data, for free. A + cloud model is there if you want the extra ceiling, never because the app requires it. +- **A coding assistant is something you live in all day, not a popup.** So it gets a real app: + multiple agents open at once, a real shell, a file tree that actually knows about git, a place to + jot down a thought without opening a text editor, and a look you can make your own instead of one + fixed dark theme. + +## Features + +- **Agent tabs, and Split view for up to four at once** — each tab is its own independent + conversation, project folder, and model. Split view puts two, three, or four side by side so you + can watch several agents work in parallel instead of babysitting one at a time. +- **A real integrated terminal** — PowerShell 7, Windows PowerShell, cmd, Git Bash, or WSL, running + as an actual shell (not a fake console), opened in the active agent's project folder. +- **Git-aware file explorer** — a live file tree with branch, status, and dirty badges, inline diff + cards, one-click commit, and drag-to-reference straight into the chat. +- **Context snapshots & session history** — closing an agent archives its conversation instead of + deleting it; reopen any past conversation later with its transcript and, when the model supports + it, its full memory. Snapshots let you carry an AI-written recap of one conversation into a + completely different model or a fresh agent. +- **Notes** — an always-there jot pad, separate from any project, with optional AI help. The + assistant has no file tools here at all — the only way a reply reaches your note is a button you + press, so nothing gets written without you. +- **Skills & MCP** — teach the assistant new, reusable capabilities (install one from a folder or a + zip, or have it write its own), and connect external tools over MCP. +- **16 built-in themes** — from Dracula, Tokyo Night, and One Dark Pro to a genuinely period-correct + Windows 98 desktop and a flickering Cathode Ray CRT tube, plus your own background image behind + the chat. +- **A tiny built-in music player** — lofi and synthwave come bundled, or point it at any folder of + your own MP3s. +- **Guided first-run setup** — on first launch it finds (or installs) Ollama for you and helps you + pick a starter model, right in the chat. No config file to hand-edit before you can say hello. ## Install (no build needed) @@ -13,17 +99,16 @@ Download the latest `MandoCode.Desktop-*-win-x64.zip` from [Releases](https://github.com/DevMando/MandoCode.Desktop/releases), extract it anywhere, and run `MandoCode.Desktop.exe`. The zip is fully self-contained — **no .NET install required**. -On first launch the app runs a **guided setup right in the chat**: it finds Ollama (offering to +On first launch the app runs a guided setup right in the chat: it finds Ollama (offering to install it via winget if it's missing), starts the daemon, and helps you pick a first model — a cloud model (best quality, no GPU needed, free ollama.com sign-in) or a local one from a short -list with size and hardware hints. Re-run the wizard any time by typing `/setup` or with the -**Run guided setup** button in Settings. +list with size and hardware hints. Re-run the wizard any time with `/setup` or the **Run guided +setup** button in Settings. Requirements: Windows 10 (1809+) or Windows 11, with the WebView2 runtime — preinstalled on -Windows 11 and kept current by Edge on Windows 10. Models are served by -[Ollama](https://ollama.com); you don't need it installed beforehand, the wizard handles it. +Windows 11 and kept current by Edge on Windows 10. -## Clone & build +## Build from source ``` git clone --recursive https://github.com/DevMando/MandoCode.Desktop.git @@ -35,331 +120,24 @@ dotnet run --project src/MandoCode.Desktop Already cloned without `--recursive`? The `MandoCode/` folder will be empty — run `git submodule update --init` and build again. -Built exe: `src\MandoCode.Desktop\bin\Debug\net8.0-windows10.0.19041.0\win-x64\MandoCode.Desktop.exe` -- `MandoCode.Desktop.exe ` — open with that folder as the project root - (otherwise the current directory; changeable in-app via the folder button). - -Requires the WebView2 runtime (preinstalled on Windows 11) and a reachable -Ollama (`ollama serve`). Uses the same config file as the CLI, so both apps -share endpoint/model/settings. - -## The harness submodule - -`/MandoCode` is pinned at a specific CLI commit — radical changes in the CLI repo -can never break this app until the pin is deliberately moved. To roll the harness -forward (do this against CLI release tags, not random commits): - -``` -cd MandoCode -git fetch && git checkout v0.14.0 # or origin/main -cd .. -dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj # fix what the compiler flags -git add MandoCode && git commit -m "Roll harness to v0.14.0" -``` - -When rolling, also eyeball the three *ported* seams for behavioral drift (they -compile independently of the CLI's originals): - -| Desktop port | CLI original | -|---|---| -| `ViewModels/ChatController.cs` | `Components/App.razor` interactive loop | -| `Services/WinUiApprovalService.cs` | `Services/Approval/DiffApprovalHandler.cs` | -| `Services/TranscriptHtmlBuilder.cs` | `MarkdownHtmlRenderer` / `OperationDisplayRenderer` | - -The harness is safe to instantiate once per agent — its statics are pure functions and readonly -`Regex`, and `AIService` takes every collaborator by constructor. Rolling the pin forward, watch -for that changing. - -## Architecture - -| Layer | CLI (MandoCode) | Desktop (this app) | -|---|---|---| -| Orchestrator | `Components/App.razor` interactive loop | `ViewModels/ChatController.cs` (faithful port), one per agent | -| Approvals | `DiffApprovalHandler` (Spectre panels) | `Services/WinUiApprovalService.cs` + each agent's own XAML overlay (same labels, bypass state, `DiffApprovalResult` contract) | -| Transcript | ANSI scrollback + Spectre renderables | WebView2 + `TranscriptHtmlBuilder` (Markdig HTML, themed) | -| Busy/spinner | `SpinnerService` (ANSI) | `BusyStateService` → ProgressRing | -| Onboarding | `OnboardingFlow` terminal prompts | In-chat guided wizard (auto on first launch, `/setup` after) + Settings page | -| Everything else | `Services/`, `Plugins/`, `Models/` | **reused verbatim via project reference** | - -Key seams the harness already provided (unchanged): `AIService.ChatStreamAsync`, -`OnWrite/Delete/CommandApprovalRequested` delegates, `PlanHandoff.OnPlanRequested`, -`McpApprovalGate.OnApprovalRequested`, `TaskPlannerService.ExecutePlanAsync` -progress events, `DiffService` diff models. - -## Agents - -Each tab is an independent agent. `Services/AgentSession.cs` hand-constructs one agent's object -graph; `SessionManager` owns the set of them. The split matters: - -| Per agent | App-wide | -|---|---| -| `AIService` (its conversation, its model), `ChatController`, `TaskPlannerService` | The `MandoCodeConfig` on disk — the **defaults** a new agent starts on | -| `MandoCodeConfig` clone, `ProjectRootAccessor`, `SkillLoader`, `FileAutocompleteProvider` | `McpClientManager` (one set of server processes) | -| `TokenTrackingService`, `PlanHandoff`, `TranscriptWriter`, `BusyStateService`, `ShellRunner` | `MusicPlayerService`, `ThemeManager` (static), `TranscriptHtmlBuilder`, `SpinnerService` | -| `WinUiApprovalService`, `ApprovalPromptGate`, `McpApprovalGate` | `ConfigCoordinator`, `McpCoordinator`, `SkillCoordinator`, `SessionManager`, `SnapshotStore`, `SessionArchiveStore`, `NoteStore`, `UiUpdateCheckService` | - -Tabs default to `Agent 1`, `Agent 2`, … (the folder shows in the header); the number reuses the -lowest free slot, and a rename or folder change never overwrites it. Each tab's `⋯` options menu -carries Rename, Take snapshot, Export transcript, and Close. The model in each header opens a -quick-switch dropdown (cloud first, `cloud`/`local` badges) rather than a full-screen picker. - -Closing the **last** agent is allowed: it leaves a clean empty state (showing the chat background) -with a one-click way to start a new agent. Actions that need an agent to act on — the Settings and -MCP pages, and snapshot Import — disable while none is open, then re-enable when you open one. - -### Split view (2–4 agents at once) - -The **Split** button puts two agents side by side in a resizable view, and further panes are added -from the split bar's **Add pane** button or a tab's **Add to split view** (its `⋯` menu, or -right-click). **Add pane** is a `SplitButton`: clicking it panes the next agent that isn't shown -yet, while its chevron lists the agents still available so you can pick a specific one — the same -shape as the terminal's shell picker. The layout follows the pane count: two side by side, three -across, four as a 2×2 — -past three, columns alone leave each pane too narrow for a transcript plus an input box. Every -divider is draggable, and each one repartitions only the two panes either side of it, so adjusting -one split never nudges a third pane. - -It's called *split view* rather than *compare* because comparing two models on the same prompt is -only one of the things it's for: with three or four panes open you're usually watching agents work -in parallel on different folders, not comparing their output. - -The pane set is an explicit, remembered choice — never set by plain-clicking a tab. Clicking a paned -agent's tab shows the split; clicking any other agent shows it normally while the set waits. -Dropping below two panes turns the split off and leaves you on the agent that survived. The set and -its divider positions persist across restarts (in `workspace.json`, keyed by each agent's durable -persist-key so a skipped project folder drops one pane rather than shifting all of them). - -Panes are ordinary agent views moved between grid cells with `Grid.SetColumn`/`Grid.SetRow` — -**never re-parented**, so every WebView (and its live transcript) survives the switch, which is the -whole reason the tab surface is built the way it is (see below). The row and column tracks are -rebuilt in code per pane count, interleaving a divider track between adjacent panes; track -definitions and dividers are the only things that change, so no agent view ever leaves the tree. -The geometry and divider math live in `Services/PaneLayout.cs`, kept free of WinUI types so they're -unit tested directly. - -The split bar uses chips with `MenuFlyout` pickers rather than `ComboBox`es on purpose: rebuilding -ComboBox items as containers makes WinUI throw `COMException 0x80070490` on the next selection. +`MandoCode.Desktop.exe ` opens with that folder as the project root (otherwise the current +directory; changeable in-app via the folder button). Requires a reachable Ollama (`ollama serve`). +Uses the same config file as the CLI, so both apps share endpoint/model/settings. -The three approval services are per-agent for **correctness**, not tidiness. Shared, they break -in ways that are invisible until a second tab exists: `WinUiApprovalService` holds the -"don't ask again" bypass set, so one agent's blanket approval would auto-approve writes in every -other; `ApprovalPromptGate` is a `SemaphoreSlim(1,1)` built for one console, so an unanswered -approval in one agent would stop another's from ever rendering; and `ChatController` **assigns** -(not `+=`) five approval delegates, so on shared services the last agent constructed silently -steals every approval. +## Under the hood -One setting can't be per-agent and is labelled app-wide in the UI: **Appearance** is a property -of the window (and lives outside the shared config). **Context window** used to share that label — -it was applied as `OLLAMA_CONTEXT_LENGTH` at daemon start, one daemon, one window — but it now -rides on every chat request as `num_ctx`, so each agent's own value governs its own conversations, -like any other per-agent setting. - -### Settings and the config file - -`~/.mandocode/config.json` is not "the current settings". It is the **defaults a new agent starts -on**. Editing Settings changes the selected agent, live, for that session; **Make Default for New -Agents** is the only action that writes the file (plus corrections like a healed endpoint URL, the -onboarding flag, and the app-wide MCP server list). - -`ConfigCoordinator` is the only code that calls `MandoCodeConfig.Save()`. The rule can't be -enforced by the type system — `Save()` is public and non-virtual on a harness type — so the -`MANDO001` build target fails the build if `ChatController` calls `_config.Save()` on its clone, -which would publish one agent's model as everybody's default. Cloning is a JSON round-trip -**followed by `ValidateAndClamp()`**: `System.Text.Json` rebuilds `McpServers` with a -case-sensitive comparer, and without the clamp every MCP lookup in the clone silently misses on -a casing difference. - -### Context snapshots - -A snapshot is a portable, AI-written recap of a conversation — save the gist of one agent's -context and carry it into another model or a fresh agent. Snapshots are offered when switching a -model would clear the conversation, and on demand via `Take snapshot` (tab `⋯` menu), which goes -straight to the save step. The recap is generated by `SnapshotEnhancer` (a bare, tool-less Ollama -kernel, map-reduce over the full history so nothing is truncated) using a summarizer model you -pick; a snapshot is therefore always born with a real recap — there is no "light"/un-enhanced state. -Leave the name blank and the summarizer proposes a short title, which is then made unique against -existing titles in code (`SnapshotNaming`) — an LLM can't be trusted to guarantee that itself. - -The **Snapshots** rail icon opens a global panel (the `SnapshotStore` is app-wide, one list for -every tab, **persisted** to `snapshots.json`). Cards **group by project** and are **searchable**, -and each project group is a collapsible `Expander` whose fold state is remembered -(`PanelState` → `panel-state.json`). **Import** arms a snapshot's recap to ride along, invisibly, -with the active agent's next message — carrying context into any model. The rail badge is an -**unread count** (snapshots captured since you last opened the panel), not a running total, and -clears when you open it. - -### Session history (reopen closed conversations) - -Closing an agent no longer discards its conversation — it **archives** it. `SessionArchiveStore` -keeps an app-wide index (`sessions.json`) of closed conversations; the transcript, model memory, and -conversation-log journals stay on disk (see [docs/session-persistence.md](docs/session-persistence.md)). -The **History** rail panel lists them (grouped by project, searchable, collapsible), and **Open** -reopens one as a fresh tab on its original persist-key so the normal restore cascade replays the -transcript and — when the model supports it — rehydrates the full memory. `/clear` still forgets a -conversation for good; only *closing* softened from "gone" to "recoverable." The archive is capped -(newest 60); evicting a row deletes its journals so the on-disk stores stay bounded. - -### Notes (the jot pad) - -The **Notes** rail panel is an always-there scratchpad: plain text files under -`~\.mandocode\notes`, beside the config file the CLI shares. **New** creates one and opens an editor docked next to the -chat — autosaved on a 1.2s debounce (plus Ctrl+S, leaving the note, closing the panel, closing the -window), renameable in place, with **Show in Explorer**. - -Notes are **app-wide**, the same call as snapshots and session history. A note is something you want -to write down *now*, which is often between projects or before an agent is even open, so nothing here -needs one. What survives of "which project was this about" is a plain **subfolder**: a new note is -filed under the active agent's folder name when there is one, and sits loose at the top when there -isn't. Grouping therefore costs no metadata, can't drift, and is corrected by dragging files around -in Explorer. - -**The filesystem is the store** — no index, no JSON, which is also why the pad lives in -`~\.mandocode` rather than LocalAppData: these are your files, meant to be greppable, syncable, and -openable in any editor. Discovery walks one folder plus its immediate subfolders (one level only — a -jot pad with a hierarchy is a filing system, and the search box is a better answer to "where did I -put it"). In exchange, no row can ever point at a file that isn't there: a note added in Notepad -shows up, and one deleted outside the app disappears. Search matches note **bodies** and quotes the -matching line on the card; only search and the assistant see a body truncated, capped at -`NoteStore.MaxTextBytes` (128 KB). - -#### The prompt bar - -Both surfaces — the open note and the list — carry a prompt at the bottom. It's chat-shaped, but the -document above it is your note rather than a transcript, so replies land in the bar's own strip and -reach a note only through **Insert** (at the cursor, replacing the selection if there is one) or -**Replace note**. With text selected, the question is about the selection. - -The note's text travels **in the message**, taken from the live editor buffer — so the model always -sees the note as it is right now, including keystrokes autosave hasn't written yet, with nothing to -fall out of sync. Only the current message carries the note; earlier turns in the thread keep just -their words, so a long back-and-forth doesn't ship five stale copies. On the list, the question is -about the pad: every note's title and first line, plus the full text of whatever the search box is -currently matching, and the bar states what it was given (`12 notes listed · 3 read in full`) because -a capped read that looks total is the one thing an "ask about all my notes" box must not do. - -`Services/NoteAssistant.cs` builds a bare Ollama kernel with **no plugins, filters, or tools** — the -same shape as `SnapshotEnhancer`. That's the design, not an optimization: with no file tools, "nothing -writes your note but you" is true by construction rather than by policy, and no approval machinery is -needed because the only route from a reply into a note is a button you pressed. It also isn't an -`AIService` agent, because those exist to change your files (the opposite of what a notepad wants) -and are scoped to a project root the pad deliberately sits outside of. Its model is picked from the -chip under the prompt, defaults to the app-wide default model, and is remembered; the thread is -per-note, in memory, and cleared when you switch notes — notes aren't conversations. - -The editor is not the only thing that writes these files, and doesn't assume it is. A -`FileSystemWatcher` compares the file against what the editor last wrote: identical means the write -was ours; changed-while-clean is adopted silently; changed-while-you-were-typing raises a conflict you -resolve — *use the version on disk* or *keep what I typed*. A note deleted from under unsaved edits -offers to save it back. `Services/NoteText.cs` handles the newline round trip, because a WinUI -`TextBox` normalizes every newline to a bare CR — write that straight back out and a Notepad-authored -CRLF note becomes one endless line. Both of those are why `NoteStore`, `NoteEntry`, and `NoteText` are -WinUI-free with an injectable pad root: the tests drive them against real temp folders. - -Notes deliberately lack the **Delete all *n*** group button that Snapshots and History carry: those -are artifacts the app generated, a note is something you wrote by hand, and one button that deletes a -folder's worth of writing is a different class of risk. - - -### Why the tab strip isn't a `TabView` - -WinUI's `TabView` hosts only the selected item's content, which detaches the previous tab and -closes its `CoreWebView2`. `TranscriptWriter` retains nothing — the WebView2 DOM is the only copy -of a conversation — so that would destroy the transcript on every tab switch. The strip carries -headers only; content lives in `Visibility`-toggled sibling panels and is never re-parented. +MandoCode Desktop reuses the CLI's entire harness — the AI service, task planner, plugins, MCP, +skills, config, approvals, token tracking — as a pinned git submodule. Only the interface layer +(agents, split view, the terminal, notes, theming, and everything else that makes this a native app +rather than a console) is new. If you want the deep dive — how the harness submodule is pinned and +rolled forward, how agents and split view are wired, how snapshots/notes/history actually persist — +see **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)**. ## Releasing -Push a tag (`git tag v0.2.0 && git push --tags`) and the Release workflow -publishes a self-contained win-x64 zip to GitHub Releases. The app's built-in -update checker watches those releases — older installs show an update notice -within 24 hours. - -## What's in v0.1 - -- Chat with streaming turns, function-call operation cards, markdown transcript -- Write / command / delete / MCP approvals with native diff viewer (same - approve / don't-ask-again / deny / new-instructions / cancel-plan semantics) -- propose_plan flow: plan table, execute/reject/cancel, per-step progress bar, - step-failure skip/cancel -- Slash commands with autocomplete: /help /setup /clear /model /config /retry /learn - /copy /copy-code /skills /force-skill /mcp /mcp tools /mcp remove /mcp-reload - /music* /command /exit — plus `!cmd` shell escape and `@file` references -- Token tracking + per-response summaries, per agent -- Agent tabs — `+` opens another agent (`Agent 1`, `Agent 2`, …) with its own - conversation, project folder, model, and settings; an approval waiting in a - background agent badges its tab and the toast names it. Each tab's `⋯` menu: - Rename, Take snapshot, Export transcript, Close. The header model opens a - quick-switch dropdown (cloud first, `cloud`/`local` badges). Closing the last - agent is allowed and leaves an empty state that shows the chat background -- Split view — the **Split** button shows two agents side by side, and **Add pane** - (or a tab's **Add to split view**) grows that to three across or four as a 2×2, every - divider draggable; the pane set is a remembered, explicit choice, so clicking - other tabs navigates without disturbing it, and it survives a restart -- Session history — closing an agent archives its conversation instead of deleting - it; the **History** panel reopens any past conversation as a new tab (with its - transcript, and full memory when the model supports it), grouped by project and - searchable. `/clear` still forgets for good -- History cards show both ends of a conversation — the opening message (what it was - about, since rows are titled by agent name) and a dimmer **last ·** line with your - most recent message (whether it's worth resuming) -- Full-text history search — the search box reads each archived conversation's whole - text, not just its title and preview, and quotes the matching line on the card so - every hit explains itself. Debounced and off the UI thread behind a per-session - cache, so typing never waits on file IO -- Bulk cleanup — opening a project group of two or more in History or Snapshots - reveals a **Delete all *n*** button at the top of it, clearing the whole group - after one confirmation, batched into a single store write rather than one per item -- Context snapshots — save an AI-written recap of a conversation (summarized by a - model you pick) and Import it into another model or a fresh agent; a global - left-rail panel lists them, **persisted**, grouped by project, searchable, with - collapsible groups. Unnamed snapshots get an auto-generated, unique title -- Notes — a **Notes** rail panel that jots into the project itself: **New** writes - a plain text file under `~\.mandocode\notes` and opens an editor beside the chat - (autosave, rename in place, Show in Explorer). App-wide like snapshots, so a note never - needs an agent open; new notes are filed under the folder you're working in when there is - one, which is what the panel groups by. The filesystem is the store — no index, so a note - written in Notepad appears and one deleted outside the app is gone. Searchable across note - bodies with the matching line quoted -- A prompt bar under both notes surfaces — ask about the open note (it's sent the live - buffer, so the model always sees what you see) or about the whole pad, with the bar - stating what it was given. Replies land in their own strip and reach a note only via - **Insert** / **Replace note**; the assistant has no tools at all, so "nothing writes your - note but you" is structural. External edits are adopted into the open editor, while an - external change landing on unsaved typing raises a conflict you resolve — never a silent - overwrite -- Rail badges on History and Snapshots are unread counts that clear when you open - the panel (persisted), not running totals -- Integrated terminal — a sliding panel (Ctrl+` / Ctrl+Shift+` to maximize) running a - real shell via ConPTY, rendered with xterm.js in WebView2; a shell picker chooses the - shell and it opens in the active agent's project folder -- Per-agent file explorer with git awareness — a live file tree (FileSystemWatcher) plus a - Changes tab (GitQuickStatus): branch chip, add/modify/delete status, dirty badges, inline - diff cards, one-click commit and per-file undo; tree items drag into the input as `@`-refs. - WorkspaceDeltaTracker notes commits/reverts/branch switches made outside the conversation -- Sidebar: Settings, MCP, and Skills as full-screen pages, acting on the selected agent - - Settings — the whole config as a native form (toggles, sliders, number boxes, - grouped Appearance/Connection/Generation/Behavior/Limits/Integrations); every - change is validated through the shared ConfigKeySetter, same as the CLI, and - applies to that agent alone. "Make Default for New Agents" saves it to disk - - MCP — live server list with status/tool counts; add/edit servers in a single - form modal with a Test button (isolated connection check + tool table preview). - Servers are one app-wide set; each agent chooses whether to attach their tools - - Skills — list installed skills (searchable, filterable, enabled per agent), install - from a folder or zip, and an editor that can generate or refine a skill body with a - model you pick (`SkillAuthor`); `SkillCoordinator` fans changes to every open agent -- Guided wizards, built on the approval-overlay select + text primitives: - - First-run setup — fires automatically in the chat on a fresh install: probe/start - Ollama (offering a winget install when the CLI is missing), change endpoint, pick a - starter model from a curated list (cloud recommended, or local tiers with size and - hardware hints), pull it with live progress, cloud-auth check + sign-in walkthrough. - Re-run any time via `/setup` or Settings → Run guided setup - - `/model`, `/force-skill`, `/music-playlist` — pickers - - 401 auto-recovery — a cloud 401 offers the `ollama signin` walkthrough inline -- Branded application icon across the exe, taskbar, and window title bar -- Update check against this repo's GitHub Releases (24h throttle, fail-silent) - -Not ported: matrix easter eggs; the CLI's ANSI terminal-theme service (N/A — the app ships -its own integrated terminal instead, see above). +Push a tag (`git tag v0.2.0 && git push --tags`) and the Release workflow publishes a +self-contained win-x64 zip to GitHub Releases. The app's built-in update checker watches those +releases — older installs show an update notice within 24 hours. ## License diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..d259e43 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,328 @@ +# Architecture + +This is the internals doc for contributors — how MandoCode Desktop is put together, how it relates +to the [MandoCode CLI](https://github.com/DevMando/MandoCode), and the reasoning behind the less +obvious decisions. If you just want to use the app, see the [README](../README.md) instead. + +## The harness submodule + +The MandoCode AI coding agent with a native Windows desktop interface. Same engine +as the [MandoCode CLI](https://github.com/DevMando/MandoCode) — literally: the CLI +repo is pinned here as a **git submodule** (`/MandoCode`) and this app project-references +`MandoCode/src/MandoCode/MandoCode.csproj`, reusing the entire harness (AIService, +task planner, plugins, MCP, skills, config, approvals, token tracking). Only the +user interface is different: WinUI 3 instead of RazorConsole. + +`/MandoCode` is pinned at a specific CLI commit — radical changes in the CLI repo +can never break this app until the pin is deliberately moved. To roll the harness +forward (do this against CLI release tags, not random commits): + +``` +cd MandoCode +git fetch && git checkout v0.14.0 # or origin/main +cd .. +dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj # fix what the compiler flags +git add MandoCode && git commit -m "Roll harness to v0.14.0" +``` + +When rolling, also eyeball the three *ported* seams for behavioral drift (they +compile independently of the CLI's originals): + +| Desktop port | CLI original | +|---|---| +| `ViewModels/ChatController.cs` | `Components/App.razor` interactive loop | +| `Services/WinUiApprovalService.cs` | `Services/Approval/DiffApprovalHandler.cs` | +| `Services/TranscriptHtmlBuilder.cs` | `MarkdownHtmlRenderer` / `OperationDisplayRenderer` | + +The harness is safe to instantiate once per agent — its statics are pure functions and readonly +`Regex`, and `AIService` takes every collaborator by constructor. Rolling the pin forward, watch +for that changing. + +## Layer map + +| Layer | CLI (MandoCode) | Desktop (this app) | +|---|---|---| +| Orchestrator | `Components/App.razor` interactive loop | `ViewModels/ChatController.cs` (faithful port), one per agent | +| Approvals | `DiffApprovalHandler` (Spectre panels) | `Services/WinUiApprovalService.cs` + each agent's own XAML overlay (same labels, bypass state, `DiffApprovalResult` contract) | +| Transcript | ANSI scrollback + Spectre renderables | WebView2 + `TranscriptHtmlBuilder` (Markdig HTML, themed) | +| Busy/spinner | `SpinnerService` (ANSI) | `BusyStateService` → ProgressRing | +| Onboarding | `OnboardingFlow` terminal prompts | In-chat guided wizard (auto on first launch, `/setup` after) + Settings page | +| Everything else | `Services/`, `Plugins/`, `Models/` | **reused verbatim via project reference** | + +Key seams the harness already provided (unchanged): `AIService.ChatStreamAsync`, +`OnWrite/Delete/CommandApprovalRequested` delegates, `PlanHandoff.OnPlanRequested`, +`McpApprovalGate.OnApprovalRequested`, `TaskPlannerService.ExecutePlanAsync` +progress events, `DiffService` diff models. + +## Agents + +Each tab is an independent agent. `Services/AgentSession.cs` hand-constructs one agent's object +graph; `SessionManager` owns the set of them. The split matters: + +| Per agent | App-wide | +|---|---| +| `AIService` (its conversation, its model), `ChatController`, `TaskPlannerService` | The `MandoCodeConfig` on disk — the **defaults** a new agent starts on | +| `MandoCodeConfig` clone, `ProjectRootAccessor`, `SkillLoader`, `FileAutocompleteProvider` | `McpClientManager` (one set of server processes) | +| `TokenTrackingService`, `PlanHandoff`, `TranscriptWriter`, `BusyStateService`, `ShellRunner` | `MusicPlayerService`, `ThemeManager` (static), `TranscriptHtmlBuilder`, `SpinnerService` | +| `WinUiApprovalService`, `ApprovalPromptGate`, `McpApprovalGate` | `ConfigCoordinator`, `McpCoordinator`, `SkillCoordinator`, `SessionManager`, `SnapshotStore`, `SessionArchiveStore`, `NoteStore`, `UiUpdateCheckService` | + +Tabs default to `Agent 1`, `Agent 2`, … (the folder shows in the header); the number reuses the +lowest free slot, and a rename or folder change never overwrites it. Each tab's `⋯` options menu +carries Rename, Take snapshot, Export transcript, and Close. The model in each header opens a +quick-switch dropdown (cloud first, `cloud`/`local` badges) rather than a full-screen picker. + +Closing the **last** agent is allowed: it leaves a clean empty state (showing the chat background) +with a one-click way to start a new agent. Actions that need an agent to act on — the Settings and +MCP pages, and snapshot Import — disable while none is open, then re-enable when you open one. + +### Split view (2–4 agents at once) + +The **Split** button puts two agents side by side in a resizable view, and further panes are added +from the split bar's **Add pane** button or a tab's **Add to split view** (its `⋯` menu, or +right-click). **Add pane** is a `SplitButton`: clicking it panes the next agent that isn't shown +yet, while its chevron lists the agents still available so you can pick a specific one — the same +shape as the terminal's shell picker. The layout follows the pane count: two side by side, three +across, four as a 2×2 — +past three, columns alone leave each pane too narrow for a transcript plus an input box. Every +divider is draggable, and each one repartitions only the two panes either side of it, so adjusting +one split never nudges a third pane. + +It's called *split view* rather than *compare* because comparing two models on the same prompt is +only one of the things it's for: with three or four panes open you're usually watching agents work +in parallel on different folders, not comparing their output. + +The pane set is an explicit, remembered choice — never set by plain-clicking a tab. Clicking a paned +agent's tab shows the split; clicking any other agent shows it normally while the set waits. +Dropping below two panes turns the split off and leaves you on the agent that survived. The set and +its divider positions persist across restarts (in `workspace.json`, keyed by each agent's durable +persist-key so a skipped project folder drops one pane rather than shifting all of them). + +Panes are ordinary agent views moved between grid cells with `Grid.SetColumn`/`Grid.SetRow` — +**never re-parented**, so every WebView (and its live transcript) survives the switch, which is the +whole reason the tab surface is built the way it is (see below). The row and column tracks are +rebuilt in code per pane count, interleaving a divider track between adjacent panes; track +definitions and dividers are the only things that change, so no agent view ever leaves the tree. +The geometry and divider math live in `Services/PaneLayout.cs`, kept free of WinUI types so they're +unit tested directly. + +The split bar uses chips with `MenuFlyout` pickers rather than `ComboBox`es on purpose: rebuilding +ComboBox items as containers makes WinUI throw `COMException 0x80070490` on the next selection. + +The three approval services are per-agent for **correctness**, not tidiness. Shared, they break +in ways that are invisible until a second tab exists: `WinUiApprovalService` holds the +"don't ask again" bypass set, so one agent's blanket approval would auto-approve writes in every +other; `ApprovalPromptGate` is a `SemaphoreSlim(1,1)` built for one console, so an unanswered +approval in one agent would stop another's from ever rendering; and `ChatController` **assigns** +(not `+=`) five approval delegates, so on shared services the last agent constructed silently +steals every approval. + +One setting can't be per-agent and is labelled app-wide in the UI: **Appearance** is a property +of the window (and lives outside the shared config). **Context window** used to share that label — +it was applied as `OLLAMA_CONTEXT_LENGTH` at daemon start, one daemon, one window — but it now +rides on every chat request as `num_ctx`, so each agent's own value governs its own conversations, +like any other per-agent setting. + +### Settings and the config file + +`~/.mandocode/config.json` is not "the current settings". It is the **defaults a new agent starts +on**. Editing Settings changes the selected agent, live, for that session; **Make Default for New +Agents** is the only action that writes the file (plus corrections like a healed endpoint URL, the +onboarding flag, and the app-wide MCP server list). + +`ConfigCoordinator` is the only code that calls `MandoCodeConfig.Save()`. The rule can't be +enforced by the type system — `Save()` is public and non-virtual on a harness type — so the +`MANDO001` build target fails the build if `ChatController` calls `_config.Save()` on its clone, +which would publish one agent's model as everybody's default. Cloning is a JSON round-trip +**followed by `ValidateAndClamp()`**: `System.Text.Json` rebuilds `McpServers` with a +case-sensitive comparer, and without the clamp every MCP lookup in the clone silently misses on +a casing difference. + +### Context snapshots + +A snapshot is a portable, AI-written recap of a conversation — save the gist of one agent's +context and carry it into another model or a fresh agent. Snapshots are offered when switching a +model would clear the conversation, and on demand via `Take snapshot` (tab `⋯` menu), which goes +straight to the save step. The recap is generated by `SnapshotEnhancer` (a bare, tool-less Ollama +kernel, map-reduce over the full history so nothing is truncated) using a summarizer model you +pick; a snapshot is therefore always born with a real recap — there is no "light"/un-enhanced state. +Leave the name blank and the summarizer proposes a short title, which is then made unique against +existing titles in code (`SnapshotNaming`) — an LLM can't be trusted to guarantee that itself. + +The **Snapshots** rail icon opens a global panel (the `SnapshotStore` is app-wide, one list for +every tab, **persisted** to `snapshots.json`). Cards **group by project** and are **searchable**, +and each project group is a collapsible `Expander` whose fold state is remembered +(`PanelState` → `panel-state.json`). **Import** arms a snapshot's recap to ride along, invisibly, +with the active agent's next message — carrying context into any model. The rail badge is an +**unread count** (snapshots captured since you last opened the panel), not a running total, and +clears when you open it. + +### Session history (reopen closed conversations) + +Closing an agent no longer discards its conversation — it **archives** it. `SessionArchiveStore` +keeps an app-wide index (`sessions.json`) of closed conversations; the transcript, model memory, and +conversation-log journals stay on disk (see [session-persistence.md](session-persistence.md)). +The **History** rail panel lists them (grouped by project, searchable, collapsible), and **Open** +reopens one as a fresh tab on its original persist-key so the normal restore cascade replays the +transcript and — when the model supports it — rehydrates the full memory. `/clear` still forgets a +conversation for good; only *closing* softened from "gone" to "recoverable." The archive is capped +(newest 60); evicting a row deletes its journals so the on-disk stores stay bounded. + +### Notes (the jot pad) + +The **Notes** rail panel is an always-there scratchpad: plain text files under +`~\.mandocode\notes`, beside the config file the CLI shares. **New** creates one and opens an editor docked next to the +chat — autosaved on a 1.2s debounce (plus Ctrl+S, leaving the note, closing the panel, closing the +window), renameable in place, with **Show in Explorer**. + +Notes are **app-wide**, the same call as snapshots and session history. A note is something you want +to write down *now*, which is often between projects or before an agent is even open, so nothing here +needs one. What survives of "which project was this about" is a plain **subfolder**: a new note is +filed under the active agent's folder name when there is one, and sits loose at the top when there +isn't. Grouping therefore costs no metadata, can't drift, and is corrected by dragging files around +in Explorer. + +**The filesystem is the store** — no index, no JSON, which is also why the pad lives in +`~\.mandocode` rather than LocalAppData: these are your files, meant to be greppable, syncable, and +openable in any editor. Discovery walks one folder plus its immediate subfolders (one level only — a +jot pad with a hierarchy is a filing system, and the search box is a better answer to "where did I +put it"). In exchange, no row can ever point at a file that isn't there: a note added in Notepad +shows up, and one deleted outside the app disappears. Search matches note **bodies** and quotes the +matching line on the card; only search and the assistant see a body truncated, capped at +`NoteStore.MaxTextBytes` (128 KB). + +#### The prompt bar + +Both surfaces — the open note and the list — carry a prompt at the bottom. It's chat-shaped, but the +document above it is your note rather than a transcript, so replies land in the bar's own strip and +reach a note only through **Insert** (at the cursor, replacing the selection if there is one) or +**Replace note**. With text selected, the question is about the selection. + +The note's text travels **in the message**, taken from the live editor buffer — so the model always +sees the note as it is right now, including keystrokes autosave hasn't written yet, with nothing to +fall out of sync. Only the current message carries the note; earlier turns in the thread keep just +their words, so a long back-and-forth doesn't ship five stale copies. On the list, the question is +about the pad: every note's title and first line, plus the full text of whatever the search box is +currently matching, and the bar states what it was given (`12 notes listed · 3 read in full`) because +a capped read that looks total is the one thing an "ask about all my notes" box must not do. + +`Services/NoteAssistant.cs` builds a bare Ollama kernel with **no plugins, filters, or tools** — the +same shape as `SnapshotEnhancer`. That's the design, not an optimization: with no file tools, "nothing +writes your note but you" is true by construction rather than by policy, and no approval machinery is +needed because the only route from a reply into a note is a button you pressed. It also isn't an +`AIService` agent, because those exist to change your files (the opposite of what a notepad wants) +and are scoped to a project root the pad deliberately sits outside of. Its model is picked from the +chip under the prompt, defaults to the app-wide default model, and is remembered; the thread is +per-note, in memory, and cleared when you switch notes — notes aren't conversations. + +The editor is not the only thing that writes these files, and doesn't assume it is. A +`FileSystemWatcher` compares the file against what the editor last wrote: identical means the write +was ours; changed-while-clean is adopted silently; changed-while-you-were-typing raises a conflict you +resolve — *use the version on disk* or *keep what I typed*. A note deleted from under unsaved edits +offers to save it back. `Services/NoteText.cs` handles the newline round trip, because a WinUI +`TextBox` normalizes every newline to a bare CR — write that straight back out and a Notepad-authored +CRLF note becomes one endless line. Both of those are why `NoteStore`, `NoteEntry`, and `NoteText` are +WinUI-free with an injectable pad root: the tests drive them against real temp folders. + +Notes deliberately lack the **Delete all *n*** group button that Snapshots and History carry: those +are artifacts the app generated, a note is something you wrote by hand, and one button that deletes a +folder's worth of writing is a different class of risk. + +Undo/Redo for the assistant's inserts and replacements is a single-level **toggle** rather than a +history stack: the editor remembers the buffer right before and right after the last assistant edit, +and Undo/Redo just flip between those two exact states. Typing anything that lands outside both +retires the toggle — the same rule a full undo stack uses to clear its redo branch on a new edit, +just with only one edit remembered. + +### Why the tab strip isn't a `TabView` + +WinUI's `TabView` hosts only the selected item's content, which detaches the previous tab and +closes its `CoreWebView2`. `TranscriptWriter` retains nothing — the WebView2 DOM is the only copy +of a conversation — so that would destroy the transcript on every tab switch. The strip carries +headers only; content lives in `Visibility`-toggled sibling panels and is never re-parented. + +## Full feature inventory (v0.1) + +- Chat with streaming turns, function-call operation cards, markdown transcript +- Write / command / delete / MCP approvals with native diff viewer (same + approve / don't-ask-again / deny / new-instructions / cancel-plan semantics) +- propose_plan flow: plan table, execute/reject/cancel, per-step progress bar, + step-failure skip/cancel +- Slash commands with autocomplete: /help /setup /clear /model /config /retry /learn + /copy /copy-code /skills /force-skill /mcp /mcp tools /mcp remove /mcp-reload + /music* /command /exit — plus `!cmd` shell escape and `@file` references +- Token tracking + per-response summaries, per agent +- Agent tabs — `+` opens another agent (`Agent 1`, `Agent 2`, …) with its own + conversation, project folder, model, and settings; an approval waiting in a + background agent badges its tab and the toast names it. Each tab's `⋯` menu: + Rename, Take snapshot, Export transcript, Close. The header model opens a + quick-switch dropdown (cloud first, `cloud`/`local` badges). Closing the last + agent is allowed and leaves an empty state that shows the chat background +- Split view — the **Split** button shows two agents side by side, and **Add pane** + (or a tab's **Add to split view**) grows that to three across or four as a 2×2, every + divider draggable; the pane set is a remembered, explicit choice, so clicking + other tabs navigates without disturbing it, and it survives a restart +- Session history — closing an agent archives its conversation instead of deleting + it; the **History** panel reopens any past conversation as a new tab (with its + transcript, and full memory when the model supports it), grouped by project and + searchable. `/clear` still forgets for good +- History cards show both ends of a conversation — the opening message (what it was + about, since rows are titled by agent name) and a dimmer **last ·** line with your + most recent message (whether it's worth resuming) +- Full-text history search — the search box reads each archived conversation's whole + text, not just its title and preview, and quotes the matching line on the card so + every hit explains itself. Debounced and off the UI thread behind a per-session + cache, so typing never waits on file IO +- Bulk cleanup — opening a project group of two or more in History or Snapshots + reveals a **Delete all *n*** button at the top of it, clearing the whole group + after one confirmation, batched into a single store write rather than one per item +- Context snapshots — save an AI-written recap of a conversation (summarized by a + model you pick) and Import it into another model or a fresh agent; a global + left-rail panel lists them, **persisted**, grouped by project, searchable, with + collapsible groups. Unnamed snapshots get an auto-generated, unique title +- Notes — a **Notes** rail panel that jots into the project itself: **New** writes + a plain text file under `~\.mandocode\notes` and opens an editor beside the chat + (autosave, rename in place, Show in Explorer). App-wide like snapshots, so a note never + needs an agent open; new notes are filed under the folder you're working in when there is + one, which is what the panel groups by. The filesystem is the store — no index, so a note + written in Notepad appears and one deleted outside the app is gone. Searchable across note + bodies with the matching line quoted +- A prompt bar under both notes surfaces — ask about the open note (it's sent the live + buffer, so the model always sees what you see) or about the whole pad, with the bar + stating what it was given. Replies land in their own strip and reach a note only via + **Insert** / **Replace note**; the assistant has no tools at all, so "nothing writes your + note but you" is structural. External edits are adopted into the open editor, while an + external change landing on unsaved typing raises a conflict you resolve — never a silent + overwrite. Undo/Redo toggles the assistant's last insert/replace +- Rail badges on History and Snapshots are unread counts that clear when you open + the panel (persisted), not running totals +- Integrated terminal — a sliding panel (Ctrl+` / Ctrl+Shift+` to maximize) running a + real shell via ConPTY, rendered with xterm.js in WebView2; a shell picker chooses the + shell and it opens in the active agent's project folder +- Per-agent file explorer with git awareness — a live file tree (FileSystemWatcher) plus a + Changes tab (GitQuickStatus): branch chip, add/modify/delete status, dirty badges, inline + diff cards, one-click commit and per-file undo; tree items drag into the input as `@`-refs. + WorkspaceDeltaTracker notes commits/reverts/branch switches made outside the conversation +- Sidebar: Settings, MCP, and Skills as full-screen pages, acting on the selected agent + - Settings — the whole config as a native form (toggles, sliders, number boxes, + grouped Appearance/Connection/Generation/Behavior/Limits/Integrations); every + change is validated through the shared ConfigKeySetter, same as the CLI, and + applies to that agent alone. "Make Default for New Agents" saves it to disk + - MCP — live server list with status/tool counts; add/edit servers in a single + form modal with a Test button (isolated connection check + tool table preview). + Servers are one app-wide set; each agent chooses whether to attach their tools + - Skills — list installed skills (searchable, filterable, enabled per agent), install + from a folder or zip, and an editor that can generate or refine a skill body with a + model you pick (`SkillAuthor`); `SkillCoordinator` fans changes to every open agent +- Guided wizards, built on the approval-overlay select + text primitives: + - First-run setup — fires automatically in the chat on a fresh install: probe/start + Ollama (offering a winget install when the CLI is missing), change endpoint, pick a + starter model from a curated list (cloud recommended, or local tiers with size and + hardware hints), pull it with live progress, cloud-auth check + sign-in walkthrough. + Re-run any time via `/setup` or Settings → Run guided setup + - `/model`, `/force-skill`, `/music-playlist` — pickers + - 401 auto-recovery — a cloud 401 offers the `ollama signin` walkthrough inline +- Branded application icon across the exe, taskbar, and window title bar +- Update check against this repo's GitHub Releases (24h throttle, fail-silent) + +Not ported: matrix easter eggs; the CLI's ANSI terminal-theme service (N/A — the app ships +its own integrated terminal instead, see above). diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md new file mode 100644 index 0000000..926a92a --- /dev/null +++ b/docs/screenshots/README.md @@ -0,0 +1,20 @@ +# Screenshots + +Drop images in this folder with the filenames below and they'll automatically appear in the main +`README.md` — nothing else needs to change. + +| Filename | What it should show | +|---|---| +| `chat-and-agents.png` | The main chat view with a couple of agent tabs open — the "hero" shot, wide | +| `split-view.png` | Split view with two or more agents side by side | +| `terminal.png` | The integrated terminal panel open, running a real shell | +| `notes.png` | The Notes panel with a note open (and, ideally, the AI prompt bar in view) | +| `explorer.png` | The file explorer panel with git status badges / a diff card visible | +| `theme-crt.png` | The app in the "Cathode Ray (CRT)" theme | +| `theme-w98.png` | The app in the "W98 - Y2K" theme | + +PNG or JPG both work. No fixed size — GitHub scales them to the column width — but a consistent +window size across shots makes the table on the README line up more cleanly. + +Feel free to add more (a snapshots/history panel, the skills page, one of the other themes) — just +add a matching `` tag in the README's Screenshots section.