dsh-composer-plus 1.13.0 → 1.14.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.de.md +65 -11
- package/README.es.md +65 -11
- package/README.fr.md +65 -11
- package/README.hi.md +65 -11
- package/README.id.md +65 -11
- package/README.ja.md +65 -11
- package/README.ko.md +65 -11
- package/README.md +65 -11
- package/README.pt-BR.md +65 -11
- package/README.ru.md +65 -11
- package/README.tr.md +65 -11
- package/README.vi.md +65 -11
- package/README.zh.md +65 -11
- package/assets/features/changesPanel.webp +0 -0
- package/assets/features/defaultWorkspace.webp +0 -0
- package/assets/features/findInChat.webp +0 -0
- package/assets/features/goalCheck.webp +0 -0
- package/assets/features/sessionBranch.webp +0 -0
- package/assets/features/worktreeChats.webp +0 -0
- package/client.js +1222 -222
- package/index.js +1414 -52
- package/package.json +2 -2
- package/tether/gateway/server.mjs +3 -2
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Codex-style quality-of-life features for the [DeepSeek Harness](https://github.c
|
|
|
12
12
|
|
|
13
13
|
<p align="center"><img src="docs/images/hero.webp" width="820" alt="DeepSeek Harness with Composer Plus: the running project shows a spinner in the sidebar and the Changes panel shows a git diff"></p>
|
|
14
14
|
|
|
15
|
-
Unofficial community plugin. The only network calls are the update check (GitHub releases and the npm registry); switch off **Automatic updates** to stop them at startup. **Feature ideas** calls your chat's own model only when you click **Suggest features**. **Next message prediction** (on by default) calls the model chosen in Settings (dsh's default unless you pick another) once after each finished turn, without a click. Switch it off in **Settings → Composer Plus** to stop these calls. **Complete while typing** (off by default) calls its chosen model (by default the prediction's) at each pause while you type, after you switch it on. **Improve prompt** calls a model only when you click its magic wand. **Mobile connect** (off by default) accepts iPhone connections from your local network or tailnet only after you switch it on.
|
|
15
|
+
Unofficial community plugin. The only network calls are the update check (GitHub releases and the npm registry); switch off **Automatic updates** to stop them at startup. **Feature ideas** calls your chat's own model only when you click **Suggest features**. **Next message prediction** (on by default) calls the model chosen in Settings (dsh's default unless you pick another) once after each finished turn, without a click. Switch it off in **Settings → Composer Plus** to stop these calls. **Complete while typing** (off by default) calls its chosen model (by default the prediction's) at each pause while you type, after you switch it on. **Improve prompt** calls a model only when you click its magic wand. **Goal check** (on by default) calls a model once each time the agent marks a `/goal` complete in an automatic round. **Mobile connect** (off by default) accepts iPhone connections from your local network or tailnet only after you switch it on.
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
@@ -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.
|
|
30
|
+
> dsh's pnpm skips versions younger than 24 hours. Right after a release, add the version: `dsh-composer-plus@1.14.0`.
|
|
31
31
|
|
|
32
32
|
## Features
|
|
33
33
|
|
|
@@ -35,8 +35,8 @@ 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 href="assets/features/changesPanel.webp"><img src="assets/features/changesPanel.webp" width="160" alt="Screenshot: Changes panel"></a> |
|
|
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, the
|
|
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> |
|
|
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> |
|
|
42
42
|
| **Delete chat** | <a id="feature-deletechat"></a>Sidebar → a chat's **…** menu (or right-click) → **Delete…** → **Delete**. dsh itself can only archive. The chat stops (its turn, jobs, subagents and schedules, like dsh's own archive) and its saved history, with its subagent chats, moves to the macOS Trash (on other systems: `$DSH_HOME/trash`). To get it back, put the folder back and restart dsh: it shows in the sidebar's **Archived only** view. | <a href="assets/features/deleteChat.webp"><img src="assets/features/deleteChat.webp" width="160" alt="Screenshot: Delete chat"></a> |
|
|
@@ -53,11 +53,16 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
|
|
|
53
53
|
| **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> |
|
|
54
54
|
| **Copy project path** | <a id="feature-copyprojectpath"></a>Right-click a project in the sidebar → **Copy path** copies the full path of its folder. | <a href="assets/features/copyProjectPath.webp"><img src="assets/features/copyProjectPath.webp" width="160" alt="Screenshot: Copy project path"></a> |
|
|
55
55
|
| **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
|
+
| **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
|
+
| **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 next to the project button → **New branch**, then type the first message and send it. 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> |
|
|
56
59
|
| **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> |
|
|
57
60
|
| **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> |
|
|
58
61
|
| **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
|
+
| **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> |
|
|
59
63
|
| **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> |
|
|
60
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> |
|
|
65
|
+
| **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 href="assets/features/sessionBranch.webp"><img src="assets/features/sessionBranch.webp" width="160" alt="Screenshot: Git branch on chat rows"></a> |
|
|
61
66
|
| **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> |
|
|
62
67
|
| **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> |
|
|
63
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> |
|
|
@@ -65,7 +70,7 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
|
|
|
65
70
|
| **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> |
|
|
66
71
|
| **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> |
|
|
67
72
|
| **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> |
|
|
68
|
-
| **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, 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> |
|
|
73
|
+
| **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> |
|
|
69
74
|
| **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> |
|
|
70
75
|
|
|
71
76
|
<!-- END GENERATED FEATURES -->
|
|
@@ -74,7 +79,7 @@ Buttons, tooltips, notices and notifications follow the selected dsh language (s
|
|
|
74
79
|
|
|
75
80
|
## Settings
|
|
76
81
|
|
|
77
|
-
Every feature can be switched on or off on its own: dsh **Settings → Composer Plus**. Each row has a small screenshot: hover it for a larger preview, click it to open it full size (`Esc` closes). The **Search features** box at the top filters the page by name or description (any language, case- and accent-insensitive); `Esc` clears it. Each row has a switch and a short description; changes apply immediately, without a restart, and are remembered on that device. The small **?** after a row's name opens the part of this README that describes that feature, in the README of dsh's interface language. Everything is on by default, except **Mobile connect**, **Resume after restart** and **Complete while typing**. dsh's Host keeps the switches of these three and of **Content search**, **Turn rollback
|
|
82
|
+
Every feature can be switched on or off on its own: dsh **Settings → Composer Plus**. Each row has a small screenshot: hover it for a larger preview, click it to open it full size (`Esc` closes). The **Search features** box at the top filters the page by name or description (any language, case- and accent-insensitive); `Esc` clears it. Each row has a switch and a short description; changes apply immediately, without a restart, and are remembered on that device. The small **?** after a row's name opens the part of this README that describes that feature, in the README of dsh's interface language. Everything is on by default, except **Mobile connect**, **Resume after restart** and **Complete while typing**. dsh's Host keeps the switches of these three and of **Content search**, **Turn rollback**, **Next message prediction** and **Goal check**, so they apply to every window and survive a restart. The Host also keeps the **Model** choice of **Next message prediction**, the one of **Complete while typing** (by default **Same as Next message prediction**), the one of **Improve prompt** and the one of **Goal check** (both by default **Same as the chat's model**), and the **Time limit** of **Allow for this chat** (1 hour by default). **Content search** changes only after a dsh restart (dsh reads it at start); until then its row says **Restart dsh to apply this change.**
|
|
78
83
|
|
|
79
84
|
<p align="center"><img src="docs/images/settings.webp" width="720" alt="Settings → Composer Plus: feature search, language picker, update row and a switch per feature, with a screenshot preview on hover"></p>
|
|
80
85
|
|
|
@@ -88,7 +93,7 @@ English · 中文 · Türkçe · Español · Português (Brasil) · Français ·
|
|
|
88
93
|
|
|
89
94
|
The whole app switches immediately. The choice is dsh’s own saved `locale.preference` (the same one as General → Language), so it survives restarts. **System language** clears that choice: the desktop app follows the OS language, the web UI the browser language, and an unsupported language falls back to English. Installing the plugin never overrides a language you already picked.
|
|
90
95
|
|
|
91
|
-
Each pack has 3,
|
|
96
|
+
Each pack has 3,280 texts: every UI text of dsh `0.2.0-rc.2` and `0.2.1-rc.2` (chats, sidebar, settings, approvals, schedules, file previews, plugin manager, plugin market), the plugin’s own controls, and the popular [dsh-rewind](https://github.com/SiriLee/dsh-rewind) plugin. Not translated: your messages, model output, code, slash-command names (`/plan`, `/compact`), provider-owned labels such as the `High` reasoning effort, other plugins’ texts, and the desktop app’s native menus/update dialogs (dsh ships those in English/Chinese only). Strings added by a newer dsh show in English until the packs are updated. If another plugin already provides a language, that plugin keeps it.
|
|
92
97
|
|
|
93
98
|
## Keyboard shortcuts
|
|
94
99
|
|
|
@@ -108,6 +113,12 @@ Each pack has 3,143 texts: every UI text of dsh `0.2.0-rc.2` and `0.2.1-rc.2` (c
|
|
|
108
113
|
| `Tab` | empty composer with a prediction | put the prediction in the composer |
|
|
109
114
|
| `Tab` | grey completion after the cursor | add the completion to the message |
|
|
110
115
|
| `Esc` | empty composer with a prediction | hide the prediction |
|
|
116
|
+
| `Cmd/Ctrl+Z` | composer, after **Improve prompt** | bring back the text that you typed |
|
|
117
|
+
| `Esc` | composer, while **Improve prompt** runs | cancel the improvement |
|
|
118
|
+
| `Enter` / `Esc` | queued message that you double-clicked | save / cancel the edit |
|
|
119
|
+
| `Cmd+F` on a Mac, `Ctrl+F` on Windows and Linux | open chat | open **Find in chat** |
|
|
120
|
+
| `Enter` / `Shift+Enter`, `↓` / `↑` | **Find in chat** box | next / previous match |
|
|
121
|
+
| `Esc` | **Find in chat** box | close the box |
|
|
111
122
|
|
|
112
123
|
An open `/` or `@` menu keeps these keys for itself.
|
|
113
124
|
|
|
@@ -120,10 +131,14 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
120
131
|
- **Continue:** the authenticated local endpoint resumes only the last interrupted turn, rejects stale or duplicate requests, and preserves the existing approval hooks. It does not send a new user message.
|
|
121
132
|
- **Resume after restart:** off until you switch it on. The Host half keeps only chat ids and start times in `$DSH_HOME/storages/dsh-composer-plus/resume.json` (owner-only permissions), and nothing leaves your machine. It continues a chat only when dsh's own log says that dsh (not you) stopped its last turn. Approvals and Auto Review apply to the continued turn as usual.
|
|
122
133
|
|
|
123
|
-
- **Changes panel:** git runs read-only on your machine, in the chat's own folder, through dsh's authenticated `/api` channel. It never commits, stages or fetches, and only shows paths git itself listed.
|
|
124
|
-
- **Feature ideas:** nothing runs until you click **Suggest features**. Then the Host half reads only the chat's own folder (the browser never sends a path): the names of the top-level entries, the first 6 kB of the README, AGENTS.md and the manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, …) and `git log --
|
|
134
|
+
- **Changes panel:** git runs read-only on your machine, in the chat's own folder, through dsh's authenticated `/api` channel. It never commits, stages or fetches, and only shows paths git itself listed. Tree lists up to 1000 local and remote branches and shows the newest 300 commits of one branch: its first-parent line, so a merge commit stands for the commits that it brought in. It never checks out a branch. **This chat** reads the chat's own log for the files that its file tools wrote and compares them with the chat's Turn rollback snapshot in a temporary folder that is deleted at once; it writes nothing to your repository or to the snapshot store.
|
|
135
|
+
- **Feature ideas:** nothing runs until you click **Suggest features**. Then the Host half reads only the chat's own folder (the browser never sends a path): the names of the top-level entries, the first 6 kB of the README, AGENTS.md and the manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, …), an outline of the rest of the README (headings, table rows, list items; at most 6 kB) and the titles of the newest commits (`git log --format=%s`, at most 6 kB), at most 28 kB in total. It skips `.env*`, key files and folders such as `node_modules`, and it reads only regular files that stay inside the folder: a symlink that points out of the folder, a FIFO or a device is skipped. This summary goes to the model that the chat uses, through dsh's own provider settings, as one request; it is not added to any chat. Nothing in the project is written. A swiped-right idea starts a new chat in plan mode, so approvals and plan review apply as usual. **Build now** starts the chat without plan mode: the model can change files at once, with the chat's usual approvals and Auto Review. Ideas saved for later (title, summary, why, effort) are kept with the project's folder path in `storages/dsh-composer-plus/later.json` under the dsh home (owner-only file); nothing goes into the project. The history of suggested ideas (title, summary, why, effort, date, started) is kept the same way in `storages/dsh-composer-plus/idea-history.json`, at most 200 per project (the oldest leave first); its titles go to the model with the next **Suggest features** of that project.
|
|
125
136
|
- **Content search:** no new code searches your chats: the plugin only switches on dsh's own message index (SQLite FTS5, `dsh-session-query-sqlite`) and gives it a file, `$DSH_HOME/cache/composer-plus/session-search.db` (owner-only file and folder). The file holds the text of your user and assistant messages, so it is as private as the chat logs in `$DSH_HOME/sessions`. Nothing leaves your machine. It is a copy that dsh can make again: you can delete it while dsh is closed. The switch is in `$DSH_HOME/storages/dsh-composer-plus/content-search.json`.
|
|
126
137
|
- **Turn rollback:** before each of your messages, the Host takes a git snapshot of the chat's folder in its own store, `$DSH_HOME/cache/composer-plus/rollback/<chat>.git` (owner-only folder). It holds copies of the files in that folder that git sees (not ignored files), so it is as private as the project. It reads your repository's objects (no copy of what git already has) and never writes your repository's index, `HEAD`, branches, tags, stash or config; the only change git makes there is a newer time stamp on object files it reuses (this delays their cleanup by `git gc`). A rollback with files writes only regular files inside the chat's folder; it skips symlinks, pipes and anything outside the folder. Nothing leaves your machine. You can delete the store while dsh is closed (rollback then works for the chat only, until new messages). **Delete chat** deletes a chat's store too. The switch is in `$DSH_HOME/storages/dsh-composer-plus/rollback.json`. A rollback with files also keeps the files as they were just before it (`refs/r/…` in the same store), for **Redo**; the rollbacks that were redone are listed in `$DSH_HOME/storages/dsh-composer-plus/redone.json` (owner-only).
|
|
138
|
+
- **Open GitHub issue** (Feature ideas → **Later** list): runs only when you click it. The Host half runs your own GitHub CLI (`gh issue create`) in the chat's folder, with the idea's saved title and summary (and why it matters); `gh` uses its own login and the folder's GitHub remote, so the issue goes to that repository with your account, and GitHub shows it to everyone who can see that repository. dsh removes variables named like a token from the commands it starts; when you gave `gh` a token through `GH_TOKEN` or `GITHUB_TOKEN`, the plugin passes only these to `gh`. The issue link is kept with the idea in `later.json`.
|
|
139
|
+
- **Chats that changed this file** (Changes, a file's "…" menu): the Host half reads the logs of the chats whose folder is in the same repository, as dsh keeps them, and answers each chat's title, the time and the first line of the request; it never opens the file itself.
|
|
140
|
+
- **Git branch on chat rows:** the Host half runs `git branch --show-current` (read-only) in the chat's folder when a turn starts and when it ends, and keeps only the chat id, the branch name and the time in `$DSH_HOME/storages/dsh-composer-plus/session-branches.json` (owner-only file, newest 2000 chats). The switch is in `branches.json` next to it.
|
|
141
|
+
- **Find in chat** runs only in the page: it reads the text that the page shows and sends nothing.
|
|
127
142
|
- **Export chat as Markdown:** the Host half reads only the chat's own log (the same messages that dsh shows) and gives them to the page; it reads no project file and sends nothing over the network. The Markdown goes only to your clipboard or to the file that you save.
|
|
128
143
|
- **Delete chat:** moves the chat's own log folders (`$DSH_HOME/sessions/<project>/<chat>`) to the Trash. It does not touch the project folder.
|
|
129
144
|
- **Allow for this chat:** grants are stored with the time they were given in `$DSH_HOME/storages/dsh-composer-plus/chat-grants.json` (owner-only permissions). A grant covers one exact tool in one chat, until the time limit ends (1 hour by default). If that tool can write, the grant covers any write too. Revoke it from the header chip.
|
|
@@ -133,6 +148,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
133
148
|
- **Next message prediction:** on by default; switch it off in **Settings → Composer Plus** to stop these model calls. After each finished turn, the Host half sends the chat's own user messages and the agent's text replies (no tool output, no hidden context, no reasoning), at most about 6,000 characters (the newest part), to the chosen model (dsh's default unless you pick another). It goes through dsh's own provider settings, as one request. Nothing is saved in the chat: the guess is not added to the chat history. The switch and the model choice are in `$DSH_HOME/storages/dsh-composer-plus/predict.json` (owner-only file).
|
|
134
149
|
- **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).
|
|
135
150
|
- **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
|
+
- **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).
|
|
136
152
|
|
|
137
153
|
## Troubleshooting
|
|
138
154
|
|
|
@@ -147,7 +163,11 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
147
163
|
- **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.
|
|
148
164
|
- **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.
|
|
149
165
|
- **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).
|
|
150
|
-
- **
|
|
166
|
+
- **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
|
+
- **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
|
+
- **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
|
+
- **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.14.0`), see the install note above.
|
|
151
171
|
|
|
152
172
|
## Limits
|
|
153
173
|
|
|
@@ -171,14 +191,20 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
171
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).
|
|
172
192
|
- **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.
|
|
173
193
|
- **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
|
+
- **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
|
+
- **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
|
+
- **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.
|
|
197
|
+
- **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).
|
|
174
198
|
- A notification sent before a restart only brings dsh to the front when clicked.
|
|
175
199
|
- 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.
|
|
176
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.
|
|
177
201
|
- 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.
|
|
178
202
|
- 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
|
+
- 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.
|
|
179
204
|
- dsh has no extension point for a composer hint either, so the **Next message prediction** text sits over dsh's own placeholder (`[data-composer-placeholder]`). If a dsh update changes that markup, no prediction shows. While it is on, each finished turn costs one short model call.
|
|
180
205
|
- **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.
|
|
181
206
|
- **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
|
+
- **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.
|
|
182
208
|
- 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).
|
|
183
209
|
|
|
184
210
|
## FAQ
|
|
@@ -205,6 +231,8 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
205
231
|
|
|
206
232
|
**Can dsh improve my prompt before I send it, like the Enhance prompt button in Roo Code or Augment?** Yes, with **Improve prompt** (on by default): type a rough request and click the magic wand next to the model menu. It rewrites the text with this chat and the project as context, in your language, and does not send it. `Cmd/Ctrl+Z` or **Undo improvement** brings your text back. Choose its model in **Settings → Composer Plus → Improve prompt → Model**.
|
|
207
233
|
|
|
234
|
+
**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
|
+
|
|
208
236
|
**How do I reorder queued messages in dsh?** Drag the ⠿ grip on a queued message, or focus it and press `↑` / `↓`.
|
|
209
237
|
|
|
210
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.
|
|
@@ -215,6 +243,10 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
215
243
|
|
|
216
244
|
**I rolled back a turn by mistake. Can I get it back?** Yes, with **Redo** above the composer, right after the rollback: it starts a new chat from just before the rollback with the same messages, and **Chat and files** also puts the files back.
|
|
217
245
|
|
|
246
|
+
**Which chat changed this file?** Right sidebar → **Changes** → the file's **…** → **Chats that changed this file**. Click a row to open that chat at the request that made the change.
|
|
247
|
+
|
|
248
|
+
**Can I search inside one long chat?** Yes: press `Cmd+F` on a Mac (`Ctrl+F` on Windows and Linux) in the chat. **Find in chat** highlights every match and moves between them with `Enter` and `Shift+Enter`.
|
|
249
|
+
|
|
218
250
|
**How do I see only the files that one chat changed?** Right sidebar → **Changes** → **This chat**. Click **␣** to hide changes that are only whitespace.
|
|
219
251
|
|
|
220
252
|
**How do I stop Auto Review asking about the same tool in one chat?** Click **Allow for this chat** on the approval card. It covers that exact tool in that chat only, for 1 hour by default; revoke it from the header chip. Change the time in **Settings → Composer Plus → Allow for this chat**.
|
|
@@ -235,6 +267,12 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
|
|
|
235
267
|
|
|
236
268
|
**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.
|
|
237
269
|
|
|
270
|
+
**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
|
+
|
|
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 → **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.
|
|
273
|
+
|
|
274
|
+
**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
|
+
|
|
238
276
|
**Does it work with the desktop app?** Yes: Settings → Plugins → **Add plugin** → `dsh-composer-plus`. Tested on dsh 0.2.0-rc.2 (desktop and web) and 0.2.1-rc.2 (web).
|
|
239
277
|
|
|
240
278
|
## Development
|
|
@@ -271,6 +309,22 @@ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-content-search.mjs /path
|
|
|
271
309
|
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-turn-rollback.mjs /path/to/dsh/lib/bin.js
|
|
272
310
|
# Improve prompt (fake model, made-up git project): the wand sends the draft with the chat and the project, the answer replaces the draft, Cmd+Z and Undo bring it back + its thumbnail
|
|
273
311
|
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-prompt-improve.mjs /path/to/dsh/lib/bin.js
|
|
312
|
+
# Goal check (fake model, made-up project): a failing test and a verifier "not done" keep the goal open, the agent's own test run is reused, a pass closes it, a token budget blocks it + its thumbnail
|
|
313
|
+
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-goal-check.mjs /path/to/dsh/lib/bin.js
|
|
314
|
+
# 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
|
+
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
|
|
317
|
+
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-changes-tree.mjs /path/to/dsh/lib/bin.js
|
|
318
|
+
# Allow for this chat with a time limit (fake model that is also the Auto Review reviewer): the grant ends after 1 hour
|
|
319
|
+
PLAYWRIGHT=/path/to/playwright node test/browser-grant-ttl.mjs /path/to/dsh/lib/bin.js
|
|
320
|
+
# Next message prediction and Complete while typing (fake models): calls, Tab, Esc, model choice + their thumbnails
|
|
321
|
+
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-predict.mjs /path/to/dsh/lib/bin.js
|
|
322
|
+
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-complete.mjs /path/to/dsh/lib/bin.js
|
|
323
|
+
# Project search in a new chat: A–Z order, filter, Enter + its thumbnail
|
|
324
|
+
SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-project-picker.mjs /path/to/dsh/lib/bin.js
|
|
325
|
+
# Queue drag-and-drop and Continue with a queued message (slow fake model)
|
|
326
|
+
PLAYWRIGHT=/path/to/playwright node test/browser-queue-drag.mjs /path/to/dsh/lib/bin.js
|
|
327
|
+
PLAYWRIGHT=/path/to/playwright node test/browser-continue-queue.mjs /path/to/dsh/lib/bin.js
|
|
274
328
|
```
|
|
275
329
|
|
|
276
330
|
```sh
|
|
@@ -281,7 +335,7 @@ npm test
|
|
|
281
335
|
|
|
282
336
|
**Rule for new features:** every feature row in Settings → Composer Plus shows a screenshot. Add the feature id to `SHOT_OF` in `client.js` and to `SHOTS` in `index.js`, and ship `assets/features/<id>.webp` (taken from the throwaway demo dsh, English UI, about 60 kB or less). Add the feature, with the same image, to `docs/features.json` and run `npm run build:readme`: it writes the feature table of `README.md` and of each translated `README.<id>.md`, and the list in `llms.txt`. `test/shots.test.mjs` fails when a feature has no screenshot, and `test/browser-settings-shots.mjs` checks the real Settings page. The full checklist for every change is in [AGENTS.md](AGENTS.md).
|
|
283
337
|
|
|
284
|
-
The tests run the real `client.js` against a fake React/DOM and the real `index.js` (Host half) against a temporary git repository. The Tether gateway tests (`test/tether-gateway.test.mjs`) use a mock dsh host and a real TLS gateway. The iPhone app is in `tether/ios` (see [tether/README.md](tether/README.md) and [tether/AGENTS.md](tether/AGENTS.md)). `node test/browser-history.mjs '<dsh web URL with ?token=…>'` checks input history in a real browser against a throwaway `dsh web` with its own `DSH_HOME`.
|
|
338
|
+
The tests run the real `client.js` against a fake React/DOM and the real `index.js` (Host half) against a temporary git repository. The Tether gateway tests (`test/tether-gateway.test.mjs`) use a mock dsh host and a real TLS gateway. The iPhone app is in `tether/ios` (see [tether/README.md](tether/README.md) and [tether/AGENTS.md](tether/AGENTS.md)). Each Host route, its request and whether Tether can call it is listed in [docs/ROUTES.md](docs/ROUTES.md). `node test/browser-history.mjs '<dsh web URL with ?token=…>'` checks input history in a real browser against a throwaway `dsh web` with its own `DSH_HOME`.
|
|
285
339
|
|
|
286
340
|
Language packs are data-only JSON in `locales/<id>.json`, embedded into the single client entry by `npm run build:locales` (no translation service or runtime download). `npm test` checks every pack for full key coverage, identical placeholders and line breaks, and the system/override behaviour. For isolated real-app checks with a locally installed Playwright:
|
|
287
341
|
|