bookmark-research 0.5.0-beta.17 → 0.5.0-beta.19

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.
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "bookmark-research",
12
- "version": "0.5.0-beta.17",
12
+ "version": "0.5.0-beta.19",
13
13
  "registry": "https://registry.npmjs.org"
14
14
  },
15
15
  "policy": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.17",
3
+ "version": "0.5.0-beta.19",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "author": {
6
6
  "name": "kwenxu"
package/README.md CHANGED
@@ -3,15 +3,12 @@
3
3
  **English** · [中文](README.zh.md)
4
4
 
5
5
  [![GitHub Bookmark-Canvas](https://img.shields.io/badge/GitHub-Bookmark--Canvas-181717?logo=github&logoColor=white)](https://github.com/Browser-bookmark-hub/Bookmark-Canvas)
6
- [![npm version](https://img.shields.io/npm/v/bookmark-research/beta?logo=npm)](https://www.npmjs.com/package/bookmark-research)
6
+ [![npm version](https://img.shields.io/npm/v/bookmark-research?logo=npm)](https://www.npmjs.com/package/bookmark-research)
7
7
 
8
8
  Research with bookmark context and retain reusable knowledge. Read a [Bookmark Canvas](https://github.com/Browser-bookmark-hub/Bookmark-Canvas) package, investigate with the host model or a research API, and save sourced Wiki pages. Works in **Codex, Claude Code and DeepSeek Harness (dsh)**.
9
9
 
10
10
  [Installation guide](docs/installation.en.md) · [User guide](docs/user-guide.en.md) · [Details](docs/details.en.md) · [npm](https://www.npmjs.com/package/bookmark-research)
11
11
 
12
- > [!NOTE]
13
- > **0.5.0 is a prerelease**, published on npm's `beta` tag; the `latest` tag still points at an older build. Report problems on [GitHub Issues](https://github.com/Browser-bookmark-hub/Bookmark-Research/issues).
14
-
15
12
  ## How it works
16
13
 
17
14
  - **Local understanding.** A Bookmark Canvas package is read as structure, not a flat URL list: card and group layout, edges with their direction and labels, and the same bookmark appearing across cards (spatial context); dated fields, protocol-defined date segments in IDs, and Git commits that changed content (temporal context). These queries run offline.
@@ -28,10 +25,10 @@ Research with bookmark context and retain reusable knowledge. Read a [Bookmark C
28
25
  ## Install
29
26
 
30
27
  ```sh
31
- npx bookmark-research@beta install
28
+ npx bookmark-research install
32
29
  ```
33
30
 
34
- `@beta` follows the newest prerelease; pin one build with `@0.5.0-beta.17`. Add `--lang zh` for the Chinese wizard. For a permanent command, run `npm install -g bookmark-research`; the GitHub convenience script uses the same npm release (macOS/Linux/WSL):
31
+ Installs the latest version directly. Add `--lang zh` for the Chinese wizard. For a permanent command, run `npm install -g bookmark-research`; the GitHub convenience script uses the same npm release (macOS/Linux/WSL):
35
32
 
36
33
  ```sh
37
34
  curl -fsSL https://raw.githubusercontent.com/Browser-bookmark-hub/Bookmark-Research/main/install.sh | bash
@@ -54,11 +51,11 @@ Enter keeps the highlighted value, so a full run can be just a few keystrokes. T
54
51
 
55
52
  | Client | Command | Registers with |
56
53
  | --- | --- | --- |
57
- | Codex | `npx bookmark-research@beta install codex --non-interactive` | `codex plugin add` |
58
- | Claude Code | `npx bookmark-research@beta install claude --scope user --non-interactive` | `claude plugin install` |
59
- | DeepSeek Harness (dsh) | `npx bookmark-research@beta install dsh --profile web --non-interactive` | `dsh plugin add` |
54
+ | Codex | `npx bookmark-research install codex --non-interactive` | `codex plugin add` |
55
+ | Claude Code | `npx bookmark-research install claude --scope user --non-interactive` | `claude plugin install` |
56
+ | DeepSeek Harness (dsh) | `npx bookmark-research install dsh --profile web --non-interactive` | `dsh plugin add` |
60
57
 
61
- Several at once: `npx bookmark-research@beta install claude dsh --profile web --non-interactive`. Project scope: `--scope project --project /path/to/project` (Claude Code). All options: [installation guide](docs/installation.en.md).
58
+ Several at once: `npx bookmark-research install claude dsh --profile web --non-interactive`. Project scope: `--scope project --project /path/to/project` (Claude Code). All options: [installation guide](docs/installation.en.md).
62
59
 
63
60
  **Ask your agent to install it.** Paste this into Codex, Claude Code or DeepSeek Harness (dsh):
64
61
 
@@ -66,7 +63,7 @@ Several at once: `npx bookmark-research@beta install claude dsh --profile web --
66
63
  Install the Bookmark Research plugin into the client you are running in.
67
64
 
68
65
  1. Identify your own client: codex, claude or dsh.
69
- 2. Run: npx bookmark-research@beta install <client> --non-interactive
66
+ 2. Run: npx bookmark-research install <client> --non-interactive
70
67
  - Claude Code: add --scope user (or --scope project --project <dir>).
71
68
  - DeepSeek Harness (dsh): add --profile <profile>; ask me for the profile name.
72
69
  3. The command prints JSON. Confirm "verified": true and report any "error".
@@ -93,9 +90,9 @@ Install notes:
93
90
  | Scripted preferences | `bookmark-research config show` · `bookmark-research config set --input prefs.json` |
94
91
  | Update or verify all clients | `bookmark-research update` · `bookmark-research verify` |
95
92
 
96
- Use `npx bookmark-research@beta …` if the command is not installed globally. Preferences and keys are user-wide, so one change applies to every installed client; start a new client session afterwards. `--lang zh` shows the wizard and menu in Chinese. The default `quick` preset exposes 21 everyday tools. Use `bookmark-research config set --tool-scope-preset deep` for the full 43-tool catalog when a persistent collection audit is needed. An explicit `tool_scope` list still overrides the preset.
93
+ Use `npx bookmark-research …` if the command is not installed globally. Preferences and keys are user-wide, so one change applies to every installed client; start a new client session afterwards. `--lang zh` shows the wizard and menu in Chinese. The default `quick` preset exposes 21 everyday tools. Use `bookmark-research config set --tool-scope-preset deep` for the full 43-tool catalog when a persistent collection audit is needed. An explicit `tool_scope` list still overrides the preset.
97
94
 
98
- **Update.** `bookmark-research update` refreshes every installed client from its registered source and keeps preferences, keys and data. Re-running `npx bookmark-research@beta install` also upgrades an existing Claude Code or DeepSeek Harness (dsh) installation in place. A Codex installation keeps the source and version it was registered with; to move it to another version, remove that registration first (`codex plugin marketplace remove bookmark-research`) and install again.
95
+ **Update.** `bookmark-research update` refreshes every installed client from its registered source and keeps preferences, keys and data. Re-running `npx bookmark-research install` or `bookmark-research update` also upgrades existing installations in place (including Codex, Claude Code, and DeepSeek Harness).
99
96
 
100
97
  **API keys are optional.** Enter them in `bookmark-research setup` (hidden input, checked immediately, saved in a private local file) or set environment variables, which take precedence.
101
98
 
package/README.zh.md CHANGED
@@ -3,15 +3,12 @@
3
3
  [English](README.md) · **中文**
4
4
 
5
5
  [![GitHub Bookmark-Canvas](https://img.shields.io/badge/GitHub-Bookmark--Canvas-181717?logo=github&logoColor=white)](https://github.com/Browser-bookmark-hub/Bookmark-Canvas)
6
- [![npm version](https://img.shields.io/npm/v/bookmark-research/beta?logo=npm)](https://www.npmjs.com/package/bookmark-research)
6
+ [![npm version](https://img.shields.io/npm/v/bookmark-research?logo=npm)](https://www.npmjs.com/package/bookmark-research)
7
7
 
8
8
  带着书签上下文调研,把结论留成可复用知识。读取 [Bookmark Canvas](https://github.com/Browser-bookmark-hub/Bookmark-Canvas) 数据包,使用宿主模型或研究 API 调查,再保存带来源的 Wiki 页面。支持 **Codex、Claude Code、DeepSeek Harness (dsh)**。
9
9
 
10
10
  [安装说明](docs/installation.md) · [使用指南](docs/user-guide.md) · [详细说明](docs/details.md) · [npm](https://www.npmjs.com/package/bookmark-research)
11
11
 
12
- > [!NOTE]
13
- > **0.5.0 是测试版**,发布在 npm 的 `beta` 标签上;`latest` 标签仍指向更早的构建。遇到问题请到 [GitHub Issues](https://github.com/Browser-bookmark-hub/Bookmark-Research/issues) 反馈。
14
-
15
12
  ## 它是怎么工作的
16
13
 
17
14
  - **本地理解层。** 数据包按结构读,而不是一张平铺的 URL 清单:卡片与分组的位置、连线的方向与标签、同一书签在不同卡片的出现位置(空间上下文);日期字段、ID 中协议定义的日期段,以及真正改过内容的 Git 提交(时间上下文)。这些都是离线查询。
@@ -28,10 +25,10 @@
28
25
  ## 安装
29
26
 
30
27
  ```sh
31
- npx bookmark-research@beta install
28
+ npx bookmark-research install
32
29
  ```
33
30
 
34
- `@beta` 跟随最新测试版;要固定某个构建可写 `@0.5.0-beta.17`。加 `--lang zh` 可让向导显示中文。想一直能用 `bookmark-research` 命令,运行 `npm install -g bookmark-research`;GitHub 便捷脚本使用同一 npm 发布版(macOS/Linux/WSL):
31
+ 默认直接安装最新版本。加 `--lang zh` 可让向导显示中文。想一直能用 `bookmark-research` 命令,运行 `npm install -g bookmark-research`;GitHub 便捷脚本使用同一 npm 发布版(macOS/Linux/WSL):
35
32
 
36
33
  ```sh
37
34
  curl -fsSL https://raw.githubusercontent.com/Browser-bookmark-hub/Bookmark-Research/main/install.sh | bash
@@ -54,11 +51,11 @@ curl -fsSL https://raw.githubusercontent.com/Browser-bookmark-hub/Bookmark-Resea
54
51
 
55
52
  | 宿主 | 命令 | 实际登记方式 |
56
53
  | --- | --- | --- |
57
- | Codex | `npx bookmark-research@beta install codex --non-interactive` | `codex plugin add` |
58
- | Claude Code | `npx bookmark-research@beta install claude --scope user --non-interactive` | `claude plugin install` |
59
- | DeepSeek Harness (dsh) | `npx bookmark-research@beta install dsh --profile web --non-interactive` | `dsh plugin add` |
54
+ | Codex | `npx bookmark-research install codex --non-interactive` | `codex plugin add` |
55
+ | Claude Code | `npx bookmark-research install claude --scope user --non-interactive` | `claude plugin install` |
56
+ | DeepSeek Harness (dsh) | `npx bookmark-research install dsh --profile web --non-interactive` | `dsh plugin add` |
60
57
 
61
- 一次装多个:`npx bookmark-research@beta install claude dsh --profile web --non-interactive`。装到指定项目:`--scope project --project /path/to/project`(Claude Code)。全部参数见[安装说明](docs/installation.md)。
58
+ 一次装多个:`npx bookmark-research install claude dsh --profile web --non-interactive`。装到指定项目:`--scope project --project /path/to/project`(Claude Code)。全部参数见[安装说明](docs/installation.md)。
62
59
 
63
60
  **让 agent 帮你装。** 把下面这段粘贴给 Codex、Claude Code 或 DeepSeek Harness (dsh):
64
61
 
@@ -66,7 +63,7 @@ curl -fsSL https://raw.githubusercontent.com/Browser-bookmark-hub/Bookmark-Resea
66
63
  请把 Bookmark Research 插件安装到你当前运行的这个宿主里。
67
64
 
68
65
  1. 先确认你自己是哪个宿主:codex、claude 或 dsh。
69
- 2. 运行:npx bookmark-research@beta install <宿主> --non-interactive
66
+ 2. 运行:npx bookmark-research install <宿主> --non-interactive
70
67
  - Claude Code:加 --scope user(或 --scope project --project <目录>)。
71
68
  - DeepSeek Harness (dsh):加 --profile <名称>;profile 名称请先问我。
72
69
  3. 命令会输出 JSON。确认 "verified": true,有 "error" 就告诉我。
@@ -92,9 +89,9 @@ curl -fsSL https://raw.githubusercontent.com/Browser-bookmark-hub/Bookmark-Resea
92
89
  | 用脚本改偏好 | `bookmark-research config show` · `bookmark-research config set --input prefs.json` |
93
90
  | 更新或验证所有宿主 | `bookmark-research update` · `bookmark-research verify` |
94
91
 
95
- 没有全局安装时,在前面加 `npx bookmark-research@beta`。偏好和 Key 是用户级的,改一次就对所有已安装宿主生效;改完新建宿主会话即可。加 `--lang zh` 可让向导和菜单显示中文。工具面可用 `bookmark-research config set --tool-scope-preset quick` 使用默认 21 个日常工具;`deep`(或 `all`)启用完整 43 个工具,供明确需要的完整资料审计使用。显式 `tool_scope` 仍优先于预设。
92
+ 没有全局安装时,在前面加 `npx bookmark-research`。偏好和 Key 是用户级的,改一次就对所有已安装宿主生效;改完新建宿主会话即可。加 `--lang zh` 可让向导和菜单显示中文。工具面可用 `bookmark-research config set --tool-scope-preset quick` 使用默认 21 个日常工具;`deep`(或 `all`)启用完整 43 个工具,供明确需要的完整资料审计使用。显式 `tool_scope` 仍优先于预设。
96
93
 
97
- **更新。** `bookmark-research update` 会按各自登记的来源刷新所有已安装宿主,并保留偏好、Key 和数据。重跑 `npx bookmark-research@beta install` 也会就地升级已装的 Claude Code 或 DeepSeek Harness (dsh)。Codex 安装会保留登记时的来源与版本,要换版本需先移除该登记(`codex plugin marketplace remove bookmark-research`)再重新安装。
94
+ **更新。** `bookmark-research update` 会按各自登记的来源刷新所有已安装宿主,并保留偏好、Key 和数据。重跑 `npx bookmark-research install` 或 `bookmark-research update` 也会就地升级已装的宿主(包括 Codex、Claude Code、DeepSeek Harness)。
98
95
 
99
96
  **API Key 都是可选的。** 在 `bookmark-research setup` 里输入(隐藏显示,输完当场检查,保存在仅当前用户可读的本地文件),或者设置环境变量(优先于已保存的 Key)。
100
97
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  候选版本在 `release/` 分支同步三个版本清单并运行 CI。该分支的 registry 集成测试使用当前已发布渠道版本,源码测试、原生安装测试和 npm 打包检查仍验证候选内容;主分支的 registry 测试始终验证 marketplace 固定版本。发布通过 CI 的 npm 包并核验 registry 后,再将候选分支合入主分支,避免默认安装清单提前指向未发布版本。
22
22
 
23
- 测试版使用 npm 的 `beta` 标签;若创建 GitHub 版本说明,则标记为预发布。安装测试版推荐直接使用 `npx bookmark-research@0.5.0-beta.17 install` 或 `npx bookmark-research@beta install`,启动器会自动读取当前运行包版本并固定内容来源,无需重复指定 `--source`。已存在的宿主配置支持无缝覆盖升级。
23
+ 发布时保持 npm 的 `latest` 标签始终指向最新构建,用户直接使用 `npx bookmark-research install`(或 `npx bookmark-research@latest install`)即可默认安装最新版。启动器会自动读取当前运行包版本并固定内容来源,无需重复指定 `--source`。已存在的宿主配置支持无缝覆盖升级。
24
24
 
25
25
  ## 官方资料与项目实例
26
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.17",
3
+ "version": "0.5.0-beta.19",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
@@ -157,7 +157,9 @@ def _check_source(marketplace, requested):
157
157
  elif current["sourceType"] == requested["sourceType"] == "git":
158
158
  equal = _repository(current["source"]) == requested["source"]
159
159
  elif current["sourceType"] == requested["sourceType"] == "npm":
160
- equal = current == requested
160
+ equal = current["source"] == requested["source"] and (
161
+ current == requested or requested.get("ref") != "latest"
162
+ )
161
163
  else:
162
164
  equal = False
163
165
  if not equal:
@@ -401,8 +403,9 @@ def manage(action, cli, source=None, ref=None, dry_run=False, installed_path=Non
401
403
  # Native list JSON omits the registered ref. Equal commit IDs
402
404
  # cannot distinguish a moving branch from an immutable tag.
403
405
  raise ValueError("An existing Git marketplace retains its registered ref. Reinstall without --ref, or explicitly change the source in Codex first")
404
- if origin:
405
- requested = origin
406
+ if origin and ref is None and requested["sourceType"] == "git":
407
+ if origin.get("sourceType") == "git" and requested["source"] == origin["source"] and origin.get("ref"):
408
+ requested["ref"] = origin["ref"]
406
409
  else:
407
410
  if not current or not _installed(cli):
408
411
  raise ValueError("No existing bookmark-research installation; run install first")
package/src/onboarding.py CHANGED
@@ -119,57 +119,72 @@ class Console(Prompter):
119
119
  client = resolve_dsh(getattr(args, "dsh", None))
120
120
  if client["source"] == "desktop":
121
121
  args.dsh = client["binary"]
122
- if requested:
123
- hosts = list(dict.fromkeys(requested))
124
- else:
125
- found = {name: bool(shutil.which(getattr(args, name, None) or name)) for name, _, _ in HOSTS}
126
- hints = {name: ("", None) if found[name] else ("CLI not found", "未找到 CLI") for name in found}
127
- found["dsh"], hints["dsh"] = client["usable"], _dsh_hint(client)
128
- if not any(found.values()):
129
- self.warn("No supported host CLI (codex, claude, dsh) was found on PATH.",
130
- "PATH 中未找到任何受支持的宿主 CLI(codex、claude、dsh)。")
131
- raise ValueError("No host CLI found; install Codex, Claude Code or DeepSeek Harness (dsh), "
132
- "or pass --codex/--claude/--dsh PATH to point at its executable")
133
- hosts = self.multiselect("Install into which hosts?", "安装到哪些宿主?", [
134
- Option(name, en, zh, *hints[name], disabled=not found[name]) for name, en, zh in HOSTS],
135
- [name for name, _, _ in HOSTS if found[name]])
136
- # A carrier that ships its own pnpm is the Desktop CLI, however it was found:
137
- # only that CLI may manage the profile name Desktop reserves for itself.
138
- desktop = client["bundled_pnpm"] is not None
139
- targets = []
140
- for host in hosts:
141
- label = next((en for name, en, _ in HOSTS if name == host), host)
142
- target = {"host": host, "scope": None, "project": None, "profile": None}
143
- if host == "claude":
144
- scope = args.scope
145
- if scope is None:
146
- options = [Option("user", "This user", "当前用户"), Option("project", "This project", "指定项目")]
147
- if host == "claude":
148
- options.append(Option("local", "This project, only for me", "指定项目(仅自己)"))
149
- scope = self.select(label + ": installation scope", label + ":安装范围", options, "user")
150
- target["scope"] = scope
151
- if scope in ("project", "local"):
152
- target["project"] = args.project or self.input(
153
- label + ": project directory", label + ":项目目录", str(Path.cwd()), _directory_problem)
154
- elif host == "dsh":
155
- # Never guess a DSH profile: it selects a distinct composed runtime.
156
- target["profile"] = args.profile or self.dsh_profile(desktop)
157
- targets.append(target)
158
- lines = []
159
- for target in targets:
160
- label = next((en for name, en, _ in HOSTS if name == target["host"]), target["host"])
161
- detail = ", ".join(key + "=" + target[key] for key in ("scope", "project", "profile") if target[key])
162
- lines.append((label + (": " + detail if detail else ""), label + (":" + detail if detail else "")))
163
- self.note("Installation plan", "安装计划", lines)
164
- if client["source"] == "desktop" and any(target["host"] == "dsh" for target in targets):
165
- self.note("DeepSeek Harness (dsh) CLI", "DeepSeek Harness (dsh) CLI", [(client["binary"], client["binary"])])
166
- if not self.confirm("Install now?", "现在安装?", True):
167
- raise KeyboardInterrupt
168
- first = targets[0]
169
- args.host, args.scope, args.project, args.profile = first["host"], first["scope"], first["project"], first["profile"]
170
- return targets
171
-
172
- def dsh_profile(self, desktop):
122
+ while True:
123
+ if requested:
124
+ hosts = list(dict.fromkeys(requested))
125
+ else:
126
+ found = {name: bool(shutil.which(getattr(args, name, None) or name)) for name, _, _ in HOSTS}
127
+ hints = {name: ("", None) if found[name] else ("CLI not found", "未找到 CLI") for name in found}
128
+ found["dsh"], hints["dsh"] = client["usable"], _dsh_hint(client)
129
+ if not any(found.values()):
130
+ self.warn("No supported host CLI (codex, claude, dsh) was found on PATH.",
131
+ "PATH 中未找到任何受支持的宿主 CLI(codex、claude、dsh)。")
132
+ raise ValueError("No host CLI found; install Codex, Claude Code or DeepSeek Harness (dsh), "
133
+ "or pass --codex/--claude/--dsh PATH to point at its executable")
134
+ hosts = self.multiselect("Install into which hosts? (Space to toggle, Enter to confirm)",
135
+ "安装到哪些宿主?(空格 勾选/取消,回车 确认)", [
136
+ Option(name, en, zh, *hints[name], disabled=not found[name]) for name, en, zh in HOSTS],
137
+ [name for name, _, _ in HOSTS if found[name]])
138
+ # A carrier that ships its own pnpm is the Desktop CLI, however it was found:
139
+ # only that CLI may manage the profile name Desktop reserves for itself.
140
+ desktop = client["bundled_pnpm"] is not None
141
+ targets = []
142
+ go_back = False
143
+ for host in hosts:
144
+ label = next((en for name, en, _ in HOSTS if name == host), host)
145
+ target = {"host": host, "scope": None, "project": None, "profile": None}
146
+ if host == "claude":
147
+ scope = args.scope
148
+ if scope is None:
149
+ options = [Option("user", "This user", "当前用户"), Option("project", "This project", "指定项目"),
150
+ Option("local", "This project, only for me", "指定项目(仅自己)")]
151
+ if not requested:
152
+ options.append(Option("back", "← Back to host selection", "← 返回重新选择宿主"))
153
+ scope = self.select(label + ": installation scope", label + ":安装范围", options, "user")
154
+ if scope == "back":
155
+ go_back = True
156
+ break
157
+ target["scope"] = scope
158
+ if scope in ("project", "local"):
159
+ target["project"] = args.project or self.input(
160
+ label + ": project directory", label + ":项目目录", str(Path.cwd()), _directory_problem)
161
+ elif host == "dsh":
162
+ # Never guess a DSH profile: it selects a distinct composed runtime.
163
+ profile = args.profile or self.dsh_profile(desktop, allow_back=not requested)
164
+ if profile == "back":
165
+ go_back = True
166
+ break
167
+ target["profile"] = profile
168
+ targets.append(target)
169
+ if go_back:
170
+ continue
171
+ lines = []
172
+ for target in targets:
173
+ label = next((en for name, en, _ in HOSTS if name == target["host"]), target["host"])
174
+ detail = ", ".join(key + "=" + target[key] for key in ("scope", "project", "profile") if target[key])
175
+ lines.append((label + (": " + detail if detail else ""), label + (":" + detail if detail else "")))
176
+ self.note("Installation plan", "安装计划", lines)
177
+ if client["source"] == "desktop" and any(target["host"] == "dsh" for target in targets):
178
+ self.note("DeepSeek Harness (dsh) CLI", "DeepSeek Harness (dsh) CLI", [(client["binary"], client["binary"])])
179
+ if not self.confirm("Install now?", "现在安装?", True):
180
+ if not requested and self.confirm("Re-select hosts and options?", "重新选择宿主与选项?", True):
181
+ continue
182
+ raise KeyboardInterrupt
183
+ first = targets[0]
184
+ args.host, args.scope, args.project, args.profile = first["host"], first["scope"], first["project"], first["profile"]
185
+ return targets
186
+
187
+ def dsh_profile(self, desktop, allow_back=False):
173
188
  """Choose an initialized DSH profile, or name a new one.
174
189
 
175
190
  `dsh plugin` initializes a profile on first use, so the profiles on disk
@@ -186,8 +201,12 @@ class Console(Prompter):
186
201
  return self.input(question[0], question[1], "desktop" if desktop else "", check)
187
202
  options = [Option(row["name"], row["name"], row["name"], *_profile_hint(row)) for row in rows]
188
203
  options.append(Option("", "Another profile name…", "其他 profile 名称…"))
204
+ if allow_back:
205
+ options.append(Option("back", "← Back to host selection", "← 返回重新选择宿主"))
189
206
  default = "desktop" if any(row["name"] == "desktop" for row in rows) else None
190
207
  chosen = self.select("Install into which DeepSeek Harness (dsh) profile?", "安装到哪个 DeepSeek Harness (dsh) profile?", options, default)
208
+ if chosen == "back":
209
+ return "back"
191
210
  if chosen:
192
211
  return chosen
193
212
  return self.input(question[0], question[1], "desktop" if desktop else "", check)
package/src/tui.py CHANGED
@@ -449,8 +449,11 @@ class Prompter:
449
449
  while True:
450
450
  lines = self._render_list(question, options, cursor, chosen, True, message)
451
451
  if not message:
452
- lines[-1] = "%s %s" % (self._color("36", "└"), self._color("2", self.text(
453
- "space toggle · a all · enter confirm", "空格 切换 · a 全选 · 回车 确认")))
452
+ shortcut_hint = self.text(
453
+ "[Space] toggle [a] all/none [Enter] confirm",
454
+ "[空格] 勾选/取消 [a] 全选/全不选 [回车] 确认",
455
+ )
456
+ lines[-1] = "%s %s" % (self._color("36", "└"), self._color("36", shortcut_hint))
454
457
  drawn = self._redraw(lines, drawn)
455
458
  key = self._key()
456
459
  message = None