dsh-composer-plus 0.6.0 → 0.6.1
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 +5 -9
- package/client.js +29 -7
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Small composer helpers for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (web / desktop UI). The client half adds the UI; a small Host half serves read-only git changes and saves Auto Review grants. No external network calls.
|
|
4
4
|
|
|
5
5
|
- **Input history** – in an empty composer, `↑` / `↓` walk your previously sent messages, like a terminal. An open `/` menu or an edited draft keeps the normal keys.
|
|
6
|
-
-
|
|
6
|
+
- **Plan mode toggle** – while the chat is in plan mode, `Shift+Tab` leaves it (same as clicking the plan chip). Otherwise it adds `/plan ` to the start of the draft, even when it already has text; press it again to remove that leading `/plan`. The rest of the draft, file references and attachments stay intact, and the caret ends up at the end. It never sends. An open `/` or `@` menu keeps `Shift+Tab` for closing itself.
|
|
7
7
|
- **One-click Continue** – when a turn has stopped and the composer is empty, the Send button becomes **▶**; one click sends `continue`. Hidden on a blank chat, while running, in subagent views, or once you type or attach something.
|
|
8
8
|
- **Copy deeplink** – right-click a session in the sidebar (or use its `…` menu) and pick **Copy deeplink** (label follows dsh's language: English or Chinese). You get a link like `dsh-session:InNlc3Npb24t…`; paste it into another chat and dsh attaches that session as read-only context.
|
|
9
9
|
- **Allow for this chat (Auto Review)** – when Auto Review denies a call and asks you, the approval card gets an **Allow for this chat** button next to Reject / Allow once. It allows this call and every later Auto Review ask for the **same exact tool in the same chat**, with any arguments (e.g. any read-only SQL query on one database tool). Other tools, other chats and non-Auto-Review approvals still ask. A header chip (“N tool(s) allowed · Revoke”) lists the grants and revokes them in one click. Grants are saved by the plugin's Host half (`$DSH_HOME/storages/dsh-composer-plus/grants.json`, user-only permissions), so they survive a restart until you revoke them. If the tool can write, the grant covers any write too.
|
|
@@ -15,22 +15,18 @@ Tested on dsh `0.2.0-rc.2` (desktop and web) and `0.2.1-alpha.2` (web). CI runs
|
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
|
-
**Desktop app:** Settings → Plugins → **Add plugin**, paste
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
https://github.com/furkancak1r/dsh-composer-plus
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
then **Enable now** (or restart dsh). (The desktop profile is managed by the app, so the CLI cannot install into it.)
|
|
18
|
+
**Desktop app:** Settings → Plugins → **Add plugin**, enter `dsh-composer-plus` (npm) or paste `https://github.com/furkancak1r/dsh-composer-plus`, then **Enable now** (or restart dsh). The desktop profile is managed by the app, so the CLI cannot install into it.
|
|
25
19
|
|
|
26
20
|
**Web / CLI profiles:**
|
|
27
21
|
|
|
28
22
|
```sh
|
|
29
|
-
dsh plugin --profile web add
|
|
23
|
+
dsh plugin --profile web add dsh-composer-plus
|
|
30
24
|
```
|
|
31
25
|
|
|
32
26
|
This adds the dependency and the profile bundle. Restart dsh.
|
|
33
27
|
|
|
28
|
+
pnpm in dsh skips versions younger than 24 hours by default (`minimum-release-age`). Right after a release, name the version explicitly, e.g. `dsh-composer-plus@0.6.1`.
|
|
29
|
+
|
|
34
30
|
## How it works / limits
|
|
35
31
|
|
|
36
32
|
- dsh's `session.updateQueue` has no `move` action, so a reorder rotates the affected rows' text with `edit` calls (tail → head). If the agent claims a row mid-move, the applied edits are reverted and a notice is shown; nothing is lost or duplicated.
|
package/client.js
CHANGED
|
@@ -96,12 +96,16 @@ window.__ModuleLoader__.load({
|
|
|
96
96
|
/** Shift+Tab on every platform. */
|
|
97
97
|
const isPlanKey = (event) => event.key === 'Tab' && event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
98
98
|
|
|
99
|
-
/** The plan key
|
|
100
|
-
*
|
|
101
|
-
|
|
99
|
+
/** The plan key leaves plan mode when the chat is in it (as dsh's plan chip does: `/plan off`); otherwise it
|
|
100
|
+
* toggles a leading `/plan` and leaves the caret at the end. Rides `conversation.input.right` because, unlike
|
|
101
|
+
* the composer dock, it also renders on a blank chat. */
|
|
102
|
+
function PlanToggle({ useInput, inputActions, useProjection, exitPlanMode, notify }) {
|
|
102
103
|
const draft = useInput((state) => (state === undefined ? '' : state.draft));
|
|
104
|
+
const plan = typeof useProjection === 'function' ? useProjection('plan') : undefined;
|
|
105
|
+
// Same effective target as dsh's PlanChip: a pending switch flips the folded host value.
|
|
106
|
+
const planOn = plan !== undefined && (plan.pending ? !plan.active : plan.active);
|
|
103
107
|
const live = React.useRef(null);
|
|
104
|
-
live.current = { draft, inputActions };
|
|
108
|
+
live.current = { draft, inputActions, planOn, exitPlanMode, notify };
|
|
105
109
|
const anchor = React.useRef(null);
|
|
106
110
|
|
|
107
111
|
React.useEffect(() => {
|
|
@@ -114,8 +118,14 @@ window.__ModuleLoader__.load({
|
|
|
114
118
|
if (seat.querySelector('[data-trigger-menu]') !== null) return; // Shift+Tab closes an open slash/@ menu
|
|
115
119
|
event.preventDefault();
|
|
116
120
|
event.stopPropagation();
|
|
117
|
-
const { draft, inputActions } = live.current;
|
|
118
|
-
if (event.repeat || event.isComposing || event.keyCode === 229
|
|
121
|
+
const { draft, inputActions, planOn, exitPlanMode, notify } = live.current;
|
|
122
|
+
if (event.repeat || event.isComposing || event.keyCode === 229) return;
|
|
123
|
+
if (planOn && exitPlanMode !== undefined) {
|
|
124
|
+
exitPlanMode().then((failure) => { if (failure !== null && notify !== undefined) notify('error', failure); },
|
|
125
|
+
(reason) => { if (notify !== undefined) notify('error', reason instanceof Error ? reason.message : String(reason)); });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (inputActions === undefined) return;
|
|
119
129
|
const { start, end, text } = planToggle(draft);
|
|
120
130
|
// Edit only the prefix so the rest of the draft, its file chips and attachments stay intact.
|
|
121
131
|
if (!inputActions.insertText(text, { ...inputActions.captureInsertion(), start, end })) return;
|
|
@@ -667,7 +677,7 @@ window.__ModuleLoader__.load({
|
|
|
667
677
|
}
|
|
668
678
|
|
|
669
679
|
return {
|
|
670
|
-
inject: ['slots', 'sessions', 'locale'],
|
|
680
|
+
inject: ['slots', 'sessions', 'locale', 'remote', 'remote.commands'],
|
|
671
681
|
apply(ctx) {
|
|
672
682
|
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
673
683
|
name: 'conversation.input.dock',
|
|
@@ -686,6 +696,18 @@ window.__ModuleLoader__.load({
|
|
|
686
696
|
name: 'conversation.input.right',
|
|
687
697
|
id: 'composer-plan-toggle',
|
|
688
698
|
order: 999,
|
|
699
|
+
inject: (sessionId) => {
|
|
700
|
+
const commands = ctx.remote === undefined ? undefined : ctx.remote.commands;
|
|
701
|
+
const actx = ctx.sessions === undefined ? undefined : ctx.sessions.scope(sessionId);
|
|
702
|
+
return {
|
|
703
|
+
exitPlanMode: commands === undefined ? undefined : async () => {
|
|
704
|
+
const result = await commands.execute(sessionId, '/plan off', []);
|
|
705
|
+
if (!result.ok) return `${result.error.message} (${result.error.code})`;
|
|
706
|
+
return result.value === undefined ? 'unknown command: /plan off' : null;
|
|
707
|
+
},
|
|
708
|
+
notify: (level, text) => { try { actx.get('conversation').input.for(actx).notify(level, text); } catch { /* notice is best-effort */ } },
|
|
709
|
+
};
|
|
710
|
+
},
|
|
689
711
|
}, PlanToggle));
|
|
690
712
|
ctx.slots.inject('conversation.input.right', () => ctx.slots.register({
|
|
691
713
|
name: 'conversation.input.right',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-composer-plus",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "DeepSeek Harness helpers: Codex-style Changes panel, input history, Shift+Tab /plan, one-click Continue, queue drag-and-drop, Copy deeplink, Auto Review allow-for-chat, background notifications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"@deepseek-ai/dsh-client-ui-workspace",
|
|
41
41
|
"@deepseek-ai/dsh-client-ui-sidebar-right",
|
|
42
42
|
"@deepseek-ai/dsh-client-ui-session",
|
|
43
|
-
"@deepseek-ai/dsh-api-remotes"
|
|
43
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
44
|
+
"@deepseek-ai/dsh-api-session-controller"
|
|
44
45
|
]
|
|
45
46
|
}
|
|
46
47
|
},
|