@norman-else/dsh-claude 0.1.36 → 0.1.37

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/INSTALL.md CHANGED
@@ -1,55 +1,55 @@
1
- # Agent Installation Runbook
2
-
3
- This runbook is idempotent for a local `dsh-claude` checkout and an existing DSH `web` profile.
4
-
5
- ## 1. Verify the checkout
6
-
7
- ```sh
8
- cd /path/to/dsh-claude
9
- export PATH="/opt/homebrew/bin:$PATH"
10
- pnpm install
11
- pnpm check
12
- node lib/bin.mjs doctor
13
- ```
14
-
15
- Stop if Doctor cannot find an authenticated local Claude Code installation. Do not request, copy, or write credentials.
16
-
17
- ## 2. Link the bundle into the current Web profile
18
-
19
- The bundle keeps a protected compatibility preset at `$DSH_HOME/.agent-presets/claude`, because DSH Desktop 2.0.4 does not retain third-party preset roots from bundle patches. Its route uses the active profile package source to avoid duplicate client-module Loaders. User-modified preset files are preserved.
20
-
21
- ```sh
22
- dsh plugin --profile web add "link:$(pwd)"
23
- ```
24
-
25
- Do not start another DSH Web server. This bundle must load in the existing app at `http://127.0.0.1:56454`.
26
-
27
- ## 3. Refresh and smoke test
28
-
29
- 1. Refresh the existing DSH Web page.
30
- 2. Confirm **Claude** appears in the new-session Agent Preset picker.
31
- 3. Run one existing native preset prompt and verify it remains native.
32
- 4. Create a Claude session and send a read-only prompt.
33
- 5. Send one edit prompt; verify DSH displays a permission request. Exercise reject first, then allow once with a harmless temporary file.
34
- 6. Cancel a running prompt and confirm the cancelled session owns no child `claude` process that is still executing cancelled work or left orphaned/unowned.
35
- 7. Refresh the browser and continue the session.
36
- 8. Restart DSH and confirm the next prompt resumes the persisted Claude session.
37
-
38
- A live prompt can consume the user's Claude subscription. Keep it minimal.
39
-
40
- ## 4. Uninstall
41
-
42
- Clean up the managed preset while the profile can still execute the package CLI, then remove the plugin:
43
-
44
- ```sh
45
- dsh plugin --profile web exec dsh-claude remove-preset
46
- dsh plugin --profile web remove @norman-else/dsh-claude
47
- ```
48
-
49
- DSH has no plugin uninstall lifecycle hook, so direct package removal can leave the compatibility preset behind. After direct removal, no source checkout is required; run the matching package version:
50
-
51
- ```sh
52
- pnpm dlx @norman-else/dsh-claude@<version> remove-preset
53
- ```
54
-
55
- Cleanup refuses to delete user-modified preset content.
1
+ # Agent Installation Runbook
2
+
3
+ This runbook is idempotent for a local `dsh-claude` checkout and an existing DSH `web` profile.
4
+
5
+ ## 1. Verify the checkout
6
+
7
+ ```sh
8
+ cd /path/to/dsh-claude
9
+ export PATH="/opt/homebrew/bin:$PATH"
10
+ pnpm install
11
+ pnpm check
12
+ node lib/bin.mjs doctor
13
+ ```
14
+
15
+ Stop if Doctor cannot find an authenticated local Claude Code installation. Do not request, copy, or write credentials.
16
+
17
+ ## 2. Link the bundle into the current Web profile
18
+
19
+ The bundle keeps a protected compatibility preset at `$DSH_HOME/.agent-presets/claude`, because DSH Desktop 2.0.4 does not retain third-party preset roots from bundle patches. Its route uses the active profile package source to avoid duplicate client-module Loaders. User-modified preset files are preserved.
20
+
21
+ ```sh
22
+ dsh plugin --profile web add "link:$(pwd)"
23
+ ```
24
+
25
+ Do not start another DSH Web server. This bundle must load in the existing app at `http://127.0.0.1:56454`.
26
+
27
+ ## 3. Refresh and smoke test
28
+
29
+ 1. Refresh the existing DSH Web page.
30
+ 2. Confirm **Claude** appears in the new-session Agent Preset picker.
31
+ 3. Run one existing native preset prompt and verify it remains native.
32
+ 4. Create a Claude session and send a read-only prompt.
33
+ 5. Send one edit prompt; verify DSH displays a permission request. Exercise reject first, then allow once with a harmless temporary file.
34
+ 6. Cancel a running prompt and confirm the cancelled session owns no child `claude` process that is still executing cancelled work or left orphaned/unowned.
35
+ 7. Refresh the browser and continue the session.
36
+ 8. Restart DSH and confirm the next prompt resumes the persisted Claude session.
37
+
38
+ A live prompt can consume the user's Claude subscription. Keep it minimal.
39
+
40
+ ## 4. Uninstall
41
+
42
+ Clean up the managed preset while the profile can still execute the package CLI, then remove the plugin:
43
+
44
+ ```sh
45
+ dsh plugin --profile web exec dsh-claude remove-preset
46
+ dsh plugin --profile web remove @norman-else/dsh-claude
47
+ ```
48
+
49
+ DSH has no plugin uninstall lifecycle hook, so direct package removal can leave the compatibility preset behind. After direct removal, no source checkout is required; run the matching package version:
50
+
51
+ ```sh
52
+ pnpm dlx @norman-else/dsh-claude@<version> remove-preset
53
+ ```
54
+
55
+ Cleanup refuses to delete user-modified preset content.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 dsh-claude contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-claude contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/cordis.patch.yml CHANGED
@@ -1,12 +1,12 @@
1
- # dsh-claude host bundle. The preset stays inside this package as a system
2
- # preset, so removing the profile dependency removes the complete integration.
3
- - id: agent-presets
4
- config:
5
- default: standard
6
- roots:
7
- - path: !!js "process.getBuiltinModule('node:path').join(process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(ctx.baseUrl).resolve('@norman-else/dsh-claude/package.json')), 'preset')"
8
- trust: system
9
-
10
- - insert:
11
- - id: llm-claude
12
- name: '@norman-else/dsh-claude'
1
+ # dsh-claude host bundle. The preset stays inside this package as a system
2
+ # preset, so removing the profile dependency removes the complete integration.
3
+ - id: agent-presets
4
+ config:
5
+ default: standard
6
+ roots:
7
+ - path: !!js "process.getBuiltinModule('node:path').join(process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(ctx.baseUrl).resolve('@norman-else/dsh-claude/package.json')), 'preset')"
8
+ trust: system
9
+
10
+ - insert:
11
+ - id: llm-claude
12
+ name: '@norman-else/dsh-claude'
@@ -1,11 +1,11 @@
1
- # Shipped by dsh-claude. Claude Code owns the inner agent loop and tools;
2
- # this system preset contributes only the request-route override. DSH resolves
3
- # the bare package specifier from the host profile when mounting system roots.
4
- - id: claude-code-route
5
- name: '@norman-else/dsh-claude/preset-route'
6
- # Entry-local isolate realm: each session's preset mount gets its own
7
- # claudeCommands service instance (keeps mountPreset's leaked-service check
8
- # happy and prevents cross-session collisions). The host reads it through
9
- # dsh-agent-presets' serviceForAgent().
10
- isolate:
11
- claudeCommands: true
1
+ # Shipped by dsh-claude. Claude Code owns the inner agent loop and tools;
2
+ # this system preset contributes only the request-route override. DSH resolves
3
+ # the bare package specifier from the host profile when mounting system roots.
4
+ - id: claude-code-route
5
+ name: '@norman-else/dsh-claude/preset-route'
6
+ # Entry-local isolate realm: each session's preset mount gets its own
7
+ # claudeCommands service instance (keeps mountPreset's leaked-service check
8
+ # happy and prevents cross-session collisions). The host reads it through
9
+ # dsh-agent-presets' serviceForAgent().
10
+ isolate:
11
+ claudeCommands: true
@@ -1,4 +1,4 @@
1
- # Shipped by dsh-claude. Copy this system preset under a new id to customize it.
2
- name: Claude
3
- description: Use the local Claude Code as the complete agent runtime inside DSH.
4
- order: 10
1
+ # Shipped by dsh-claude. Copy this system preset under a new id to customize it.
2
+ name: Claude
3
+ description: Use the local Claude Code as the complete agent runtime inside DSH.
4
+ order: 10
package/lib/bin.mjs CHANGED
File without changes
package/lib/bin.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.mjs","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { access } from 'node:fs/promises'\r\nimport { constants } from 'node:fs'\r\nimport { homedir } from 'node:os'\r\nimport { delimiter, isAbsolute, join } from 'node:path'\r\nimport { spawnSync } from 'node:child_process'\r\nimport { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess'\r\nimport { parseClaudeVersion } from './executable.ts'\r\nimport { ensureManagedPreset, removeManagedPreset } from './preset-installer.ts'\r\n\r\nfunction option(name: string): string | undefined {\r\n const index = process.argv.indexOf(name)\r\n return index < 0 ? undefined : process.argv[index + 1]\r\n}\r\n\r\nasync function executable(configured?: string): Promise<string | undefined> {\r\n const candidates: string[] = []\r\n if (configured !== undefined) candidates.push(configured)\r\n else {\r\n for (const directory of (process.env.PATH ?? '').split(delimiter)) {\r\n if (directory.length > 0) candidates.push(join(directory, 'claude'))\r\n }\r\n candidates.push(join(homedir(), '.local', 'bin', 'claude'), '/opt/homebrew/bin/claude', '/usr/local/bin/claude')\r\n }\r\n for (const candidate of [...new Set(candidates)]) {\r\n if (!isAbsolute(candidate)) continue\r\n try {\r\n await access(candidate, constants.X_OK)\r\n return candidate\r\n } catch {\r\n // Continue to the next documented candidate.\r\n }\r\n }\r\n return undefined\r\n}\r\n\r\nfunction run(executablePath: string, args: string[]) {\r\n return spawnSync(executablePath, args, {\r\n encoding: 'utf8',\r\n env: scrubbedParentEnv(),\r\n timeout: 15_000,\r\n windowsHide: true,\r\n })\r\n}\r\n\r\nasync function doctor(): Promise<number> {\r\n const configured = option('--executable') ?? process.env.DSH_CLAUDE_CODE_EXECUTABLE\r\n const path = await executable(configured)\r\n if (path === undefined) {\r\n process.stdout.write(`${JSON.stringify({\r\n executable: { status: 'missing', searched: configured === undefined ? ['claude', '~/.local/bin/claude', '/opt/homebrew/bin/claude', '/usr/local/bin/claude'] : [configured] },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n message: 'Set executablePath to an absolute Claude Code CLI path',\r\n }, null, 2)}\\n`)\r\n return 1\r\n }\r\n const versionProbe = run(path, ['--version'])\r\n const version = parseClaudeVersion(`${versionProbe.stdout ?? ''}\\n${versionProbe.stderr ?? ''}`)\r\n const authProbe = run(path, ['auth', 'status', '--json'])\r\n let authentication: Record<string, unknown> = { status: 'unknown' }\r\n if (authProbe.status === 0) {\r\n try {\r\n const value = JSON.parse(authProbe.stdout) as Record<string, unknown>\r\n authentication = {\r\n status: value.loggedIn === true ? 'signed-in' : 'signed-out',\r\n ...(typeof value.authMethod === 'string' ? { method: value.authMethod } : {}),\r\n ...(typeof value.apiProvider === 'string' ? { provider: value.apiProvider } : {}),\r\n ...(typeof value.subscriptionType === 'string' ? { subscription: value.subscriptionType } : {}),\r\n }\r\n } catch {\r\n authentication = { status: 'unknown', message: 'Authentication status was not valid JSON' }\r\n }\r\n }\r\n process.stdout.write(`${JSON.stringify({\r\n executable: { status: 'found', path },\r\n version: version === undefined ? { status: 'error' } : { status: 'ok', value: version },\r\n authentication,\r\n handshake: 'not-run',\r\n }, null, 2)}\\n`)\r\n return version === undefined ? 1 : 0\r\n}\r\n\r\nasync function main(): Promise<number> {\r\n const command = process.argv[2] ?? 'doctor'\r\n if (command === 'doctor') return doctor()\r\n if (command === 'install-preset') {\r\n process.stdout.write(`${await ensureManagedPreset()}\\n`)\r\n return 0\r\n }\r\n if (command === 'remove-preset') {\r\n process.stdout.write(`${await removeManagedPreset()}\\n`)\r\n return 0\r\n }\r\n process.stderr.write(`Usage: dsh-claude [doctor [--executable PATH] | install-preset | remove-preset]\\n`)\r\n return 2\r\n}\r\n\r\ntry {\r\n process.exitCode = await main()\r\n} catch (error) {\r\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\r\n process.exitCode = 1\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"bin.mjs","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { access } from 'node:fs/promises'\nimport { constants } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, isAbsolute, join } from 'node:path'\nimport { spawnSync } from 'node:child_process'\nimport { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess'\nimport { parseClaudeVersion } from './executable.ts'\nimport { ensureManagedPreset, removeManagedPreset } from './preset-installer.ts'\n\nfunction option(name: string): string | undefined {\n const index = process.argv.indexOf(name)\n return index < 0 ? undefined : process.argv[index + 1]\n}\n\nasync function executable(configured?: string): Promise<string | undefined> {\n const candidates: string[] = []\n if (configured !== undefined) candidates.push(configured)\n else {\n for (const directory of (process.env.PATH ?? '').split(delimiter)) {\n if (directory.length > 0) candidates.push(join(directory, 'claude'))\n }\n candidates.push(join(homedir(), '.local', 'bin', 'claude'), '/opt/homebrew/bin/claude', '/usr/local/bin/claude')\n }\n for (const candidate of [...new Set(candidates)]) {\n if (!isAbsolute(candidate)) continue\n try {\n await access(candidate, constants.X_OK)\n return candidate\n } catch {\n // Continue to the next documented candidate.\n }\n }\n return undefined\n}\n\nfunction run(executablePath: string, args: string[]) {\n return spawnSync(executablePath, args, {\n encoding: 'utf8',\n env: scrubbedParentEnv(),\n timeout: 15_000,\n windowsHide: true,\n })\n}\n\nasync function doctor(): Promise<number> {\n const configured = option('--executable') ?? process.env.DSH_CLAUDE_CODE_EXECUTABLE\n const path = await executable(configured)\n if (path === undefined) {\n process.stdout.write(`${JSON.stringify({\n executable: { status: 'missing', searched: configured === undefined ? ['claude', '~/.local/bin/claude', '/opt/homebrew/bin/claude', '/usr/local/bin/claude'] : [configured] },\n version: { status: 'not-run' },\n authentication: { status: 'not-run' },\n message: 'Set executablePath to an absolute Claude Code CLI path',\n }, null, 2)}\\n`)\n return 1\n }\n const versionProbe = run(path, ['--version'])\n const version = parseClaudeVersion(`${versionProbe.stdout ?? ''}\\n${versionProbe.stderr ?? ''}`)\n const authProbe = run(path, ['auth', 'status', '--json'])\n let authentication: Record<string, unknown> = { status: 'unknown' }\n if (authProbe.status === 0) {\n try {\n const value = JSON.parse(authProbe.stdout) as Record<string, unknown>\n authentication = {\n status: value.loggedIn === true ? 'signed-in' : 'signed-out',\n ...(typeof value.authMethod === 'string' ? { method: value.authMethod } : {}),\n ...(typeof value.apiProvider === 'string' ? { provider: value.apiProvider } : {}),\n ...(typeof value.subscriptionType === 'string' ? { subscription: value.subscriptionType } : {}),\n }\n } catch {\n authentication = { status: 'unknown', message: 'Authentication status was not valid JSON' }\n }\n }\n process.stdout.write(`${JSON.stringify({\n executable: { status: 'found', path },\n version: version === undefined ? { status: 'error' } : { status: 'ok', value: version },\n authentication,\n handshake: 'not-run',\n }, null, 2)}\\n`)\n return version === undefined ? 1 : 0\n}\n\nasync function main(): Promise<number> {\n const command = process.argv[2] ?? 'doctor'\n if (command === 'doctor') return doctor()\n if (command === 'install-preset') {\n process.stdout.write(`${await ensureManagedPreset()}\\n`)\n return 0\n }\n if (command === 'remove-preset') {\n process.stdout.write(`${await removeManagedPreset()}\\n`)\n return 0\n }\n process.stderr.write(`Usage: dsh-claude [doctor [--executable PATH] | install-preset | remove-preset]\\n`)\n return 2\n}\n\ntry {\n process.exitCode = await main()\n} catch (error) {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exitCode = 1\n}\n"],"mappings":"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"}
package/lib/client.d.ts CHANGED
@@ -148,6 +148,7 @@ declare const en: {
148
148
  readonly repositoryProgressDismiss: "Dismiss Worktree progress";
149
149
  readonly repositoryProgress_inspecting: "Checking repository and branch";
150
150
  readonly repositoryProgress_fetching: "Refreshing remote references";
151
+ readonly repositoryProgress_summarizing: "Naming the branch after the request";
151
152
  readonly 'repositoryProgress_creating-worktree': "Creating branch and Worktree";
152
153
  readonly 'repositoryProgress_saving-worktree': "Saving Worktree state";
153
154
  readonly 'repositoryProgress_switching-branch': "Switching local branch";
@@ -205,6 +206,9 @@ declare const en: {
205
206
  readonly diffCommentNext: "Next comment";
206
207
  readonly diffCommentPosition: "Comment {index} of {total}";
207
208
  readonly diffCommentCounter: "{index}/{total}";
209
+ readonly diffCardExpand: "Show {count} more lines";
210
+ readonly diffCardCollapse: "Show less";
211
+ readonly diffCardFiles: "{count} files";
208
212
  readonly diffExpandAll: "Expand all files";
209
213
  readonly diffCollapseAll: "Collapse all files";
210
214
  readonly diffRestore: "Restore diff panel";
package/lib/client.js CHANGED
@@ -3630,7 +3630,7 @@ window.__ModuleLoader__.load({
3630
3630
  `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE}{`,
3631
3631
  palette(PIERRE_DARK, "#1a1a19", "#1a1a19", INLINE_FILL_DARK),
3632
3632
  "}",
3633
- `.${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY_EMPHASIZED};border-radius:4px;padding:1px 2px}`,
3633
+ `.${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY_EMPHASIZED};border-radius:4px;padding:1px 2px;font-size:1.05em!important}`,
3634
3634
  `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY}}`,
3635
3635
  `.${CLAUDE_MARKDOWN_SCOPE} [class*="bannerWrap"]{position:absolute;top:0;right:0;z-index:7;background:transparent;border-radius:0}`,
3636
3636
  `.${CLAUDE_MARKDOWN_SCOPE} [class*="banner"]:not([class*="bannerWrap"]){background:transparent;padding:6px 8px}`,
@@ -4578,6 +4578,7 @@ window.__ModuleLoader__.load({
4578
4578
  ".dsh-claude-transcript-text{color:var(--dsw-alias-label-primary);font-size:15px;line-height:24px;overflow-wrap:anywhere}",
4579
4579
  ".dsh-claude-tool-group-native{overflow:visible}",
4580
4580
  ".dsh-claude-tool-group-native>.dsh-claude-flow-row{padding:0}",
4581
+ ".dsh-claude-flow-row .dsh-claude-tool-stats{margin-left:8px}",
4581
4582
  ".dsh-claude-tool-list{max-height:min(420px,calc(100vh - 320px));overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;margin-top:4px;border:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent));border-radius:10px;scrollbar-gutter:stable}",
4582
4583
  ".dsh-claude-tool-item{border-top:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
4583
4584
  ".dsh-claude-tool-item:first-child{border-top:0}",
@@ -4816,13 +4817,36 @@ window.__ModuleLoader__.load({
4816
4817
  if (!Array.isArray(value)) return [];
4817
4818
  return value.filter((item) => typeof item === "string");
4818
4819
  }
4820
+ /** The diff card's chrome, which primitives 0.1.2 moved onto the caller.
4821
+ *
4822
+ * The card reads `labels.copy` while it builds, so omitting them throws
4823
+ * mid-render -- and React answers by tearing down the whole conversation, not
4824
+ * the one tool row.
4825
+ *
4826
+ * The aria strings deliberately repeat the visible ones: both already say
4827
+ * exactly what the control does. */
4828
+ function diffBlockLabels(t) {
4829
+ const expand = (hidden) => t("diffCardExpand", { count: hidden });
4830
+ return {
4831
+ copy: t("markdownCopy"),
4832
+ copied: t("markdownCopied"),
4833
+ collapse: t("diffCardCollapse"),
4834
+ collapseAria: t("diffCardCollapse"),
4835
+ expand,
4836
+ expandAria: expand,
4837
+ files: (count) => t("diffCardFiles", { count })
4838
+ };
4839
+ }
4819
4840
  function ToolPresentation({ tool, t }) {
4820
4841
  const inputValue = parsedValue(tool.input);
4821
4842
  const outputValue = parsedValue(tool.output);
4822
4843
  const input = record$6(inputValue);
4823
4844
  const output = record$6(outputValue);
4824
4845
  const outputTitle = tool.isError === true ? t("toolError") : t("toolOutput");
4825
- if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, { diffs: [...tool.diffs] }), /* @__PURE__ */ jsx(TextDetail, {
4846
+ if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, {
4847
+ diffs: [...tool.diffs],
4848
+ labels: diffBlockLabels(t)
4849
+ }), /* @__PURE__ */ jsx(TextDetail, {
4826
4850
  title: outputTitle,
4827
4851
  value: typeof outputValue === "string" ? outputValue : void 0
4828
4852
  })] });
@@ -6555,10 +6579,14 @@ window.__ModuleLoader__.load({
6555
6579
  const HOST_STAGES = /* @__PURE__ */ new Set([
6556
6580
  "inspecting",
6557
6581
  "fetching",
6582
+ "summarizing",
6558
6583
  "creating-worktree",
6559
6584
  "saving-worktree",
6560
6585
  "switching-branch"
6561
6586
  ]);
6587
+ /** The draft only has to describe the work; the host truncates it again before
6588
+ * summarizing, and the setup route caps the whole body at 16 KiB. */
6589
+ const MAX_INTENT_CHARS = 2e3;
6562
6590
  function record$3(value) {
6563
6591
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
6564
6592
  }
@@ -6594,7 +6622,7 @@ window.__ModuleLoader__.load({
6594
6622
  throw error;
6595
6623
  }
6596
6624
  }
6597
- async function prepareRepository(cwd, branch, worktree, branchName, onProgress = () => {}) {
6625
+ async function prepareRepository(cwd, branch, worktree, branchName, onProgress = () => {}, intent) {
6598
6626
  const carrier = new AbortController();
6599
6627
  try {
6600
6628
  const reader = await pluginNdjson(CLAUDE_REPOSITORY_SETUP_PATH, carrier.signal, {
@@ -6603,7 +6631,8 @@ window.__ModuleLoader__.load({
6603
6631
  cwd,
6604
6632
  branch,
6605
6633
  worktree,
6606
- ...branchName === void 0 ? {} : { branchName }
6634
+ ...branchName === void 0 ? {} : { branchName },
6635
+ ...intent === void 0 || intent.trim().length === 0 ? {} : { intent: intent.slice(0, MAX_INTENT_CHARS) }
6607
6636
  }
6608
6637
  });
6609
6638
  const decoder = new TextDecoder();
@@ -6892,10 +6921,40 @@ window.__ModuleLoader__.load({
6892
6921
  const CLAUDE_SCOPED_CSS_VARIABLES = ["--dsh-composer-card-max-width"];
6893
6922
  /** Marks the bar the scoped-property probe measures. */
6894
6923
  const CLAUDE_COMPOSER_BAR_ATTRIBUTE = "data-dsh-claude-composer-bar";
6895
- /** One line per service the Host no longer provides; empty when all resolve. */
6896
- function claudeBootCheckFindings(input) {
6924
+ /** Methods this plugin calls on the Host services it injects.
6925
+ *
6926
+ * A service that still resolves but has lost a method is the drift the
6927
+ * service list above cannot see: Desktop 0.1.2 moved `connectWorkspace` off
6928
+ * `workspaces` onto a new service, and the worktree flow went on registering
6929
+ * itself and only broke once a user ran it. Naming the methods here turns
6930
+ * that into a boot-time line. Methods with a runtime fallback stay out. */
6931
+ const CLAUDE_REQUIRED_SERVICE_METHODS = {
6932
+ sessions: [
6933
+ "scope",
6934
+ "open",
6935
+ "binding"
6936
+ ],
6937
+ workspaces: [
6938
+ "create",
6939
+ "delete",
6940
+ "archiveSession"
6941
+ ],
6942
+ uiConversation: ["binding"],
6943
+ uiSession: ["provide"],
6944
+ inputTriggers: ["registerSource"]
6945
+ };
6946
+ /** One line per service the Host no longer provides, or provides without a
6947
+ * method this plugin calls on it; empty when everything resolves. */
6948
+ function claudeBootCheckFindings(input, methods = CLAUDE_REQUIRED_SERVICE_METHODS) {
6897
6949
  const findings = [];
6898
- for (const name of input.services) if (input.resolve(name) === void 0) findings.push(`service "${name}" is declared in inject but the Host does not provide it`);
6950
+ for (const name of input.services) {
6951
+ const service = input.resolve(name);
6952
+ if (service === void 0) {
6953
+ findings.push(`service "${name}" is declared in inject but the Host does not provide it`);
6954
+ continue;
6955
+ }
6956
+ for (const method of methods[name] ?? []) if (typeof service[method] !== "function") findings.push(`service "${name}" no longer provides ${method}(); the features calling it are broken`);
6957
+ }
6899
6958
  return findings;
6900
6959
  }
6901
6960
  /** One line per scoped custom property the measured element cannot see.
@@ -7828,9 +7887,15 @@ window.__ModuleLoader__.load({
7828
7887
  style: repositoryRemote,
7829
7888
  children: repositoryName$1(repository.remote)
7830
7889
  }),
7831
- /* @__PURE__ */ jsx("span", {
7832
- style: repositoryBranch,
7833
- children: branch
7890
+ /* @__PURE__ */ jsx(Tooltip, {
7891
+ label: branch,
7892
+ side: "top",
7893
+ delayMs: 250,
7894
+ maxWidth: 420,
7895
+ children: /* @__PURE__ */ jsx("span", {
7896
+ style: repositoryBranch,
7897
+ children: branch
7898
+ })
7834
7899
  }),
7835
7900
  repository.worktree === true ? /* @__PURE__ */ jsx("span", {
7836
7901
  style: repositoryWorktree,
@@ -8022,7 +8087,9 @@ window.__ModuleLoader__.load({
8022
8087
  children: comment.text
8023
8088
  })]
8024
8089
  }),
8025
- openDelayMs: 250
8090
+ openDelayMs: 250,
8091
+ copyLabel: t("markdownCopy"),
8092
+ copiedLabel: t("markdownCopied")
8026
8093
  });
8027
8094
  }
8028
8095
  /** Pending review comments docked above the composer; drained by the next user turn. */
@@ -14091,7 +14158,7 @@ window.__ModuleLoader__.load({
14091
14158
  * @returns the preset id, or undefined when neither source carries one.
14092
14159
  */
14093
14160
  function sessionRowPreset(row) {
14094
- return row?.agentPreset ?? row?.projectionValues?.agentPreset;
14161
+ return row?.agentPreset ?? row?.projectionValues?.agentPreset ?? void 0;
14095
14162
  }
14096
14163
  //#endregion
14097
14164
  //#region src/client/ClaudePullRequestsPanel.tsx
@@ -15322,10 +15389,15 @@ window.__ModuleLoader__.load({
15322
15389
  }
15323
15390
  //#endregion
15324
15391
  //#region src/client/ClaudeHeroRepositoryControls.tsx
15392
+ /** The host composer was a textarea through rc.8 and is a Lexical
15393
+ * contenteditable from 0.1.2 on, so match the composer seat rather than the
15394
+ * element type: an element-type check silently stops intercepting Enter, and
15395
+ * the capsule's branch and Worktree choices go nowhere. */
15325
15396
  function shouldInterceptKey(event) {
15326
15397
  if (event.key !== "Enter" || event.shiftKey || event.repeat || event.isComposing) return false;
15327
15398
  const target = event.target;
15328
- return target instanceof HTMLTextAreaElement && target.closest("[data-phase=\"hero\"]") !== null;
15399
+ if (!(target instanceof Element)) return false;
15400
+ return target.closest("textarea, [data-composer-input]") !== null && target.closest("[data-phase=\"hero\"]") !== null;
15329
15401
  }
15330
15402
  function shouldInterceptClick(event) {
15331
15403
  const target = event.target;
@@ -15361,6 +15433,7 @@ window.__ModuleLoader__.load({
15361
15433
  const WORKTREE_PROGRESS_STAGES = [
15362
15434
  "inspecting",
15363
15435
  "fetching",
15436
+ "summarizing",
15364
15437
  "creating-worktree",
15365
15438
  "saving-worktree",
15366
15439
  "creating-workspace",
@@ -15371,6 +15444,7 @@ window.__ModuleLoader__.load({
15371
15444
  const PROGRESS_LABEL_KEYS = {
15372
15445
  inspecting: "repositoryProgress_inspecting",
15373
15446
  fetching: "repositoryProgress_fetching",
15447
+ summarizing: "repositoryProgress_summarizing",
15374
15448
  "creating-worktree": "repositoryProgress_creating-worktree",
15375
15449
  "saving-worktree": "repositoryProgress_saving-worktree",
15376
15450
  "switching-branch": "repositoryProgress_switching-branch",
@@ -17050,6 +17124,7 @@ window.__ModuleLoader__.load({
17050
17124
  repositoryProgressDismiss: "关闭 Worktree 进度",
17051
17125
  repositoryProgress_inspecting: "检查仓库和分支",
17052
17126
  repositoryProgress_fetching: "刷新远程引用",
17127
+ repositoryProgress_summarizing: "总结需求生成分支名",
17053
17128
  "repositoryProgress_creating-worktree": "创建分支和 Worktree",
17054
17129
  "repositoryProgress_saving-worktree": "保存 Worktree 状态",
17055
17130
  "repositoryProgress_switching-branch": "切换本地分支",
@@ -17107,6 +17182,9 @@ window.__ModuleLoader__.load({
17107
17182
  diffCommentNext: "下一条评论",
17108
17183
  diffCommentPosition: "第 {index} 条,共 {total} 条",
17109
17184
  diffCommentCounter: "{index}/{total}",
17185
+ diffCardExpand: "展开其余 {count} 行",
17186
+ diffCardCollapse: "收起",
17187
+ diffCardFiles: "{count} 个文件",
17110
17188
  diffExpandAll: "展开全部文件",
17111
17189
  diffCollapseAll: "收起全部文件",
17112
17190
  diffRestore: "还原 Diff 面板",
@@ -17423,6 +17501,7 @@ window.__ModuleLoader__.load({
17423
17501
  repositoryProgressDismiss: "Dismiss Worktree progress",
17424
17502
  repositoryProgress_inspecting: "Checking repository and branch",
17425
17503
  repositoryProgress_fetching: "Refreshing remote references",
17504
+ repositoryProgress_summarizing: "Naming the branch after the request",
17426
17505
  "repositoryProgress_creating-worktree": "Creating branch and Worktree",
17427
17506
  "repositoryProgress_saving-worktree": "Saving Worktree state",
17428
17507
  "repositoryProgress_switching-branch": "Switching local branch",
@@ -17480,6 +17559,9 @@ window.__ModuleLoader__.load({
17480
17559
  diffCommentNext: "Next comment",
17481
17560
  diffCommentPosition: "Comment {index} of {total}",
17482
17561
  diffCommentCounter: "{index}/{total}",
17562
+ diffCardExpand: "Show {count} more lines",
17563
+ diffCardCollapse: "Show less",
17564
+ diffCardFiles: "{count} files",
17483
17565
  diffExpandAll: "Expand all files",
17484
17566
  diffCollapseAll: "Collapse all files",
17485
17567
  diffRestore: "Restore diff panel",
@@ -17680,6 +17762,17 @@ window.__ModuleLoader__.load({
17680
17762
  "conversation",
17681
17763
  "connection"
17682
17764
  ];
17765
+ /** Resolve one session's composer facade.
17766
+ *
17767
+ * `sessions.scope()` returns the client runtime's AgentContext, and the
17768
+ * conversation package -- a release ahead of the runtime in the graph the Host
17769
+ * itself ships -- types `input.for` against a Context whose `remote` has since
17770
+ * gained `$stream` and `$host`. It is the same object at runtime; only the two
17771
+ * declarations disagree, and they disagree inside the Host too, so the cast
17772
+ * lives here once instead of at all nine call sites. */
17773
+ function sessionInput(conversation, scope) {
17774
+ return conversation.input.for(scope);
17775
+ }
17683
17776
  function apply(ctx) {
17684
17777
  const namespace = "settings.claude-code";
17685
17778
  const diagnostics = createClaudeDiagnosticsReporter();
@@ -17705,6 +17798,8 @@ window.__ModuleLoader__.load({
17705
17798
  ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(ctx, projections)), "dsh-claude: Claude slash source");
17706
17799
  const sessions = ctx.get("sessions");
17707
17800
  const workspaces = ctx.get("workspaces");
17801
+ const uiWorkspace = ctx.get("uiWorkspace");
17802
+ const connectWorkspace = uiWorkspace?.connectWorkspace?.bind(uiWorkspace) ?? (typeof workspaces?.connectWorkspace === "function" ? workspaces.connectWorkspace.bind(workspaces) : void 0);
17708
17803
  const conversation = ctx.get("conversation");
17709
17804
  const connection = ctx.get("connection");
17710
17805
  const remote = ctx.get("remote");
@@ -17716,7 +17811,7 @@ window.__ModuleLoader__.load({
17716
17811
  return (draft, mode = "append") => {
17717
17812
  const scope = sessions.scope(sessionId);
17718
17813
  if (scope === void 0) return false;
17719
- const input = conversation.input.for(scope);
17814
+ const input = sessionInput(conversation, scope);
17720
17815
  const current = input.state.getSnapshot().draft;
17721
17816
  if (current.trim() === "") input.setDraft(draft);
17722
17817
  else if (mode === "append") input.setDraft(`${current}\n\n${draft}`);
@@ -17962,7 +18057,7 @@ window.__ModuleLoader__.load({
17962
18057
  ...sessions === void 0 || conversation === void 0 ? {} : { submitWith: (fallbackDraft) => {
17963
18058
  const scope = sessions.scope(sessionId);
17964
18059
  if (scope === void 0) return;
17965
- const input = conversation.input.for(scope);
18060
+ const input = sessionInput(conversation, scope);
17966
18061
  if (input.state.getSnapshot().draft.trim() === "") input.setDraft(fallbackDraft);
17967
18062
  input.submit();
17968
18063
  } }
@@ -18000,7 +18095,7 @@ window.__ModuleLoader__.load({
18000
18095
  ...sessions === void 0 || conversation === void 0 ? {} : { insertIntoChat: (sessionId, text) => {
18001
18096
  const scope = sessions.scope(sessionId);
18002
18097
  if (scope === void 0) return;
18003
- const input = conversation.input.for(scope);
18098
+ const input = sessionInput(conversation, scope);
18004
18099
  const current = input.state.getSnapshot().draft;
18005
18100
  input.setDraft(current.trim() === "" ? text : `${current}\n\n${text}`);
18006
18101
  } }
@@ -18062,7 +18157,7 @@ window.__ModuleLoader__.load({
18062
18157
  ...conversation === void 0 ? {} : { setDraft: (sessionId, text) => {
18063
18158
  const scope = sessions.scope(sessionId);
18064
18159
  if (scope === void 0) return;
18065
- conversation.input.for(scope).setDraft(text);
18160
+ sessionInput(conversation, scope).setDraft(text);
18066
18161
  } }
18067
18162
  };
18068
18163
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
@@ -18084,12 +18179,12 @@ window.__ModuleLoader__.load({
18084
18179
  t,
18085
18180
  updateQueue: (itemId, action) => target.updateQueue(itemId, action),
18086
18181
  notify: (level, text) => {
18087
- if (scope !== void 0) conversation.input.for(scope).notify(level, text);
18182
+ if (scope !== void 0) sessionInput(conversation, scope).notify(level, text);
18088
18183
  }
18089
18184
  };
18090
18185
  }
18091
18186
  }, ClaudeQueueDock));
18092
- if (sessions !== void 0 && workspaces !== void 0 && conversation !== void 0 && connection !== void 0 && remote !== void 0) {
18187
+ if (sessions !== void 0 && workspaces !== void 0 && connectWorkspace !== void 0 && conversation !== void 0 && connection !== void 0 && remote !== void 0) {
18093
18188
  /** Attach a prepared worktree to its session without blocking the flow. */
18094
18189
  const bindLease = (leaseId, targetSessionId) => {
18095
18190
  if (leaseId === void 0) return;
@@ -18107,11 +18202,11 @@ window.__ModuleLoader__.load({
18107
18202
  prepare: async (cwd, branch, useWorktree, onProgress, ticket) => {
18108
18203
  const sourceScope = sessions.scope(sourceSessionId);
18109
18204
  if (sourceScope === void 0) throw new Error(t("repositorySessionUnavailable"));
18110
- const sourceInput = conversation.input.for(sourceScope);
18205
+ const sourceInput = sessionInput(conversation, sourceScope);
18111
18206
  const rawDraft = sourceInput.state.getSnapshot().draft;
18112
18207
  const draft = ticket === void 0 ? rawDraft : rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`;
18113
18208
  const imageIds = sourceInput.state.getSnapshot().imageIds;
18114
- const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress);
18209
+ const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress, ticket === void 0 ? draft : void 0);
18115
18210
  if (ticket !== void 0) assignJiraTicket(ticket.key).catch((reason) => {
18116
18211
  console.warn(`dsh-claude: could not assign ${ticket.key}: ${reason instanceof Error ? reason.message : String(reason)}`);
18117
18212
  });
@@ -18123,13 +18218,13 @@ window.__ModuleLoader__.load({
18123
18218
  onProgress("creating-workspace");
18124
18219
  const workspace = await workspaces.create({ path: prepared.path });
18125
18220
  onProgress("starting-session");
18126
- const targetSessionId = await workspaces.connectWorkspace(workspace.workspaceId);
18221
+ const targetSessionId = await connectWorkspace(workspace.workspaceId);
18127
18222
  const targetScope = sessions.scope(targetSessionId);
18128
18223
  if (targetScope === void 0) throw new Error(t("repositorySessionUnavailable"));
18129
18224
  const presetResponse = await remote.agentPresets.select(targetSessionId, "claude");
18130
18225
  if (!presetResponse.ok) throw new Error(presetResponse.error.message);
18131
- sessions.noteAgentPreset(targetSessionId, presetResponse.value);
18132
- const targetInput = conversation.input.for(targetScope);
18226
+ sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
18227
+ const targetInput = sessionInput(conversation, targetScope);
18133
18228
  onProgress("transferring-draft");
18134
18229
  if (imageIds.length > 0 && !targetInput.addImages(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
18135
18230
  if (draft !== "") targetInput.setDraft(draft);
@@ -18143,7 +18238,7 @@ window.__ModuleLoader__.load({
18143
18238
  prepareMany: async (cwd, branch, tickets, onProgress) => {
18144
18239
  const sourceScope = sessions.scope(sourceSessionId);
18145
18240
  if (sourceScope === void 0) throw new Error(t("repositorySessionUnavailable"));
18146
- const sourceInput = conversation.input.for(sourceScope);
18241
+ const sourceInput = sessionInput(conversation, sourceScope);
18147
18242
  const rawDraft = sourceInput.state.getSnapshot().draft;
18148
18243
  const failures = [];
18149
18244
  for (const [index, ticket] of tickets.entries()) {
@@ -18164,13 +18259,13 @@ window.__ModuleLoader__.load({
18164
18259
  report("creating-workspace");
18165
18260
  const workspace = await workspaces.create({ path: prepared.path });
18166
18261
  report("starting-session");
18167
- const targetSessionId = await workspaces.connectWorkspace(workspace.workspaceId);
18262
+ const targetSessionId = await connectWorkspace(workspace.workspaceId);
18168
18263
  const targetScope = sessions.scope(targetSessionId);
18169
18264
  if (targetScope === void 0) throw new Error(t("repositorySessionUnavailable"));
18170
18265
  const presetResponse = await remote.agentPresets.select(targetSessionId, "claude");
18171
18266
  if (!presetResponse.ok) throw new Error(presetResponse.error.message);
18172
- sessions.noteAgentPreset(targetSessionId, presetResponse.value);
18173
- const targetInput = conversation.input.for(targetScope);
18267
+ sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
18268
+ const targetInput = sessionInput(conversation, targetScope);
18174
18269
  report("transferring-draft");
18175
18270
  targetInput.setDraft(rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`);
18176
18271
  bindLease(prepared.leaseId, targetSessionId);