dsh-composer-plus 1.22.0 → 1.24.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
@@ -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. **Task board** calls the new chat's model once for the tags of each task that you start on the board. With its **AI summary** setting (the default is **Fast (no AI)**), it also calls a running chat's model for a short progress note while you look at that task's progress on the board or in Tether. **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.
15
+ Unofficial community plugin. The update check (GitHub releases and the npm registry) makes network calls; switch off **Automatic updates** to stop them at startup. **Turkish skill pack** (on by default) downloads the turkish-anti-ai-skill from GitHub and checks it for a newer version once a day, only while **Application language** is Türkçe; switch it off in **Settings → Composer Plus** to stop these calls. **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. **Task board** calls the new chat's model once for the tags of each task that you start on the board. With its **AI summary** setting (the default is **Fast (no AI)**), it also calls a running chat's model for a short progress note while you look at that task's progress on the board or in Tether. **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.22.0`.
30
+ > dsh's pnpm skips versions younger than 24 hours. Right after a release, add the version: `dsh-composer-plus@1.24.0`.
31
31
 
32
32
  ## Features
33
33
 
@@ -46,6 +46,7 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
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
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> |
48
48
  | **Fit chat header to window** | <a id="feature-headerfit"></a>In a narrow window, the buttons in dsh's chat header (the mode, subagents, background jobs, Open in, the branch pill and the grants chip) no longer cover each other. The chat title keeps at least 120 px; the buttons that do not fit move to a second line at the right, and each one stays clickable. When everything fits on one line (a wide window), the header is exactly dsh's. Without this feature, dsh shrinks only the title: with a subagent and background jobs, its buttons slide over each other in a narrow chat column. The branch pill (**Branch per chat**) and the grants chip (**Allow for this chat**) also fit the header when this switch is off. | <a href="assets/features/headerFit.webp"><img src="assets/features/headerFit.webp" width="160" alt="Screenshot: Fit chat header to window"></a> |
49
+ | **Remember window size** | <a id="feature-windowsize"></a>dsh's desktop app opens its window at 1280 × 820 at each start. With this feature, it opens with the size and place that the window had when you last used it. The size is saved each time you resize or move the window, and it comes back when the plugin loads at the next start. A full-screen or minimized window is not saved; a place on a display that is not connected now comes back on the main display. Only in the desktop app: a browser tab cannot change the size of its window. | <a href="assets/features/windowSize.webp"><img src="assets/features/windowSize.webp" width="160" alt="Screenshot: Remember window size"></a> |
49
50
  | **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> |
50
51
  | **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> |
51
52
  | **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> |
@@ -67,9 +68,12 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
67
68
  | **Continue in a new chat** | <a id="feature-handoffchat"></a>Right-click a chat in the sidebar (or open its **…** menu) → **Continue in a new chat…**. One call to the chat's own model writes a handoff note from the chat: Task, Outputs, Completed, Decisions, Open issues and Next action, from what the chat shows (tool calls and results are the evidence; steps that you rolled back are left out). The note opens in a dialog: read it and edit it. **Write again** writes a new note. **Start new chat** (or `Cmd/Ctrl+Enter`) opens a new chat in the same project with the note and a link to the old chat as its first message; the old chat stays as it is. Use it when a long chat is full or slow, or to continue tomorrow. Nothing is written to the project. | <a href="assets/features/handoffChat.webp"><img src="assets/features/handoffChat.webp" width="160" alt="Screenshot: Continue in a new chat"></a> |
68
69
  | **Context breakdown** | <a id="feature-contextbreakdown"></a>Right sidebar → `+` → **Context**. The page shows what fills the open chat's model context now and how much of the context window it uses: the system prompt, project instructions (AGENTS.md), skills, your messages, agent replies and tool calls, tool results (by tool), MCP results (by server), tool definitions, the compact summary and other context notes, each with its tokens and its share. Click a part to see its tools or servers. It reads the chat log only, after `/compact` and rollbacks, and calls no model; it reads again after each turn and with **Refresh**. Use it to see why a chat is slow or full, for example a big MCP tool result or many tool definitions. | <a href="assets/features/contextBreakdown.webp"><img src="assets/features/contextBreakdown.webp" width="160" alt="Screenshot: Context breakdown"></a> |
69
70
  | **Planned steps** | <a id="feature-plannedsteps"></a>Off by default: switch it on in **Settings → Composer Plus**. The agent gets the tool `plan_next_steps`: in the same message as a command, it can plan the next commands with their exact arguments and a condition, for example "run `npm test` when the build passes". When the result meets the condition, Composer Plus runs the next step itself, without a new model call, so a long chat uses fewer tokens. The step shows in the chat as a short line, "Composer Plus · Planned steps: ran `test` … without a model call", followed by its tool call and its result. Only `bash`, `read`, `grep` and `glob` steps; approvals, the sandbox and Protected files apply to each step as usual. The model answers again when a condition fails, when two steps are ready at the same time, after the last step, and when you send a message. The idea comes from HarnessMM (arXiv 2610.32813). | <a href="assets/features/plannedSteps.webp"><img src="assets/features/plannedSteps.webp" width="160" alt="Screenshot: Planned steps"></a> |
71
+ | **Turkish skill pack** | <a id="feature-turkishskill"></a>Only while **Application language** is Türkçe (on by default; switch it off in **Settings → Composer Plus**). Composer Plus downloads the open-source [turkish-anti-ai-skill](https://github.com/yucezerey/turkish-anti-ai-skill) (MIT, by Yüce Zerey) from GitHub and adds it to dsh's skills. Ask the agent to humanize, rewrite or write a Turkish text: dsh loads the skill, and its rules help the agent write natural Turkish without AI patterns. Only the pack's Markdown files are kept; each file must match GitHub's file list, and no script of the pack runs. Once a day the plugin asks GitHub for a newer version and installs it. The row shows the installed version, or why it is not installed. In another language the skill is not installed; switch the language back to Türkçe and it comes back. | <a href="assets/features/turkishSkill.webp"><img src="assets/features/turkishSkill.webp" width="160" alt="Screenshot: Turkish skill pack"></a> |
72
+ | **Untangle codebase** | <a id="feature-untanglecodebase"></a>On by default; switch it off in **Settings → Composer Plus**. Adds the skill `/untangle-codebase` from [caldoai/skills](https://github.com/caldoai/skills) (MIT, © 2026 Maze) to dsh's skill list. In a chat, type `/untangle-codebase` (it shows in the `/` menu), optionally followed by an area to focus on, for example `/untangle-codebase src/api`, and send it. The agent first asks whether it should also remove comments that only restate the code and tests that cannot catch a real bug. Then it lists every copy, hidden link, needless link, pass-through wrapper, piece of dead code and one-off special case, plans the fixes from first principles, makes them as a goal, and checks again with subagents until nothing worth fixing is left. Only you can start it: the model does not see it in its skill list, so it costs no tokens in other chats. A skill with the same name in the project or in `~/.dsh/skills` comes first. Start it in a chat of **Branch per chat** and review the result in **Changes panel**. In Tether: type `/untangle-codebase` in the message field. | <a href="assets/features/untangleCodebase.webp"><img src="assets/features/untangleCodebase.webp" width="160" alt="Screenshot: Untangle codebase"></a> |
70
73
  | **Protected files** | <a id="feature-protectedfiles"></a>On by default. The agent's own file tools cannot read or change files that match your patterns: `read`, `read_image` and `grep` on a file are refused for **read and write** patterns, and `write`, `edit` and `str_replace_editor` for both kinds. The agent reads why and is told to ask you. The defaults are `.env*`, `*.pem` and `*.key`. Settings → Composer Plus → **Protected files**: add a pattern (for example `published/**` or `config/*.yml`), click its kind to switch between **read and write** and **write only**, remove it with ✕, or **Use the defaults**. A pattern without `/` matches a file name in any folder. This is a guard rail, not a sandbox: a shell command (`cat .env`), a script or an MCP tool can still reach the file (see Limits). | <a href="assets/features/protectedFiles.webp"><img src="assets/features/protectedFiles.webp" width="160" alt="Screenshot: Protected files"></a> |
71
74
  | **Blocked commands** | <a id="feature-blockedcommands"></a>On by default, with an empty list. The agent's shell commands (`bash`, `pwsh`) cannot run a command that matches your rules: a **block** rule refuses the call (the agent reads why and is told to ask you), an **ask first** rule shows dsh's approval card, and the command runs only when you allow it. Settings → Composer Plus → **Blocked commands**: type a rule (for example `npm publish`, `git push --force` or `ssh *192.168.2.19`), click the kind to switch between **block** and **ask first**, then **Add**; click a rule's kind to change it, remove it with ✕. A rule is a command name and words that must come after it in this order; `*` matches any text. It also finds the command after `&&`, `;`, `\|`, inside `$(…)`, `bash -c "…"` or `eval`, and after `sudo` or `env`. This is a guard rail, not a sandbox: a variable (`$CMD`), a script file or another program can still run the command (see Limits). | <a href="assets/features/blockedCommands.webp"><img src="assets/features/blockedCommands.webp" width="160" alt="Screenshot: Blocked commands"></a> |
72
75
  | **Task board** | <a id="feature-taskboard"></a>Left sidebar → **Task board**, or press `Cmd+Shift+K` (`Ctrl+Shift+K` on Windows and Linux) anywhere in dsh. A kanban board of your dsh chats with the live state that the sidebar shows: a chat that waits for your approval, answer or plan review is in **Review** with an orange dot, a running chat is in **Doing**, a finished chat that you did not open yet is in **Done** (green). When you open a chat, its card leaves **Done** by itself; click **Recently finished (N)** under the green cards to see the finished chats that you read (the newest 10, then **Show N older**; open or closed is kept on this device). A chat that finishes while it is open behind the board (a **New task**) stays green until you open it. Click the project name at the top of the board to open the project list, then pick a project or **All projects**; a Branch per chat folder counts as its project. The list closes after the pick, with `Esc` or with a click outside it. Type in **Search projects** at the top of the list to find a project by its name (case and accents do not matter; `Esc` empties the box) and press `Enter` to pick the first match, and click **Active in the last 2 hours** to show only projects with a chat that waits, runs, finished unseen or changed in the last 2 hours (kept on this device). **New task** (or `n`) opens dsh's own new chat in the project that the board shows (with **All projects**, the project that dsh's New session picks), so you can add files, use commands and pick the model as in any chat. When your first message goes, the board comes back by itself. Until then the chat waits at the top of **Doing** as **New task**: click it to write the first message. A project with a chat that waits for you has an orange dot in the project list (the project name at the top has it too while any chat waits). dsh names the chat and the chat's model writes up to 3 tags from that message. Click a card to open its chat. Click an orange card (or its **Answer**) to answer it on the board; the panel has **Open chat** for the whole chat: **Reject** or **Allow once**, **Approve** or **Request changes** for a plan, or the options and your own text for a question. Click **×** on a finished card to take it off the board, or open **Recently finished** and click **Clear all** at the top of **Done** to take those cards off it: it first shows how many cards of which project go, and unread (green) cards stay. For 8 seconds after, **Undo** puts the cards back; **Removed (N)** under **Done** lists the cards that are off the board, and **Put back** shows one in **Done** again. No chat is deleted: the chats stay in dsh's sidebar, the board remembers this after a restart (Tether shows the same board), and a chat that runs again comes back. Point at a card in **Doing** (or move the keyboard focus to it) to see its progress: how long it runs, the step of its todo list, the tool that runs now and the agent's last line. In Settings → Composer Plus → Task board, **Progress on hover** picks **Fast (no AI)** (the default: it reads the chat only) or **AI summary** (one short call to the chat's model also writes one or two sentences). | <a href="assets/features/taskBoard.webp"><img src="assets/features/taskBoard.webp" width="160" alt="Screenshot: Task board"></a> |
76
+ | **Assistant** | <a id="feature-assistant"></a>Off by default: switch it on in **Settings → Composer Plus**. Then click **Assistant** in the left sidebar (right under **Task board**): it opens one lasting chat that you use as your personal assistant, like a ChatGPT or Grok chat. The first click makes the folder `~/Assistant`, adds it as the project "Assistant" and starts the chat with the workspace-write permission mode and dsh's default model; each later click opens the same chat (an archived one comes back, a deleted one is made again). Ask it to remind you ("remind me to drink water in 2 minutes"): it uses dsh's scheduled tasks, so the reminder runs the chat at that time, and Tether sends its "Turn finished" notification when Mobile connect is on. Ask it to search the web, or about your other chats: it can list the chats of all projects (running, waiting for you or idle) and read the newest messages of one. It can also send a message to a chat, start a new chat in a project, stop a running chat or archive one: each of these four actions shows dsh's approval card first (also in Tether), and nothing happens until you click **Allow once**. It has no delete tool. Only this chat has these tools and its short instructions; text that it reads in a web page or another chat counts as data, not as an order. Keep your profile in this project's memory (**Project memory**, topic "profil") instead of telling it each time. | <a href="assets/features/assistant.webp"><img src="assets/features/assistant.webp" width="160" alt="Screenshot: Assistant"></a> |
73
77
  | **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. **Open memory** in the same row shows every saved note, grouped by project (and the global memory), with a filter. Write what to change in your own words (for example "I switched from yarn to pnpm"), then **Preview changes**: dsh's default model proposes the changes, and only **Apply** saves them. In Tether: chat title menu → Project memory. | <a href="assets/features/projectMemory.webp"><img src="assets/features/projectMemory.webp" width="160" alt="Screenshot: Project memory"></a> |
74
78
  | **Memory version history** | <a id="feature-memoryhistory"></a>On by default while **Project memory** is on. Before each save of the memory (the agent, the review after a turn, **Open memory**), a copy of the notes as they were is kept. In **Open memory**, select a project or **Global memory**, then **History**: the versions by date and who changed the memory after each one. Select a version to see what a restore changes as a diff, then **Restore**. The notes before a restore are kept too, so you can undo it. The newest 50 versions of each memory stay. In Tether: chat title menu → Project memory → History. | <a href="assets/features/memoryHistory.webp"><img src="assets/features/memoryHistory.webp" width="160" alt="Screenshot: Memory version history"></a> |
75
79
  | **Export memory to AGENTS.md** | <a id="feature-memoryexport"></a>In **Open memory**, select a project, then **Export to AGENTS.md**: the notes of that project go into a marked section of `AGENTS.md` in its folder, so you can commit them and share them with your team and other agent tools. You see the change as a diff first; only **Write AGENTS.md** saves it. The next export replaces only that section. The global memory, sources and dates stay out. In Tether: chat title menu → Project memory → Export to AGENTS.md. | <a href="assets/features/memoryExport.webp"><img src="assets/features/memoryExport.webp" width="160" alt="Screenshot: Export memory to AGENTS.md"></a> |
@@ -82,6 +86,7 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
82
86
  | **Reject with note** | <a id="feature-rejectnote"></a>An approval card gets **Reject with note…** next to **Reject**. Click it, write why, or what to do instead, and click **Reject** (or press `Enter`; `Shift+Enter` adds a line, `Esc` closes the field). The note goes to the agent first, as a message into the waiting turn, then the request is rejected: the model reads the rejection and your note in the same turn, without one more message from you. If the note cannot be sent, nothing is rejected and the field stays. In Tether: approval sheet → Reject with note. | <a href="assets/features/rejectNote.webp"><img src="assets/features/rejectNote.webp" width="160" alt="Screenshot: Reject with note"></a> |
83
87
  | **Secret input** | <a id="feature-secretinput"></a>The agent gets the tool `request_secret`. When it needs a password, a token or an API key, it asks with it instead of in the chat: a card shows on the tool's row with the label, why it needs the value, a password field, **Save** and **Decline**. **Save** writes the value to a file on this Mac that only you can read, and the agent gets only the file's path, which it uses as input in a command (for example `"$(cat <path>)"`). When a later tool result shows the value, `[secret]` replaces it before the model sees it. The file is deleted when the turn ends. **Decline** tells the agent that you did not give it. Switch it off in **Settings → Composer Plus** to remove the tool. In Tether: a "Secret: …" bar above the composer → Enter. | <a href="assets/features/secretInput.webp"><img src="assets/features/secretInput.webp" width="160" alt="Screenshot: Secret input"></a> |
84
88
  | **Quote in reply** | <a id="feature-quotereply"></a>Select a part of an answer: a **Quote** button shows above the selection. Click it: the part goes into the composer at the caret as a Markdown quote (`> …`), with an empty line after it, and the composer gets the focus, so you write your reply below it. Only the answer text counts, not the reasoning, tool rows or your own messages. A quote has at most 2,000 characters. `Cmd+Z` (`Ctrl+Z`) removes it. In Tether: touch and hold an answer → Quote (the whole answer). | <a href="assets/features/quoteReply.webp"><img src="assets/features/quoteReply.webp" width="160" alt="Screenshot: Quote in reply"></a> |
89
+ | **Quote from file preview** | <a id="feature-previewquote"></a>Open a file in the right sidebar (**Workspace files**, or a file that the chat opened) and select lines in it: a **Quote** button shows above the selection. Click it: the file path, the line numbers and the lines go into the composer at the caret, and the composer gets the focus, so the agent knows which part you mean. Code and text files give `src/login.js:2-3` and the lines as a code block; Markdown and PDF files give the path (a PDF also its page, `docs/spec.pdf#page=3`) and a `> …` quote. A path inside the chat's folder is relative. At most 2,000 characters. `Cmd+Z` (`Ctrl+Z`) removes it. Not in Tether. | <a href="assets/features/previewQuote.webp"><img src="assets/features/previewQuote.webp" width="160" alt="Screenshot: Quote from file preview"></a> |
85
90
  | **Side chat** | <a id="feature-sidechat"></a>Type a side question in the composer and click the speech-bubbles button (**Ask in a side chat**): a copy of the chat opens and answers it in dsh's read-only mode, while the chat goes on working. The copy knows the chat up to now, except messages that still wait in the queue, and keeps its model. A bar above its composer has **Bring back**: the chat opens and its composer gets your questions (as `> ` quotes) and the answers. **Back to the chat** only opens the chat. The side chat stays in the sidebar as "↳ <question>"; archive it there. In Tether: the chat title menu → Side chat…. | <a href="assets/features/sideChat.webp"><img src="assets/features/sideChat.webp" width="160" alt="Screenshot: Side chat"></a> |
86
91
  | **Diff notes** | <a id="feature-diffcomments"></a>In **Changes panel**, open a file's diff and click **+** at a line (Shift+click a second line of the same block for a range), write a note and click **Save note**. The chat's notes wait in a bar at the top of the panel; **Send to composer** puts them at the composer's caret as Markdown: `path:12-14`, the lines as a `diff` block and your note. **Clear** drops them. The notes stay in this browser until you send or clear them. Needs **Changes panel**. In Tether: a file's diff → touch and hold a line → Add note…. | <a href="assets/features/diffComments.webp"><img src="assets/features/diffComments.webp" width="160" alt="Screenshot: Diff notes"></a> |
87
92
  | **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> |
@@ -93,12 +98,14 @@ Then restart dsh. (The CLI can't install into the desktop profile; the app manag
93
98
  | **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> |
94
99
  | **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> |
95
100
  | **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), or finishes a turn that you have not read yet (the green dot), and you do not use dsh for 30 seconds, dsh opens that chat. Orange chats open before green ones. In each group, 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. When a green chat opens, its dot goes; the next green chat opens only after you use dsh again and then do not use it for 30 seconds. 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> |
96
- | **Next waiting chat** | <a id="feature-nextwaiting"></a>A button in the sidebar header shows how many other chats wait for you, with a dot in the color of the next one: orange when it asks a question or waits for an approval or a plan review, green when it finished a turn that you have not read. Click it, or press Cmd+Shift+E (Ctrl+Shift+E on Windows and Linux), to open that chat. Orange chats come before green ones; in each group, the chat that waits longest comes first. The chat on screen does not count. When no other chat waits, the button is not shown and the key does nothing. You can change the key on dsh's Keyboard shortcuts page (Next waiting chat). | <a href="assets/features/nextWaiting.webp"><img src="assets/features/nextWaiting.webp" width="160" alt="Screenshot: Next waiting chat"></a> |
101
+ | **Next waiting chat** | <a id="feature-nextwaiting"></a>A button in the sidebar header shows how many other chats wait for you, with a dot in the color of the next one: orange when it asks a question or waits for an approval or a plan review, green when it finished a turn that you have not read. Click it, or press Cmd+Shift+E (Ctrl+Shift+E on Windows and Linux), to open that chat. A click on the button also finds the chat in the sidebar: a collapsed project opens, the list scrolls to the chat and the chat blinks for 5 seconds. Orange chats come before green ones; in each group, the chat that waits longest comes first. The chat on screen does not count. When no other chat waits, the button is not shown and the key does nothing. You can change the key on dsh's Keyboard shortcuts page (Next waiting chat). | <a href="assets/features/nextWaiting.webp"><img src="assets/features/nextWaiting.webp" width="160" alt="Screenshot: Next waiting chat"></a> |
97
102
  | **Option buttons** | <a id="feature-optionpicks"></a>When an answer ends with a list of options (a numbered list of 1 to 9 items under an "Options" heading or a bold "Options" line, also "Next steps" and the same words in the app's languages), a button for each option shows under the answer. A click sends "Option N: …" with the whole option as your message, as if you typed it. The option marked "(recommended)" is marked. The buttons show only under the newest answer, while the chat is idle and the composer is empty; a picked answer and an answer that a rollback withdrew show none. While they show, Next message prediction makes no guess. On a question card of the agent, the keys 1–9 pick that option, as a click does (not in the answer field, and not in the first 0.8 seconds after the question shows). | <a href="assets/features/optionPicks.webp"><img src="assets/features/optionPicks.webp" width="160" alt="Screenshot: Option buttons"></a> |
98
103
  | **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> |
99
104
  | **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> |
100
- | **Remote computers** | <a id="feature-remotecomputers"></a>Open the dsh of your other Macs over [Tailscale](https://tailscale.com) and continue their chats here. Switch it on in **Settings → Composer Plus** on each Mac (it is off by default, because it opens network ports). On the Mac that you want to open, switch on **Let other computers open this Mac**, click **Create pairing link** and copy the link. On this Mac, pick **Add a computer…** in the computer picker at the left of the chat title, paste the link, click **Connect**, then click **Approve** on the other Mac. The picker then lists **This Mac** and your computers: pick one and its dsh opens in the Browser tab of the right sidebar (or in a browser window), with its own chats, tools and files. The Settings panel renames and removes computers, and lists the computers that can open this Mac (**Remove** cuts one off at once). | <a href="assets/features/remoteComputers.webp"><img src="assets/features/remoteComputers.webp" width="160" alt="Screenshot: Remote computers"></a> |
101
- | **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, in the language of **Application language** (they come with the new version, no extra network call; a language without notes shows the English notes); **Close** or `Esc` closes it. The window also shows after an update from Settings → Plugins or a copied folder, once per new version; not at the first install. **What’s new in this version** in the update row opens it again. When a newer version is on disk than the one that runs, the row says to restart dsh. | <a href="assets/features/autoUpdate.webp"><img src="assets/features/autoUpdate.webp" width="160" alt="Screenshot: Automatic updates"></a> |
105
+ | **Remote computers** | <a id="feature-remotecomputers"></a>Open the dsh of your other Macs over [Tailscale](https://tailscale.com) and use them in this dsh window. Switch it on in **Settings → Composer Plus** on each Mac (it is off by default, because it opens network ports). On the Mac that you want to open, switch on **Let other computers open this Mac**, click **Create pairing link** and copy the link. On this Mac, pick **Add a computer…** in the computer picker of the left sidebar (right of the brand, above **New session**), paste the link, type a **Name** (not the name of this Mac or of another computer), click **Connect**, then click **Approve** on the other Mac. The picker then lists **This Mac** and your computers: pick one and its dsh fills this window, with its own chats, sidebar, tools and files, while this Mac keeps running behind it. The picker stays in the same place and says **Back to this Mac**: click it to come back. With more than one computer, the click opens the menu, where you pick **This Mac** or another computer. The Settings panel renames and removes computers, and lists the computers that can open this Mac (**Remove** cuts one off at once). | <a href="assets/features/remoteComputers.webp"><img src="assets/features/remoteComputers.webp" width="160" alt="Screenshot: Remote computers"></a> |
106
+ | **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, in the language of **Application language** (they come with the new version, no extra network call; a language without notes shows the English notes); **Close** or `Esc` closes it. Below the notes, one line asks for a GitHub star, at most once in 30 days (no network call); after **Star on GitHub** or **Do not ask again**, it does not show again. The window also shows after an update from Settings → Plugins or a copied folder, once per new version; not at the first install. **What’s new in this version** in the update row opens it again. When a newer version is on disk than the one that runs, the row says to restart dsh. | <a href="assets/features/autoUpdate.webp"><img src="assets/features/autoUpdate.webp" width="160" alt="Screenshot: Automatic updates"></a> |
107
+ | **On/off filter** | <a id="feature-featurefilter"></a>Next to **Search features** in **Settings → Composer Plus**: **All** shows every feature, **On** only the features whose switch is on, and **Off** only the ones whose switch is off. Each row's own switch decides, also a switch that dsh's Host keeps (for example **Mobile connect**). It works with the search: type a word and pick **Off** to find a feature that you switched off. A row that you switch while **On** or **Off** is picked stays until you pick a filter again, so the focus stays on its switch. **On** and **Off** hide the **Application language** and **Composer Plus updates** rows (they have no switch). Settings opens with **All** each time. | <a href="assets/features/featureFilter.webp"><img src="assets/features/featureFilter.webp" width="160" alt="Screenshot: On/off filter"></a> |
108
+ | **Report an issue** | <a id="feature-issuereport"></a>Settings → Composer Plus → **Report an issue…** (under the intro), or right-click a chat in the sidebar (or open its **…** menu) → **Report a Composer Plus issue…**. Pick a type: **Bug**, **Feature request**, **Improvement**, **Translation** or **Question**. Describe it in your own words and language, then click **Write draft** (or `Cmd/Ctrl+Enter`). One call to dsh's default model writes an English issue: a title with the type's prefix (for example `[Bug]`) and the sections of that type (for a bug: What happened, Steps to reproduce, Expected, Actual), plus one line with the Composer Plus, dsh and system versions. Read and edit the title and the description; **Write again** writes a new draft. **Open issue** opens it in the Composer Plus GitHub repository with your GitHub CLI (`gh`) login and shows its link. The issue is public: text that looks like a key or a password is refused. When `gh` cannot open it, **Open on GitHub instead** opens GitHub's own page with the draft filled in. | <a href="assets/features/issueReport.webp"><img src="assets/features/issueReport.webp" width="160" alt="Screenshot: Report an issue"></a> |
102
109
  | **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> |
103
110
  | **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> |
104
111
  | **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> |
@@ -110,7 +117,7 @@ Buttons, tooltips, notices and notifications follow the selected dsh language (s
110
117
 
111
118
  ## Settings
112
119
 
113
- 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.**
120
+ 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. Next to it, **All**, **On** and **Off** show every feature, only the features that are on, or only the ones that are off (switch off **On/off filter** to hide them). 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.**
114
121
 
115
122
  <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>
116
123
 
@@ -170,6 +177,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
170
177
  - **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`.
171
178
  - **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).
172
179
  - **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`.
180
+ - **Report an issue** (Settings → Composer Plus → **Report an issue…**, or a chat's **…** menu → **Report a Composer Plus issue…**): nothing runs until you click. **Write draft** sends only the type and the text that you typed (not the chat, not the project) to dsh's default model, in one call. The Host adds one line with the Composer Plus, dsh and operating system versions and replaces your home folder path with `~`. **Open issue** runs your own GitHub CLI (`gh issue create --repo furkancak1r/dsh-composer-plus`) with your `gh` login, so the issue is public and shows your GitHub account. Text that looks like a key, a token or a password is refused and does not leave your Mac.
173
181
  - **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.
174
182
  - **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.
175
183
  - **Find in chat** runs only in the page: it reads the text that the page shows and sends nothing.
@@ -178,9 +186,10 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
178
186
  - **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.
179
187
  - Auto Review still runs on every call; review failures are never auto-allowed.
180
188
  - **Mobile connect:** off until you switch it on. While it is on, the Host half runs the Tether gateway: HTTPS on port 19390 that accepts only your Wi-Fi subnet and your tailnet (100.64.0.0/10), and a pairing page on `127.0.0.1:19391` (this Mac only). A phone needs a one-time code from the QR code (5 minutes) and your **Approve** on the Mac. The phone pins the gateway's certificate. The Mac keeps only a hash of each phone's token (30 days, renewed while used); **Remove** closes that phone's connections at once. Phones can call only an allowlist of dsh methods (chats, prompts, attachments, slash commands, approvals, questions, models, agents, goals), not settings, credentials, the file browser or the terminal. **Tailscale only** refuses every connection from the local network, also open ones. The access log keeps the newest 200 connection attempts (time, IP address, device name, allowed or refused, and why) in `access.json`; it never holds a token or a pairing code. Data: `$DSH_HOME/storages/dsh-composer-plus/mobile/` (owner-only files). Details: [tether/README.md](tether/README.md).
181
- - **Remote computers:** off until you switch it on, on each Mac. **Let other computers open this Mac** opens a second HTTPS port (19392) on this Mac's Tailscale address only (not on the Wi-Fi, not announced with Bonjour). Another Mac needs a one-time pairing link (5 minutes), the matching certificate (pinned) and your **Approve**; this Mac keeps only a hash of its token. A paired computer gets full control of dsh on this Mac, as if you used it here: its chats, models, tools, files and terminal. Remove it in the Settings panel and its connections close at once. The Mac that opens another one keeps that computer's token in `$DSH_HOME/storages/dsh-composer-plus/computers.json` (owner-only). It opens a relay on `127.0.0.1` for each computer that you pick: the page gets a one-time link (60 seconds), and the relay accepts only its own page (its address, no other site). A page that another Mac opened shows no computer picker and cannot change this Mac's computers.
189
+ - **Remote computers:** off until you switch it on, on each Mac. **Let other computers open this Mac** opens a second HTTPS port (19392) on this Mac's Tailscale address only (not on the Wi-Fi, not announced with Bonjour). Another Mac needs a one-time pairing link (5 minutes), the matching certificate (pinned) and your **Approve**; this Mac keeps only a hash of its token. A paired computer gets full control of dsh on this Mac, as if you used it here: its chats, models, tools, files and terminal. Remove it in the Settings panel and its connections close at once. The Mac that opens another one keeps that computer's token in `$DSH_HOME/storages/dsh-composer-plus/computers.json` (owner-only). It opens a relay on `127.0.0.1` for each computer that you pick: the page gets a one-time link (60 seconds), and the relay accepts only its own page (its address, no other site). The other Mac's page in this window can read the names and states of this Mac's computers (no address, no token) and can switch this window between them; it cannot add, rename or remove a computer or change this Mac's settings.
182
190
  - **Updates:** the Host half asks `api.github.com` for the latest release and `registry.npmjs.org` for that exact version; nothing about you or your chats is sent. Installation uses dsh's plugin manager (`pnpm add dsh-composer-plus@<version>`; when dsh 0.2.1 refuses that for an installed plugin, `pnpm add https://registry.npmjs.org/dsh-composer-plus/-/dsh-composer-plus-<version>.tgz`, and the profile then lists that URL as the plugin's source), so dsh's own registry, script and version-compatibility checks apply. Pre-release tags and older versions are never installed.
183
- - **Usage count:** once a day, the Host half asks GitHub for the small `active.txt` file of the release of the installed version (`github.com/furkancak1r/dsh-composer-plus/releases/download/v<version>/active.txt`). GitHub counts these requests per file, so the owner sees only how many copies of each version asked that day. The request has no id, cookie or data about you or your chats; GitHub sees your IP address, as with each update check. The day of the last request is kept in `$DSH_HOME/storages/dsh-composer-plus/active.json`. A git checkout of this repository and a renamed copy send nothing. There is no switch for it.
191
+ - **Turkish skill pack:** on by default, and it works only while the app language is Türkçe: the browser tells the Host dsh's UI language. Then, at most once a day, the Host asks `api.github.com` for the newest commit of `yucezerey/turkish-anti-ai-skill` and its file list, and downloads the changed Markdown files from `raw.githubusercontent.com`. GitHub sees your IP address, as with each update check; nothing about you or your chats is sent. Only regular `.md` files of the `skills/turkish-anti-ai-skill/` folder are kept (at most 20 files and 1 MB), and each file must match its id in GitHub's file list. The copy is in `$DSH_HOME/cache/composer-plus/turkish-skill/<commit>` (owner-only) and goes into dsh's skill list; no script of the pack runs. The switch and the last reported language are in `$DSH_HOME/storages/dsh-composer-plus/turkish-skill.json`. Switching it off removes the skill and deletes the copy.
192
+ - **Usage count:** the Host half asks GitHub for two small files of the release of the installed version (`github.com/furkancak1r/dsh-composer-plus/releases/download/v<version>/<file>`): `active.txt` once a day, and `installed.txt` only once for each dsh home. GitHub counts these requests per file, so the owner sees only how many copies of each version asked for `active.txt` that day, and how many copies asked for `installed.txt` in total (the number of copies). The request has no id, cookie or data about you or your chats; GitHub sees your IP address, as with each update check. The day of the last `active.txt` request is kept in `$DSH_HOME/storages/dsh-composer-plus/active.json`, and the mark that `installed.txt` was counted in `$DSH_HOME/storages/dsh-composer-plus/installed.json`. A git checkout of this repository and a renamed copy send nothing. There is no switch for it.
184
193
  - **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).
185
194
  - **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).
186
195
  - **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).
@@ -188,6 +197,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
188
197
  - **Continue in a new chat:** nothing runs until you click it. Then the Host half sends the chat (as Goal check reads it: no reasoning, no rolled-back steps) to the chat's own model and shows the note; only **Start new chat** makes a new chat. Nothing is written to the project.
189
198
  - **Context breakdown:** reads only the chat log on the Host; no model call, no project file.
190
199
  - **Task board:** the board shows dsh's own chats from dsh's own lists in this window; it adds no chat data of its own. **New task** opens dsh's own new chat, and you write the first message in dsh's composer, so approvals, the sandbox and the chat's model apply as usual. The Host half keeps that chat's first message as the card's prompt and sends it (its first 4,000 characters, nothing else) to that chat's model in one more request, with no tools, for up to 3 tags. The prompt, the tags and the start time of each task are kept in `$DSH_HOME/storages/dsh-composer-plus/board-chats.json` (owner-only file, the newest 2,000), with the ids of the chats that you took off the board with **×** or **Clear all** (the newest 5,000); the switch is in `board-switch.json` next to it. Nothing is written to a project folder. **Answer** sends your answer to dsh's own pending request, the same way the chat's own card does. The old task list of earlier versions (`board.json`) stays on disk as it was and is not read. **Progress on hover** reads the chat's log on this Mac. With **Fast (no AI)** (the default), nothing is sent. With **AI summary**, while a running card's progress box shows (or while Tether's board is open, for up to 8 running cards), the Host half sends that chat's newest part (at most about 6,000 characters: your messages, the agent's text replies, its tool calls and the end of their results; no reasoning, no rolled-back messages) to the chat's model, as one request with no tools. It sends again only after the chat moved on, at most once every 20 seconds per chat. The note is kept only in memory; the setting is in `$DSH_HOME/storages/dsh-composer-plus/board-progress.json` (owner-only file).
200
+ - **Assistant:** the assistant chat is an ordinary dsh chat in `~/Assistant`; its model calls go to dsh's default model as in any chat. Its tools read your other chats' logs on this Mac (titles, projects, status, and with chat_read their newest messages) and give them to that model only when it calls a tool. Sending, starting, stopping and archiving a chat each wait for your approval. The chat id is kept in `$DSH_HOME/storages/dsh-composer-plus/assistant.json` and the switch in `assistant-switch.json` (owner-only files). Reminders and automations are dsh's own scheduled tasks. Nothing leaves this Mac except the model requests and the web searches that the chat makes.
191
201
  - **Protected files:** the list and the switch are in `$DSH_HOME/storages/dsh-composer-plus/protected-files.json` (owner-only file). The check reads only the path of each file tool call (and its real path), never the file.
192
202
  - **Blocked commands:** the rules and the switch are in `$DSH_HOME/storages/dsh-composer-plus/blocked-commands.json` (owner-only file). The check reads only the command text of each `bash` and `pwsh` call; it runs nothing and sends nothing.
193
203
  - **Secret input:** the value goes from the card (or from Tether, through the gateway, which does not log request bodies) to the Host only in the answer request. The Host writes it to `$DSH_HOME/storages/dsh-composer-plus/secrets/<random name>` (folder 0700, file 0600); nothing goes into the project, the chat log or the tool result. The Host keeps the value in memory to replace it with `[secret]` in later tool results of that chat. The file and that copy go when the chat's turn ends and at dsh start. The switch is in `secrets.json` next to it. A command that the agent runs with the file can send the value where that command sends it: that is the purpose.
@@ -210,10 +220,11 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
210
220
  - **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).
211
221
  - **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.
212
222
  - **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.
223
+ - **Report an 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`. Or click **Open on GitHub instead**: GitHub's own new issue page opens with the draft filled in, and you submit it there.
213
224
  - **No branch pill on a chat row:** the sidebar is not in “In one list” (in “WorkSpace” and “Workspace Tree”, no chat row shows a pill), 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).
214
225
  - **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.
215
226
  - **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).
216
- - **Install fails right after a release:** add the version (`dsh-composer-plus@1.22.0`), see the install note above.
227
+ - **Install fails right after a release:** add the version (`dsh-composer-plus@1.24.0`), see the install note above.
217
228
  - **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.
218
229
  - **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.
219
230
  - **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.
@@ -235,9 +246,11 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
235
246
  - 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.
236
247
  - **What’s new in Composer Plus N** shows the notes in the language of **Application language**. A UI language with another region gets the notes of its base language (`en-GB` gets the English notes, `zh-TW` the Chinese notes of `zh`). A language without notes (one that another language pack adds, or `pt-PT`, which is not `pt-BR`) shows the English notes. The notes come with the new version. When the running copy has no readable notes file (`release-notes.json`), the window shows the English notes from GitHub.
237
248
  - A version that dsh installed while it runs (an update from Settings → Plugins or a copied folder) loads only after a dsh restart: until then, **Composer Plus updates** shows the running version and asks for a restart. Its **What’s new in Composer Plus N** window shows once at the first start of that version. The first start after this plugin is installed shows no window; **What’s new in this version** opens the notes of the running version.
249
+ - The GitHub star line below the notes cannot know whether you starred the repository: the plugin makes no network call for it. Each browser (and the desktop app) keeps its own record, so the line can show once more in another one.
238
250
  - **Reject with note** sends the note as a message into the waiting turn, then rejects the request; dsh shows the note in the chat as your message. While the note field is open, the card shows only **Cancel** and **Reject**, so a click cannot allow the request by mistake; `Esc` or **Cancel** brings dsh's buttons back. An empty note sends nothing (use dsh's own **Reject**). If the rejection fails after the note went (for example, the request was answered elsewhere in the meantime), the note stays in the chat and the model reads it at its next step. The button goes into dsh's approval card markup (`data-approval-key`), because dsh has no slot for card actions; if a dsh update changes that markup, the button is not shown and dsh's own **Reject** works as before. A note can have at most 4,000 characters.
239
251
  - **Secret input** hides only the exact text of a value: `[secret]` replaces it in the later tool results of the same chat. The agent can still read the file with a shell command and print the value in another form (base64, a part of it, with spaces), and that form reaches the model. The agent's own file tools (read, grep, write, edit) cannot open the secrets folder. When the value is also a common word, other results of that chat show `[secret]` too. The file stays until the turn ends; when dsh quits during the turn, the next start deletes it. A chat can have one open request at a time. The tool adds about 870 characters (about 220 tokens, estimated) to each model request: switch it off when you do not use it.
240
252
  - **Quote in reply** quotes the text as the page shows it: a code block, a table or a list comes as plain lines, without its Markdown marks. A selection that starts in one answer and ends in another, or that touches the reasoning, a tool row or your own message, shows no button: select inside one answer. The button finds an answer through dsh's chat markup, because dsh has no slot for a text selection; if a dsh update changes that markup, the button does not show. When the quote ends the field, one more empty line goes after it so the next line that you type starts below it; dsh removes empty lines at the end when it sends.
253
+ - **Quote from file preview** reads dsh's own file preview: the line numbers come from its rows, so they are right for code and text files. A Markdown file shows as a formatted page, so its quote has the text without its Markdown marks and without line numbers; a PDF quote has the page, not a line. A selection in the path header, in another viewer (a spreadsheet, an Office file, an image) or over two open files shows no button, and neither does a right sidebar in full screen (the composer is not on screen then). The button finds the preview through dsh's markup, because dsh has no slot for a selection there; if a dsh update changes that markup, the button does not show. Only the selected text goes into the composer: if the file changes later, the quote does not change.
241
254
  - **Side chat** is read-only through dsh's own read-only mode: file tools cannot write and shell commands run in dsh's sandbox without writes (or do not run when the sandbox cannot start). The agent can still ask for more access with dsh's approval card, and a network call (for example an API request) can still change something outside the computer. The copy is made at the newest moment at which no message waits in the queue, so a question asked while queued messages wait does not see what happened after them. A running step of the chat is closed in the copy, so the side chat knows the start of that step but not its result. The side chat is an ordinary chat: it stays in the sidebar and counts toward your model use; archive it there. Bring back takes the side chat's own messages (at most 12,000 characters), only the last reply of each question; the agent's notes between its tool calls stay out. dsh has no slot for a chat inside the right sidebar for a plugin, so the side chat opens in the main view.
242
255
  - **Diff notes** works in the Changes panel only, not on dsh's own Last Turn view. A range must stay in one block of the diff (one `@@` part). The notes are kept in this browser's storage, so another browser or the dsh app on another computer does not see them; Tether keeps its own notes in memory. A note keeps the lines as they were when you clicked: when the agent changes the file later, the note still shows the old lines and numbers. At most 50 notes per chat, 2,000 characters per note and 40 lines per note (then a `…` line).
243
256
  - **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.
@@ -245,7 +258,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
245
258
  - **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.
246
259
  - **Fit chat header to window** finds dsh's chat header by its generated class names (`_titleRow`, `_titleCluster`, `_crumbs`, `_headerActions`, `_headerUtilities`), because dsh has no layout hook for it. If a dsh update renames them, the header shows as dsh draws it. In a very narrow chat column the header can take two or three lines, so the chat area above the messages gets lower.
247
260
  - **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). With an APNs key in `apns.json` (see `tether/README.md`), the Mac sends the notifications through Apple's push service, so they come also while Tether is closed; the Mac must be awake with dsh open. Without the key, the phone gets them only while Tether is open or just went to the background. Tether sends them only after you switch on **Settings → Notifications → Send notifications** in Tether. Tether keeps this switch and its hours separately for each paired Mac. The switch is off by default for each Mac, 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. Each notification shows the Mac and the chat's project (`Studio Mac · recipe-box`), the chat's title and the text: Turn finished shows the agent's last reply. With the APNs key, this text goes through Apple's push service. 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.
248
- - **Remote computers** needs Tailscale on both Macs, and Composer Plus with this feature on the Mac that you open. That Mac must be awake with dsh running. The other computer opens in the Browser tab of the right sidebar of the chat on screen: the tab belongs to that chat, so another chat shows its own sidebar. Without a chat (or on dsh web without its Browser tab), the computer opens in a browser window. The picker shows only while a chat or the New chat screen is in the main area, not on the Task board. It uses port 19392 (only one dsh on each Mac can let other computers in). A computer that you opened inside another computer's page has no picker, so you cannot open a third Mac from there.
261
+ - **Remote computers** needs Tailscale on both Macs, and Composer Plus with this feature on the Mac that you open. That Mac must be awake with dsh running. The picker shows only while the left sidebar is open (not in its icon rail). When the other Mac's page has no picker of its own (Composer Plus older than 1.23.0 there), this Mac's picker shows over that page in the same place. With 1.23.0 there, the picker shows that Mac's name: pick **This Mac** in its menu to come back. Each computer that you opened keeps its page in memory until you switch the feature off or restart dsh. On the desktop app, the other Mac's page is an embedded page: dsh's own keyboard shortcuts (for example a new chat) do not work in it, and file downloads, microphone and notifications do not work there either. It uses port 19392 (only one dsh on each Mac can let other computers in). From another Mac's page, the picker switches only among the computers of this Mac: you cannot open a third Mac through the other Mac.
249
262
  - 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.
250
263
  - **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.
251
264
  - **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).
@@ -253,6 +266,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
253
266
  - 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.
254
267
  - **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.
255
268
  - **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`).
269
+ - **Report an issue** opens issues only in the Composer Plus repository (`furkancak1r/dsh-composer-plus`), not in your project (for that, use **Open GitHub issue** in the Feature ideas **Later** list). It needs a default model in dsh for the draft, and the GitHub CLI (`gh`) with a login to open the issue. The draft is always in English. The model can get a detail wrong: read the draft before you click **Open issue**. Opening runs at most 60 s; the same title and text again within an hour shows the earlier issue instead of a copy. **Open on GitHub instead** cuts a very long description to keep the link short.
256
270
  - **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.
257
271
  - **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. The pill shows only in “In one list”.
258
272
  - **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.
@@ -277,7 +291,10 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
277
291
  - **Continue in a new chat** sends the chat (messages, tool calls and the end of each tool result, at most about 60,000 characters, the newest part) to the chat's own model. The note can miss a decision or a file: read it before you start. The new chat knows only the note and the linked chat (dsh attaches the old chat as read-only context); its model context starts small, the old chat stays as it is. One note or start per chat at a time.
278
292
  - **Context breakdown** shows estimates with dsh's own rule (about 4 characters per token), the same rule as dsh's context ring; the model's own count can differ. Tool definitions are the ones of the newest request. It shows the context as the chat log has it; a turn that runs now appears after its next step.
279
293
  - **Planned steps** saves tokens only when the model plans: while it is on, each request carries the `plan_next_steps` tool and a short instruction (about 500 tokens), also in chats where the model never plans. A condition proves only what it says (for example: the build exit code was 0), not that the work is right. `ok` reads dsh's own result marks (`[exit code: N]`, timeouts, sandbox denials); a command that fails without a non-zero exit code passes. Steps run one at a time; two steps that are ready at the same time go back to the model. The step's message is stored as an assistant message of the chat's model (dsh has no mark for a message that a plugin wrote), so its text says that Composer Plus ran it. Not in Tether.
294
+ - **Turkish skill pack** installs a third-party skill: its rules come from the author's GitHub repository, not from Composer Plus, and a new version is used from the next daily check without a review by us. The check proves that the files are the ones GitHub lists for that commit, not that the rules are good. The model decides when to load the skill (the user can also call it by name); the rules apply to Turkish texts only. The Host uses the language that a browser reported last: when two browsers use different languages, the newest one wins. GitHub allows 60 API calls per hour without a login; a refused check keeps the installed copy and shows the reason in Settings.
295
+ - **Untangle codebase** ships a fixed copy of the skill (caldoai/skills, commit f3cf477): a change upstream comes only with a new Composer Plus version. The skill runs a long goal that can change many files, and its subagents and checks use many tokens. Where the project has no tests, it can change behavior without notice: start it on a branch and read the diff before you merge. A skill with the same name in the project, in a custom skill folder or in `~/.dsh/skills` replaces it. While the switch is off, `/untangle-codebase` goes to the model as plain text.
280
296
  - **Task board** shows the chats that dsh lists in this window: a chat outside a project shows only in **All projects**, and a Branch per chat folder (`<project>/.worktrees/<name>`) counts as its project. A card's column is the chat's live state, so cards cannot be dragged: a chat goes to **Done** when its turn ends, also after an error or a stop, and back to **Doing** when it runs again. The green dot is dsh's own done dot, which dsh keeps only in this window: after a reload, finished chats show without it. **Done** shows the newest 50 chats (**Show N older** shows the rest). **Search projects** finds a project by its name only, not by its folder path. **Active in the last 2 hours** hides a project when each of its chats is older than 2 hours, idle and read; the board still shows the project that you picked. **New task** remembers only the newest new chat of this window: when you open another chat before the first message, that chat stays at the top of **Doing** as **New task** until you write in it, but the board comes back by itself only for the newest one. After a reload, a new chat without a message is not on the board (dsh keeps it as its empty New session). The tags come from one model call per task, from its first message: they can be wrong, and a chat that you did not start on the board has none. The title is dsh's own chat title (dsh's title service, with the chat's model); until it comes, the card shows the first line of the first message. **×** and **Clear all** take cards off the board only: no chat is deleted. **Undo** shows for 8 seconds only, in this window; later, **Removed (N)** → **Put back** brings a card back, and a chat that runs again comes back by itself. **Clear all** asks first and takes the read finished cards of the shown project (all projects with **All projects**), also the ones behind **Show N older**; an unread (green) card stays. dsh keeps its green dot only in this window, so after a reload Clear all can take a card whose answer you did not read. The list is kept on this Mac for every window and for Tether; when it has 5,000 chats, the oldest come back. **Answer** answers only the newest request of the chat: when the chat answered it elsewhere (another window, Tether, Auto Review) or asks something new, the panel says so and sends nothing. Earlier versions had a task list with comments and agent tools; they are gone, and the old `board.json` is not shown. The idea and the look come from [Kanzo](https://github.com/emanuele-em/kanzo) by emanuele-em (AGPL-3.0); Composer Plus uses no code from it. `Cmd+Shift+K` is a dsh shortcut: in a browser on Linux it has no default key (set one on dsh's Keyboard shortcuts page), and a browser extension or another app can take it first. A card leaves **Done** when dsh's own green dot goes (you opened the chat) or, for a chat that finished behind the board while it was the open chat, when that chat shows with no panel over it. This read state lives only in this dsh window, as dsh's own dots: after a reload or in another window every finished chat is read, so it is in **Recently finished**.
297
+ - **Assistant** has one chat for all windows of this Mac. It runs with the workspace-write permission mode: it writes files only in `~/Assistant`, and a command outside it asks first. When you switch the chat to a mode without approvals (Full access), dsh rejects each approval, so the assistant cannot send, start, stop or archive a chat. The approval card guards the six tools only: a shell command of the assistant can still reach the network, as in any chat with that mode. On dsh 0.2.0 and 0.2.1, dsh's schedule tools see only the reminders of the assistant chat itself, not the automations of other chats; from dsh 0.2.2, they list and change every scheduled task. chats_list leaves out archived, blank and subagent chats, and the assistant chat itself; "waiting for you" means an open approval, question or plan review in a running chat. chat_read gives the user's messages and the agent's replies (no tool output), at most 50 messages and about 24,000 characters. chat_archive refuses a chat that still runs or has active reminders. A model can still be misled by text that it reads: the approval card is the last check, so read it before you click **Allow once**.
281
298
  - **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.
282
299
  - **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; check them in **Open memory** and change them there, or edit the files by hand while dsh is closed. **Open memory** sends the notes of the selected project and the global notes (all projects when you select All notes; at most about 60,000 characters, the rest is counted and cannot change in that request) and your text to dsh's default model, not to the chat's model. A preview is kept on the Mac for 30 minutes and until dsh restarts; when a review or the agent changes the notes in between, **Apply** refuses and you preview again. **Apply** saves all the changes of a preview or none: when one change cannot be saved (for example, MEMORY.md is full), the notes stay as they were and the reason shows. 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.
283
300
  - **Memory version history** keeps the newest 50 versions of each memory (each project and the global memory); older ones go. A version holds the whole memory of that project, not one note. **Restore** puts back every file of that version (topics included) and refuses when the memory changed after you opened the version; the memory before the restore becomes a new version, so you can undo it. While the switch is off, no new version is kept and **Restore** refuses; the kept versions stay. A version from before this feature does not exist: the history starts with the first save after you install it.
@@ -291,6 +308,8 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
291
308
  - **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.
292
309
  - **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.
293
310
  - **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.
311
+ - **Remember window size** works only in the desktop app. At each start, dsh shows its window at 1280 × 820 first; the window moves to the saved size when the plugin loads. A page has no event for a window move, so the plugin reads the window once a second: a change in the last 2 seconds before dsh quits is not saved. The size is kept per computer, in the app's own storage.
312
+ - **Usage count:** it counts copies, not people: each dsh home is one copy. The unique count (`installed.txt`) counts only copies that update to the first version with this file or a later one; a copy that stays on an older version is not counted. A copy whose `storages` folder is deleted is counted again. GitHub can count the requests from one IP address (for example, many computers behind one NAT router) in about 10 minutes as one.
294
313
  - 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).
295
314
 
296
315
  ## FAQ
@@ -329,6 +348,8 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
329
348
 
330
349
  **Can I see all my chats as a kanban board and start new ones from it?** Yes, with **Task board** (on by default): open it in the left sidebar. Each chat is a card in **Doing**, **Review** (it waits for you) or **Done**. **New task** opens dsh's new chat in the project, and the board comes back when your first message goes; click **Answer** on an orange card to answer it without opening the chat. **Clear all** in **Done** asks first and takes the finished cards that you read off the board; **Undo** and **Removed (N)** bring them back, and the chats stay in dsh.
331
350
 
351
+ **Can I have a personal assistant chat that reminds me and manages my other chats?** Yes, with **Assistant** (off by default): switch it on in Settings → Composer Plus, then click **Assistant** in the left sidebar. It sets reminders with dsh's scheduled tasks, searches the web, lists and reads your chats, and sends a message to a chat or starts, stops or archives one only after you approve it. Keep your profile in its project memory, topic "profil".
352
+
332
353
  **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.
333
354
 
334
355
  **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.
@@ -361,6 +382,8 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
361
382
 
362
383
  **How do I answer one part of a long answer?** Select that part and click **Quote** above the selection (**Quote in reply**, on by default). The part goes into the composer as a `> …` quote, and you write your reply below it.
363
384
 
385
+ **How do I show the agent the exact lines of a file?** Open the file in the right sidebar (**Workspace files**), select the lines and click **Quote** above the selection (**Quote from file preview**, on by default). The composer gets the path and the line numbers (for example `src/login.js:2-3`) and the lines as a code block; write your question below them.
386
+
364
387
  **Can I ask something while the agent works, without stopping it?** Type the question and click **Ask in a side chat** in the composer (**Side chat**, on by default). A read-only copy of the chat answers it while the chat goes on. In the side chat, **Bring back** puts the answer into the chat's composer.
365
388
 
366
389
  **How do I tell the agent what to change in its diff?** In **Changes panel**, open the file's diff, click **+** at a line and write a note (**Diff notes**, on by default). Then click **Send to composer**: the notes go into the composer with the file, the line numbers and the lines.
@@ -369,7 +392,7 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
369
392
 
370
393
  **I lost my iPhone. How do I stop it from reaching my Mac?** Click **Mobile connect** at the bottom left of the sidebar and click **Remove** next to that iPhone. Its key stops working at once; it can come back only with a new pairing that you approve on the Mac.
371
394
 
372
- **How do I continue a chat of my other Mac from this one?** On the other Mac, switch on **Settings → Composer Plus → Remote computers** and **Let other computers open this Mac**, click **Create pairing link** and copy the link (send it to yourself, for example with AirDrop or Notes). On this Mac, switch on **Remote computers** too, pick **Add a computer…** at the left of the chat title, paste the link and click **Connect**, then click **Approve** on the other Mac. From then on, pick that computer in the same menu.
395
+ **How do I continue a chat of my other Mac from this one?** On the other Mac, switch on **Settings → Composer Plus → Remote computers** and **Let other computers open this Mac**, click **Create pairing link** and copy the link (send it to yourself, for example with AirDrop or Notes). On this Mac, switch on **Remote computers** too, pick **Add a computer…** in the picker of the left sidebar (right of the brand, above **New session**), paste the link, type a **Name** and click **Connect**, then click **Approve** on the other Mac. From then on, pick that computer in the same menu: it fills this window. To come back, click **Back to this Mac** in the same place; this Mac's chats keep running meanwhile.
373
396
 
374
397
  **Can I lock Tether with Face ID?** Yes: in Tether, **Settings → Privacy → Lock with Face ID**. Tether then asks for Face ID (or the passcode) when it opens and when you come back to it.
375
398
 
@@ -401,6 +424,12 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-rc.2` (web). CI runs the
401
424
 
402
425
  **Can dsh run the next command without asking the model again?** Yes, with **Planned steps** (off by default): switch it on in Settings → Composer Plus. The model can then plan its next commands with a condition, for example "run the tests when the build passes", and Composer Plus runs a step without a new model call when the result meets the condition. Approvals and the sandbox stay as they are.
403
426
 
427
+ **Where does the Turkish skill come from, and can I remove it?** From [yucezerey/turkish-anti-ai-skill](https://github.com/yucezerey/turkish-anti-ai-skill) on GitHub (MIT). Composer Plus installs it only while **Application language** is Türkçe, keeps only its Markdown files and runs none of its scripts. To remove it, switch off **Turkish skill pack** in Settings → Composer Plus: the skill and the downloaded copy go at once.
428
+
429
+ **How do I report a bug or ask for a feature in Composer Plus?** Settings → Composer Plus → **Report an issue…** (or a chat's **…** menu → **Report a Composer Plus issue…**). Pick a type, describe it in your own language and click **Write draft**. Check the English draft, then click **Open issue**.
430
+
431
+ **How can the agent clean up a messy codebase?** Type `/untangle-codebase` in a chat (**Untangle codebase**, on by default), optionally with an area, for example `/untangle-codebase src/api`. The agent first lists the copies, hidden links, needless wrappers, dead code and special cases, then fixes them as a goal and checks again with subagents. Start it in a chat of **Branch per chat**, so the work stays on its own branch.
432
+
404
433
  **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).
405
434
 
406
435
  ## Development
@@ -420,6 +449,8 @@ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-status-order.mjs /path/t
420
449
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-agentbox-ideas.mjs /path/to/dsh/lib/bin.js
421
450
  # Reject with note, Secret input, Quote in reply, Side chat and Diff notes (fake model + a test-only approval gate): the button on dsh's card, the note after the rejection in the next request, Esc; the secret card, Save, the value in no request, file or page, Decline, Stop; a selected part of an answer → Quote → the sent message; a side question while the chat waits on a card (read-only, no queued message in it), Bring back; notes on a diff line and range → Send to composer → the sent message; switch off + their thumbnails
422
451
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-t3code-ideas.mjs /path/to/dsh/lib/bin.js
452
+ # Quote from file preview (fake model): lines of a code, text, Markdown and PDF file in the right sidebar → Quote → the path, the line numbers and the lines in the sent message; no button for the path header; switch off + its thumbnail
453
+ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-preview-quote.mjs /path/to/dsh/lib/bin.js
423
454
  # every Settings feature row shows a loading screenshot and a ? link to its README row
424
455
  PLAYWRIGHT=/path/to/playwright node test/browser-settings-shots.mjs /path/to/dsh/lib/bin.js
425
456
  # What's new after an update: the release notes show once (also after an install through another path), Esc and Close close them; Settings → What’s new in this version opens them again
@@ -471,6 +502,8 @@ SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-queue-stop-send.mjs /pat
471
502
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-queue-edit.mjs /path/to/dsh/lib/bin.js
472
503
  # Task board: New task opens dsh's new chat and comes back after the first message, AI tags, live columns, Answer on the board, × with Undo, Clear all with its question (unread cards stay), Removed and Put back (kept after a restart), the switch
473
504
  SHOT=1 PLAYWRIGHT=/path/to/playwright node test/browser-task-board.mjs /path/to/dsh/lib/bin.js
505
+ # Assistant: off by default, sidebar row under Task board, one chat in ~/Assistant (workspace-write), a reminder that runs the chat, chats_list, chat_send after its approval card, an injected chat_send rejected, no tools in other chats, automations of other chats (dsh 0.2.2+), switch off
506
+ SHOT=1 CHECK_DELAY=20 PLAYWRIGHT=/path/to/playwright node test/browser-assistant.mjs /path/to/dsh/lib/bin.js
474
507
  ```
475
508
 
476
509
  ```sh
@@ -492,3 +525,5 @@ PLAYWRIGHT=/path/to/playwright node test/browser-language.mjs /path/to/dsh/lib/b
492
525
  ## License
493
526
 
494
527
  MIT
528
+
529
+ `assets/skills/untangle-codebase/SKILL.md` is the `untangle-codebase` skill of [caldoai/skills](https://github.com/caldoai/skills), © 2026 Maze, under the MIT License ([its license](assets/skills/untangle-codebase/LICENSE)).