跳转至

Claude Code右键菜单集成

概述

在 Windows 系统下,在任意文件夹上点击鼠标右键,即可在弹出的上下文菜单中选择对应菜单项,系统会自动打开终端、切换至该文件夹目录,并执行 claude 命令。

目前提供两套并行方案,可以同时安装,菜单中会同时出现两项:

方案 终端 菜单项 注册表键名
方案 A Windows Terminal 在此处用 Windows Terminal 打开 Claude Code ClaudeCodeHere
方案 B Dinotty 在此处用 Dinotty 打开 Claude Code DinottyClaudeHere

两套方案的四个注册表键统一使用同一个 Claude 图标:D:\AI\ClaudeCode\claude-code.ico。

两种方案都覆盖两个右键位置:

右键位置 对应注册表键
文件夹图标上 HKEY_CLASSES_ROOT\Directory\shell
文件夹内空白处 HKEY_CLASSES_ROOT\Directory\Background\shell

只注册前者时,只有「点在文件夹图标上」才会出现菜单;在文件夹内部空白处右键不会出现。两者需要分别注册。

核心实现原理

方案 A 的命令解析:wt.exe -d "%V" claude

  • wt.exe -d <目录>:Windows Terminal 启动时直接以指定目录作为工作目录
  • %V:Windows 传给右键菜单的被点击对象绝对路径
  • claude:Windows Terminal 启动后执行的命令

%V 在两种场景下的取值

场景 %V 的值
文件夹图标右键 该文件夹路径
文件夹内空白处右键 当前文件夹路径

两种场景下 %V 都指向正确的目录,因此 command 无需区分场景,同一行命令即可复用。

不要用 %W 代替 %V:%W 是「工作目录」,在 Directory\shell 场景下取到的是父目录,不是被右键的文件夹本身。Directory\Background 下两者才恰好相同。

安装脚本(Claudeinstall.reg)

当前实际文件内容(UTF-16LE + BOM):

Windows Registry Editor Version 5.00

; ==========================================================
; 在此处打开 Claude Code
;
; 覆盖两种右键位置:
;   1. 文件夹图标上     -> Directory\shell
;   2. 文件夹内空白处   -> Directory\Background\shell
;
; %V 在两种场景下都是被右键那一层的绝对路径,
; 因此 command 无需区分场景。
;
; 修改 HKEY_CLASSES_ROOT 需要管理员权限。
; 卸载请用 uninstall.reg。
; ==========================================================

; ---------- 文件夹图标右键 ----------

[HKEY_CLASSES_ROOT\Directory\shell\ClaudeCodeHere]
@="在此处用 Windows Terminal 打开 Claude Code"
"Icon"="D:\\AI\\ClaudeCode\\claude-code.ico"

[HKEY_CLASSES_ROOT\Directory\shell\ClaudeCodeHere\command]
@="wt.exe -d \"%V\" claude"

; ---------- 文件夹内空白处右键 ----------

[HKEY_CLASSES_ROOT\Directory\Background\shell\ClaudeCodeHere]
@="在此处用 Windows Terminal 打开 Claude Code"
"Icon"="D:\\AI\\ClaudeCode\\claude-code.ico"

[HKEY_CLASSES_ROOT\Directory\Background\shell\ClaudeCodeHere\command]
@="wt.exe -d \"%V\" claude"

注意:.reg 文件中路径里的 \ 是转义字符,需全部写成 \\,否则注册表会解析成乱码。

卸载脚本(uninstall.reg)

Windows Registry Editor Version 5.00

; ==========================================================
; 卸载「在此处打开 Claude Code」右键菜单
;
; 同时移除文件夹图标右键与文件夹内空白处右键两处注册。
; 删除不存在的键不会报错,可重复执行。
;
; 修改 HKEY_CLASSES_ROOT 需要管理员权限。
; ==========================================================

[-HKEY_CLASSES_ROOT\Directory\shell\ClaudeCodeHere]
[-HKEY_CLASSES_ROOT\Directory\Background\shell\ClaudeCodeHere]

文件编码:必须用 UTF-16LE + BOM 或 ANSI

regedit 对 .reg 文件的编码要求:

编码 结果
UTF-16LE + BOM(FF FE) ✅ 推荐,中文菜单名正常
ANSI / GBK ✅ 可用,中文正常
UTF-8 无 BOM ❌ 按 ANSI 解析,中文菜单名变乱码

实测踩坑:先写成 UTF-8 无 BOM,导入后中文菜单名显示为乱码。

当前四个 .reg(Claudeinstall.reg、uninstall.reg、install-dinotty-claude.reg、uninstall-dinotty-claude.reg)均已统一为 UTF-16LE + BOM,文件头是 FF FE。

用 Python 转换为 UTF-16LE + BOM:

1
2
3
4
5
6
with open('Claudeinstall.reg', encoding='utf-8') as f:
    text = f.read()

with open('Claudeinstall.reg', 'wb') as f:
    f.write(b'\xff\xfe')                 # UTF-16LE BOM
    f.write(text.encode('utf-16-le'))

验证 BOM 与内容:

1
2
3
4
5
with open('Claudeinstall.reg', 'rb') as f:
    b = f.read()

print('BOM:', b[:2].hex())               # 期望 fffe
print(b[2:].decode('utf-16-le'))

自定义图标

Icon 只认 .ico、.exe、.dll

Icon 字段不支持 PNG / WebP 等图片格式,直接指向 PNG 会导致菜单不显示图标。

当前四个键(两套方案 × 两个右键位置)统一使用同一个独立图标:

位置 Icon
方案 A:Directory\shell / Directory\Background\shell D:\AI\ClaudeCode\claude-code.ico
方案 B:Directory\shell / Directory\Background\shell D:\AI\ClaudeCode\claude-code.ico

方案 B 的 Icon 刻意不指向 Dinotty-portable.exe,而是指向 Claude 图标——这样两条菜单在视觉上都代表「打开 Claude Code」,与 Dinotty 自带图标区分开。

从 PNG 生成多尺寸 ICO

Windows 在不同 DPI 缩放下会取 ICO 内最接近的尺寸,建议打包多档(16/24/32/48/64/128/256):

1
2
3
4
5
6
from PIL import Image

src = Image.open('claude-logo.png').convert('RGBA')
sizes = [(16, 16), (24, 24), (32, 32), (48, 48),
         (64, 64), (128, 128), (256, 256)]
src.save('claude-code.ico', format='ICO', sizes=sizes)

需要 Pillow。本项目 .venv 中已可用(Pillow 12.x),源图 claude-logo.png 为 2500×2500 RGBA,已生成含 7 档尺寸的 claude-code.ico。

验证已生成 ICO 的尺寸档位:

1
2
3
4
from PIL import Image

print(sorted(Image.open('claude-code.ico').info.get('sizes')))
# [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)]

生效方式

  1. 双击 Claudeinstall.reg(方案 B 则双击 install-dinotty-claude.reg)→ 允许 UAC → 确认合并
  2. 重启资源管理器,让菜单缓存刷新:
taskkill /f /im explorer.exe ; start explorer.exe
  1. 在文件夹图标上 / 文件夹内空白处分别右键验证

两套方案都装好时,右键菜单中应出现两项,且图标相同:

在此处用 Windows Terminal 打开 Claude Code
在此处用 Dinotty 打开 Claude Code

核对注册表实际值

导入后可以用下面的命令核对,避免「脚本改了但注册表还是旧值」。

PowerShell(推荐,HKCR: 路径可直接用):

1
2
3
4
5
6
7
8
9
foreach ($k in @(
  'Registry::HKEY_CLASSES_ROOT\Directory\shell\ClaudeCodeHere',
  'Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\ClaudeCodeHere',
  'Registry::HKEY_CLASSES_ROOT\Directory\shell\DinottyClaudeHere',
  'Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\DinottyClaudeHere'
)) {
  $i = Get-Item -LiteralPath $k
  '{0}`n  name = {1}`n  Icon = {2}' -f $k, $i.GetValue(''), $i.GetValue('Icon')
}

CMD:

1
2
3
4
reg query "HKCR\Directory\shell\ClaudeCodeHere" /s
reg query "HKCR\Directory\Background\shell\ClaudeCodeHere" /s
reg query "HKCR\Directory\shell\DinottyClaudeHere" /s
reg query "HKCR\Directory\Background\shell\DinottyClaudeHere" /s

Git Bash 下不要直接用上面两条:MSYS 会把 HKCR\Directory\... 误当成本地路径转换,导致 ERROR: Invalid syntax。要么改用 cmd.exe /c 'reg query ...',要么加 MSYS_NO_PATHCONV=1 前缀,或干脆切到 PowerShell。

当前本机实测值(2026-10-20):

键 菜单名 Icon
Directory\shell\ClaudeCodeHere 在此处用 Windows Terminal 打开 Claude Code D:\AI\ClaudeCode\claude-code.ico
Directory\Background\shell\ClaudeCodeHere 在此处用 Windows Terminal 打开 Claude Code D:\AI\ClaudeCode\claude-code.ico
Directory\shell\DinottyClaudeHere 在此处用 Dinotty 打开 Claude Code D:\AI\ClaudeCode\claude-code.ico
Directory\Background\shell\DinottyClaudeHere 在此处用 Dinotty 打开 Claude Code D:\AI\ClaudeCode\claude-code.ico

四个键与两套 .reg 脚本内容完全一致,四个键的实际落点均为 HKEY_LOCAL_MACHINE\Software\Classes(见文末注意事项)。

改动过 .reg 脚本后必须重新双击导入,注册表不会自动跟随脚本变化。核对时若发现菜单名或 Icon 与脚本不同,说明注册表里是上一版或手动改过的值,重新导入即可覆盖。

替代方案:Dinotty 终端

如果不想用 Windows Terminal,可以改为在 Dinotty 中打开(实测版本 0.29.0,portable 版)。

为什么不能直接套用 wt.exe -d

Dinotty 的桌面版可执行文件只接受 --background / --server / --port 这类启动参数,没有 -d、--cwd、--command;多余参数会被忽略。并且它是单实例程序,第二次启动只会把已有窗口提到前台,并不能「在指定目录开新标签页」。因此需要「两段式」:

  1. 由 VBS 读取 Dinotty 的本地 token
  2. 调用 Dinotty 的 REST API 新建标签页,指定 cwd 与 argv
1
2
3
4
5
6
7
右键文件夹
  └─ wscript.exe //nologo //B dinotty-claude.vbs "%V"
       └─ 读 %APPDATA%\dinotty\token
            ├─ Dinotty 未运行 → 启动它,轮询端口 + 等前端就绪
            └─ POST http://127.0.0.1:8999/api/tabs
                 {"cwd": "...", "argv": ["claude"], "title": "Claude Code"}
                      └─ 失败则回退:建普通标签页 → 等提示符 → 发送 "claude" + CR

相关接口要点

  • 接口:POST http://127.0.0.1:8999/api/tabs
  • 鉴权:请求头 Authorization: Bearer <token>。实测 0.29.0 在本机回环(127.0.0.1,且在 ip_whitelist 内)下,GET /api/tabs 与 POST /api/tabs 并不强制校验 token;但 /api 根路径仍要求 token(无 token 返回 401 UNAUTHORIZED)。脚本保留 Bearer 头是为了兼容旧版本与会话接口。
  • token 文件:%APPDATA%\dinotty\token(64 位十六进制字符串,无换行)
  • argv 非空时绕过 shell 直接启动进程;这是首选路径,cwd 天然正确,也不会往正在运行的 shell 里注入按键
  • cwd 必须是已存在的目录,否则返回 400 {"error":"cwd must exist and be a directory"}
  • 成功响应包含 tab_id,脚本据此判定成功,而不是只看 HTTP 200
  • 回退路径会用到 POST /api/sessions/:pane_id/input 与 GET /api/sessions/:pane_id/screen,这条路径需要 Dinotty 设置里 open_api.enabled = true
  • 端口绑定 0.0.0.0,但 ip_whitelist 仅放行 127.0.0.1 / ::1

安装脚本(install-dinotty-claude.reg)

Windows Registry Editor Version 5.00

; ==========================================================
; 在此处用 Dinotty 打开 Claude Code
;
; 安装位置:HKEY_CLASSES_ROOT(与 WT 方案一致)
; 注意:修改 HKCR 需要管理员权限,导入时会弹 UAC
;
; 与 WT 方案的差异:
;   WT 方案   wt.exe -d "%V" claude     (wt 支持 -d 参数)
;   本方案    wscript.exe ...vbs "%V"   (Dinotty 不支持 -d,
;                                        需走本地 REST API 建标签页)
;
; 配套脚本:dinotty-claude.vbs(与本文件放在同一目录)
; 卸载:uninstall-dinotty-claude.reg
; ==========================================================

; ---------- 右键点击文件夹 ----------

[HKEY_CLASSES_ROOT\Directory\shell\DinottyClaudeHere]
@="在此处用 Dinotty 打开 Claude Code"
"Icon"="D:\\AI\\ClaudeCode\\claude-code.ico"

[HKEY_CLASSES_ROOT\Directory\shell\DinottyClaudeHere\command]
@="wscript.exe //nologo //B \"D:\\AI\\Dinotty-portable\\dinotty-claude.vbs\" \"%V\""

; ---------- 文件夹内空白处右键 ----------

[HKEY_CLASSES_ROOT\Directory\Background\shell\DinottyClaudeHere]
@="在此处用 Dinotty 打开 Claude Code"
"Icon"="D:\\AI\\ClaudeCode\\claude-code.ico"

[HKEY_CLASSES_ROOT\Directory\Background\shell\DinottyClaudeHere\command]
@="wscript.exe //nologo //B \"D:\\AI\\Dinotty-portable\\dinotty-claude.vbs\" \"%V\""

卸载脚本(uninstall-dinotty-claude.reg)

Windows Registry Editor Version 5.00

; ==========================================================
; 卸载「在此处用 Dinotty 打开 Claude Code」右键菜单
;
; 与 install 对应,只删 HKEY_CLASSES_ROOT 下的两个键。
; 删除不存在的键不会报错,可重复执行。
;
; 修改 HKCR 需要管理员权限,导入时会弹 UAC。
; ==========================================================

[-HKEY_CLASSES_ROOT\Directory\shell\DinottyClaudeHere]
[-HKEY_CLASSES_ROOT\Directory\Background\shell\DinottyClaudeHere]

配套脚本(dinotty-claude.vbs)

文件保持纯 ASCII + LF 换行:VBScript 没有编码声明,非 ASCII 文本除非另存为 ANSI/GBK 或 UTF-16LE + BOM,否则会乱码。

' ============================================================
' Open a folder in Dinotty as a NEW tab and run Claude Code
'
' Usage: wscript.exe //nologo //B dinotty-claude.vbs "C:\path\to\folder"
'
' Why so little code (and why not "wt.exe -d" style)?
'   Dinotty's desktop exe only understands --background / --server /
'   --port; any other argument is ignored, and a second launch hits
'   the single-instance plugin, which just focuses the existing
'   window. There is no CLI for "open this folder in a tab".
'   The supported way is its own HTTP API, so this script talks to
'   POST /api/tabs on the local server.
'
'   With argv:["claude"] Dinotty starts claude directly as the PTY
'   process, so we never type "cd" and never inject keys into a
'   shell - that avoids the old bug of typing into a running agent.
'
' Requires: open_api.enabled = true in Dinotty settings
'           (needed for POST /api/sessions/:pane_id/input, the
'            fallback path only).
'
' NOTE: keep this file ASCII-only. VBScript has no encoding
' declaration, so non-ASCII text garbles unless saved as
' ANSI/GBK or UTF-16LE with BOM.
' ============================================================
Option Explicit

' ---------- configuration ----------
Const DINOTTY_EXE = "D:\AI\Dinotty-portable\Dinotty-portable.exe"
Const DINOTTY_PORT = "8999"
Const CLAUDE_ARGV = "claude"
Const TAB_TITLE = "Claude Code"
Const STARTUP_WAIT_MS = 8000
' -----------------------------------

Dim fso, shell, folder, token, apiBase

Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")

If WScript.Arguments.Count < 1 Then
    WScript.Quit 1
End If

folder = WScript.Arguments(0)

' Trim trailing backslash except for drive roots like C:\
If Len(folder) > 3 And Right(folder, 1) = "\" Then
    folder = Left(folder, Len(folder) - 1)
End If

If Not fso.FolderExists(folder) Then
    MsgBox "Folder not found:" & vbCrLf & folder, 16, "Dinotty Claude"
    WScript.Quit 2
End If

apiBase = "http://127.0.0.1:" & DINOTTY_PORT

' ---------- read the global bearer token ----------
Dim tokenPath
tokenPath = shell.ExpandEnvironmentStrings("%APPDATA%") & "\dinotty\token"

If fso.FileExists(tokenPath) Then
    token = Trim(fso.OpenTextFile(tokenPath, 1).ReadAll())
Else
    If IsPortListening() Then
        MsgBox "Dinotty token not found:" & vbCrLf & tokenPath & vbCrLf & _
               "Start Dinotty once and sign in, then retry.", 16, "Dinotty Claude"
        WScript.Quit 4
    End If
    token = ""
End If

' ---------- start Dinotty if needed ----------
If Not IsPortListening() Then
    If Not fso.FileExists(DINOTTY_EXE) Then
        MsgBox "Dinotty executable not found:" & vbCrLf & DINOTTY_EXE, 16, "Dinotty Claude"
        WScript.Quit 3
    End If
    On Error Resume Next
    shell.Run """" & DINOTTY_EXE & """", 1, False
    On Error GoTo 0

    Dim waited
    waited = 0
    Do While waited < STARTUP_WAIT_MS
        WScript.Sleep 500
        waited = waited + 500
        If IsPortListening() Then Exit Do
    Loop
    If Not IsPortListening() Then
        MsgBox "Dinotty did not start listening on port " & DINOTTY_PORT & ".", 16, "Dinotty Claude"
        WScript.Quit 6
    End If

    ' The server listens well before the frontend has connected. Wait until
    ' the frontend has published its startup tab, so our TabCreated event is
    ' broadcast to a live client.
    If Not WaitForFrontend() Then
        MsgBox "Dinotty started but its window did not become ready.", 16, "Dinotty Claude"
        WScript.Quit 7
    End If
End If

' ---------- create a dedicated tab running claude ----------
Dim resp, paneId

resp = HttpPost(apiBase & "/api/tabs", _
    "{""cwd"":""" & JsonEscape(folder) & """," & _
    """argv"":[""" & CLAUDE_ARGV & """]," & _
    """title"":""" & JsonEscape(TAB_TITLE) & """}")

If InStr(1, resp, """tab_id""", vbTextCompare) > 0 Then
    WScript.Quit 0
End If

' ---------- fallback: plain shell tab, then type claude ----------
resp = HttpPost(apiBase & "/api/tabs", _
    "{""cwd"":""" & JsonEscape(folder) & """," & _
    """title"":""" & JsonEscape(TAB_TITLE) & """}")

paneId = JsonGetString(resp, "pane_id")
If paneId = "" Then
    MsgBox "Could not create a Dinotty tab." & vbCrLf & vbCrLf & resp, 16, "Dinotty Claude"
    WScript.Quit 5
End If

' Let the shell print its prompt. If it never shows up we still send:
' PTY input is buffered until the shell reads it.
WaitForPrompt paneId

' PowerShell/cmd under PSReadLine need CR, not LF, to submit.
If Not SendRaw(paneId, CLAUDE_ARGV & Chr(13)) Then
    MsgBox "Tab created, but writing to its terminal failed.", 16, "Dinotty Claude"
    WScript.Quit 8
End If

WScript.Quit 0


' ============================================================
' helpers
' ============================================================

Function WaitForFrontend()
    Dim i, body
    WaitForFrontend = False
    For i = 1 To 40          ' 40 x 500 ms = 20 s
        WScript.Sleep 500
        body = HttpGet(apiBase & "/api/tabs")
        If InStr(1, body, """tab_id""", vbTextCompare) > 0 Then
            WaitForFrontend = True
            Exit Function
        End If
    Next
End Function

Function WaitForPrompt(paneId)
    Dim i, body, screen
    WaitForPrompt = False
    For i = 1 To 20          ' 20 x 500 ms = 10 s
        WScript.Sleep 500
        body = HttpGet(apiBase & "/api/sessions/" & paneId & "/screen?format=plain")
        screen = JsonGetString(body, "content")
        If InStr(screen, ">") > 0 Or InStr(screen, "$") > 0 Or InStr(screen, "#") > 0 Then
            WaitForPrompt = True
            Exit Function
        End If
    Next
End Function

Function SendRaw(paneId, data)
    Dim resp
    SendRaw = False
    resp = HttpPost(apiBase & "/api/sessions/" & paneId & "/input", _
        "{""data"":""" & JsonEscape(data) & """}")
    If InStr(1, resp, """ok""", vbTextCompare) > 0 Then
        SendRaw = True
    End If
End Function

' ---------- HTTP ----------

Function HttpGet(url)
    Dim http
    HttpGet = ""
    On Error Resume Next
    Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    http.setTimeouts 2000, 2000, 4000, 8000
    http.Open "GET", url, False
    If token <> "" Then http.setRequestHeader "Authorization", "Bearer " & token
    http.Send
    If Err.Number = 0 And http.Status = 200 Then
        HttpGet = http.responseText
    End If
    Err.Clear
    On Error GoTo 0
End Function

Function HttpPost(url, payload)
    Dim http
    HttpPost = ""
    On Error Resume Next
    Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    http.setTimeouts 2000, 2000, 4000, 8000
    http.Open "POST", url, False
    If token <> "" Then http.setRequestHeader "Authorization", "Bearer " & token
    http.setRequestHeader "Content-Type", "application/json; charset=utf-8"
    http.Send payload
    If Err.Number = 0 Then
        HttpPost = http.responseText
    End If
    Err.Clear
    On Error GoTo 0
End Function

Function IsPortListening()
    Dim probe
    IsPortListening = False
    On Error Resume Next
    Set probe = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    probe.setTimeouts 500, 500, 500, 500
    probe.Open "GET", apiBase & "/api/tabs", False
    If token <> "" Then probe.setRequestHeader "Authorization", "Bearer " & token
    probe.Send
    If Err.Number = 0 Then IsPortListening = True
    Err.Clear
    On Error GoTo 0
End Function

' ---------- minimal JSON helpers ----------

Function JsonEscape(s)
    Dim r
    r = Replace(s, "\", "\\")
    r = Replace(r, """", "\""")
    r = Replace(r, Chr(27), "\u001B")
    r = Replace(r, vbCr, "\r")
    r = Replace(r, vbLf, "\n")
    r = Replace(r, vbTab, "\t")
    JsonEscape = r
End Function

Function JsonGetString(json, key)
    Dim needle, p, startPos, endPos
    JsonGetString = ""
    needle = """" & key & """"
    p = InStr(1, json, needle, vbTextCompare)
    If p = 0 Then Exit Function
    p = InStr(p + Len(needle), json, ":")
    If p = 0 Then Exit Function
    p = InStr(p, json, """")
    If p = 0 Then Exit Function
    startPos = p + 1
    endPos = startPos
    Do While endPos <= Len(json)
        If Mid(json, endPos, 1) = "\" Then
            endPos = endPos + 2
        ElseIf Mid(json, endPos, 1) = """" Then
            Exit Do
        Else
            endPos = endPos + 1
        End If
    Loop
    JsonGetString = Mid(json, startPos, endPos - startPos)
End Function

退出码说明

退出码 含义
0 成功
1 没传路径参数
2 路径不存在
3 找不到 Dinotty exe
4 Dinotty 已在运行,但找不到 token 文件
5 建标签页失败(响应里既无 tab_id 也无 pane_id)
6 启动 Dinotty 后 8 秒内端口未监听
7 Dinotty 已监听,但 20 秒内前端未就绪
8 标签页建好了,但向终端写入 claude 失败

启动与等待逻辑

脚本对「Dinotty 没开」的处理比早期版本复杂,是因为踩过两个坑:

  1. 固定 sleep 不够:机器快慢不同,固定等 6 秒可能不够或浪费。现在改为每 500 ms 轮询 GET /api/tabs,最多等 STARTUP_WAIT_MS(8 秒)。
  2. 端口就绪 ≠ 窗口就绪:后端服务先监听,前端界面要晚一些才连上并发布启动标签页。如果此时就 POST /api/tabs,TabCreated 事件会广播给一个还没连上的客户端,看起来像「没反应」。因此再加一层 WaitForFrontend(),每 500 ms 查一次标签页列表,最多等 20 秒。

已知取舍:token 只在启动 Dinotty 之前读一次。首次运行且 token 文件尚未生成时,本次调用以空 token 继续;当前 0.29.0 在回环地址下不强制 token,因此仍能成功创建标签页。

使用顺序

  1. 先手动测 VBS(不碰注册表):
wscript.exe //nologo "D:\AI\Dinotty-portable\dinotty-claude.vbs" "D:\AI\AI_Project"
  1. 确认 Dinotty 中出现了 Claude Code 标签页
  2. 双击 install-dinotty-claude.reg → 允许 UAC
  3. 右键任意文件夹验证

关于杀毒软件拦截

电脑管家等软件会拦截这个 VBS,因为它同时具备几个典型可疑特征:

触发特征 实际用途
CreateObject("WScript.Shell") 启动 Dinotty 进程
MSXML2.ServerXMLHTTP 调用本地 API
读取 AppData 下的文件 读取 Dinotty token
VBScript + 注册表关联 右键菜单入口

实际行为边界:

  • 只访问 127.0.0.1,不联网外发
  • 只读 token,不写任何文件
  • 不修改注册表(注册表由 .reg 文件操作)
  • 无混淆、无提权,源码可完整审计

可自行核验:

1
2
3
4
5
6
7
8
# 应只出现 http://127.0.0.1:8999 相关拼接
Select-String -Path "D:\AI\Dinotty-portable\dinotty-claude.vbs" -Pattern "http"

# 应无输出(无写文件操作)
Select-String -Path "D:\AI\Dinotty-portable\dinotty-claude.vbs" -Pattern "Write|CreateTextFile|DeleteFile"

# 应只有一处 shell.Run
Select-String -Path "D:\AI\Dinotty-portable\dinotty-claude.vbs" -Pattern "\.Run|\.Exec"

安全提醒:该脚本持有 Dinotty 的全局 Bearer Token,拥有者可以将 argv 换成任意命令。当前硬编码为 ["claude"],请勿让该文件处于可被他人随意改写的位置,并定期核对内容是否被篡改。

两套方案对比

维度 方案 A:Windows Terminal 方案 B:Dinotty
启动方式 直接 wt.exe -d VBS + 本地 REST API
是否需要管理员权限 导入注册表时需要 导入注册表时需要
首次启动等待 无(wt 自行处理) 轮询端口 + 等前端就绪,最多约 28 秒
失败排查 看 wt 是否在 PATH 看退出码 1–8
依赖 Windows Terminal、claude 在 PATH Dinotty 运行中、token 文件
安全软件误报 无 有,需手动放行

注意事项

  • 修改 HKEY_CLASSES_ROOT 需要系统管理员权限(会弹 UAC)
  • HKCR 是 HKLM\Software\Classes 与 HKCU\Software\Classes 的合并视图;以管理员身份导入时实际写入 HKLM,因此该菜单对所有用户生效(本机实测四个键均在 HKEY_LOCAL_MACHINE\Software\Classes 下)
  • 确保 claude 命令已添加至系统环境变量 PATH 中,否则 WT 打开后提示找不到命令。本机实测 where claude → D:\AI\ClaudeCode\claude.exe,PATH 中已包含 D:\AI\ClaudeCode
  • 请勿在右键菜单工具的安装目录中运行 claude 命令,应在业务项目文件夹中使用
  • 桌面空白处不会出现这些菜单:桌面右键读取的是 HKEY_CLASSES_ROOT\DesktopBackground\Shell,与 Directory\Background\shell 是两个不同的键。若想在桌面右键也加入菜单,需要单独注册到 DesktopBackground\Shell(实测本机该键下没有 Claude 相关项)
  • 导入后若菜单没有立即出现,重启资源管理器:taskkill /f /im explorer.exe ; start explorer.exe
  • 两套方案可同时安装,菜单会同时出现两项;若不想要其中一项,用对应的 uninstall-*.reg 单独卸载即可