dsh-composer-plus 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,7 @@ dsh plugin --profile web add dsh-composer-plus
27
27
 
28
28
  Then restart dsh. (The CLI can't install into the desktop profile; the app manages it.)
29
29
 
30
- > dsh's pnpm skips versions younger than 24 hours. Right after a release, add the version: `dsh-composer-plus@1.15.0`.
30
+ > dsh's pnpm skips versions younger than 24 hours. Right after a release, add the version: `dsh-composer-plus@1.16.0`.
31
31
 
32
32
  ## Features
33
33
 
@@ -35,7 +35,7 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
35
35
 
36
36
  | Feature | How to use it | Screenshot |
37
37
  |---|---|---|
38
- | **Changes panel** | <a id="feature-changespanel"></a>Right sidebar → `+` → **Changes**. Pick **Uncommitted**, **Unstaged**, **Staged** or **Branch** to see changed files with `+/-` counts; click a file for its diff. **Last Turn** opens dsh's own review of the latest turn. Click the branch name (or pick **Tree**) to see the branch tree: click a branch above it to go to that branch, click a commit to see its files and their diffs. **This chat** lists only the files that the open chat's file tools (write, edit) wrote, in all its turns, and their total diff since its first message. The **␣** button hides whitespace changes (`git diff -w`): lines that changed only in indentation or line endings do not show. A file's **…** menu → **Chats that changed this file** lists the chats of the same repository whose write or edit tools changed that file, newest first, with the request that did it; a click opens that chat at that request. | <a href="assets/features/changesPanel.webp"><img src="assets/features/changesPanel.webp" width="160" alt="Screenshot: Changes panel"></a> |
38
+ | **Changes panel** | <a id="feature-changespanel"></a>Right sidebar → `+` → **Changes**. The page opens on **Tree**: a branch map of the chat's repository. Three cards count the branches: all of them, the merged ones and the ones that wait to merge. Click a card to show only those branches; click it again to show all. Each branch is a box with its last commit: green when the main branch has its work, orange when not, blue for the main branch. An arrow goes from each branch to the branch that took it (✅ **Merged**) or to the branch that it started from (⏳ **Waiting**, dashed). Click a box to see the branch's commits, then click a commit to see its files and their diffs. Click **Merged** to see the files of the merge commit, or **Waiting** to see the files that the branch brings into its target. A remote branch shows only when no local branch has the same name. In the mode menu, pick **Uncommitted**, **Unstaged**, **Staged** or **Branch** to see changed files with `+/-` counts; click a file for its diff. **Last Turn** opens dsh's own review of the latest turn. **This chat** lists only the files that the open chat's file tools (write, edit) wrote, in all its turns, and their total diff since its first message. The **␣** button hides whitespace changes (`git diff -w`): lines that changed only in indentation or line endings do not show. A file's **…** menu → **Chats that changed this file** lists the chats of the same repository whose write or edit tools changed that file, newest first, with the request that did it; a click opens that chat at that request. | <a href="assets/features/changesPanel.webp"><img src="assets/features/changesPanel.webp" width="160" alt="Screenshot: Changes panel"></a> |
39
39
  | **Feature ideas** | <a id="feature-featureideas"></a>Right sidebar → `+` → **Feature ideas** → **Suggest features**. The chat's own model reads a short summary of the chat's project (top-level files, the README and manifest heads, an outline of the rest of the README, the titles of recent commits) and suggests up to 8 features, shown as a stack of cards in dsh's UI language. Swipe a card right (or click **✓**, or press `→`; the card flies off to its side in each case) to start a new chat in the same project: it starts in plan mode with the idea as the request, so nothing changes before you approve the plan. To skip the plan, click **▶ Build now** (or press `↑`; the card flies up): the new chat starts at once with the idea as an ordinary request, and the model makes the change and runs its checks (approvals apply as usual). Swipe left (or **✕**, or `←`) to skip; **Undo** (or `Z`) brings a skipped card back. Click **↓** (or press `↓`) to save a good idea for later: it goes to the project's **Later** list, which every chat in the same project folder shows (also after a restart). **Later (N)** opens the list; **Plan** starts the same plan-mode chat for the idea and **Build now** the chat without a plan, both take it off the list; **Plan all (N)** starts one plan-mode chat for every idea in the list, so shared parts are planned once; **Remove from the list** deletes it. **Copy as checklist** copies the whole list to the clipboard as a Markdown checklist (`- [ ] Title — Summary`, one line per idea) for an issue tracker or a TODO file; it makes no network call. **Open GitHub issue** on an idea opens an issue with its title and summary (and why it matters) in the GitHub repository of the project, with your own GitHub CLI (`gh`) login; the link **Issue #N** then shows next to the idea and opens the issue. **More ideas** adds cards. To get ideas about one area only, type a short **focus topic** in the field next to **Suggest features** (at most 80 characters; `Enter` also suggests), or click one of the fixed topics under it: **Performance**, **Accessibility**, **Tests**, **Security**, **Usability** or **Documentation** (click it again to clear it). The model then suggests only features about that topic; an empty field means any feature. Ideas are not suggested twice in a project: every suggested idea (skipped, saved or started) goes to the project's **History**, the model is told not to suggest those again, and a repeated title is dropped. **History** lists them with **Started** or **Suggested**; **Forget** (or **Forget all**) lets one come again; an idea that is still in the Later list stays out of the suggestions until you remove it there. The model runs only when you click. | <a href="assets/features/featureIdeas.webp"><img src="assets/features/featureIdeas.webp" width="160" alt="Screenshot: Feature ideas"></a> |
40
40
  | **Content search** | <a id="feature-contentsearch"></a>dsh's own sidebar search (the magnifier next to **Workspaces**; `Cmd+K` in the desktop app, `Cmd+Alt+K` in a browser) also finds chats by the words in your messages and in the replies, not only by title, and shows the matching text under the chat. dsh already has this search, but ships it switched off; this feature switches it on with a search index file on your computer. The index is made at the first search after a start (about 15 s for 250 chats, see Limits) and is kept for the next starts. On by default; a change applies after you restart dsh. | <a href="assets/features/contentSearch.webp"><img src="assets/features/contentSearch.webp" width="160" alt="Screenshot: Content search"></a> |
41
41
  | **Turn rollback** | <a id="feature-turnrollback"></a>Hover one of your messages and click **↶** (next to Copy). **Chat only** takes that message and everything after it (replies, tool calls) out of the chat and out of the model's context, and puts the message back in the composer: change it and send it again. **Chat and files** first lists the project files that changed since that message, then also puts them back as they were, including changes made by commands (`bash`), not only by dsh's edit tools. A running turn stops first. The chat log is never rewritten: one hidden marker hides the withdrawn part, the same way dsh's own `/compact` hides old messages. When [dsh-rewind-plugin](https://www.npmjs.com/package/dsh-rewind-plugin) is installed, it does this job and Composer Plus shows no second button. After a rollback, a **Redo** bar shows above the composer: it starts a new chat from just before the rollback (the same messages and context) and, with **Chat and files**, puts the files back; a file that you changed after the rollback is listed and kept, unless you tick the box that overwrites it. | <a href="assets/features/turnRollback.webp"><img src="assets/features/turnRollback.webp" width="160" alt="Screenshot: Turn rollback"></a> |
@@ -44,10 +44,12 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
44
44
  | **Input history** | <a id="feature-inputhistory"></a>In an empty composer, press `↑` / `↓` to walk through messages you sent, like a terminal. | <a href="assets/features/inputHistory.webp"><img src="assets/features/inputHistory.webp" width="160" alt="Screenshot: Input history"></a> |
45
45
  | **Shift+Tab plan mode** | <a id="feature-plantoggle"></a>`Shift+Tab` adds `/plan ` to the start of the draft (press again to remove it). If the chat is already in plan mode, `Shift+Tab` leaves it. | <a href="assets/features/planToggle.webp"><img src="assets/features/planToggle.webp" width="160" alt="Screenshot: Shift+Tab plan mode"></a> |
46
46
  | **Keep composer focus** | <a id="feature-keepfocus"></a>After you send a `/` command or skill, the cursor stays in the composer, and you can type again at once. This also works for keyboard macros that type a command and press `Enter`. dsh makes the composer read-only while it resolves the command, and the browser then removes the focus. This feature puts the focus back, unless a menu, a dialog or another field has it. | <a href="assets/features/keepFocus.webp"><img src="assets/features/keepFocus.webp" width="160" alt="Screenshot: Keep composer focus"></a> |
47
+ | **Fit question card to window** | <a id="feature-questionfit"></a>When the agent asks you a question, the question and its answers scroll together in the card, and **Skip** and **Submit** stay in view at the bottom. The card uses the height of the window (less 200 px for the chat header and a strip of the chat), so it grows and shrinks with the window. Without this feature, dsh keeps the card at 60% of the window (at most 520 px) and only the answers scroll: a long question can push its own end, the answers and the buttons out of the card. | <a href="assets/features/questionFit.webp"><img src="assets/features/questionFit.webp" width="160" alt="Screenshot: Fit question card to window"></a> |
47
48
  | **One-click Continue** | <a id="feature-continuebutton"></a>After a stopped, failed or cut-off turn, an empty composer shows **▶**. One click resumes from the existing conversation without writing in the input or adding a visible chat message (the model receives a short hidden “continue from where it stopped” note, because some providers, such as Claude, reject a request that ends on an assistant reply). Messages that wait in the queue stay there: ▶ continues only the stopped task, then the queue runs. A completed turn keeps the regular Send button. | <a href="assets/features/continueButton.webp"><img src="assets/features/continueButton.webp" width="160" alt="Screenshot: One-click Continue"></a> |
48
49
  | **Resume after restart** | <a id="feature-resumeafterrestart"></a>Off by default: switch it on in **Settings → Composer Plus**. If you quit dsh (or it closes) while a chat is working, that chat continues by itself when you open dsh again, with the same hidden note as One-click Continue. If a goal round (`/goal`) was running, the goal goes on with its next round (dsh pauses goals at every start until you resume them). Chats that you stopped, that finished or that failed do not continue. dsh itself has no setting for this: it only marks such a turn as interrupted. | <a href="assets/features/resumeAfterRestart.webp"><img src="assets/features/resumeAfterRestart.webp" width="160" alt="Screenshot: Resume after restart"></a> |
49
50
  | **Queue drag-and-drop** | <a id="feature-queuereorder"></a>Queued messages get a grip (⠿). Drag to reorder, or focus the grip and press `↑` / `↓`. `Esc` cancels. | <a href="assets/features/queueReorder.webp"><img src="assets/features/queueReorder.webp" width="160" alt="Screenshot: Queue drag-and-drop"></a> |
50
- | **Edit queued message in place** | <a id="feature-queueedit"></a>Double-click a queued message. It turns into dsh's own edit box: change the text, press `Enter` to save or `Esc` to cancel. The message keeps its place in the queue. Messages with images or files cannot be edited this way (dsh's queue edits text only). | <a href="assets/features/queueEdit.webp"><img src="assets/features/queueEdit.webp" width="160" alt="Screenshot: Edit queued message in place"></a> |
51
+ | **Edit queued message in place** | <a id="feature-queueedit"></a>Double-click a queued message, or click its Edit button (**Edit queued message**). An editor opens in the row: change the text, remove an image or a file with its ✕, or add new ones with the paperclip (**Add images or files**) or paste an image. `Enter` saves, `Esc` cancels, `Shift+Enter` adds a line. The message keeps its place in the queue, and the images and files that you keep stay as they are. While the editor is open, the queue cannot be reordered. In Tether: queued message menu → Edit. | <a href="assets/features/queueEdit.webp"><img src="assets/features/queueEdit.webp" width="160" alt="Screenshot: Edit queued message in place"></a> |
52
+ | **Send after Stop goes first** | <a id="feature-stopsendfirst"></a>On by default. Stop a chat while messages wait in its queue, then send a new message: it runs at once, alone. The queued messages keep their order and run after it, one per turn. Without this, dsh adds the new message at the end of the queue and runs the first queued message instead, so the new one runs last. A message that you send while the chat works still waits at the end of the queue. | <a href="assets/features/stopSendFirst.webp"><img src="assets/features/stopSendFirst.webp" width="160" alt="Screenshot: Send after Stop goes first"></a> |
51
53
  | **Copy session link** | <a id="feature-copydeeplink"></a>Right-click a chat in the sidebar → **Copy session link**. Paste the `dsh-session:…` link into another chat to attach that chat as read-only context. dsh 0.2.1 has its own **Copy Session ID**, which copies only the plain id; this item comes right after it. | <a href="assets/features/copyDeeplink.webp"><img src="assets/features/copyDeeplink.webp" width="160" alt="Screenshot: Copy session link"></a> |
52
54
  | **Copy message link** | <a id="feature-copymessagelink"></a>Hover a message and click **🔗** (in its action row; for an answer, next to the time). The link is the session link plus the message number: `dsh-session:…#42`. Paste it in a message and send it: on dsh 0.2.1 and later the link shows as a chip, and a click opens that chat, scrolls to the message and highlights it for 2 seconds. Like a session link, it also attaches that chat as read-only context. | <a href="assets/features/copyMessageLink.webp"><img src="assets/features/copyMessageLink.webp" width="160" alt="Screenshot: Copy message link"></a> |
53
55
  | **Export chat as Markdown** | <a id="feature-exportmarkdown"></a>Right-click a chat in the sidebar (or open its **…** menu) → **Export as Markdown…**. A dialog shows how many messages the chat has; **Copy to clipboard** copies the chat as Markdown, **Save .md file** saves it as `<chat title>.md` through the browser's download (dsh desktop: your Downloads folder or a save dialog, as your system is set). The file has the chat title, the project name and the date, then one `## You` or `## Assistant` section per message. Code blocks stay as they are; a code block that a message leaves open is closed. | <a href="assets/features/exportMarkdown.webp"><img src="assets/features/exportMarkdown.webp" width="160" alt="Screenshot: Export chat as Markdown"></a> |
@@ -55,23 +57,30 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
55
57
  | **Project search in a new chat** | <a id="feature-projectpicker"></a>New chat → the project button above the composer. The projects show in A–Z order, with a **Search projects** box on top. Type part of a name (case and accents do not matter) and press `Enter` to pick the first match. **No matching project** shows when nothing matches. dsh's saved project order in the sidebar does not change. | <a href="assets/features/projectPicker.webp"><img src="assets/features/projectPicker.webp" width="160" alt="Screenshot: Project search in a new chat"></a> |
56
58
  | **Default workspace for New session** | <a id="feature-defaultworkspace"></a>Settings → Composer Plus → **Default workspace for New session** → **Open New session in**: choose a project, or **General folder (default-workspace)** (the default: dsh's own folder `Documents/deepseek-harness/default-workspace`, made at the first use). Then **New session** at the top of the sidebar (and its shortcut) always opens there, also while another project's chat is open. The `+` next to a project still opens that project, and the project button above the composer of the new chat still changes the project. | <a href="assets/features/defaultWorkspace.webp"><img src="assets/features/defaultWorkspace.webp" width="160" alt="Screenshot: Default workspace for New session"></a> |
57
59
  | **Find in chat** | <a id="feature-findinchat"></a>Press `Cmd+F` on a Mac (`Ctrl+F` on Windows and Linux) in a chat: a find box opens over it. Type a word: every match in the messages and replies on screen is highlighted, the current one in a stronger color, and the box shows **n / total**. `Enter` or `↓` goes to the next match and `Shift+Enter` or `↑` to the previous one (also the arrow buttons); the chat scrolls the match to the middle. Case, accents and the Turkish dotless ı do not matter. `Esc` closes the box and puts the cursor back where it was. It follows the chat while a reply streams in. | <a href="assets/features/findInChat.webp"><img src="assets/features/findInChat.webp" width="160" alt="Screenshot: Find in chat"></a> |
58
- | **Branch per chat** | <a id="feature-worktreechats"></a>New chat → the main button (branch icon) between the project button and the mode button → **New branch**, then type the first message and send it. The first message can carry images and files. The AI names the branch (`feat/…`, `fix/…`) from that message, Composer Plus makes a git worktree for it in `<project>/.worktrees/`, copies the ignored files that `.worktreeinclude` lists (for example `.env` and `node_modules/`) and opens a new chat there with your message. The open chat's header shows ⑂ branch · N changes · ↑ ↓ (or **not pushed**); click it to open **Changes**. The same menu lists the open worktrees: pick one to start a chat in it. To see the branch folders inside their project in the sidebar, choose dsh's **View options** → **Workspace tree**. | <a href="assets/features/worktreeChats.webp"><img src="assets/features/worktreeChats.webp" width="160" alt="Screenshot: Branch per chat"></a> |
60
+ | **Branch per chat** | <a id="feature-worktreechats"></a>On the New chat screen, the branch button (branch icon) between the project button and the mode button shows **New branch** by default. Type the first message and send it. To stay on the current branch, choose “Current branch (main)” in the same button. The first message can carry images and files. In plan mode the new chat starts in plan mode too: send the message as `/plan <message>` (Shift+Tab), or put the New chat in plan mode first. The AI names the branch (`feat/…`, `fix/…`) from that message, Composer Plus makes a git worktree for it in `<project>/.worktrees/`, copies the ignored files that `.worktreeinclude` lists (for example `.env` and `node_modules/`) and opens a new chat there with your message. The open chat's header shows ⑂ branch · N changes · ↑ ↓ (or **not pushed**); click it to open **Changes**. The same menu lists the open worktrees: pick one to start a chat in it. To see the branch folders inside their project in the sidebar, choose dsh's **View options** → **Workspace tree**. | <a href="assets/features/worktreeChats.webp"><img src="assets/features/worktreeChats.webp" width="160" alt="Screenshot: Branch per chat"></a> |
61
+ | **Branches in the workspace tree** | <a id="feature-branchtree"></a>Settings → Composer Plus → **Branches in the workspace tree** (off by default). Each branch that has a worktree folder in `<project>/.worktrees/` then shows as a project inside its project; choose dsh's **View options** → **Workspace Tree** to see it nested. A branch whose folder is deleted leaves the list, and dsh shows its chats under **Ungrouped**. When the agent of a chat in the project folder creates a branch and switches to it (`git switch -c`, `git checkout -b`) while the folder has no uncommitted changes, the project folder goes back to its branch, the branch gets its own folder, and the chat moves into it: a copy opens in the branch project and goes on with the task, and the old chat is archived. The list is checked at start, every 30 seconds, after each turn and when you come back to the window. | <a href="assets/features/branchTree.webp"><img src="assets/features/branchTree.webp" width="160" alt="Screenshot: Branches in the workspace tree"></a> |
59
62
  | **Next message prediction** | <a id="feature-nextprediction"></a>On by default; switch it off in **Settings → Composer Plus**. When the agent finishes a turn (not a stopped or failed one), the empty composer shows a guess of your next message in grey, with a **Tab to accept** hint. `Tab` puts the text in the composer, but does not send it. `Esc` hides it for that turn. Typing also hides it, and a new turn replaces it. The **Model** menu under the switch lists dsh's own models (the same list as dsh's model menu). **dsh default** is selected by default and follows dsh's default model. Each finished turn costs one short model call. In Tether, the guess shows above the empty composer: tap it to put it in the field. | <a href="assets/features/nextPrediction.webp"><img src="assets/features/nextPrediction.webp" width="160" alt="Screenshot: Next message prediction"></a> |
60
63
  | **Complete while typing** | <a id="feature-typingcompletion"></a>Off by default; switch it on in **Settings → Composer Plus**. While you type, grey text right after the cursor shows how your message may go on. `Tab` adds it to the message, but does not send it. `Esc` hides it. It suggests only your own next words: never an answer to you, and no made-up error texts or names. When the message looks complete or the model is not sure, nothing shows. Typing the next letters of the grey text keeps it, with no new model call. It has its own **Model** menu under its switch. Its first entry, **Same as Next message prediction**, uses the model of **Next message prediction** (the default); pick another model to use a different one for each feature, for example a faster model for completions. It makes one model call at each pause while you type, so it uses many more tokens than the prediction: pick a fast, low-cost model. It does not run for a slash command, a message with a file chip, or when the cursor is not at the end. In Tether, the rest shows above the field while you type: tap it to add it. | <a href="assets/features/typingCompletion.webp"><img src="assets/features/typingCompletion.webp" width="160" alt="Screenshot: Complete while typing"></a> |
61
64
  | **Improve prompt** | <a id="feature-promptimprove"></a>Type a rough request, then click the magic wand (**Improve prompt**) next to the model menu. One model call rewrites it as a clearer prompt: the goal first, short numbered steps when there are several parts, and the check to run. It reads this chat (rolled-back messages left out), the project's README, AGENTS.md and manifest, the changed files and the files whose names match your words, and keeps your language. It never makes up file names. The new text replaces your draft but is not sent: read it, edit it, then press `Enter`. `Cmd/Ctrl+Z` brings your text back in one step, and so does the button (**Undo improvement**). If you type while it works, your new text stays. `Esc` or a second click cancels. It has its own **Model** menu in Settings (default: **Same as the chat's model**). Not for slash commands or a message with a file chip. In Tether, the wand is next to the field. | <a href="assets/features/promptImprove.webp"><img src="assets/features/promptImprove.webp" width="160" alt="Screenshot: Improve prompt"></a> |
62
65
  | **Goal check** | <a id="feature-goalcheck"></a>Start a goal with `/goal`. When the agent marks the goal complete in an automatic round, the goal closes only after two checks. First, the project's test command runs in the chat's folder, under the chat's sandbox mode. The command is your own one or the one found in `package.json`, `Makefile`, `Cargo.toml`, `go.mod` or pytest. When the agent's own newest step was a passing run of that test, it does not run again. Then one independent model call, with no tools, reads the goal, this chat, `git status` and the test result. If a check fails, the goal stays active and the agent reads why, then the next round goes on. A row under the goal bar (**Goal check**) shows the test and the last result. **Edit** sets your own test command and a time or token limit; over the limit the goal becomes blocked. **Run now** runs the check without closing the goal. It has its own **Model** menu in Settings (default: **Same as the chat's model**). When you complete the goal yourself, nothing is checked. | <a href="assets/features/goalCheck.webp"><img src="assets/features/goalCheck.webp" width="160" alt="Screenshot: Goal check"></a> |
66
+ | **Project memory** | <a id="feature-projectmemory"></a>Off by default: switch it on in **Settings → Composer Plus**. Each project folder gets its own memory, which later chats of that project read, as DS Bot keeps one per Bot. After each finished turn, one call to the chat's model reads the new messages (not rolled-back ones) and keeps your lasting preferences, decisions and facts as short notes, and changes or removes old ones. The agent can also save, remove and search notes itself (`memory_remember`, `memory_forget`, `memory_recall`). A note can also be global: every project reads it. A new chat gets the notes in its system prompt; a note saved later shows from the next chat. The files are Markdown under the dsh home (`storages/dsh-composer-plus/memory/`), never in the project folder. | <a href="assets/features/projectMemory.webp"><img src="assets/features/projectMemory.webp" width="160" alt="Screenshot: Project memory"></a> |
63
67
  | **Allow for this chat** | <a id="feature-allowforchat"></a>When Auto Review asks you, the approval card gets **Allow for this chat**: the same tool in the same chat is allowed from then on, with any arguments, until the time limit ends (1 hour by default; choose it under **Settings → Composer Plus → Allow for this chat**). Then the agent asks again. A header chip lists the grants with their end time and revokes them. Grants survive a restart. | <a href="assets/features/allowForChat.webp"><img src="assets/features/allowForChat.webp" width="160" alt="Screenshot: Allow for this chat"></a> |
64
- | **Project status** | <a id="feature-projectstatus"></a>A project row in the sidebar shows the same spinner / waiting / done marker as its chats, so a collapsed project still tells you a chat inside is working or needs you. A chat waiting for you shows first, then a finished chat you have not opened yet (the green dot, also while another chat still runs), then the spinner. Hover it for details. | <a href="assets/features/projectStatus.webp"><img src="assets/features/projectStatus.webp" width="160" alt="Screenshot: Project status"></a> |
68
+ | **Project status** | <a id="feature-projectstatus"></a>A project row in the sidebar shows the same spinner / waiting / done marker as its chats, so a collapsed project still tells you a chat inside is working or needs you. A chat waiting for you shows first, then a finished chat you have not opened yet (the green dot, also while another chat still runs), then the spinner. Hover it for details. In **Workspace Tree**, the row of a parent folder also shows the chats of each project inside it (for example each branch folder in `.worktrees/`), also while it is collapsed. | <a href="assets/features/projectStatus.webp"><img src="assets/features/projectStatus.webp" width="160" alt="Screenshot: Project status"></a> |
65
69
  | **Git branch on chat rows** | <a id="feature-sessionbranch"></a>Each chat row in the sidebar shows a small grey pill with the git branch that the chat's project folder was on at the chat's last turn (for example `main` or `feature/login`; a detached HEAD shows its short commit id in parentheses). Two chats of one project on different branches no longer look the same, and you see at once when you are about to continue a chat on the wrong branch. Hover the pill for the full name. The Host half reads the branch (`git branch --show-current`, read-only) when a turn starts and when it ends; a chat with no turn since you installed this, or a folder that is not a git repository, shows no pill. A chat under a project row with its branch's name (a “Branch per chat” folder) shows no pill, except in “In one list”. | <a href="assets/features/sessionBranch.webp"><img src="assets/features/sessionBranch.webp" width="160" alt="Screenshot: Git branch on chat rows"></a> |
66
70
  | **Mark all as read** | <a id="feature-markprojectread"></a>In the sidebar, click a project's **…** button → **Mark all as read**. Every chat in that project loses its green unread dot at once; other projects keep theirs. The item is greyed out (**No unread chats**) when the project has none. | <a href="assets/features/markProjectRead.webp"><img src="assets/features/markProjectRead.webp" width="160" alt="Screenshot: Mark all as read"></a> |
67
71
  | **Active projects first** | <a id="feature-activeprojectsfirst"></a>When a chat starts running or needs you, its project moves to the top of the sidebar with dsh's own row animation (the same move as dragging it there). Finished projects stay where they are; drag them anywhere as usual. Moves are saved to dsh's project order, so turning this off keeps the current order. | <a href="assets/features/projectStatus.webp"><img src="assets/features/projectStatus.webp" width="160" alt="Screenshot: Active projects first"></a> |
68
- | **Recent projects filter** | <a id="feature-recentprojects"></a>Sidebar → view options (sliders icon) → **Active in the last 2 hours**. While it is selected, the sidebar shows only projects with a chat that was active in the last 2 hours: a chat updated in that time, a running or waiting chat, the open chat, or a finished chat that you did not open yet (dsh's green “done” dot), also after the 2 hours. Projects without such a chat are hidden, also new and empty ones. Select it again to show all projects. The choice is kept on this device. | <a href="assets/features/recentProjects.webp"><img src="assets/features/recentProjects.webp" width="160" alt="Screenshot: Recent projects filter"></a> |
72
+ | **Recent projects filter** | <a id="feature-recentprojects"></a>Sidebar → view options (sliders icon) → **Active in the last 2 hours**. While it is selected, the sidebar shows only projects with a chat that was active in the last 2 hours: a chat updated in that time, a running or waiting chat, the open chat, or a finished chat that you did not open yet (dsh's green “done” dot), also after the 2 hours. Projects without such a chat are hidden, also new and empty ones. In **Workspace Tree**, a parent folder stays while a project inside it is active, and the chats that were not active in the last 2 hours hide too, so only the recent chats show. Select it again to show all projects. The choice is kept on this device. | <a href="assets/features/recentProjects.webp"><img src="assets/features/recentProjects.webp" width="160" alt="Screenshot: Recent projects filter"></a> |
69
73
  | **Collapse all projects** | <a id="feature-collapseprojects"></a>While a project is open in the sidebar, a button appears next to the view options (sliders icon). Click it to close every open project at once, for example 3 open projects out of 5. The button hides again when no project is open. | <a href="assets/features/collapseProjects.webp"><img src="assets/features/collapseProjects.webp" width="160" alt="Screenshot: Collapse all projects"></a> |
70
74
  | **Order by status** | <a id="feature-statusorder"></a>Sidebar → view options → **Order by** → **By status** (with **In one list**). Finished chats come first (dsh's “done” dot, which a turn that ended with an error also gets), then running or waiting chats, then chats with an active automation (Schedule), then the rest in dsh's order. Opening a finished chat moves it down. Only the view changes: select **Manual** or **Last updated** to go back. | <a href="assets/features/statusOrder.webp"><img src="assets/features/statusOrder.webp" width="160" alt="Screenshot: Order by status"></a> |
71
75
  | **Background notifications** | <a id="feature-notifications"></a>While dsh is in the background you get a notification when a turn finishes (first line of the reply), or when a chat really waits for you: an approval (tool name), an answer (the question) or a plan review. A request must still be open 1.5 s later, so approvals answered automatically (Auto Review grants, other plugins) stay silent, and a notification is withdrawn as soon as its request is answered. The Dock/taskbar badge counts waiting chats. Clicking opens that chat. | <a href="assets/features/notifications.webp"><img src="assets/features/notifications.webp" width="160" alt="Screenshot: Background notifications"></a> |
76
+ | **Open waiting chat when idle** | <a id="feature-idleswitch"></a>When a chat asks a question, or waits for an approval or a plan review (the orange dot), and you do not use dsh for 30 seconds, dsh opens that chat. When more than one chat waits, the one that waits longest opens first; the next one opens after another 30 seconds without use. A key, a click, a mouse move, the scroll wheel or a touch starts the 30 seconds again. Text in the field that has the focus, selected text, or an open menu or dialog also stops the switch. While a waiting chat is on screen, nothing moves. When you leave a waiting chat without an answer, it can open again after the next 30 seconds without use. This also works while dsh is in the background, but dsh does not come to the front. | <a href="assets/features/idleSwitch.webp"><img src="assets/features/idleSwitch.webp" width="160" alt="Screenshot: Open waiting chat when idle"></a> |
72
77
  | **Application language** | <a id="feature-language"></a>Settings → Composer Plus → **Application language** switches the whole dsh interface to Türkçe, Español, Português (Brasil), Français, Deutsch, Русский, 日本語, 한국어, Bahasa Indonesia, Tiếng Việt or हिन्दी (see below). | <a href="assets/features/language.webp"><img src="assets/features/language.webp" width="160" alt="Screenshot: Application language"></a> |
73
78
  | **Mobile connect (iPhone)** | <a id="feature-mobileconnect"></a>Use dsh from your iPhone with the [Tether](tether/) app, on the same Wi-Fi or over [Tailscale](https://tailscale.com). Switch it on in **Settings → Composer Plus** (it is off by default, because it opens a network port on this Mac). Then click **Mobile connect** at the bottom left of the sidebar: scan the QR code with the iPhone Camera (tap **Open in Tether**) or with **Scan code** in Tether, tap **Connect** and click **Approve**. Tether must be installed first: without it the Camera cannot open the code. The phone shows your chats, sends messages (with photos: the composer's **+** → Camera, Photos or Paste image), answers approvals and questions, and switches models and agents. Pair as many Macs as you like: the Mac name at the top left of the app switches between them. The **Mobile connect** panel lists the paired iPhones with their last connection: **Remove** cuts one off at once. **Tailscale only** refuses the local network (for a shared Wi-Fi), and the access log shows who tried to connect. In Tether, **Settings → Privacy** locks the app with Face ID. | <a href="assets/features/mobileConnect.webp"><img src="assets/features/mobileConnect.webp" width="160" alt="Screenshot: Mobile connect (iPhone)"></a> |
74
79
  | **Automatic updates** | <a id="feature-autoupdate"></a>When dsh starts, the plugin checks the [latest GitHub release](https://github.com/furkancak1r/dsh-composer-plus/releases/latest). If it is newer and already on npm, it installs that exact version through dsh's own plugin manager (the same path as Settings → Plugins) and notifies you. Restart dsh to load it. **Settings → Composer Plus → Composer Plus updates** shows the version and has **Check for updates** / **Update now**. The first time dsh starts with the new version, a **What’s new in Composer Plus N** window shows the release notes of that version once (taken from the same update check, no extra network call); **Close** or `Esc` closes it. | <a href="assets/features/autoUpdate.webp"><img src="assets/features/autoUpdate.webp" width="160" alt="Screenshot: Automatic updates"></a> |
80
+ | **Backup** | <a id="feature-backup"></a>Settings → Composer Plus → **Backup** → **Back up now** writes your whole dsh home (chats, settings, plugins, MCP, skills) into one archive in `~/Desktop/dsh-backups`, with a checksum. Secrets are masked in the archive: credential files, key files (`.pem`, `.key`) and the token, secret and password values of the yml settings stay on this computer only, in the folder's `vault`. `cache/`, `dsh-runtimes/` and `node_modules` are not archived (dsh makes them again). Each backup in the list has **Check** (reads it fully), **For another computer** (a folder to copy to the new computer, with the rescue console) and **Delete**. dsh-backup's archives in the same folder show and work too. To restore, quit dsh and use the rescue console. | <a href="assets/features/backup.webp"><img src="assets/features/backup.webp" width="160" alt="Screenshot: Backup"></a> |
81
+ | **Automatic backup** | <a id="feature-autobackup"></a>Switch on **Automatic backup** (off by default: each copy is about as large as your dsh home without its cache). Choose how often (**Every**: 6 hours to 7 days, 24 hours by default) and how many backups stay (**Keep**: 3 to 14, 7 by default). The next time shows under the switch; a failed backup shows there too and is tried again 30 minutes later. When dsh or Composer Plus starts with a new version, a snapshot comes soon after the start, before the new version has used your data for long (the two newest stay, marked **Before an upgrade**). | <a href="assets/features/autoBackup.webp"><img src="assets/features/autoBackup.webp" width="160" alt="Screenshot: Automatic backup"></a> |
82
+ | **Session log check** | <a id="feature-sessiondoctor"></a>**Check session logs** reads every chat's log and lists the ones that dsh cannot open (a broken frame, a cut record, a wrong sequence number). A torn tail is not listed: dsh repairs it itself. **Repair from backups** puts back the copy from the newest backup that has a healthy one and keeps the damaged file next to it (`.corrupt-<time>`). Messages written after that backup are not in the copy. A chat that is open in this dsh is not repaired here: quit dsh and repair it in the rescue console. | <a href="assets/features/sessionDoctor.webp"><img src="assets/features/sessionDoctor.webp" width="160" alt="Screenshot: Session log check"></a> |
83
+ | **Rescue console** | <a id="feature-rescueconsole"></a>Each backup also puts a rescue console into the backup folder. When dsh is closed or does not start, open `Composer Plus Rescue` in that folder (macOS: `Composer Plus Rescue.command`): a menu in a terminal window lists, checks and restores backups and checks and repairs session logs. It opens no network port. A restore asks you to type `RESTORE`, keeps your current dsh home as `.dsh.pre-restore-<time>`, puts the masked secrets back from the vault and moves `cache/`, `dsh-runtimes/` and `node_modules` over. It works without dsh: it uses Node.js, or the DeepSeek Harness app itself. On a new computer, copy the folder of **For another computer** and open `Composer Plus Rescue` there. | <a href="assets/features/rescueConsole.webp"><img src="assets/features/rescueConsole.webp" width="160" alt="Screenshot: Rescue console"></a> |
75
84
 
76
85
  <!-- END GENERATED FEATURES -->
77
86
 
@@ -115,7 +124,7 @@ Each pack has 3,280 texts: every UI text of dsh `0.2.0-rc.2` and `0.2.1-rc.2` (c
115
124
  | `Esc` | empty composer with a prediction | hide the prediction |
116
125
  | `Cmd/Ctrl+Z` | composer, after **Improve prompt** | bring back the text that you typed |
117
126
  | `Esc` | composer, while **Improve prompt** runs | cancel the improvement |
118
- | `Enter` / `Esc` | queued message that you double-clicked | save / cancel the edit |
127
+ | `Enter` / `Esc` | queued message editor | save / cancel the edit |
119
128
  | `Cmd+F` on a Mac, `Ctrl+F` on Windows and Linux | open chat | open **Find in chat** |
120
129
  | `Enter` / `Shift+Enter`, `↓` / `↑` | **Find in chat** box | next / previous match |
121
130
  | `Esc` | **Find in chat** box | close the box |
@@ -149,6 +158,9 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
149
158
  - **Complete while typing:** off until you switch it on. While it is on, at each pause while you type, the Host half sends the text that you typed so far and the chat's own user messages and agent text replies (no tool output, no hidden context, no reasoning), at most about 3,000 characters of chat, to the model chosen under **Complete while typing** (by default the model of **Next message prediction**). A newer pause stops the older request. Nothing is saved in the chat. The switch is in `$DSH_HOME/storages/dsh-composer-plus/complete.json` (owner-only file).
150
159
  - **Improve prompt:** nothing runs until you click the magic wand. Then the Host half sends your draft, the chat's own user messages and agent text replies (no tool output, no hidden context, no reasoning, no rolled-back messages; at most about 6,000 characters, the newest part), the same project summary as **Feature ideas** (with the same file rules: no `.env*` or key files, no symlink out of the folder, no FIFO), the paths that `git status` lists as changed and up to 30 tracked or untracked (not ignored) paths whose names share words with your draft, to the model chosen under **Improve prompt** (by default the chat's own model), as one request. File contents other than that summary are not sent. Nothing is written to the chat or the project: the answer only replaces your draft in the composer, and you decide whether to send it. The model choice is in `$DSH_HOME/storages/dsh-composer-plus/improve.json` (owner-only file).
151
160
  - **Goal check:** it runs only when the agent marks a `/goal` complete in an automatic round (or when you click **Run now**). The Host half runs the test command in the chat's folder with the chat's sandbox mode (as dsh's own `bash` tool: the same file-access policy; when that sandbox cannot start, the test does not run and the check fails). It reads only `package.json`, `Makefile`, `Cargo.toml`, `go.mod`, `pyproject.toml` and `pytest.ini` to find the command, as regular files inside the folder (no symlink out of it, no FIFO). Then it sends the goal, the chat since the goal started (your messages, the agent's replies, tool calls cut to 1,000 characters and the last 2,000 characters of each tool result; no reasoning, no rolled-back steps; at most about 60,000 characters, the newest part), `git status --short` and the end of the test output to the model chosen under **Goal check** (by default the chat's own model), as one request with no tools. Tool output can contain what the agent read, so this request can hold parts of your files, as the chat itself does. Nothing is written to the chat or the project. Your test command, the limits, the last result (with the end of its output) and the model choice are in `$DSH_HOME/storages/dsh-composer-plus/goal-check.json` (owner-only file, newest 500 chats).
161
+ - **Project memory:** off until you switch it on. While it is on, after each finished turn of a top-level chat, the Host half sends the chat's own new messages since the last review (your messages and the agent's text replies; no tool output, no reasoning, no hidden context, no rolled-back messages; a fork's copy of its parent is left out; at most about 16,000 characters) and the saved notes to the chat's own model, as one request with no tools. The notes are Markdown files under `$DSH_HOME/storages/dsh-composer-plus/memory/` (owner-only files): `projects/<id>/` for each project folder (`<id>` is a hash of the folder's real path; `project.json` names the folder), `global/`, `journal.jsonl` (every change with the line before and after) and `reviewed.json` (how far each chat was read). Nothing is written to the project folder. Notes that look like a key, a token, a password or an ID number are refused. The notes of a project go into the system prompt of each new chat in that folder, so they reach the model with every request of that chat. The switch is in `memory.json` next to it.
162
+ - **Backup:** nothing leaves your machine. Archives go to `~/Desktop/dsh-backups` (owner-only files). Secrets are not in an archive: credential files (`.credentials.yaml`, `.env`), key files (`.pem`, `.key`, `.p12`, `.pfx`) and the values of token, secret, password and API key settings in the yml files are masked; their real copies stay in `composer-plus-vault/<archive>/` in the same folder (owner-only), on this computer only. **For another computer** copies no vault: enter those secrets again there. Your chat logs, settings and the rest of the dsh home are in the archive as they are: keep the folder private, as `$DSH_HOME` itself. The backup reads files with `O_NOFOLLOW | O_NONBLOCK`; a symbolic link is stored as a link (never followed) and a FIFO or device is skipped. The automatic backup switch and settings are in `$DSH_HOME/storages/dsh-composer-plus/auto-backup.json`, the rescue console switch in `rescue-console.json`, and while dsh runs, `running/host-<pid>.json` names each dsh process (the rescue console reads them to refuse a restore).
163
+ - **Rescue console:** a terminal menu; it opens no network port. A restore writes only into a new folder next to the dsh home (files with `O_EXCL | O_NOFOLLOW`, symbolic links last and never followed) and refuses an archive with an unsafe name (`..`, an absolute path), a device or FIFO, more than one top folder or an entry below a link.
152
164
 
153
165
  ## Troubleshooting
154
166
 
@@ -163,11 +175,15 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
163
175
  - **Delete says another dsh has this chat open:** a second dsh on the same dsh home holds the chat's lock. Close that window or dsh, then delete again. The chat stays archived meanwhile.
164
176
  - **My iPhone cannot connect after I switched on Tailscale only:** Tailscale must be on in both the Mac and the iPhone. The Mobile connect panel warns when it is off on the Mac. The access log shows each refused attempt with its reason.
165
177
  - **A chat did not continue after a restart:** Resume after restart was off when dsh quit, you stopped that turn yourself, the turn had ended (done or failed), the chat was archived, or more than 20 chats were running (the newest 20 continue).
178
+ - **The queue is empty after a restart:** dsh itself empties the queue when it quits. Resume after restart puts it back only in a chat that it continues; in other chats, send the messages again. A message that you removed just before the quit can come back: remove it again.
166
179
  - **Open GitHub issue says "gh could not open the issue":** the message is `gh`'s own first line. Run `gh auth status` in a terminal and log in with `gh auth login`; the chat's folder must have a GitHub remote (`git remote -v`). "not installed or not on the PATH of dsh" means dsh did not find `gh`: install it, or start dsh from a terminal where `gh` works.
167
180
  - **No branch pill on a chat row:** the chat had no turn since you installed this version, its folder is not a git repository, or **Git branch on chat rows** is off. After an update, restart dsh (the Host half records the branch).
168
181
  - **Goal check says the test exited with code 127 or "not found":** dsh started from the Finder or the Dock can have a shorter PATH than your terminal, so it does not find `npm`, `pnpm` or `cargo`. Start dsh from a terminal, or set your own test command with the full path under **Goal check** → **Edit**. **Run now** shows the result at once.
169
182
  - **The goal does not close and the agent keeps working:** **Goal check** found a failing test or missing work; the row under the goal shows why (hover the result for the details). Fix the cause or the test command, or complete the goal yourself with `/goal` or the goal bar (that is not checked).
170
- - **Install fails right after a release:** add the version (`dsh-composer-plus@1.15.0`), see the install note above.
183
+ - **Install fails right after a release:** add the version (`dsh-composer-plus@1.16.0`), see the install note above.
184
+ - **Back up now says "another backup action is running":** an action runs in another dsh window or in the rescue console (they share a lock file, `.composer-plus.lock`, in the backup folder). Wait for it. If no action runs, the lock of a closed process is taken over at the next click.
185
+ - **The rescue console says "dsh is running":** quit dsh first. If dsh is really closed, the file named in the message is left from a crash: remove it and try again.
186
+ - **After a restore, dsh does not start or a plugin is missing:** the plugin packages of a profile must be installed again. The rescue console offers it after a restore; or run `pnpm install` in each folder under `$DSH_HOME/profiles/` that it lists. Secrets that were not in the vault (another computer) must be entered again: the console lists them.
171
187
 
172
188
  ## Limits
173
189
 
@@ -175,6 +191,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
175
191
  - **Feature ideas** keeps the cards in memory for each chat until dsh or the page reloads; then **Suggest features** asks the model again. The cards come in the UI language that dsh has when you click; after you change the language, cards already on the stack keep theirs and **More ideas** uses the new one. The model sees only the short project summary, not the code, so an idea can repeat something the project already has: skip it. The new chat runs in plan mode only when the profile has dsh's `/plan` command; otherwise the idea goes as an ordinary first message. The **Later** list belongs to the chat's folder path: the same folder opened through a symlink has its own list. It keeps at most 50 ideas per project (remove one to add more) and the same title only once. Dragging a card decides left or right only; use **↓** to save it and **▶** to build it now. A repeat is found by its title only: the model can still suggest an earlier idea under a new name; skip it. When the model answers only earlier ideas, nothing new shows: click again. A narrow focus topic makes that more likely; use a wider topic or clear it. The focus topic stays in the field for each chat until dsh or the page reloads.
176
192
  - **Export chat as Markdown** has only your own messages and the assistant's text replies: tool calls and their output, thinking, images (a `[image]` line marks them), hidden context and messages that Turn rollback or dsh-rewind-plugin took out are left out. Replies of one turn join into one section. At most about 4 million characters go into one export; a longer chat gets its first part and a note.
177
193
  - **Resume after restart** continues the turn; it does not replay it. A tool call that was running at the quit ends with dsh's “outcome unknown” result, and the model is told not to repeat finished work, but a command that is not safe to run twice can run again. Subagents, approvals and questions that were open are not restored: their parent chat continues and asks again. At most 20 chats continue at one start.
194
+ - **Resume after restart** and the queue: dsh empties a chat's queue when it quits. The plugin puts your queued messages back from the chat's log, with their order, edits, images and files; they run after the continued turn. Only chats that continue get their queue back (switch on, a quit while the turn ran). dsh logs your own removal of the last queued messages the same way: if you remove them and dsh quits before the next step starts, they come back.
178
195
 
179
196
  - **Content search** is dsh's own search, so it has dsh's limits. It searches only user and assistant messages: not tool output (commands, file contents, results), not reasoning, and it has no filter for user, assistant or tool messages. It matches whole words, not parts of a word (`deploy` does not find `deployment`); letters with accents match without them (`seker` finds `şeker`). One row per chat, with dsh's 240-character snippet, at most 20 rows.
180
197
  - **Content search** cost, measured on a copy of 247 real chat logs (159 MB, dsh 0.2.1-rc.2, Apple Silicon, 32 GB): the first search after a start builds the index in about 15 s. During that time dsh's Host answers nothing for about 7 s, so other chats stop streaming for that moment (the page itself does not freeze). The Host's memory grows by about 1.1 GB at the peak; one minute later it is still about 0.5–0.6 GB above the start, until dsh quits. The index file is about 165 MB. Later searches take about 0.5 s. After a restart, the first search takes about 1 s (only new or changed chats are added) and adds about 70 MB. Switch it off if your computer has little memory.
@@ -185,19 +202,25 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
185
202
  - dsh 0.2.1 loads an update at once when the new version changes no dsh settings; otherwise, and on dsh 0.2.0, restart dsh to load it. The plugin never restarts dsh for you. Web/CLI profiles without dsh's plugin manager only report a new version.
186
203
  - **Allow for this chat** counts the time limit from the moment of the grant. A new time limit applies at once to the grants that run, so a shorter one can end them at once. Grants from version 1.12 and earlier have no time: after the update, the agent asks once again for those tools.
187
204
  - **Keep composer focus** gives the focus back only after a `/` command or skill. While dsh resolves the command, the composer stays read-only for a few milliseconds, so keys pressed in that time are lost. If dsh's `/` menu is still open, `Enter` picks the highlighted row and does not send.
188
- - **Mobile connect** works on macOS only (it uses `en0`, `openssl`, `swift` and `dns-sd`). It uses fixed ports 19390 and 19391, so only one dsh on each Mac can run it at a time (each Mac is one entry in the phone's Mac list). The phone gets notifications only while Tether is open or just went to the background (no push service). The iOS app is not on the App Store yet: build it from `tether/ios` with Xcode. **Tailscale only** still needs a Wi-Fi or Ethernet address on the Mac (the gateway starts only with one). The access log is per Mac and keeps only the newest 200 attempts; a repeated attempt within a minute counts up one row. Tether's app lock does not hide notification previews on the iPhone Lock Screen: turn them off in iOS Settings. Tether's green dots follow the Mac: a chat that you open in dsh loses its dot on the phone too. A chat that you open in Tether loses its dot on the phones only; dsh's own dot on the Mac stays until you open the chat there.
205
+ - **Fit question card to window** changes only dsh's question card, not the plan review card. It finds the card by dsh's markup (`data-question-key`, `data-question-scroll`); if a dsh update changes that markup, the card shows as dsh draws it. The card's height follows the window, not the chat: in a chat in a narrow side panel, a tall card can still reach above the chat.
206
+ - **Mobile connect** works on macOS only (it uses `en0`, `openssl`, `swift` and `dns-sd`). It uses fixed ports 19390 and 19391, so only one dsh on each Mac can run it at a time (each Mac is one entry in the phone's Mac list). The phone gets notifications only while Tether is open or just went to the background (no push service). Tether sends them only after you switch on **Settings → Notifications → Send notifications** in Tether. This switch is off by default, and iOS asks for the permission when you switch it on. The iOS app is not on the App Store yet: build it from `tether/ios` with Xcode. **Tailscale only** still needs a Wi-Fi or Ethernet address on the Mac (the gateway starts only with one). The access log is per Mac and keeps only the newest 200 attempts; a repeated attempt within a minute counts up one row. Tether's app lock does not hide notification previews on the iPhone Lock Screen: turn them off in iOS Settings. Tether's green dots follow the Mac: a chat that you open in dsh loses its dot on the phone too. A chat that you open in Tether loses its dot on the phones only; dsh's own dot on the Mac stays until you open the chat there.
189
207
  - A session link works inside chats; clicking it does not open the chat. A **Copy message link** link opens its message only on dsh 0.2.1 and later, and only from a message in a chat (dsh 0.2.0 has no user-text rules, so there the link is copied but shows as plain text). A message that a rollback took out, or that compaction removed from the history that dsh loads, cannot be shown: the link then says so. The 🔗 buttons attach to dsh's message rows; if a dsh update changes that markup, they do not show.
190
208
  - **Mark all as read** changes dsh's own unread dots through a part of dsh that is not a public API (the `completionUnread` list of dsh's session status). If a dsh update renames it, the item does not show. The item goes into dsh's project "…" menu, which has no slot for plugins; if dsh changes that menu, the item does not show either.
191
- - **Edit queued message in place** opens dsh's own queue editor, so it has its limits: a message with images or files cannot be edited, and a message that started to send meanwhile is not changed (dsh shows its own error).
209
+ - **Edit queued message in place** does not change a message that started to send meanwhile, and it does not overwrite a message that changed meanwhile (for example in Tether on the iPhone): a notice tells you. A new image needs a model that takes images (dsh's own image limits apply), and a new file must finish its upload before you can save. Only your own queued messages can be edited: a goal round or a question reply in the queue cannot. After a plugin update, only text-only edits work at once: restart dsh before you save a message with images or files (the Host half loads at start).
192
210
  - **Redo** never changes the rolled-back chat: it starts a new chat from just before the rollback (dsh's own fork, the same model context), so the new chat has a new id and title. Only the newest rollback of a chat can be redone, once, and only while nothing came after it (a new message ends the offer). A file that you changed after the rollback is kept unless you tick the box that overwrites it. Redo needs the snapshot that the rollback kept (the same 50-entry window); a rollback made before this version, or with **Chat only**, brings back the messages only.
211
+ - The **Tree** branch map guesses where each arrow goes, because git does not save the branch that a branch merges into. A merged branch points to the branch whose merge commit took it first. A branch that waits points to the nearest branch that it started from (the main branch first); when two branches started from the same commit, the map can choose the wrong one. A branch that moved forward with a fast-forward has no merge commit, so its **Merged** label opens nothing. The main branch is the default branch of `origin`, else `main` or `master`, else the open branch. The map reads the newest 20,000 commits and shows at most 60 branches; in a longer history, it says that some arrows can be wrong. A branch that was merged with a squash or a rebase (for example on GitHub) shows as **Waiting**: git keeps no link to its commits.
193
212
  - **This chat** counts only files that dsh's file tools (`write`, `edit`, `str_replace_editor`) wrote. Files that a command (`bash`) changed are not in this view; they show under **Uncommitted**. Without a Turn rollback snapshot of the chat (Turn rollback off, dsh-rewind-plugin installed, or a chat older than the plugin), the view compares with the last commit and says so. Turn rollback keeps the 50 newest snapshots of a chat: in a longer chat, the view compares with the oldest kept one, says from which message, and does not count earlier changes. **Hide whitespace changes** is `git diff -w`: a line that changed only in spaces inside it is hidden too.
194
213
  - **Open GitHub issue** needs the GitHub CLI (`gh`) on the PATH of dsh and a GitHub remote in the chat's folder. It runs at most 60 s; if `gh` takes longer, the issue can exist on GitHub without its link in the list (click again: the list reads the link only from a finished run, so look on GitHub before you open a second one). A folder with more than one GitHub remote uses `gh`'s own default (`gh repo set-default`).
195
214
  - **Chats that changed this file** knows only dsh's file tools (`write`, `edit`, `str_replace_editor`), as **This chat**: a change made by a command (`bash`) is not listed. It reads the newest 300 chats of the repository (by when they were made) and says so when there are more; it shows at most 100 rows. A renamed file is listed under the name that the tool wrote. A chat whose folder is outside the repository (also through another path to the same files) is not counted. The first open after a dsh start reads every chat log of the repository (about 4 s for 275 chats); later opens read only the chats that changed since.
196
215
  - **Git branch on chat rows** shows the branch of the chat's folder when its last turn started or ended, not the branch now: a branch switch outside a turn shows after the next turn. Chats from before this version show no pill until their next turn. The pill sits in dsh's chat row markup (dsh has no slot there); if a dsh update changes that markup, no pill shows. A chat under a project row with its branch's name (a “Branch per chat” folder) shows no pill, except in “In one list”.
216
+ - **Branch per chat** in plan mode: dsh sends `/plan <message>` as a command, not as a message, so Composer Plus takes the command's submit in dsh's composer (not a public API). If a dsh update changes it, `/plan <message>` with **New branch** chosen runs in the open New chat as dsh's own command. When the Host cannot turn on plan mode in the new chat, the branch folder stays and the message is not sent, so the new chat does not change files in the default mode.
217
+ - **Branch per chat**: when a chat's branch folder (`<project>/.worktrees/<name>`) is deleted, for example when the chat's own agent removes the worktree and deletes the branch, the chat's next step moves the chat out of it. Its branch folder project goes from the sidebar, and the chat shows under Ungrouped and stays open. On dsh 0.2.1 and later the chat continues in the main project folder, and the model gets a note about the new folder. On dsh 0.2.0 the chat keeps the deleted folder: a command there fails until you start a new chat. The move happens only at the chat's next step, so a project of a deleted branch that no chat uses stays in the sidebar: remove it with dsh's own project menu. While a chat moves, **New branch** in the same repository answers "try again". With the switch off, dsh's own error shows again ("working-directory: directory does not exist").
218
+ - **Branches in the workspace tree** writes to git without a click: when it moves a chat, it switches the project folder back to its branch (`git switch`) and adds a worktree (`git worktree add`). It never deletes a branch, a worktree or a file. dsh cannot change the folder of a chat, so a moved chat is a copy (dsh's own fork, with a new id) and the old chat is archived. The copy continues the stopped task; a tool call that was running when the branch appeared ends with a refusal, and the agent runs it again in the new folder. A goal of the old chat does not move with it. Only a chat whose folder is the repository's main folder moves, and only for a branch that it created and switched to in that turn (`git switch -c`, `git checkout -b`): a branch made with `git branch` alone, a folder with uncommitted changes, a linked worktree or a subfolder moves nothing. Only worktrees directly in `<project>/.worktrees/` with a branch show in the tree; a detached worktree or one elsewhere does not. The copy opens by itself only when its chat was the open one (the plugin reads dsh's sidebar row for that); else it is in the branch project.
197
219
  - **Find in chat** searches only what the page shows: the messages and replies that are loaded (scroll up or click **Load earlier** for older ones), not tool steps that are closed and not the text inside a tool's output that is folded. It counts at most 2,000 matches (then "2000+"). It uses the browser's highlight API (Chromium 105 and later; the desktop app has it): in an older browser the count and the moves work without the color. `Cmd+F` on a Mac (`Ctrl+F` on Windows and Linux) opens it only while a chat is on screen; elsewhere the browser's own find works as before. On a Mac, `Ctrl+F` keeps its macOS use in text fields (the cursor moves one character forward).
220
+ - **Open waiting chat when idle** knows only what dsh's own window sees: work in another app does not count, so while dsh is in the background it can open a waiting chat after 30 seconds. It does not bring dsh to the front. Reading without a key, a click or a mouse move counts as idle. It looks only at chats with dsh's orange dot (a question, an approval or a plan review); a subagent's request shows in its parent chat. Nothing opens while a waiting chat is on screen; a chat that you left without an answer can open again after the next 30 seconds without use. The 30 seconds cannot be changed.
198
221
  - A notification sent before a restart only brings dsh to the front when clicked.
199
222
  - Active projects are moved with dsh's saved project order, so the new order is kept after a restart. Chats outside any project ("Ungrouped") can't be moved; dsh always lists them last.
200
- - dsh has no extension point in the sidebar's view options menu, so the recent projects option is added to dsh's own menu. If a dsh update changes that menu, the option is not shown and all projects stay visible. The filter works on project rows, so **In one list** is not filtered. Use it with **WorkSpace**: in **Workspace Tree**, a hidden parent folder also hides the active projects inside it. **By status** has the same kind of limit: dsh has no order slot, so the item and the row order attach to dsh's menu and list markup.
223
+ - dsh has no extension point in the sidebar's view options menu, so the recent projects option is added to dsh's own menu. If a dsh update changes that menu, the option is not shown and all projects stay visible. The filter works on project rows, so **In one list** is not filtered. In **Workspace Tree**, a parent folder stays while a project inside it is active, and the chat rows that were not active in the last 2 hours hide too; in **WorkSpace**, only project rows are filtered. The filter reads which view dsh shows from dsh's saved view setting (not a public API); if a dsh update renames it, **Workspace Tree** is filtered like **WorkSpace**. Project status reads the same setting: in **Workspace Tree**, a parent folder's row also counts the chats of the projects inside it; if dsh renames the setting, each row shows only its own chats. **By status** has the same kind of limit: dsh has no order slot, so the item and the row order attach to dsh's menu and list markup.
201
224
  - dsh has no project-row menu or Workspaces header slot either. **Copy path** finds the project row by dsh's row markup, and **Collapse all projects** goes into dsh's header and closes each open project with the same click as the project row. If a dsh update changes that markup, right-click shows the browser's menu and the button is not shown.
202
225
  - dsh has no extension point in the new chat project menu either, so the **Search projects** box and the A–Z order attach to dsh's menu markup. If a dsh update changes that markup, the menu shows as dsh draws it: in the saved order, without the box. The A–Z order is a view only: it does not change dsh's saved project order.
203
226
  - dsh has no hook that chooses where **New session** opens either. **Default workspace for New session** wraps a method of dsh that is not a public API (`startSession` of dsh's workspace service). If a dsh update renames it, **New session** opens where dsh chooses (the open or the most recent project). Every call that names no project uses the default: the sidebar button, its shortcut, the logo, and also **New** on the **Automation tasks** page. dsh still opens the last chat when it starts. The general folder is made and added to your projects at the first use; on Windows or Linux with a moved Documents folder it can be a different folder from dsh's own.
@@ -205,6 +228,12 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
205
228
  - **Complete while typing** shows its text through dsh's own composer hint (the `--dsh-composer-hint` style that dsh uses for a slash command's argument hint). It does not run for a slash command, a message with a file or symbol chip, or when the cursor is not at the end of the message. If a dsh update changes that style, no completion shows. Each pause while you type costs one model call.
206
229
  - **Improve prompt** sees only a short project summary, the changed paths and the paths whose names match your words: not the code itself. So it can miss the right file, and it names only paths that it was given. Read the new text before you send it. It does not run for a slash command or a message with a file or symbol chip (the new text is plain text, so it would lose the chip), and a message can have at most 8,000 characters. Images stay attached: only the text changes. Each click costs one model call; a strong model can take a minute (`Esc` cancels).
207
230
  - **Goal check** checks only a completion that the agent makes in an automatic goal round: a completion that you ask for in the chat, `/goal` and the goal bar are not checked. The agent can change the tests or the test command's files itself; the verifier reads the chat and can see that, but it can be wrong in both directions (a wrong "not done" costs one more round; a wrong "done" closes the goal). The verifier sees the chat and `git status`, not the code itself. A test run is reused only when the agent's newest step that can change files was its own passing `bash` run of exactly the same command in the same folder; a command with a pipe (`| tail`) or `&&` is not reused. The budget is measured when a turn ends, so one turn can go past it. Only the chat's own model calls count (not its subagents'). `update_goal` waits while the check runs (the test at most 15 minutes, the verifier at most 3 minutes). A fork gets a new chat id, so it starts without the parent's own test command and limits. Each completion that reaches the verifier costs one model call.
231
+ - **Send after Stop goes first** works only after you stop the chat (or after a turn that failed, or when you open a chat that still has a queue): a message that you send while a turn runs still waits at the end of the queue. It moves only past your own queued messages: when a goal round or a question reply waits in the queue, dsh's own order stays. It reads a part of dsh that is not a public API (whether the stopped turn still ends); if a dsh update renames it, the message moves only after that turn has ended. Subagent chats keep dsh's own order.
232
+ - **Project memory** belongs to the chat's first folder (dsh's project folder): a chat that changes its folder with `cd` keeps its first project's memory, and the same folder opened through another path to the same files shares it. A chat freezes its memory section when it starts, so a note saved later shows from the next chat (`memory_recall` finds it at once). The review keeps only what the model decides to keep: it can miss a fact or keep a wrong one; open the files to check them, and edit or delete them by hand while dsh is closed. Each finished turn with a message from you costs one model call; a failed call reads the same messages again after the next turn. Subagent chats are not reviewed. The idea and the file format come from [DS Bot](https://github.com/Fei2411/DS-Bot) (Apache-2.0); Composer Plus uses no code from it, and it never reads or writes DS Bot's own memory.
233
+ - **Backup** keeps one fixed folder, `~/Desktop/dsh-backups` (dsh-backup's default, so its archives stay usable); there is no setting for another folder. An archive is about as large as the dsh home without `cache/` and `dsh-runtimes/` (measured: 1.6 GB of files gave an 830 MB archive in about 30 s on Apple Silicon; dsh stayed responsive). A file that changes while it is read is stored as it was when the backup opened it, so an open chat's log can end in a cut-off record, which dsh repairs at its next write. Masking finds a secret in a yml file by its key name on one line (`token:`, `apiKey:`, …); a secret in a flow map (`{token: x}`), in a list under such a key, in a JSON file (for example an MCP server's `apiKey` in a `.json` file) or under another name stays in the archive. dsh-backup's typed archives (`dsh-t-…`) are not listed. **For another computer** copies the archive (a clone on APFS: no extra space); the vault does not go with it. **Back up now** deletes no backup. An automatic backup keeps the newest backups that Composer Plus made (the **Keep** setting of **Automatic backup**) and deletes its older ones; snapshots before an upgrade are counted apart (the newest 2 stay). Archives of dsh-backup are never deleted: remove them with **Delete**. While dsh-backup is still installed, it counts the Composer Plus archives in its own keep count and can delete them.
234
+ - **Restore** runs only in the rescue console, while dsh is closed: it replaces the whole dsh home. The old one stays next to it as `.dsh.pre-restore-<time>` until you remove it, so the disk needs room for both. Chats keep their project paths: on another computer, a project at another path shows as missing until you add it again. Symbolic links that point outside the dsh home (for example skills linked from another folder) come back as links; their targets must exist on that computer.
235
+ - **Session log check** reads the newest log of each chat with dsh's own rules for what opens: a row that is not JSON or is out of order counts as damage only when a turn end comes after it (else dsh opens the chat without the rows from there, so it is not listed). It does not check a log's events against dsh's own event rules, so a log that it calls healthy can still fail to open after a dsh upgrade. A repair takes the copy from a backup: the messages after that backup are lost from that chat (the damaged file stays next to it). A chat that is open in this dsh is not repaired in Settings: dsh holds it.
236
+ - **Automatic backup** runs only while dsh runs: a backup that was due while dsh was closed runs about 45 s after the next start. The snapshot for a new version is taken after that start, not before the update.
208
237
  - Built-in support is requested upstream: queue `move` in [#773](https://github.com/deepseek-ai/deepseek-harness/discussions/773), a Continue state in [#9269](https://github.com/deepseek-ai/deepseek-harness/discussions/9269).
209
238
 
210
239
  ## FAQ
@@ -233,9 +262,19 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
233
262
 
234
263
  **Can the agent stop marking its own work as done, like the verifier loops in loop engineering?** Yes, with **Goal check** (on by default): start a goal with `/goal`. When the agent says the goal is complete, the project's tests run and an independent model call checks the goal against the chat; if either fails, the goal stays open and the agent reads why. Set your own test command, a time limit or a token limit with **Edit** in the **Goal check** row under the goal.
235
264
 
265
+ **Can the agent remember my preferences across chats, like DS Bot's memory?** Yes, with **Project memory** (off by default): switch it on in **Settings → Composer Plus**. After each turn, your lasting preferences and decisions are kept as short notes for the chat's project, and each new chat in that project reads them. Ask the agent to remember something and it saves it at once.
266
+
267
+ **Can dsh back up my chats and settings, like dsh-backup?** Yes: **Settings → Composer Plus → Backup → Back up now**. It writes the same archive format into the same folder (`~/Desktop/dsh-backups`), so dsh-backup's archives show and restore too, and you can remove dsh-backup. Switch on **Automatic backup** for a schedule.
268
+
269
+ **dsh does not start any more. How do I restore a backup?** Open the backup folder (`~/Desktop/dsh-backups`) and double-click `Composer Plus Rescue.command` (Linux: `composer-plus-rescue.sh`, Windows: `Composer Plus Rescue.cmd`). Choose **3) Restore a backup**, pick one and type `RESTORE`. It works without dsh.
270
+
271
+ **How do I move dsh to a new computer?** On the old one: **Backup** → **For another computer** next to a backup. Copy that folder to the new computer, install dsh there, quit it, open `Composer Plus Rescue` in the copied folder and restore. Enter your API keys again (they stay on the old computer).
272
+
273
+ **A chat does not open after a crash. Can I repair it?** **Settings → Composer Plus → Session log check → Check session logs**. A damaged chat comes back from the newest backup with **Repair from backups** (the messages after that backup are lost).
274
+
236
275
  **How do I reorder queued messages in dsh?** Drag the ⠿ grip on a queued message, or focus it and press `↑` / `↓`.
237
276
 
238
- **How do I fix a typo in a queued message?** Double-click the message in the queue, change the text and press `Enter` (`Esc` cancels). It keeps its place in the queue.
277
+ **How do I fix a typo in a queued message?** Double-click the message in the queue, change the text and press `Enter` (`Esc` cancels). It keeps its place in the queue. You can also remove its images and files there, or add new ones.
239
278
 
240
279
  **How do I clear all unread dots of a project at once?** Sidebar → the project's **…** button → **Mark all as read**.
241
280
 
@@ -263,13 +302,17 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
263
302
 
264
303
  **How do I get macOS / Windows notifications when a dsh task finishes or needs approval?** Keep **Background notifications** on (default) and allow notifications for DeepSeek Harness in the OS settings.
265
304
 
305
+ **Can dsh open the chat that asks me a question by itself?** Yes, with **Open waiting chat when idle** (on by default): when a chat waits for an answer, an approval or a plan review and you do not use dsh for 30 seconds, dsh opens it. While you type, scroll or move the mouse, it waits.
306
+
266
307
  **How do I switch the DeepSeek Harness interface to Turkish, Spanish, Japanese…?** Settings → Composer Plus → **Application language**.
267
308
 
268
309
  **How do I use dsh from my iPhone?** Build the Tether app from `tether/ios` (Xcode), switch on **Settings → Composer Plus → Mobile connect (iPhone)**, click **Mobile connect** at the bottom left, scan the QR code (iPhone Camera or **Scan code** in Tether), tap **Connect** and click **Approve**. Away from home, use Tailscale on both devices.
269
310
 
270
311
  **How do I make New session always open in the same project?** Settings → Composer Plus → **Default workspace for New session** → **Open New session in** → choose the project. The `+` next to another project still opens that project.
271
312
 
272
- **How do I run several features of one project at the same time, each on its own branch?** With **Branch per chat**: new chat → the main button with the branch icon → **New branch**, then send the first message. Each chat gets its own branch and worktree folder, with your `.env` and `node_modules` when `.worktreeinclude` lists them. The chat header shows which branch you are on.
313
+ **How do I run several features of one project at the same time, each on its own branch?** With **Branch per chat**: on the New chat screen, the branch button shows **New branch** by default: send the first message. Each chat gets its own branch and worktree folder, with your `.env` and `node_modules` when `.worktreeinclude` lists them. The chat header shows which branch you are on.
314
+
315
+ **Can the sidebar show each branch of a project, and move my chat when the agent creates a branch?** Yes, with **Branches in the workspace tree** (off by default): switch it on in Settings → Composer Plus, then choose dsh's **View options** → **Workspace Tree**. Each branch with a worktree in `<project>/.worktrees/` shows inside its project. When you ask the agent in the project folder to create a branch and work there, and the folder has no uncommitted changes, the chat moves into the branch by itself. Delete the branch's folder and it leaves the list; its chats go to **Ungrouped**.
273
316
 
274
317
  **Can I send a photo from my iPhone?** Yes. In a Tether chat, tap **+** next to the message field → Camera, Photos (up to 4) or Paste image. The photos go to the chat on the Mac as attachments of your message, so the model sees them.
275
318
 
@@ -285,6 +328,7 @@ PLAYWRIGHT=/path/to/playwright node scripts/capture-screenshots.mjs /path/to/dsh
285
328
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-sidebar-projects.mjs /path/to/dsh/lib/bin.js
286
329
  # recent filter + a finished chat older than 2 hours (fake model): the project stays until you open the chat
287
330
  PLAYWRIGHT=/path/to/playwright node test/browser-recent-finished.mjs /path/to/dsh/lib/bin.js
331
+ PLAYWRIGHT=/path/to/playwright node test/browser-recent-tree.mjs /path/to/dsh/lib/bin.js
288
332
  # Order by status (fake model + Schedule): real-app test + its thumbnail
289
333
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-status-order.mjs /path/to/dsh/lib/bin.js
290
334
  # every Settings feature row shows a loading screenshot and a ? link to its README row
@@ -295,10 +339,14 @@ PLAYWRIGHT=/path/to/playwright node test/browser-whats-new.mjs /path/to/dsh/lib/
295
339
  PLAYWRIGHT=/path/to/playwright node test/browser-readme-anchors.mjs
296
340
  # Keep composer focus (fake model + demo skill): typing goes on after a / command + its thumbnail
297
341
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-keep-focus.mjs /path/to/dsh/lib/bin.js
342
+ # Fit question card to window (fake model asks a long question at 1280 × 820): question, answers, Skip and Submit readable, height follows the window + its thumbnail
343
+ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-question-fit.mjs /path/to/dsh/lib/bin.js
298
344
  # Mobile connect: switch on, QR popover, a fake iPhone pairs and calls dsh through the gateway, switch off + its thumbnail
299
345
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-mobile-connect.mjs /path/to/dsh/lib/bin.js
300
346
  # Resume after restart (fake slow model, real quit and start): a running chat and a goal continue, a stopped chat does not
301
347
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-resume-restart.mjs /path/to/dsh/lib/bin.js
348
+ # Resume after restart with a queue (fake model, real quit and start): text, image, file, a drag and an edit come back in order
349
+ PLAYWRIGHT=/path/to/playwright node test/browser-resume-queue.mjs /path/to/dsh/lib/bin.js
302
350
  # Feature ideas (fake model, made-up project): suggest, drag left, Undo, ✓ starts a new chat in plan mode, ▶ Build now one without, Plan all one for the Later list, a focus topic chip, Copy as checklist + its thumbnail
303
351
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-feature-ideas.mjs /path/to/dsh/lib/bin.js
304
352
  # Export chat as Markdown (fake model, made-up project): right-click → Export as Markdown…, copy and save keep the code blocks, switch off + its thumbnail
@@ -313,8 +361,10 @@ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-prompt-improve.mjs /path
313
361
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-goal-check.mjs /path/to/dsh/lib/bin.js
314
362
  # Mark all as read, Copy message link, Edit queued message in place, Redo, Changes → This chat and Hide whitespace (fake model, made-up git projects) + their thumbnails
315
363
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-six-features.mjs /path/to/dsh/lib/bin.js
316
- # Changes Tree (fake model, made-up git history): branch list, a branch's commits, commit files and diffs, back marks the row + its thumbnail
364
+ # Changes Tree (fake model, made-up git history): the branch map (counts, colors, arrows), a branch's commits, commit files and diffs, Merged and Waiting files, back marks the box + its thumbnail
317
365
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-changes-tree.mjs /path/to/dsh/lib/bin.js
366
+ # Branches in the workspace tree (fake model, made-up git project): a worktree becomes a project, the agent's new branch moves the chat, a deleted folder leaves the tree + its thumbnail
367
+ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-branch-tree.mjs /path/to/dsh/lib/bin.js
318
368
  # Allow for this chat with a time limit (fake model that is also the Auto Review reviewer): the grant ends after 1 hour
319
369
  PLAYWRIGHT=/path/to/playwright node test/browser-grant-ttl.mjs /path/to/dsh/lib/bin.js
320
370
  # Next message prediction and Complete while typing (fake models): calls, Tab, Esc, model choice + their thumbnails
@@ -325,6 +375,9 @@ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-project-picker.mjs /path
325
375
  # Queue drag-and-drop and Continue with a queued message (slow fake model)
326
376
  PLAYWRIGHT=/path/to/playwright node test/browser-queue-drag.mjs /path/to/dsh/lib/bin.js
327
377
  PLAYWRIGHT=/path/to/playwright node test/browser-continue-queue.mjs /path/to/dsh/lib/bin.js
378
+ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-queue-stop-send.mjs /path/to/dsh/lib/bin.js
379
+ # Edit queued message in place with images and files (slow fake model) + its thumbnail
380
+ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-queue-edit.mjs /path/to/dsh/lib/bin.js
328
381
  ```
329
382
 
330
383
  ```sh