release-skill 0.9.30 → 0.9.31

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.
Files changed (36) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.qoder-plugin/plugin.json +1 -1
  7. package/CHANGELOG.md +29 -0
  8. package/INSTALL.md +2 -2
  9. package/INSTALL.zh-CN.md +2 -2
  10. package/README.md +14 -15
  11. package/README.zh-CN.md +14 -15
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/release-skill.bundle.mjs +159 -11
  14. package/adapters/claude/skills/release-help/SKILL.md +8 -2
  15. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  16. package/adapters/codex/bin/release-skill.bundle.mjs +159 -11
  17. package/adapters/codex/skills/release-help/SKILL.md +8 -2
  18. package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
  19. package/adapters/cursor/bin/release-skill.bundle.mjs +159 -11
  20. package/adapters/cursor/skills/release-help/SKILL.md +8 -2
  21. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  22. package/adapters/kimi/bin/release-skill.bundle.mjs +159 -11
  23. package/adapters/kimi/skills/release-help/SKILL.md +8 -2
  24. package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
  25. package/adapters/qoder/bin/release-skill.bundle.mjs +159 -11
  26. package/adapters/qoder/skills/release-help/SKILL.md +8 -2
  27. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  28. package/adapters/workbuddy/bin/release-skill.bundle.mjs +159 -11
  29. package/adapters/workbuddy/skills/release-help/SKILL.md +8 -2
  30. package/bin/release-skill.bundle.mjs +159 -11
  31. package/package.json +1 -1
  32. package/platform-manifest.json +4 -4
  33. package/skills/release-help/SKILL.md +8 -2
  34. package/skills-src/release-help/SKILL.md +8 -2
  35. package/src/commands/post-release-finish.mjs +1 -1
  36. package/src/commands/post-release-local.mjs +156 -8
@@ -9,9 +9,9 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
9
9
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
10
10
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
11
11
  // Package identity injected at build time — closure-independent --version probe.
12
- const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.30"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.31"});
13
13
  // Build-time source digest for the BUNDLE_STALE freshness gate (see above).
14
- const __bundleSourceDigest = "52acbaddf4e589b42b49defb03ff5dfa59e0306f37a3ce0e8e3aaa240b9cfba4";
14
+ const __bundleSourceDigest = "1ac240c6ac1cc3eaec8662f1f04ed43c069136e65400823f01c951e7976dc9e8";
15
15
 
16
16
  var __create = Object.create;
17
17
  var __defProp = Object.defineProperty;
@@ -137228,17 +137228,162 @@ function parseCodeBuddyRemoteObservation(target, stdout) {
137228
137228
  };
137229
137229
  }
137230
137230
  function kimiExpectProgram({ removePlugin } = {}) {
137231
- const removeCommand = removePlugin ? `submitCommand "/plugins remove $removePlugin"
137232
- expect {
137233
- -nocase -re {(remove|delete|uninstall).*(confirm|sure)|(confirm|sure).*(remove|delete|uninstall)} {
137231
+ const removeCommand = removePlugin ? `proc kimi22RemoveIdentity {dialog} {
137232
+ set identity ""
137233
+ foreach rawLine [split $dialog "\\n"] {
137234
+ set line [string trim $rawLine]
137235
+ if {[regexp -nocase {Remove .+ \\(([^()]+)\\)\\?} $line match pluginId]} {
137236
+ if {$identity ne "" && $identity ne $pluginId} { return "" }
137237
+ set identity $pluginId
137238
+ }
137239
+ }
137240
+ return $identity
137241
+ }
137242
+
137243
+ proc kimi22RemoveDialogComplete {dialog} {
137244
+ set hasCancel 0
137245
+ set hasRemove 0
137246
+ foreach rawLine [split $dialog "\\n"] {
137247
+ set line [string trim $rawLine]
137248
+ regsub {^\u276F[ \\t]*} $line {} label
137249
+ set label [string trim $label]
137250
+ if {[string equal -nocase $label "Cancel"]} { set hasCancel 1 }
137251
+ if {[string equal -nocase $label "Remove plugin"]} { set hasRemove 1 }
137252
+ }
137253
+ return [expr {$hasCancel && $hasRemove}]
137254
+ }
137255
+
137256
+ proc kimi22RemoveAction {dialog state unknownCode} {
137257
+ set labels {}
137258
+ set selectedCount 0
137259
+ set selectedIndex -1
137260
+ set selectedLabel ""
137261
+ set removeCount 0
137262
+ set removeIndex -1
137263
+ foreach rawLine [split $dialog "\\n"] {
137264
+ set line [string trim $rawLine]
137265
+ if {$line eq ""} { continue }
137266
+ set selected [regexp {^\u276F[ \\t]*} $line]
137267
+ if {$selected} {
137268
+ regsub {^\u276F[ \\t]*} $line {} label
137269
+ set label [string trim $label]
137270
+ } else {
137271
+ set label $line
137272
+ }
137273
+ set knownAction [expr {
137274
+ [string equal -nocase $label "Cancel"]
137275
+ || [string equal -nocase $label "Remove plugin"]
137276
+ }]
137277
+ if {!$knownAction && $selected} {
137278
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
137279
+ exit $unknownCode
137280
+ }
137281
+ if {!$knownAction} { continue }
137282
+ set index [llength $labels]
137283
+ lappend labels $label
137284
+ if {[string equal -nocase $label "Remove plugin"]} {
137285
+ incr removeCount
137286
+ set removeIndex $index
137287
+ }
137288
+ if {$selected} {
137289
+ incr selectedCount
137290
+ set selectedIndex $index
137291
+ set selectedLabel $label
137292
+ }
137293
+ }
137294
+ if {[llength $labels] != 2 || $removeCount != 1 || $selectedCount != 1} {
137295
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
137296
+ exit $unknownCode
137297
+ }
137298
+ if {[string equal -nocase $selectedLabel "Remove plugin"]} {
137299
+ return selected-remove
137300
+ }
137301
+ if {![string equal -nocase $selectedLabel "Cancel"]} {
137302
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
137303
+ exit $unknownCode
137304
+ }
137305
+ if {$selectedIndex + 1 == $removeIndex} { return move-down }
137306
+ if {$selectedIndex - 1 == $removeIndex} { return move-up }
137307
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
137308
+ exit $unknownCode
137309
+ }
137310
+
137311
+ proc readKimi22RemoveDialog {prefix state timeoutCode unknownCode eofCode} {
137312
+ global expect_out
137313
+ set dialogBuffer $prefix
137314
+ set cleaned [cleanScreen $dialogBuffer]
137315
+ if {![regexp -nocase {Remove .+ \\(([^()]+)\\)\\?} $cleaned]} {
137316
+ expect {
137317
+ -nocase -re {Remove [^\\r\\n]*\\([^\\r\\n]+\\)\\?} {
137318
+ append dialogBuffer $expect_out(buffer)
137319
+ }
137320
+ timeout { failTimeout $state $timeoutCode $unknownCode }
137321
+ eof { failEof $state $eofCode }
137322
+ }
137323
+ set cleaned [cleanScreen $dialogBuffer]
137324
+ }
137325
+ while {![kimi22RemoveDialogComplete $cleaned]} {
137234
137326
  expect {
137235
- -ex $removePlugin {
137236
- send -- "\\033\\[B"
137237
- send -- "\\033\\[13u"
137327
+ -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
137328
+ -nocase -re {(?:Cancel|Remove plugin)[^\\r\\n]*\\r*\\n} {
137329
+ append dialogBuffer $expect_out(buffer)
137238
137330
  }
137239
- timeout { failTimeout remove-confirmation 125 127 }
137240
- eof { failEof remove-confirmation 126 }
137331
+ -re {([^\\r\\n]+\\r*\\n)} {
137332
+ append dialogBuffer $expect_out(buffer)
137333
+ }
137334
+ timeout { failTimeout $state $timeoutCode $unknownCode }
137335
+ eof { failEof $state $eofCode }
137336
+ }
137337
+ set cleaned [cleanScreen $dialogBuffer]
137338
+ }
137339
+ return $cleaned
137340
+ }
137341
+
137342
+ proc confirmKimi22Remove {} {
137343
+ global expect_out promptPattern removePlugin
137344
+ set dialog [readKimi22RemoveDialog $expect_out(buffer) remove-confirmation 125 127 126]
137345
+ set identity [kimi22RemoveIdentity $dialog]
137346
+ if {$identity eq "" || $identity ne $removePlugin} {
137347
+ puts stderr "KIMI_TUI_STATE:remove-confirmation:unknown"
137348
+ exit 127
137349
+ }
137350
+ set action [kimi22RemoveAction $dialog remove-confirmation 127]
137351
+ if {$action eq "move-down"} {
137352
+ send -- "\\033\\[B"
137353
+ } elseif {$action eq "move-up"} {
137354
+ send -- "\\033\\[A"
137355
+ }
137356
+ if {$action ne "selected-remove"} {
137357
+ set confirmedDialog [readKimi22RemoveDialog "" remove-confirm-selection 125 127 126]
137358
+ set confirmedIdentity [kimi22RemoveIdentity $confirmedDialog]
137359
+ if {$confirmedIdentity eq "" || $confirmedIdentity ne $removePlugin} {
137360
+ puts stderr "KIMI_TUI_STATE:remove-confirm-selection:unknown"
137361
+ exit 127
137362
+ }
137363
+ set confirmedAction [kimi22RemoveAction $confirmedDialog remove-confirm-selection 127]
137364
+ if {$confirmedAction ne "selected-remove"} {
137365
+ puts stderr "KIMI_TUI_STATE:remove-confirm-selection:unknown"
137366
+ exit 127
137367
+ }
137368
+ }
137369
+ send -- "\\033\\[13u"
137370
+ expect {
137371
+ -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
137372
+ -re $promptPattern {}
137373
+ timeout { failTimeout remove-prompt 128 130 }
137374
+ eof { failEof remove-prompt 129 }
137375
+ }
137376
+ }
137377
+
137378
+ submitCommand "/plugins remove $removePlugin"
137379
+ expect {
137380
+ -nocase -re {Confirm remove plugin "([^"\\r\\n]+)"\\?\\r*\\n} {
137381
+ if {$expect_out(1,string) ne $removePlugin} {
137382
+ puts stderr "KIMI_TUI_STATE:remove-confirmation:unknown"
137383
+ exit 127
137241
137384
  }
137385
+ send -- "\\033\\[B"
137386
+ send -- "\\033\\[13u"
137242
137387
  expect {
137243
137388
  -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
137244
137389
  -re $promptPattern {}
@@ -137246,6 +137391,9 @@ expect {
137246
137391
  eof { failEof remove-prompt 129 }
137247
137392
  }
137248
137393
  }
137394
+ -nocase -re {Remove [^\\r\\n]*\\([^\\r\\n]+\\)\\?} {
137395
+ confirmKimi22Remove
137396
+ }
137249
137397
  -re $promptPattern {}
137250
137398
  -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
137251
137399
  timeout { failTimeout remove-dialog 122 124 }
@@ -146909,7 +147057,7 @@ async function runPostReleaseFinish({
146909
147057
  }
146910
147058
  const setupSkill = loaded.config.releaseFinish?.setupSkill;
146911
147059
  const candidates = observations.filter((entry) => entry.installation === "current" && entry.loaded === true && typeof entry.skillFile === "string");
146912
- const candidateOwners = new Set(candidates.map((entry) => `${entry.plugin}\0${entry.version}\0${entry.skillFile}`));
147060
+ const candidateOwners = new Set(candidates.map((entry) => `${entry.unitId}\0${entry.plugin}\0${entry.version}`));
146913
147061
  const effectiveSetup = updateKeepsFeedback ? feedback.setup : null;
146914
147062
  let setupStep;
146915
147063
  if (!setupSkill) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.9.30",
3
+ "version": "0.9.31",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
@@ -2,11 +2,11 @@
2
2
  "schemaVersion": "1.0.0",
3
3
  "platformId": "release-skill",
4
4
  "platformName": "release-skill Plugin Platform",
5
- "version": "0.9.30",
5
+ "version": "0.9.31",
6
6
  "logicalRoot": "skills-src/",
7
- "logicalRootDigest": "1ea3c60db0fbc3c3943d2103f76730500886d0994d5e406e4438d6479df86b62",
8
- "projectionDigest": "77fdc2b733c05475ee6784e049ff4ce6b09dcd01f3d4391b3d264603827f1cfa",
9
- "projectionId": "release-skill:platform:0.9.30",
7
+ "logicalRootDigest": "5934bd95d2567e65a9c9191cd45b1d48e1612d3278042e141bf5827a63574d24",
8
+ "projectionDigest": "59041e4aaf5cc28da72684d294a4d8d1a2422ba6425abe16a81b6dff9ecc1eeb",
9
+ "projectionId": "release-skill:platform:0.9.31",
10
10
  "manifestRequired": true,
11
11
  "projectionSources": {
12
12
  "skills-src-root": {
@@ -29,9 +29,15 @@ description: "Discoverable entry point for release-skill: dependency and environ
29
29
 
30
30
  治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
31
31
 
32
- ## 0.9.30 能力与证据边界
32
+ ## 0.9.31 能力与证据边界
33
33
 
34
- release-skill 0.9.30 保留 Foundation 0.25.0 精确依赖、可选宿主覆盖诊断输入 `--host-coverage <file>` 与 `--observe-hosts`、Codex 0.154.0 静态加载规则检查 11 份真实生成 Skill 的原字节等既有事实;本版修复私有 Hub 的 postVerify 钩子:通过既有 `envAllowlist` 传递 `RELEASE_SKILL_HUB_RELEASE_EVIDENCE`,其值为原始描述符所指向的已有普通文件绝对路径(拒绝符号链接),交由 Hub `npm run release:check -- --release-evidence <file>` 验证。Qoder 本机更新路径处理维持既有能力,支持 `./adapters/qoder/skills/` 与 `./skills/` 两种布局,仍核对安装 name/version/skills、路径包含性与实际载荷。静态检查不证明安装、会话选择或业务执行通过,批准、发布和验证状态以对应计划、批准及执行记录为准。
34
+ 0.9.31 修复了 Kimi 2.2.0 的移除确认逻辑与共享环境 setup 归属规则。移除确认页采用 `Remove <displayName> (<id>)?` 格式,包含 Cancel 和 Remove plugin 选项;适配器精确核对插件 ID 与选中项,需移动时须先移动并重新观察选择再确认,已选中 `Remove plugin` 时无需方向键输入,仅在该选项激活时提交移除操作。显示名含 Confirm 或 Sure,或标题分块到达,不再触发旧宽泛正则路径;错误 ID、未知或歧义选择、timeout 或 EOF 均导致失败关闭,不继续安装。
35
+
36
+ 保留 Kimi 0.40.1 的实际确认措辞 `Confirm remove plugin "<id>"?`、括号粘贴与 CSI Enter 输入方式。冻结的安装 URL/ref/sha、安装后 name/version 与载荷核对规则未变。本轮验证为离线 Expect/fake 测试,不构成真实 Kimi 消费者验收。
37
+
38
+ 同一 `unitId/plugin/version` 的共享环境 setup 按单元、插件、版本归并,Claude/Codex 不同物理 `skillFile` 路径不再视为多个 setup 归属。仅需一个已加载宿主的精确 setup 反馈即可满足共享环境检查要求。各宿主的安装、加载及专属配置仍需分别核实,反馈继续绑定 `host/unit/path/plan/config/root`;真正多插件归属仍需消歧,Kimi 失败或其他宿主失败均导致收尾状态为 FAILED,不改变 `VERIFIED`/`DISTRIBUTED` 发布状态。
39
+
40
+ 本版保留 Foundation 0.25.0 精确依赖、可选宿主覆盖诊断输入 `--host-coverage <file>` 与 `--observe-hosts`、Codex 0.154.0 对 11 份真实生成 Skill 的原字节静态检查能力。静态检查不证明安装、会话选择或业务执行通过,批准、发布和验证状态以对应计划、批准及执行记录为准。Qoder 本机更新路径处理维持既有能力,支持 `./adapters/qoder/skills/` 与 `./skills/` 两种布局,仍核对安装 name/version/skills、路径包含性与实际载荷。私有 Hub 的 `RELEASE_SKILL_HUB_RELEASE_EVIDENCE` 环境变量继续要求原始描述符指向的已有普通文件绝对路径(拒绝符号链接),交由 `npm run release:check -- --release-evidence <file>` 验证。实际版本能力不能等同于发布状态或真实宿主验收。
35
41
 
36
42
  在冻结前,`prepare` 或新建 `ship` 状态时可重复传入 `--unit <id>`;未传入则默认选择全部单元。延期单元不进入发布计划,亦不获得发布状态。完整配置、生成物新鲜度检查及顶层 Hook 仍作用于全项目范围。`publicSourceAuthorityReceipt` 的 coordinator 与全部 subjects 必须共同选择。
37
43
 
@@ -29,9 +29,15 @@ description: "Discoverable entry point for release-skill: dependency and environ
29
29
 
30
30
  治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
31
31
 
32
- ## 0.9.30 能力与证据边界
32
+ ## 0.9.31 能力与证据边界
33
33
 
34
- release-skill 0.9.30 保留 Foundation 0.25.0 精确依赖、可选宿主覆盖诊断输入 `--host-coverage <file>` 与 `--observe-hosts`、Codex 0.154.0 静态加载规则检查 11 份真实生成 Skill 的原字节等既有事实;本版修复私有 Hub 的 postVerify 钩子:通过既有 `envAllowlist` 传递 `RELEASE_SKILL_HUB_RELEASE_EVIDENCE`,其值为原始描述符所指向的已有普通文件绝对路径(拒绝符号链接),交由 Hub `npm run release:check -- --release-evidence <file>` 验证。Qoder 本机更新路径处理维持既有能力,支持 `./adapters/qoder/skills/` 与 `./skills/` 两种布局,仍核对安装 name/version/skills、路径包含性与实际载荷。静态检查不证明安装、会话选择或业务执行通过,批准、发布和验证状态以对应计划、批准及执行记录为准。
34
+ 0.9.31 修复了 Kimi 2.2.0 的移除确认逻辑与共享环境 setup 归属规则。移除确认页采用 `Remove <displayName> (<id>)?` 格式,包含 Cancel 和 Remove plugin 选项;适配器精确核对插件 ID 与选中项,需移动时须先移动并重新观察选择再确认,已选中 `Remove plugin` 时无需方向键输入,仅在该选项激活时提交移除操作。显示名含 Confirm 或 Sure,或标题分块到达,不再触发旧宽泛正则路径;错误 ID、未知或歧义选择、timeout 或 EOF 均导致失败关闭,不继续安装。
35
+
36
+ 保留 Kimi 0.40.1 的实际确认措辞 `Confirm remove plugin "<id>"?`、括号粘贴与 CSI Enter 输入方式。冻结的安装 URL/ref/sha、安装后 name/version 与载荷核对规则未变。本轮验证为离线 Expect/fake 测试,不构成真实 Kimi 消费者验收。
37
+
38
+ 同一 `unitId/plugin/version` 的共享环境 setup 按单元、插件、版本归并,Claude/Codex 不同物理 `skillFile` 路径不再视为多个 setup 归属。仅需一个已加载宿主的精确 setup 反馈即可满足共享环境检查要求。各宿主的安装、加载及专属配置仍需分别核实,反馈继续绑定 `host/unit/path/plan/config/root`;真正多插件归属仍需消歧,Kimi 失败或其他宿主失败均导致收尾状态为 FAILED,不改变 `VERIFIED`/`DISTRIBUTED` 发布状态。
39
+
40
+ 本版保留 Foundation 0.25.0 精确依赖、可选宿主覆盖诊断输入 `--host-coverage <file>` 与 `--observe-hosts`、Codex 0.154.0 对 11 份真实生成 Skill 的原字节静态检查能力。静态检查不证明安装、会话选择或业务执行通过,批准、发布和验证状态以对应计划、批准及执行记录为准。Qoder 本机更新路径处理维持既有能力,支持 `./adapters/qoder/skills/` 与 `./skills/` 两种布局,仍核对安装 name/version/skills、路径包含性与实际载荷。私有 Hub 的 `RELEASE_SKILL_HUB_RELEASE_EVIDENCE` 环境变量继续要求原始描述符指向的已有普通文件绝对路径(拒绝符号链接),交由 `npm run release:check -- --release-evidence <file>` 验证。实际版本能力不能等同于发布状态或真实宿主验收。
35
41
 
36
42
  在冻结前,`prepare` 或新建 `ship` 状态时可重复传入 `--unit <id>`;未传入则默认选择全部单元。延期单元不进入发布计划,亦不获得发布状态。完整配置、生成物新鲜度检查及顶层 Hook 仍作用于全项目范围。`publicSourceAuthorityReceipt` 的 coordinator 与全部 subjects 必须共同选择。
37
43
 
@@ -372,7 +372,7 @@ export async function runPostReleaseFinish({
372
372
  const candidates = observations.filter((entry) => (
373
373
  entry.installation === 'current' && entry.loaded === true && typeof entry.skillFile === 'string'
374
374
  ));
375
- const candidateOwners = new Set(candidates.map((entry) => `${entry.plugin}\u0000${entry.version}\u0000${entry.skillFile}`));
375
+ const candidateOwners = new Set(candidates.map((entry) => `${entry.unitId}\u0000${entry.plugin}\u0000${entry.version}`));
376
376
  const effectiveSetup = updateKeepsFeedback ? feedback.setup : null;
377
377
  let setupStep;
378
378
  if (!setupSkill) {
@@ -1476,17 +1476,162 @@ function parseCodeBuddyRemoteObservation(target, stdout) {
1476
1476
 
1477
1477
  function kimiExpectProgram({ removePlugin } = {}) {
1478
1478
  const removeCommand = removePlugin
1479
- ? `submitCommand "/plugins remove $removePlugin"
1480
- expect {
1481
- -nocase -re {(remove|delete|uninstall).*(confirm|sure)|(confirm|sure).*(remove|delete|uninstall)} {
1479
+ ? `proc kimi22RemoveIdentity {dialog} {
1480
+ set identity ""
1481
+ foreach rawLine [split $dialog "\\n"] {
1482
+ set line [string trim $rawLine]
1483
+ if {[regexp -nocase {Remove .+ \\(([^()]+)\\)\\?} $line match pluginId]} {
1484
+ if {$identity ne "" && $identity ne $pluginId} { return "" }
1485
+ set identity $pluginId
1486
+ }
1487
+ }
1488
+ return $identity
1489
+ }
1490
+
1491
+ proc kimi22RemoveDialogComplete {dialog} {
1492
+ set hasCancel 0
1493
+ set hasRemove 0
1494
+ foreach rawLine [split $dialog "\\n"] {
1495
+ set line [string trim $rawLine]
1496
+ regsub {^❯[ \\t]*} $line {} label
1497
+ set label [string trim $label]
1498
+ if {[string equal -nocase $label "Cancel"]} { set hasCancel 1 }
1499
+ if {[string equal -nocase $label "Remove plugin"]} { set hasRemove 1 }
1500
+ }
1501
+ return [expr {$hasCancel && $hasRemove}]
1502
+ }
1503
+
1504
+ proc kimi22RemoveAction {dialog state unknownCode} {
1505
+ set labels {}
1506
+ set selectedCount 0
1507
+ set selectedIndex -1
1508
+ set selectedLabel ""
1509
+ set removeCount 0
1510
+ set removeIndex -1
1511
+ foreach rawLine [split $dialog "\\n"] {
1512
+ set line [string trim $rawLine]
1513
+ if {$line eq ""} { continue }
1514
+ set selected [regexp {^❯[ \\t]*} $line]
1515
+ if {$selected} {
1516
+ regsub {^❯[ \\t]*} $line {} label
1517
+ set label [string trim $label]
1518
+ } else {
1519
+ set label $line
1520
+ }
1521
+ set knownAction [expr {
1522
+ [string equal -nocase $label "Cancel"]
1523
+ || [string equal -nocase $label "Remove plugin"]
1524
+ }]
1525
+ if {!$knownAction && $selected} {
1526
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
1527
+ exit $unknownCode
1528
+ }
1529
+ if {!$knownAction} { continue }
1530
+ set index [llength $labels]
1531
+ lappend labels $label
1532
+ if {[string equal -nocase $label "Remove plugin"]} {
1533
+ incr removeCount
1534
+ set removeIndex $index
1535
+ }
1536
+ if {$selected} {
1537
+ incr selectedCount
1538
+ set selectedIndex $index
1539
+ set selectedLabel $label
1540
+ }
1541
+ }
1542
+ if {[llength $labels] != 2 || $removeCount != 1 || $selectedCount != 1} {
1543
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
1544
+ exit $unknownCode
1545
+ }
1546
+ if {[string equal -nocase $selectedLabel "Remove plugin"]} {
1547
+ return selected-remove
1548
+ }
1549
+ if {![string equal -nocase $selectedLabel "Cancel"]} {
1550
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
1551
+ exit $unknownCode
1552
+ }
1553
+ if {$selectedIndex + 1 == $removeIndex} { return move-down }
1554
+ if {$selectedIndex - 1 == $removeIndex} { return move-up }
1555
+ puts stderr "KIMI_TUI_STATE:$state:unknown"
1556
+ exit $unknownCode
1557
+ }
1558
+
1559
+ proc readKimi22RemoveDialog {prefix state timeoutCode unknownCode eofCode} {
1560
+ global expect_out
1561
+ set dialogBuffer $prefix
1562
+ set cleaned [cleanScreen $dialogBuffer]
1563
+ if {![regexp -nocase {Remove .+ \\(([^()]+)\\)\\?} $cleaned]} {
1564
+ expect {
1565
+ -nocase -re {Remove [^\\r\\n]*\\([^\\r\\n]+\\)\\?} {
1566
+ append dialogBuffer $expect_out(buffer)
1567
+ }
1568
+ timeout { failTimeout $state $timeoutCode $unknownCode }
1569
+ eof { failEof $state $eofCode }
1570
+ }
1571
+ set cleaned [cleanScreen $dialogBuffer]
1572
+ }
1573
+ while {![kimi22RemoveDialogComplete $cleaned]} {
1482
1574
  expect {
1483
- -ex $removePlugin {
1484
- send -- "\\033\\[B"
1485
- send -- "\\033\\[13u"
1575
+ -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
1576
+ -nocase -re {(?:Cancel|Remove plugin)[^\\r\\n]*\\r*\\n} {
1577
+ append dialogBuffer $expect_out(buffer)
1486
1578
  }
1487
- timeout { failTimeout remove-confirmation 125 127 }
1488
- eof { failEof remove-confirmation 126 }
1579
+ -re {([^\\r\\n]+\\r*\\n)} {
1580
+ append dialogBuffer $expect_out(buffer)
1581
+ }
1582
+ timeout { failTimeout $state $timeoutCode $unknownCode }
1583
+ eof { failEof $state $eofCode }
1584
+ }
1585
+ set cleaned [cleanScreen $dialogBuffer]
1586
+ }
1587
+ return $cleaned
1588
+ }
1589
+
1590
+ proc confirmKimi22Remove {} {
1591
+ global expect_out promptPattern removePlugin
1592
+ set dialog [readKimi22RemoveDialog $expect_out(buffer) remove-confirmation 125 127 126]
1593
+ set identity [kimi22RemoveIdentity $dialog]
1594
+ if {$identity eq "" || $identity ne $removePlugin} {
1595
+ puts stderr "KIMI_TUI_STATE:remove-confirmation:unknown"
1596
+ exit 127
1597
+ }
1598
+ set action [kimi22RemoveAction $dialog remove-confirmation 127]
1599
+ if {$action eq "move-down"} {
1600
+ send -- "\\033\\[B"
1601
+ } elseif {$action eq "move-up"} {
1602
+ send -- "\\033\\[A"
1603
+ }
1604
+ if {$action ne "selected-remove"} {
1605
+ set confirmedDialog [readKimi22RemoveDialog "" remove-confirm-selection 125 127 126]
1606
+ set confirmedIdentity [kimi22RemoveIdentity $confirmedDialog]
1607
+ if {$confirmedIdentity eq "" || $confirmedIdentity ne $removePlugin} {
1608
+ puts stderr "KIMI_TUI_STATE:remove-confirm-selection:unknown"
1609
+ exit 127
1610
+ }
1611
+ set confirmedAction [kimi22RemoveAction $confirmedDialog remove-confirm-selection 127]
1612
+ if {$confirmedAction ne "selected-remove"} {
1613
+ puts stderr "KIMI_TUI_STATE:remove-confirm-selection:unknown"
1614
+ exit 127
1615
+ }
1616
+ }
1617
+ send -- "\\033\\[13u"
1618
+ expect {
1619
+ -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
1620
+ -re $promptPattern {}
1621
+ timeout { failTimeout remove-prompt 128 130 }
1622
+ eof { failEof remove-prompt 129 }
1623
+ }
1624
+ }
1625
+
1626
+ submitCommand "/plugins remove $removePlugin"
1627
+ expect {
1628
+ -nocase -re {Confirm remove plugin "([^"\\r\\n]+)"\\?\\r*\\n} {
1629
+ if {$expect_out(1,string) ne $removePlugin} {
1630
+ puts stderr "KIMI_TUI_STATE:remove-confirmation:unknown"
1631
+ exit 127
1489
1632
  }
1633
+ send -- "\\033\\[B"
1634
+ send -- "\\033\\[13u"
1490
1635
  expect {
1491
1636
  -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
1492
1637
  -re $promptPattern {}
@@ -1494,6 +1639,9 @@ expect {
1494
1639
  eof { failEof remove-prompt 129 }
1495
1640
  }
1496
1641
  }
1642
+ -nocase -re {Remove [^\\r\\n]*\\([^\\r\\n]+\\)\\?} {
1643
+ confirmKimi22Remove
1644
+ }
1497
1645
  -re $promptPattern {}
1498
1646
  -nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
1499
1647
  timeout { failTimeout remove-dialog 122 124 }