@norman-else/dsh-claude 0.1.53 → 0.1.54

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,120 +1,120 @@
1
- # Installation and removal runbook
2
-
3
- Current guidance: 2026-09-14, plugin 0.1.51, development graph DSH 0.1.5-rc.2.
4
-
5
- ## 1. Identify the running installation
6
-
7
- Use the DSH CLI supplied by the installation you intend to modify. Determine
8
- its active profile before installing: the local Desktop setup uses `desktop`;
9
- a standalone Web setup may use `web`. Do not create another profile or server
10
- to compensate for a command aimed at the wrong one. No port is assumed here.
11
-
12
- Examples below use `desktop`; substitute the actual profile throughout.
13
- If `dsh` is not on PATH, use that installation's CLI entry point instead.
14
-
15
- ## 2. Install from npm
16
-
17
- ```sh
18
- dsh plugin --profile desktop add @norman-else/dsh-claude
19
- ```
20
-
21
- Wait for the profile operation to finish, then quit Desktop completely and
22
- reopen it. Select **Claude** in a new session. The plugin maintains a guarded
23
- compatibility preset at `$DSH_HOME/.agent-presets/claude`; its route uses the
24
- active profile package source. User-modified preset files are preserved.
25
-
26
- ## 3. Install from a source checkout
27
-
28
- Requires Node.js 20 or later and pnpm. From the checkout:
29
-
30
- ```sh
31
- pnpm install --frozen-lockfile
32
- pnpm check
33
- ```
34
-
35
- On macOS, prepend `/opt/homebrew/bin` to PATH if that is where pnpm is installed.
36
- PowerShell does not use the POSIX `PATH=... command` syntax.
37
-
38
- If this checkout is already linked to a running profile, finish active turns
39
- before rebuilding it. A build can trigger a live client reload and interrupt
40
- projection subscriptions. During active work, validate in a separate source
41
- copy with its own dependencies; do not copy its build into the live checkout.
42
- After a deliberate live rebuild, fully restart Desktop to verify activation.
43
-
44
- Link from PowerShell:
45
-
46
- ```powershell
47
- dsh plugin --profile desktop add "link:$($PWD.Path.Replace('\', '/'))"
48
- ```
49
-
50
- Or from a POSIX shell:
51
-
52
- ```sh
53
- dsh plugin --profile desktop add "link:$(pwd)"
54
- ```
55
-
56
- ## 4. Check the local Claude CLI
57
-
58
- Claude Code owns authentication. Use its existing login or supported settings
59
- configuration; never request, copy, print, or store credentials in this plugin.
60
-
61
- ```sh
62
- node lib/bin.mjs doctor
63
- ```
64
-
65
- If discovery fails, use `doctor --executable` with the absolute native Claude
66
- executable path. The standalone CLI's path search is simpler than the Host
67
- resolver; on Windows prefer the native executable over an npm `.cmd` shim.
68
-
69
- Read each report field. A zero exit code from the standalone Doctor establishes
70
- version detection, not authenticated query success; its handshake is `not-run`.
71
- An `unknown` authentication report is inconclusive. Use Settings/Doctor inside
72
- DSH and a minimal real turn to verify the installed environment.
73
-
74
- Main turns, session titles, and branch summaries load `user`, `project`, and
75
- `local` settings. Summary calls also inherit behavior settings and use their
76
- own working directory (`process.cwd()`), not a borrowed session process.
77
-
78
- ## 5. Smoke test after a full Desktop restart
79
-
80
- 1. Confirm **Claude** appears in the new-session preset picker.
81
- 2. Verify an existing native preset still works.
82
- 3. Send a minimal read-only Claude prompt; confirm streaming, final output, and title.
83
- 4. Under an access mode that requires approval, exercise deny and allow-once
84
- with a harmless temporary edit. Full access intentionally bypasses ordinary approvals.
85
- 5. Test a user question and plan review; both must remain available under Full access.
86
- 6. Stop a running turn, then send another prompt; confirm cleanup and continuity.
87
- 7. Restart Desktop and continue the same conversation to verify resume.
88
- 8. Open diff, plan, tasks, and overview tabs in the right sidebar; exercise
89
- fullscreen and close. Check command completion, prompt controls, and queue UI.
90
- 9. In a disposable repository, verify worktree creation and naming. Preserve
91
- needed changes before deleting its workspace: managed-worktree reconciliation
92
- can remove dirty files. Merged-branch cleanup is a separate guarded action.
93
- 10. Read the new Host log for `dsh-claude client [boot-check]`,
94
- `[slot-entry-crashed]`, and plugin refresh failures.
95
-
96
- On the current Windows Desktop, Host logs are under
97
- `%APPDATA%/DSH Desktop/logs/host/`; shell startup logs are one directory above.
98
- Record what was actually exercised. A source audit or green unit tests do not
99
- establish successful real authentication, approval, streaming, or resume.
100
-
101
- ## 6. Remove the plugin
102
-
103
- Clean up the compatibility preset while the profile still resolves the package:
104
-
105
- ```sh
106
- dsh plugin --profile desktop exec dsh-claude remove-preset
107
- dsh plugin --profile desktop remove @norman-else/dsh-claude
108
- ```
109
-
110
- DSH has no plugin uninstall lifecycle hook. Direct package removal can leave
111
- the compatibility preset behind. If necessary, invoke the matching version's
112
- CLI with the same `DSH_HOME` and inspect its result:
113
-
114
- ```sh
115
- pnpm dlx @norman-else/dsh-claude@<installed-version> remove-preset
116
- ```
117
-
118
- Cleanup refuses user-modified presets; review them manually rather than forcing
119
- removal. Restart Desktop after uninstalling. Removing the plugin does not mean
120
- all Claude transcripts, sidecars, settings, or worktrees have been deleted.
1
+ # Installation and removal runbook
2
+
3
+ Current guidance: 2026-09-14, plugin 0.1.51, development graph DSH 0.1.5-rc.2.
4
+
5
+ ## 1. Identify the running installation
6
+
7
+ Use the DSH CLI supplied by the installation you intend to modify. Determine
8
+ its active profile before installing: the local Desktop setup uses `desktop`;
9
+ a standalone Web setup may use `web`. Do not create another profile or server
10
+ to compensate for a command aimed at the wrong one. No port is assumed here.
11
+
12
+ Examples below use `desktop`; substitute the actual profile throughout.
13
+ If `dsh` is not on PATH, use that installation's CLI entry point instead.
14
+
15
+ ## 2. Install from npm
16
+
17
+ ```sh
18
+ dsh plugin --profile desktop add @norman-else/dsh-claude
19
+ ```
20
+
21
+ Wait for the profile operation to finish, then quit Desktop completely and
22
+ reopen it. Select **Claude** in a new session. The plugin maintains a guarded
23
+ compatibility preset at `$DSH_HOME/.agent-presets/claude`; its route uses the
24
+ active profile package source. User-modified preset files are preserved.
25
+
26
+ ## 3. Install from a source checkout
27
+
28
+ Requires Node.js 20 or later and pnpm. From the checkout:
29
+
30
+ ```sh
31
+ pnpm install --frozen-lockfile
32
+ pnpm check
33
+ ```
34
+
35
+ On macOS, prepend `/opt/homebrew/bin` to PATH if that is where pnpm is installed.
36
+ PowerShell does not use the POSIX `PATH=... command` syntax.
37
+
38
+ If this checkout is already linked to a running profile, finish active turns
39
+ before rebuilding it. A build can trigger a live client reload and interrupt
40
+ projection subscriptions. During active work, validate in a separate source
41
+ copy with its own dependencies; do not copy its build into the live checkout.
42
+ After a deliberate live rebuild, fully restart Desktop to verify activation.
43
+
44
+ Link from PowerShell:
45
+
46
+ ```powershell
47
+ dsh plugin --profile desktop add "link:$($PWD.Path.Replace('\', '/'))"
48
+ ```
49
+
50
+ Or from a POSIX shell:
51
+
52
+ ```sh
53
+ dsh plugin --profile desktop add "link:$(pwd)"
54
+ ```
55
+
56
+ ## 4. Check the local Claude CLI
57
+
58
+ Claude Code owns authentication. Use its existing login or supported settings
59
+ configuration; never request, copy, print, or store credentials in this plugin.
60
+
61
+ ```sh
62
+ node lib/bin.mjs doctor
63
+ ```
64
+
65
+ If discovery fails, use `doctor --executable` with the absolute native Claude
66
+ executable path. The standalone CLI's path search is simpler than the Host
67
+ resolver; on Windows prefer the native executable over an npm `.cmd` shim.
68
+
69
+ Read each report field. A zero exit code from the standalone Doctor establishes
70
+ version detection, not authenticated query success; its handshake is `not-run`.
71
+ An `unknown` authentication report is inconclusive. Use Settings/Doctor inside
72
+ DSH and a minimal real turn to verify the installed environment.
73
+
74
+ Main turns, session titles, and branch summaries load `user`, `project`, and
75
+ `local` settings. Summary calls also inherit behavior settings and use their
76
+ own working directory (`process.cwd()`), not a borrowed session process.
77
+
78
+ ## 5. Smoke test after a full Desktop restart
79
+
80
+ 1. Confirm **Claude** appears in the new-session preset picker.
81
+ 2. Verify an existing native preset still works.
82
+ 3. Send a minimal read-only Claude prompt; confirm streaming, final output, and title.
83
+ 4. Under an access mode that requires approval, exercise deny and allow-once
84
+ with a harmless temporary edit. Full access intentionally bypasses ordinary approvals.
85
+ 5. Test a user question and plan review; both must remain available under Full access.
86
+ 6. Stop a running turn, then send another prompt; confirm cleanup and continuity.
87
+ 7. Restart Desktop and continue the same conversation to verify resume.
88
+ 8. Open diff, plan, tasks, and overview tabs in the right sidebar; exercise
89
+ fullscreen and close. Check command completion, prompt controls, and queue UI.
90
+ 9. In a disposable repository, verify worktree creation and naming. Preserve
91
+ needed changes before deleting its workspace: managed-worktree reconciliation
92
+ can remove dirty files. Merged-branch cleanup is a separate guarded action.
93
+ 10. Read the new Host log for `dsh-claude client [boot-check]`,
94
+ `[slot-entry-crashed]`, and plugin refresh failures.
95
+
96
+ On the current Windows Desktop, Host logs are under
97
+ `%APPDATA%/DSH Desktop/logs/host/`; shell startup logs are one directory above.
98
+ Record what was actually exercised. A source audit or green unit tests do not
99
+ establish successful real authentication, approval, streaming, or resume.
100
+
101
+ ## 6. Remove the plugin
102
+
103
+ Clean up the compatibility preset while the profile still resolves the package:
104
+
105
+ ```sh
106
+ dsh plugin --profile desktop exec dsh-claude remove-preset
107
+ dsh plugin --profile desktop remove @norman-else/dsh-claude
108
+ ```
109
+
110
+ DSH has no plugin uninstall lifecycle hook. Direct package removal can leave
111
+ the compatibility preset behind. If necessary, invoke the matching version's
112
+ CLI with the same `DSH_HOME` and inspect its result:
113
+
114
+ ```sh
115
+ pnpm dlx @norman-else/dsh-claude@<installed-version> remove-preset
116
+ ```
117
+
118
+ Cleanup refuses user-modified presets; review them manually rather than forcing
119
+ removal. Restart Desktop after uninstalling. Removing the plugin does not mean
120
+ all Claude transcripts, sidecars, settings, or worktrees have been deleted.
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/README.md CHANGED
@@ -76,6 +76,7 @@ Preset cleanup removes only installer-managed content and refuses to delete user
76
76
  - **Local Claude environment compatibility** — Preserves the user's existing Claude Code authentication, settings, `CLAUDE.md`, Skills, Hooks, Plugins, tools, and MCP configuration.
77
77
  - **Real-time streaming and conversation continuity** — Streams Claude responses and tool activity into DSH while retaining multi-turn context and persisted Claude session resume.
78
78
  - **DSH permissions and questions** — Routes Claude tool permission requests through DSH approvals and Claude clarification prompts through DSH's native question forms.
79
+ - **Claude Code permission modes** — Behind a **Permission selector** setting (Plugin, or DSH native to keep the Host's three-mode control and the plain sandbox mapping), replaces DSH's three-mode access selector, for Claude sessions only, with Claude Code's own modes in the same seat of the composer's tool row: Plan, Default, Accept edits, Don't ask, Auto, and Bypass permissions, each with its own glyph, bilingual name, and description; Auto is greyed out while the catalog says the session's model cannot run it, and an ask its classifier escalates reaches the DSH approval dialog with the classifier's reason under the title. The choice is the plugin's own per-session record and is what the next turn runs under; the DSH sandbox mode and approval policy are moved onto the preset that mode is carried on, so approvals and the sandbox follow without the Host learning any new modes. Bypass asks for an acknowledged confirmation first, the control locks while a turn is running, and a sandbox switched natively afterwards (the `/permission` command) wins over the recorded choice, so DSH's own knobs are never contradicted. The Host's selector is hidden with a scoped CSS rule rather than replaced, since it is not a slot; a Host that renames it simply shows both. A new Claude session starts in the **Default permission mode** from this plugin's Settings, Auto unless changed: the DSH access preset is moved onto the sandbox that mode needs as the session is created, so a Host whose own default preset is full access does not turn it into Bypass.
79
80
  - **Plan review** — Under read-only access Claude works in plan mode, and the plan it hands back is read in a plugin-owned panel in a right-sidebar tab, rendered as Markdown under the prose palette chosen in this plugin's settings. The approval itself stays with DSH: its dialog names the decision and points at the panel instead of pasting the plan into a plain-text modal, and it is asked even under Full access — that setting waives actions, not the decision the plan was written to put in front of the user. Full access silences approvals outright (`approval/policy: never`, answered before any surface is shown), so the ask is un-silenced for as long as the plan is open and the session's own policy is put back afterwards. Approving and rejecting are the only answers the dialog has, and neither is "change this", so the panel adds the third: select a passage, write what should change, and send the plan back — the notes reach Claude as the reason it was refused, and the dialog closes itself. A session that proposes more than once keeps every plan: the panel heading becomes a picker listing each with its own decision, and a fresh proposal awaiting approval takes the panel back. The Session header carries a plan toggle, dotted while a plan is still waiting on its decision, and the turn's tool card carries the same plan.
80
81
  - **Claude command bridge** — Publishes Claude Code's own command catalog into the DSH command palette, retrying with backoff while a fresh CLI finishes loading Skills and Plugins.
81
82
  - **Prompt snippets** — Keeps the half-written messages a user retypes every day as ordinary Markdown files in `~/.claude/prompts`, one per file, and offers them as a second `/` group beside the Claude command catalog. Picking one drops its text into the composer instead of sending it, so the draft stays editable — a snippet is a message the user finishes, not a command that runs. A control in the composer's own tool row, beside the attach and access controls, saves the current draft as a new snippet: it offers the draft's opening line as the file name and names the file it wrote. It costs the layout nothing — a docked row would move the composer on every keystroke — and an existing name is never overwritten. The derived name is only the starting point: a Claude Haiku call names the snippet properly and replaces it when it lands, unless the user has already started typing, and a failed or slow suggestion is a non-event because a working name was there from the first frame. That call runs with extended thinking off, which is what keeps it around three seconds rather than ten.
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-Cj637ucf.mjs";
2
+ import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-3CuT5Lrc.mjs";
3
3
  import { delimiter, isAbsolute, join } from "node:path";
4
4
  import { access } from "node:fs/promises";
5
5
  import { constants } from "node:fs";
package/lib/bin.mjs.map CHANGED
@@ -1 +1 @@
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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"}
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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"}
package/lib/client.d.ts CHANGED
@@ -46,6 +46,12 @@ declare const en: {
46
46
  readonly globalSettingsNewSession: "Changes apply only to new Claude sessions.";
47
47
  readonly globalSettingsError: "Global settings save failed";
48
48
  readonly outputStyle: "Output Style";
49
+ readonly permissionSelector: "Permission selector";
50
+ readonly permissionSelectorPlugin: "Plugin (Claude Code permission modes)";
51
+ readonly permissionSelectorNative: "DSH native (three access modes)";
52
+ readonly permissionSelectorEffect: "Plugin: the composer shows this plugin's selector with every Claude Code permission mode, and new sessions start in the default mode below. DSH native: the Host's own read-only / workspace-write / full-access selector stays, and the mode follows the sandbox alone (read-only is Plan, workspace-write is Accept edits, full access is Bypass); per-session choices and the default mode are not consulted. Takes effect immediately; a running turn keeps its mode.";
53
+ readonly defaultPermissionMode: "Default permission mode";
54
+ readonly defaultPermissionModeEffect: "The Claude Code permission mode a new Claude session starts in; DSH's access preset is moved onto the sandbox that mode needs as the session is created. A session you have since changed with the composer's own selector runs its own choice. A sandbox switched natively (/permission) wins over the mode, and auto yields to Default on a model that cannot run it. Changes apply to sessions created from then on.";
49
55
  readonly renderer: "AI output renderer";
50
56
  readonly rendererPlugin: "Plugin renderer";
51
57
  readonly rendererNative: "DSH native renderer";
@@ -226,6 +232,30 @@ declare const en: {
226
232
  readonly promptSaved: "Saved as \"{name}\"";
227
233
  readonly promptSaveExists: "A prompt with that name already exists. Try another.";
228
234
  readonly promptSaveFailed: "Could not save: {message}";
235
+ readonly permissionMode: "Permission mode";
236
+ readonly permissionModeAria: "Permission mode: {name}";
237
+ readonly permissionModePlan: "Plan";
238
+ readonly permissionModePlanHint: "Read-only exploration; the plan comes back for your approval before anything runs";
239
+ readonly permissionModeDefault: "Default";
240
+ readonly permissionModeDefaultHint: "Every file edit and command asks first";
241
+ readonly permissionModeAcceptEdits: "Accept edits";
242
+ readonly permissionModeAcceptEditsHint: "File edits go through on their own; commands still ask";
243
+ readonly permissionModeDontAsk: "Don't ask";
244
+ readonly permissionModeDontAskHint: "Anything not pre-approved is refused instead of asked about";
245
+ readonly permissionModeAuto: "Auto";
246
+ readonly permissionModeAutoHint: "Claude Code's classifier approves on your behalf and asks you only when unsure";
247
+ readonly permissionModeAutoUnsupported: "The current model does not support auto mode";
248
+ readonly permissionModeBypass: "Bypass permissions";
249
+ readonly permissionModeBypassHint: "Skip every permission check";
250
+ readonly permissionModeLocked: "The permission mode cannot change while a turn is running";
251
+ readonly permissionModeFailed: "Could not switch the permission mode: {message}";
252
+ readonly permissionModeHostKept: "DSH kept its own access preset; Claude still runs the chosen mode";
253
+ readonly permissionBypassTitle: "Skip every permission check?";
254
+ readonly permissionBypassDescription: "Claude Code will run file edits and commands without asking for approval.";
255
+ readonly permissionBypassAcknowledge: "I understand the risk";
256
+ readonly permissionBypassCancel: "Cancel";
257
+ readonly permissionBypassClose: "Close";
258
+ readonly permissionBypassConfirm: "Enable";
229
259
  readonly presetHeaderHint: "The agent preset this session runs";
230
260
  readonly diffRepository: "Repository";
231
261
  readonly repositoryLinked: "Linked repository: changed by this session";