dsh-custom-mode 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  # 双语配对一致性记录:两侧在「上次确认一致」时的 git blob 哈希。
2
2
  # 两份文档权威相同——改完任意一侧,请把另一侧也改掉,然后重新记录:
3
3
  # node tools/verify-translation-pairing.mjs --write
4
- README.md: eea37666d29e8936957f2fd4290c325bf8e7be6c
5
- README.zh.md: 7ad5b62f4f24842261ca30f2572f76810a808f98
4
+ README.md: 00045c14e30c98d389fc658906146272bfd82c7f
5
+ README.zh.md: afac5524be240ba1c38a6ac9ba17be15e0acc4bd
package/README.md CHANGED
@@ -24,7 +24,7 @@ Edit a DeepSeek Harness (`dsh`) custom mode: system prompt, base mode, and plugi
24
24
  Pin the version. A bare name can install an older build during pnpm's release cooldown. Details: [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md).
25
25
 
26
26
  ```sh
27
- dsh plugin --profile web add dsh-custom-mode@2.2.2
27
+ dsh plugin --profile web add dsh-custom-mode@2.2.3
28
28
  ```
29
29
 
30
30
  Restart the process that serves the profile. The version on the settings page is the one that is running.
@@ -37,7 +37,7 @@ On Windows, `install.sh` is bash and it needs a `dsh` executable on `PATH`. Othe
37
37
 
38
38
  1. Open Settings → Custom mode.
39
39
  2. Edit the system prompt. Save is directly under the editor.
40
- 3. A new session uses this assistant when `settings.yaml` has no `agent-presets.default` yet. An existing value is left as it is.
40
+ 3. A new session uses this assistant when `settings.yaml` has no `agent-presets.default` yet. An existing value is left as it is. Flow-style, scalar, and alias `agent-presets` values are preserved without inserting YAML into them.
41
41
 
42
42
  The prompt is `prompt.md`. The next time a model step is assembled, that file is read again. History can load an older draft into the editor; loading does not write. A save computed against an older copy is refused when the file changed underneath. The model can change the same file only through `custom_prompt`, which asks first.
43
43
 
@@ -59,13 +59,28 @@ The page is two branches. This assistant edits the one you have open, and all fi
59
59
 
60
60
  Declared range: `>=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0`
61
61
 
62
- | dsh | Claim | Checked for 2.2.2 |
62
+ On 2026-10-09, npm `latest` and `next` still name `0.2.0-rc.2`; `alpha` names `0.2.1-alpha.2`, published that day. Upstream has not published a final stable version yet.
63
+
64
+ | dsh | Channel | Checked for 2.2.3 |
63
65
  | --- | --- | --- |
64
- | `0.2.0-rc.2` | declared | Windows web profile, before this release |
65
- | `0.2.1-alpha.1` | declared, in CI | not re-run for this release |
66
- | `0.1.7-rc.2` | declared, in CI | not re-run for this release |
66
+ | `0.2.0-rc.2` | npm `latest` / `next` | Exact npm host, packed plugin load, API lifecycle, settings and picker in Windows web profile |
67
+ | `0.2.1-alpha.2` | npm `alpha` | The same isolated checks, against this host's own preset declarations |
68
+ | `0.1.7-rc.2` | earlier declared line | Kept in the CI regression matrix |
69
+
70
+ Release waits for the unit, boot, and browser workflows. To reproduce against an already installed exact host:
71
+
72
+ ```sh
73
+ node tools/compat-check.mjs --host-install /path/to/host --out /tmp/custom-mode-check
74
+ node tools/runtime-check.mjs --host-install /path/to/host --out /tmp/custom-mode-check --browser /path/to/chrome
75
+ ```
76
+
77
+ Both tools use a generated home and ordinary mock prompts. The runtime check exercises all five base modes, repeated saves, uninstall, reinstall, data preservation, and repeated installation. No model calls are needed. VMware and native Linux desktop results are not claimed.
78
+
79
+ ## Plugin markets
80
+
81
+ `dshmarket`, DSH Plugin Hub (`dsh-plugin`), and DSH Plugin Store (`dshmarketplace-plugin`) use different catalogs but install the same profile bundle. The root npm manifest provides `dsh.bundle.patch`, the browser export, the public repository, and matching compatibility ranges; no extra runtime adapter or build script is needed.
67
82
 
68
- The unit suite also passed under WSL2 Ubuntu 26.04 (Node only, no dsh process, no browser). That is not a Linux desktop result.
83
+ Run `node tools/market-check.mjs --out /tmp/custom-mode-markets.json` to check their public discovery data. An inaccessible catalog or an unverified/stale listing is reported explicitly. The tool never runs catalog-provided shell commands or submits a listing. [Market interfaces and limits](docs/MARKETPLACES.md).
69
84
 
70
85
  ## Docs
71
86
 
package/README.zh.md CHANGED
@@ -24,7 +24,7 @@
24
24
  钉版本。只写包名时,pnpm 的发布冷却期可能装到旧版。细节见 [docs/TROUBLESHOOTING.zh.md](docs/TROUBLESHOOTING.zh.md)。
25
25
 
26
26
  ```sh
27
- dsh plugin --profile web add dsh-custom-mode@2.2.2
27
+ dsh plugin --profile web add dsh-custom-mode@2.2.3
28
28
  ```
29
29
 
30
30
  重启为这个 profile 提供服务的进程。设置页上的版本号才是正在运行的版本。
@@ -37,7 +37,7 @@ dsh plugin --profile web add dsh-custom-mode@2.2.2
37
37
 
38
38
  1. 打开设置 → 自定义模式。
39
39
  2. 改系统提示词。保存按钮在编辑器下面。
40
- 3. `settings.yaml` 里还没有 `agent-presets.default` 时,新会话用这个助手。已经写过的值保持原样。
40
+ 3. `settings.yaml` 里还没有 `agent-presets.default` 时,新会话用这个助手。已经写过的值保持原样。`agent-presets` 使用行内对象、标量或别名时也原样保留,不向里面插入 YAML。
41
41
 
42
42
  提示词是 `prompt.md`。下一步组装模型输入时会重读这个文件。历史可以把旧稿载入编辑器;载入不落盘。打开页面之后文件被改过时,这一次保存会被拒绝。模型要改同一份文件,只能走 `custom_prompt`,并且先问过你。
43
43
 
@@ -59,13 +59,28 @@ dsh plugin --profile web add dsh-custom-mode@2.2.2
59
59
 
60
60
  声明范围:`>=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0`
61
61
 
62
- | dsh | 声明 | 2.2.2 核对 |
62
+ 2026-10-09 实时核对 npm:`latest` 和 `next` 仍是 `0.2.0-rc.2`,`alpha` 是当天发布的 `0.2.1-alpha.2`。上游尚未发布去掉预发布后缀的正式稳定版。
63
+
64
+ | dsh | 通道 | 2.2.3 核对 |
63
65
  | --- | --- | --- |
64
- | `0.2.0-rc.2` | 声明支持 | 本版发布前的 Windows web profile |
65
- | `0.2.1-alpha.1` | 声明支持,CI 有这条腿 | 本版没有重跑 |
66
- | `0.1.7-rc.2` | 声明支持,CI 有这条腿 | 本版没有重跑 |
66
+ | `0.2.0-rc.2` | npm `latest` / `next` | 精确 npm 宿主、打包加载、API 生命周期、Windows web profile 设置页与选择器 |
67
+ | `0.2.1-alpha.2` | npm `alpha` | 同一套隔离检查,读取该宿主自己的预设声明 |
68
+ | `0.1.7-rc.2` | 先前声明支持的版本 | 保留在 CI 回归矩阵里 |
69
+
70
+ 发布必须等待单元、启动和浏览器工作流通过。已有精确版本的宿主安装时,可以这样复现:
71
+
72
+ ```sh
73
+ node tools/compat-check.mjs --host-install /path/to/host --out /tmp/custom-mode-check
74
+ node tools/runtime-check.mjs --host-install /path/to/host --out /tmp/custom-mode-check --browser /path/to/chrome
75
+ ```
76
+
77
+ 两项工具只使用新生成的 home 和普通 mock 提示词。运行检查覆盖全部五档基础模式、重复保存、卸载、重装、用户数据保留与重复安装,不需要调用模型。不宣称 VMware 或原生 Linux 桌面已通过验收。
78
+
79
+ ## 插件市场
80
+
81
+ `dshmarket`、DSH Plugin Hub(`dsh-plugin`)和 DSH Plugin Store(`dshmarketplace-plugin`)使用不同目录,但安装的是同一个 profile bundle。根 npm 清单提供 `dsh.bundle.patch`、浏览器导出、公开仓库和一致的兼容范围,不需要额外的运行时适配器或构建脚本。
67
82
 
68
- 单元套件在 WSL2 Ubuntu 26.04 下也通过了(只有 Node,没有 dsh 进程,没有浏览器)。这不是 Linux 桌面的结果。
83
+ 运行 `node tools/market-check.mjs --out /tmp/custom-mode-markets.json` 检查它们的公开发现数据。目录不可访问、条目未审核或数据过期都会明确报告。工具不执行目录提供的 shell 命令,也不提交收录。[市场接口与限制](docs/MARKETPLACES.zh.md)。
69
84
 
70
85
  ## 文档
71
86
 
package/client.js CHANGED
@@ -761,18 +761,19 @@ try {
761
761
  ".cpfe-hint{display:flex;flex-direction:column;gap:2px;margin:0 0 6px}",
762
762
  ".cpfe-hint-line{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
763
763
  ".cpfe-hint-detail{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
764
- // 描述:多行、自适应高度(没有多行输入组件,所以用 textarea + 同一批语义变量)
765
- ".cpfe-desc{box-sizing:border-box;min-height:40px;max-height:120px;resize:vertical;padding:8px 12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;margin-bottom:0}",
764
+ // The native InlineEditor saves on Enter and autofocuses; document editing must keep
765
+ // Enter as a newline and manual Save. Use its exact surface/font tokens for these
766
+ // controlled textareas, with only document height and width as layout differences.
767
+ ".cpfe-desc,.cpfe-editor{box-sizing:border-box;min-width:0;margin:0;padding:3px 8px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm);outline:none;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font:var(--dsw-font-xs-13);font-family:Inter,var(--dsw-font-family);overflow-y:auto}",
768
+ ".cpfe-desc:focus,.cpfe-editor:focus{border-color:var(--dsw-alias-state-business-primary)}",
769
+ ".cpfe-desc{min-height:40px;max-height:120px;resize:vertical}",
766
770
  ".cpfe-base-pending{color:var(--dsw-alias-state-warn-primary)}",
767
771
  ".cpfe-note{display:block;margin:8px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
768
- ".cpfe-mono{display:block;margin:0;font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
772
+ ".cpfe-mono{display:block;margin:0;font:inherit;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
769
773
  ".cpfe-pills{display:flex;flex-wrap:wrap;gap:6px;align-items:center}",
770
774
  ".cpfe-pills > *{flex:0 0 auto;white-space:nowrap}",
771
775
  ".cpfe-pills button{white-space:nowrap}",
772
776
  ".cpfe-picker{display:flex;align-items:center;gap:8px;margin:2px 0 4px}",
773
- // 壳的菜单浮层靠一个独立的 backing 元素上色(实测在 0.1.7-rc.2 + headless 下那块是透明的,
774
- // 文字会"压"在下面的输入框上)。用官方 token 显式补一层底色,真实浏览器与 headless 一致。
775
- ".cpfe-menu{background:var(--dsw-menu-surface-fill,var(--dsw-alias-bg-layer-3));border-radius:var(--dsw-radius-lg);box-shadow:var(--dsw-elevation-soft)}",
776
777
  ".cpfe-history{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:10px}",
777
778
  ".cpfe-history-label{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
778
779
  ".cpfe-history-hint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
@@ -784,15 +785,12 @@ try {
784
785
  ".cpfe-branch{display:flex;flex-direction:column;gap:16px;margin-top:8px;padding-left:12px;border-left:1px solid var(--dsw-alias-border-l1)}",
785
786
  ".cpfe-node{display:flex;flex-direction:column;gap:4px}",
786
787
  ".cpfe-node-label{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
787
- ".cpfe-locked button:disabled:not([aria-pressed=true]){opacity:.4;cursor:default}",
788
- ".cpfe-locked button:disabled[aria-pressed=true]{opacity:1;cursor:default}",
789
788
  ".cpfe-field{display:flex;width:100%;margin-bottom:8px}",
790
789
  ".cpfe-row-head{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}",
791
790
  ".cpfe-row-switch{flex:0 0 auto}",
792
791
  ".cpfe-row-line{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}",
793
792
  ".cpfe-row-note{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
794
- ".cpfe-row-toggle{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:var(--dsw-radius-sm);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}",
795
- ".cpfe-row-toggle:hover{background:var(--dsw-alias-bg-layer-2)}",
793
+ ".cpfe-row-toggle{flex:0 0 auto}",
796
794
  ".cpfe-row-open{border-color:var(--dsw-alias-border-l2)}",
797
795
  ".cpfe-row-detail{display:flex;flex-direction:column;gap:4px;margin:0 0 12px;padding:0 0 0 16px;border:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
798
796
  ".cpfe-detail-line{display:flex;gap:8px;min-width:0}",
@@ -812,19 +810,17 @@ try {
812
810
  ".cpfe-row-meta{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}",
813
811
  ".cpfe-row-badges{display:flex;align-items:center;gap:4px;flex:0 1 auto;min-width:0}",
814
812
  ".cpfe-row{display:flex;gap:12px;align-items:center;box-sizing:border-box;min-height:74px;padding:16px 0;border:0;border-radius:0;background:none}",
815
- ".cpfe-editor{box-sizing:border-box;width:100%;min-height:120px;resize:vertical;padding:12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:13px;line-height:20px}",
813
+ ".cpfe-editor{width:100%;min-height:120px;resize:vertical}",
816
814
  ".cpfe-bar{display:flex;align-items:center;gap:var(--g);flex-wrap:wrap;margin-top:8px}",
817
- ".cpfe-disclosure{appearance:none;display:inline-flex;align-items:center;gap:6px;margin:8px 0 0;padding:0;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;font-weight:400;cursor:pointer}",
818
- ".cpfe-filter{box-sizing:border-box;width:100%;height:36px;margin:0 0 8px;padding:0 12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}",
815
+ ".cpfe-disclosure{align-self:flex-start;margin-top:8px}",
819
816
  ".cpfe .cpfe-danger{margin-left:auto}",
820
817
  ".cpfe-status{font-size:13px;line-height:20px}",
821
818
  ".cpfe-ok{color:var(--dsw-alias-label-secondary)}",
822
819
  ".cpfe-err{color:var(--dsw-alias-state-error-primary)}",
823
820
  ".cpfe-dirty{color:var(--dsw-alias-state-warn-primary)}",
824
- ".cpfe-danger{color:var(--dsw-alias-state-error-primary)}",
825
821
  ".cpfe-meta-line{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px;min-width:0}",
826
822
  ".cpfe-version{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:nowrap}",
827
- ".cpfe-path{font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
823
+ ".cpfe-path{font:inherit;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
828
824
  // ── fallback-path controls (unused when the shell provides the atoms) ──
829
825
  ".cpfe-btn{appearance:none;cursor:pointer;padding:0 14px;height:36px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px}",
830
826
  ".cpfe-btn:disabled{opacity:.4;cursor:not-allowed}",
@@ -838,7 +834,7 @@ try {
838
834
  // 光拦状态还不够:DOM 会先显示用户敲进去的字/翻过去的开关,等下一次重渲染才被拉回来 ——
839
835
  // 那就是"我明明改了,它自己弹回去了"。`pointer-events` 让点击根本到不了控件,
840
836
  // 视觉上也给一个明确的"此刻不能动"。
841
- ".cpfe-busy .cpfe-switch,.cpfe-busy .cpfe-row-switch,.cpfe-busy .cpfe-row-toggle,.cpfe-busy .cpfe-input,.cpfe-busy .cpfe-field,.cpfe-busy .cpfe-editor,.cpfe-busy .cpfe-pill,.cpfe-busy .cpfe-selector{pointer-events:none;opacity:.65}",
837
+ ".cpfe-busy .cpfe-switch,.cpfe-busy .cpfe-row-switch,.cpfe-busy .cpfe-row-toggle,.cpfe-busy .cpfe-input,.cpfe-busy .cpfe-field,.cpfe-busy .cpfe-pill,.cpfe-busy .cpfe-selector{pointer-events:none}",
842
838
  ".cpfe-busy .cpfe-editor{cursor:progress}",
843
839
  ".cpfe-tag{font-size:12px;line-height:18px;padding:0 5px;border-radius:4px;border:.5px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}",
844
840
  ".cpfe-tag-success,.cpfe-tag-info{color:var(--dsw-alias-state-success-primary)}",
@@ -846,18 +842,7 @@ try {
846
842
  ".cpfe-tag-danger{color:var(--dsw-alias-state-error-primary)}",
847
843
  ".cpfe-pill{appearance:none;cursor:pointer;height:28px;padding:0 12px;border-radius:999px;border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);font:inherit;font-size:13px}",
848
844
  ".cpfe-pill-on{border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-label-primary)}",
849
- // ── 官方的「设置项下拉」形态 ────────────────────────────────────────────
850
- // 实测(**0.1.7-rc.2 与 0.2.0-rc.2 各量一次,结论相同**):官方的 `_selector` 是
851
- // height 36px · border-radius var(--dsw-radius-md) · background var(--dsw-alias-bg-module-platform)
852
- // · padding 0 14px · font-size 14px/line-height 22px · gap 12px,hover 换
853
- // var(--dsw-alias-interactive-bg-hover)。
854
- // 壳的 Button 只有 primary / ghost / outline / toolbar 四种变体,没有"灰底选择器"这一种 ——
855
- // 官方各设置页也是**各自定义** `_selector`(oY77xG / hVGvvW / _2XZxNq / T1PP_q 各一份、
856
- // 度量完全一致),所以这里照同一套度量自绘,做法与官方一致。
857
- // 双类选择器(`.cpfe .cpfe-selector`)用来压过壳 Button 的单类变体规则,
858
- // 免得 `_ghost_` / `_sm_` 把灰底和高度覆盖掉。
859
- ".cpfe .cpfe-selector{height:36px;padding:0 14px;gap:12px;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px;font-weight:400}",
860
- ".cpfe .cpfe-selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
845
+ // Menu surfaces and trigger Buttons keep the native atoms' own styles.
861
846
  // ── 告警区 ──────────────────────────────────────────────────────────────
862
847
  // 这一段此前**一条 CSS 都没有**(2026-10-02 逐表核对:样式表里 `cpfe-warn` 零规则),
863
848
  // 于是出现告警时它是一串没有排版的裸 `<p>`:没有行距、图标与文字不对齐、
@@ -1059,9 +1044,10 @@ try {
1059
1044
  : null,
1060
1045
  ),
1061
1046
  react.createElement(
1062
- "button",
1047
+ A.Button,
1063
1048
  {
1064
- type: "button",
1049
+ variant: "ghost",
1050
+ size: "sm",
1065
1051
  className: "cpfe-row-toggle",
1066
1052
  "aria-expanded": open,
1067
1053
  "aria-label": t(open ? "aria.collapse" : "aria.expand"),
@@ -1855,14 +1841,13 @@ try {
1855
1841
  // ★ portal:不把浮层渲染在我们的容器里。设置面板有裁剪与叠层,非 portal 的
1856
1842
  // 菜单会被裁掉表面,只剩文字"压"在下面的输入框上(实测截图如此)。
1857
1843
  portal: true,
1858
- listClassName: "cpfe-menu",
1859
1844
  align: "start",
1860
1845
  side: "bottom",
1861
1846
  selectedId: selected,
1862
1847
  anchor: react.createElement(
1863
1848
  A.Button,
1864
1849
  {
1865
- variant: "ghost",
1850
+ variant: "toolbar",
1866
1851
  className: "cpfe-selector",
1867
1852
  disabled: busy,
1868
1853
  onClick: () => setAssistantOpen((open) => open !== true),
@@ -1994,6 +1979,7 @@ try {
1994
1979
  react.createElement(SectionHint, { text: t("name.hint") }),
1995
1980
  react.createElement(A.Input, {
1996
1981
  className: "cpfe-field",
1982
+ disabled: busy,
1997
1983
  value: draft.name,
1998
1984
  placeholder: t("name.placeholder"),
1999
1985
  "aria-label": t("name.heading"),
@@ -2064,8 +2050,9 @@ try {
2064
2050
  : react.createElement(
2065
2051
  "div",
2066
2052
  null,
2067
- react.createElement("input", {
2068
- className: "cpfe-filter",
2053
+ react.createElement(A.Input, {
2054
+ className: "cpfe-field cpfe-filter",
2055
+ disabled: busy,
2069
2056
  value: rowQuery,
2070
2057
  placeholder: t("rows.filterPlaceholder"),
2071
2058
  "aria-label": t("rows.filterPlaceholder"),
@@ -2176,14 +2163,13 @@ try {
2176
2163
  ? react.createElement(A.Menu, {
2177
2164
  open: historyOpen,
2178
2165
  portal: true,
2179
- listClassName: "cpfe-menu",
2180
2166
  align: "start",
2181
2167
  side: "bottom",
2182
2168
  selectedId: draft.historyPick,
2183
2169
  anchor: react.createElement(
2184
2170
  A.Button,
2185
2171
  {
2186
- variant: "ghost",
2172
+ variant: "toolbar",
2187
2173
  className: "cpfe-selector",
2188
2174
  disabled: busy,
2189
2175
  onClick: () => setHistoryOpen((open) => open !== true),
@@ -2359,9 +2345,10 @@ try {
2359
2345
  "section",
2360
2346
  null,
2361
2347
  react.createElement(
2362
- "button",
2348
+ A.Button,
2363
2349
  {
2364
- type: "button",
2350
+ variant: "ghost",
2351
+ disabled: busy,
2365
2352
  "data-cap": "toggle",
2366
2353
  className: "cpfe-disclosure",
2367
2354
  "aria-expanded": capOpen,
package/defaults.mjs CHANGED
@@ -35,8 +35,12 @@ export function ensureUnsetDefaultPreset(home, presetId) {
35
35
  for (let i = 0; i < lines.length; i += 1) {
36
36
  const line = lines[i]
37
37
  if (line.trim() === '' || /^\s*#/.test(line)) continue
38
- if (/^agent-presets\s*:/.test(line)) {
39
- if (/default\s*:/.test(line)) return { wrote: false, reason: 'inline' }
38
+ const key = /^(?:agent-presets|'agent-presets'|"agent-presets")\s*:\s*(.*)$/.exec(line.replace(/^\uFEFF/, ''))
39
+ if (key !== null) {
40
+ // A flow mapping, alias or scalar cannot receive an indented child.
41
+ // Leave unsupported YAML forms byte-identical instead of breaking boot.
42
+ const value = key[1].trim()
43
+ if (value !== '' && !value.startsWith('#')) return { wrote: false, reason: 'inline' }
40
44
  section = i
41
45
  break
42
46
  }
@@ -50,7 +54,7 @@ export function ensureUnsetDefaultPreset(home, presetId) {
50
54
  const line = lines[i]
51
55
  if (line.trim() === '' || /^\s*#/.test(line)) continue
52
56
  if (/^\S/.test(line)) break
53
- if (/^\s+default\s*:/.test(line)) return { wrote: false, reason: 'present' }
57
+ if (/^\s+(?:default|'default'|"default")\s*:/.test(line)) return { wrote: false, reason: 'present' }
54
58
  }
55
59
  lines.splice(section + 1, 0, ' default: ' + presetId)
56
60
  const next = lines.join('\n')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-custom-mode",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Edit a DeepSeek Harness (dsh) custom mode: system prompt, base mode, and plugin switches.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -74,7 +74,10 @@
74
74
  "union-mount-check": "node tools/union-mount-check.mjs",
75
75
  "session-trace": "node tools/session-trace.mjs",
76
76
  "browser-verify": "node tools/browser-verify.mjs",
77
- "upgrade-preflight": "node tools/upgrade-preflight.mjs"
77
+ "upgrade-preflight": "node tools/upgrade-preflight.mjs",
78
+ "compat-check": "node tools/compat-check.mjs",
79
+ "runtime-check": "node tools/runtime-check.mjs",
80
+ "market-check": "node tools/market-check.mjs"
78
81
  },
79
82
  "peerDependencies": {
80
83
  "@deepseek-ai/dsh": ">=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0"
@@ -50,6 +50,18 @@ export function absoluteSpecifier(specifier, presetDir) {
50
50
  return pathToFileURL(join(presetDir, specifier)).href
51
51
  }
52
52
 
53
+ /** Preserve config expressions in the host loader's native node representation.
54
+ * The local parser returns __js without executing it. The host recognizes
55
+ * __jsExpr and evaluates it with its own baseUrl/context. Passing __js through
56
+ * as ordinary config broke Creator's customSkillDirs.
57
+ */
58
+ function loaderConfig(value) {
59
+ if (value === null || typeof value !== 'object') return value
60
+ if (typeof value.__js === 'string') return { __jsExpr: value.__js }
61
+ if (Array.isArray(value)) return value.map(loaderConfig)
62
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, loaderConfig(item)]))
63
+ }
64
+
53
65
  /**
54
66
  * Turn parsed composition rows into the row objects `register()` takes.
55
67
  *
@@ -72,7 +84,7 @@ export function toPluginRows(rows, presetDir) {
72
84
  if (row.isolate !== undefined && row.isolate !== null) plugin.isolate = row.isolate
73
85
  plugin.config = toPluginRows(row.config, presetDir)
74
86
  } else if (row.config !== undefined && row.config !== null) {
75
- plugin.config = row.config
87
+ plugin.config = loaderConfig(row.config)
76
88
  }
77
89
  if (row.disabled !== undefined) plugin.disabled = resolveDisabled(row.disabled)
78
90
  out.push(plugin)