From 57e23d661d11f1adecee68e140ea8a8b7d9fd19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Wed, 12 Aug 2026 10:57:37 +0800 Subject: [PATCH 1/5] chore(deps): upgrade DingTalk Stream SDK to v0.9.2-beta.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7555e86f..c26fe31f 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/itchyny/gojq v0.12.18 github.com/mattn/go-isatty v0.0.20 github.com/muesli/termenv v0.16.0 - github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-0.20260705041131-325e7c1049ad + github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-beta.1 github.com/spf13/cobra v1.10.2 github.com/zalando/go-keyring v0.2.8 golang.org/x/crypto v0.49.0 diff --git a/go.sum b/go.sum index 20ce02f0..99e029e0 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-0.20260705041131-325e7c1049ad h1:Bb4I+suYd+ehQ8e22aimLLze+5XTN3+WTc/x2LafmH8= -github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-0.20260705041131-325e7c1049ad/go.mod h1:ln3IqPYYocZbYvl9TAOrG/cxGR9xcn4pnZRLdCTEGEU= +github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-beta.1 h1:5WwR5TV6A12taXMH7SggT8yCMMJMF9jWE7Wj+4AuHck= +github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-beta.1/go.mod h1:ln3IqPYYocZbYvl9TAOrG/cxGR9xcn4pnZRLdCTEGEU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= From 276ab52aedb7b7cacfaf29b697280ef5675ee1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E7=9C=9F?= Date: Wed, 12 Aug 2026 12:14:10 +0800 Subject: [PATCH 2/5] chore(interface): add im id flag migration pending approvals to #85200556 --- .../approved-flag-migrations-v1.json | 1131 ++++++++++++++++- 1 file changed, 1130 insertions(+), 1 deletion(-) diff --git a/scripts/policy/interface-migrations/approved-flag-migrations-v1.json b/scripts/policy/interface-migrations/approved-flag-migrations-v1.json index 5ad17072..7a0b9a89 100644 --- a/scripts/policy/interface-migrations/approved-flag-migrations-v1.json +++ b/scripts/policy/interface-migrations/approved-flag-migrations-v1.json @@ -1 +1,1130 @@ -{"version":1,"migrations":[]} +{ + "version": 1, + "migrations": [ + { + "command": "dws chat group audit-join-validation", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group bots", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group dismiss", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group invite-url", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group notice create", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group notice edit", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group notice get", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group notice list", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group quit", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group set-admin", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group set-history", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group transfer-owner", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group update-alias", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group update-icon", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group update-nick", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group update-settings", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group upgrade-to-external", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role add", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role list", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role query-user", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role remove-user", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role remove", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role set-user", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat group-role update", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message edit", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message forward", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message list-topic-replies", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "hidden": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message recall", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message set-pin-msg", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message set-top-msg", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message unset-pin-msg", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message unset-top-msg", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws chat message update-text-emotion", + "legacy": { + "name": "msg-id", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true, + "hidden": true, + "alias_of": "message-id" + } + }, + "canonical": { + "name": "message-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + } + ] +} From 88e2f8e9e22b12bc98c31ede0449969add630544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E7=9C=9F?= Date: Wed, 12 Aug 2026 14:40:52 +0800 Subject: [PATCH 3/5] chore(interface): address migration approval review feedback to #85200556 --- docs/cli-interface-flag-migrations.md | 2 +- .../approved-flag-migrations-v1.json | 66 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/cli-interface-flag-migrations.md b/docs/cli-interface-flag-migrations.md index ada97b55..d5b0f5fa 100644 --- a/docs/cli-interface-flag-migrations.md +++ b/docs/cli-interface-flag-migrations.md @@ -41,7 +41,7 @@ PR merge-base 同时拥有快照生成器、比较器和已审批清单。门禁 scripts/policy/interface-migrations/approved-flag-migrations-v1.json ``` -清单使用严格 JSON 解析:版本、字段名大小写、JSON 值类型、命令路径和 flag 名都必须精确;拒绝重复键、未知键、scalar `null` 与尾随 JSON 值,`reason` 不能为空;禁止 `*`、`?`、前缀规则或其他 wildcard。当前清单为空,因此本治理 PR **不授权 PR #904 或任何产品接口变化**。 +清单使用严格 JSON 解析:版本、字段名大小写、JSON 值类型、命令路径和 flag 名都必须精确;拒绝重复键、未知键、scalar `null` 与尾随 JSON 值,`reason` 不能为空;禁止 `*`、`?`、前缀规则或其他 wildcard。当前清单登记了 IM ID rename 的 `pending` 记录;`pending` 只记录已评审计划,候选与 merge-base 仍必须精确匹配 `before`,因此本治理 PR **不授权 PR #904 或任何产品接口变化**。 ## 两阶段迁移与回执清理 diff --git a/scripts/policy/interface-migrations/approved-flag-migrations-v1.json b/scripts/policy/interface-migrations/approved-flag-migrations-v1.json index 7a0b9a89..d52e59e4 100644 --- a/scripts/policy/interface-migrations/approved-flag-migrations-v1.json +++ b/scripts/policy/interface-migrations/approved-flag-migrations-v1.json @@ -15,7 +15,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -49,7 +48,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -83,7 +81,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -117,7 +114,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -151,7 +147,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -185,7 +180,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -219,7 +213,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -253,7 +246,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -287,7 +279,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -321,7 +312,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -355,7 +345,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -389,7 +378,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -423,7 +411,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -457,7 +444,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -491,7 +477,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -525,7 +510,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -559,7 +543,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -593,7 +576,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -627,7 +609,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -661,7 +642,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -695,7 +675,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -729,7 +708,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -763,7 +741,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -797,7 +774,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -831,7 +807,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -865,7 +840,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -899,7 +873,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "conversation-id" } @@ -936,7 +909,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -970,7 +942,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -1004,7 +975,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -1038,7 +1008,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -1072,7 +1041,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -1106,7 +1074,6 @@ "present": true, "type": "string", "scope": "local", - "required": true, "hidden": true, "alias_of": "message-id" } @@ -1125,6 +1092,39 @@ }, "state": "pending", "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" + }, + { + "command": "dws ding message send-by-message", + "legacy": { + "name": "group", + "before": { + "present": true, + "type": "string", + "scope": "local", + "required": true + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "hidden": true, + "alias_of": "conversation-id" + } + }, + "canonical": { + "name": "conversation-id", + "before": { + "present": false + }, + "after": { + "present": true, + "type": "string", + "scope": "local", + "required": true + } + }, + "state": "pending", + "reason": "保留旧 argv 兼容性,并将 IM ID 规范 flag 设为唯一可见入口" } ] } From 6e4ea0980f8554dfb41855f8bfc1e2f58e6a9cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Wed, 12 Aug 2026 15:34:38 +0800 Subject: [PATCH 4/5] feat(skill): support ZCode skill root --- .changes/zcode-skill-root.md | 5 ++ README.md | 2 +- README_zh.md | 2 +- build/npm/install.js | 1 + internal/app/skill_command.go | 1 + internal/app/skill_command_test.go | 2 +- internal/app/skill_setup.go | 1 + internal/app/skill_setup_test.go | 26 ++++++ internal/upgrade/paths.go | 1 + internal/upgrade/paths_multi_test.go | 30 +++++++ scripts/install-skills.sh | 6 +- scripts/install.ps1 | 1 + scripts/install.sh | 6 +- scripts/release/verify-package-managers.sh | 2 + test/scripts/install_js_smoke.mjs | 27 +++++++ test/scripts/install_script_test.go | 94 ++++++++++++++++++++++ test/scripts/package_script_test.go | 1 + 17 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 .changes/zcode-skill-root.md diff --git a/.changes/zcode-skill-root.md b/.changes/zcode-skill-root.md new file mode 100644 index 00000000..f3623960 --- /dev/null +++ b/.changes/zcode-skill-root.md @@ -0,0 +1,5 @@ +--- +category: Added +--- + +- **ZCode Skill installation** — installs bundled and marketplace Skills into ZCode's canonical `~/.zcode/skills` directory. diff --git a/README.md b/README.md index e55d3104..b29410ae 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,7 @@ DWS_SKILL_SOURCE=/path/to/skills dws skill setup --mode multi | Flag | Values | Description | |------|--------|-------------| | `--mode` | `mono` \| `multi` | Skill layout; defaults to interactive prompt | -| `--target` | `all` \| `claude` \| `cursor` \| `codex` \| `opencode` \| `qoder` | Where to install; `all` covers every detected agent home | +| `--target` | `all` \| `claude` \| `cursor` \| `codex` \| `zcode` \| `opencode` \| `qoder` | Where to install; `all` covers every detected agent home, including ZCode at `~/.zcode/skills` | | `--source` | path | Local source directory (overrides bundled skills) | | `--yes` | — | Scripting-only: skip the confirmation prompt. Removals are still backed up to `~/.dws/skill-backups/` first | diff --git a/README_zh.md b/README_zh.md index c82a9228..b61cb62d 100644 --- a/README_zh.md +++ b/README_zh.md @@ -427,7 +427,7 @@ DWS_SKILL_SOURCE=/path/to/skills dws skill setup --mode multi | 参数 | 取值 | 说明 | |------|------|------| | `--mode` | `mono` \| `multi` | skill 布局,不指定则交互式询问 | -| `--target` | `all` \| `claude` \| `cursor` \| `codex` \| `opencode` \| `qoder` | 安装目标;`all` 表示铺到检测到的具体 Agent home,仅在未检测到具体 Agent 时回退到 `~/.agents/skills` | +| `--target` | `all` \| `claude` \| `cursor` \| `codex` \| `zcode` \| `opencode` \| `qoder` | 安装目标;`all` 表示铺到检测到的具体 Agent home(ZCode 为 `~/.zcode/skills`),仅在未检测到具体 Agent 时回退到 `~/.agents/skills` | | `--source` | 路径 | 本地源目录(覆盖内置 skills) | | `--yes` | — | 仅供脚本使用:跳过确认提示。删除操作仍会先备份到 `~/.dws/skill-backups/` | diff --git a/build/npm/install.js b/build/npm/install.js index 364982c0..49e9fd7f 100755 --- a/build/npm/install.js +++ b/build/npm/install.js @@ -17,6 +17,7 @@ const AGENT_DIRS = [ ".qoderwork/skills", ".gemini/skills", ".codex/skills", + ".zcode/skills", ".github/skills", ".windsurf/skills", ".augment/skills", diff --git a/internal/app/skill_command.go b/internal/app/skill_command.go index 81a39d50..8db71d07 100644 --- a/internal/app/skill_command.go +++ b/internal/app/skill_command.go @@ -126,6 +126,7 @@ var agentSkillPaths = map[string]string{ "claude": ".claude/skills", "cursor": ".cursor/skills", "codex": ".codex/skills", + "zcode": ".zcode/skills", "opencode": filepath.Join(".config", "opencode", "skills"), // IDE / agent registries also probed by `dws skill setup --target all`. "gemini": ".gemini/skills", diff --git a/internal/app/skill_command_test.go b/internal/app/skill_command_test.go index 1fd5094e..381c87bc 100644 --- a/internal/app/skill_command_test.go +++ b/internal/app/skill_command_test.go @@ -452,7 +452,7 @@ func TestSupportedTargets(t *testing.T) { // Should contain all predefined targets — including the agents/* sentinel // and the IDE/agent registries we share with skillSetupAgentHomes. expectedTargets := []string{ - "agents", "claude", "cursor", "codex", "opencode", "qoder", + "agents", "claude", "cursor", "codex", "zcode", "opencode", "qoder", "gemini", "github", "windsurf", "augment", "cline", "amp", "kiro", "trae", "openclaw", "hermes", ".", diff --git a/internal/app/skill_setup.go b/internal/app/skill_setup.go index f3391b90..62ab2c17 100644 --- a/internal/app/skill_setup.go +++ b/internal/app/skill_setup.go @@ -29,6 +29,7 @@ var skillSetupAgentHomes = []string{ ".qoderwork/skills", ".gemini/skills", ".codex/skills", + ".zcode/skills", ".github/skills", ".windsurf/skills", ".augment/skills", diff --git a/internal/app/skill_setup_test.go b/internal/app/skill_setup_test.go index 297b6274..2a776355 100644 --- a/internal/app/skill_setup_test.go +++ b/internal/app/skill_setup_test.go @@ -348,6 +348,32 @@ func TestCrossPlatformCoverageResolveSkillSetupTargetsPrefersSpecificAgentRoot(t } } +func TestCrossPlatformCoverageResolveSkillSetupTargetsDetectsZCode(t *testing.T) { + home := t.TempDir() + testseam.Swap(t, &skillSetupUserHomeDir, func() (string, error) { return home, nil }) + if err := os.MkdirAll(filepath.Join(home, ".zcode"), 0o755); err != nil { + t.Fatal(err) + } + + got, err := resolveSkillSetupTargets("all", skillSetupModeMulti) + if err != nil { + t.Fatal(err) + } + want := filepath.Join(home, ".zcode", "skills") + if len(got) != 1 || filepath.Clean(got[0]) != filepath.Clean(want) { + t.Fatalf("targets = %v, want [%s]", got, want) + } + + explicit, err := resolveSkillSetupTargets("zcode", skillSetupModeMono) + if err != nil { + t.Fatal(err) + } + wantMono := filepath.Join(want, "dws") + if len(explicit) != 1 || filepath.Clean(explicit[0]) != filepath.Clean(wantMono) { + t.Fatalf("explicit zcode targets = %v, want [%s]", explicit, wantMono) + } +} + func TestCrossPlatformCoverageInstallSkillToHomesEndToEnd(t *testing.T) { src := t.TempDir() if err := os.WriteFile(filepath.Join(src, "SKILL.md"), []byte("# test"), 0o644); err != nil { diff --git a/internal/upgrade/paths.go b/internal/upgrade/paths.go index 16c4d74b..a83309d3 100644 --- a/internal/upgrade/paths.go +++ b/internal/upgrade/paths.go @@ -47,6 +47,7 @@ var knownSkillDirs = []string{ ".qoderwork/skills", ".gemini/skills", ".codex/skills", + ".zcode/skills", ".github/skills", ".windsurf/skills", ".augment/skills", diff --git a/internal/upgrade/paths_multi_test.go b/internal/upgrade/paths_multi_test.go index f76fb8a2..ec55c21d 100644 --- a/internal/upgrade/paths_multi_test.go +++ b/internal/upgrade/paths_multi_test.go @@ -255,6 +255,36 @@ func TestCrossPlatformCoverageUpgradeUsesAgentSpecificRootWithoutGenericDuplicat } } +func TestCrossPlatformCoverageUpgradeUsesZCodeRootWithoutGenericDuplicate(t *testing.T) { + home := withFakeHome(t) + testseam.Swap(t, &knownSkillDirs, []string{".agents/skills", ".zcode/skills"}) + + genericBase := filepath.Join(home, ".agents", "skills") + zcodeBase := filepath.Join(home, ".zcode", "skills") + if err := os.MkdirAll(zcodeBase, 0o755); err != nil { + t.Fatal(err) + } + legacyNested := filepath.Join(genericBase, "dws", "multi", "dingtalk-chat") + if err := os.MkdirAll(legacyNested, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(legacyNested, "SKILL.md"), []byte("old nested"), 0o644); err != nil { + t.Fatal(err) + } + + multiRoot := writeMultiBundle(t, t.TempDir(), "dingtalk-chat") + result, err := UpgradeSkillLocationsWithOptions(multiRoot, SkillUpgradeOptions{Version: "1.2.3"}) + if err != nil || len(result.Failed()) != 0 { + t.Fatalf("UpgradeSkillLocationsWithOptions() = %#v, %v", result, err) + } + if _, err := os.Stat(filepath.Join(zcodeBase, "dingtalk-chat", "SKILL.md")); err != nil { + t.Fatalf("canonical ZCode Skill missing: %v", err) + } + if _, err := os.Stat(filepath.Join(genericBase, "dws")); !os.IsNotExist(err) { + t.Fatalf("generic duplicate still visible: %v", err) + } +} + func TestCrossPlatformCoverageUpgradeSkillLocationsMultiFallbackPrimary(t *testing.T) { home := withFakeHome(t) // No agent parent dirs at all: only .agents (index 0) is attempted and the diff --git a/scripts/install-skills.sh b/scripts/install-skills.sh index a4563dff..ad4eeb03 100755 --- a/scripts/install-skills.sh +++ b/scripts/install-skills.sh @@ -441,7 +441,7 @@ install_multi_skills_to_root() { specific_agents=0 for specific_dir in \ ".claude/skills" ".cursor/skills" ".qoder/skills" ".qoderwork/skills" \ - ".gemini/skills" ".codex/skills" ".github/skills" ".windsurf/skills" \ + ".gemini/skills" ".codex/skills" ".zcode/skills" ".github/skills" ".windsurf/skills" \ ".augment/skills" ".cline/skills" ".amp/skills" ".kiro/skills" \ ".trae/skills" ".openclaw/skills" ".hermes/skills" do @@ -455,6 +455,7 @@ install_multi_skills_to_root() { ".qoderwork/skills" \ ".gemini/skills" \ ".codex/skills" \ + ".zcode/skills" \ ".github/skills" \ ".windsurf/skills" \ ".augment/skills" \ @@ -659,7 +660,7 @@ install_skills_to_root() { specific_agents=0 for specific_dir in \ ".claude/skills" ".cursor/skills" ".qoder/skills" ".qoderwork/skills" \ - ".gemini/skills" ".codex/skills" ".github/skills" ".windsurf/skills" \ + ".gemini/skills" ".codex/skills" ".zcode/skills" ".github/skills" ".windsurf/skills" \ ".augment/skills" ".cline/skills" ".amp/skills" ".kiro/skills" \ ".trae/skills" ".openclaw/skills" ".hermes/skills" do @@ -673,6 +674,7 @@ install_skills_to_root() { ".qoderwork/skills" \ ".gemini/skills" \ ".codex/skills" \ + ".zcode/skills" \ ".github/skills" \ ".windsurf/skills" \ ".augment/skills" \ diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 8bcd5d80..d9c62a1b 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -61,6 +61,7 @@ $AgentDirs = @( ".qoderwork\skills", ".gemini\skills", ".codex\skills", + ".zcode\skills", ".github\skills", ".windsurf\skills", ".augment\skills", diff --git a/scripts/install.sh b/scripts/install.sh index f7ef7fea..016055fe 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -679,7 +679,7 @@ install_skills_to_homes() { specific_agents=0 for specific_dir in \ ".claude/skills" ".cursor/skills" ".qoder/skills" ".qoderwork/skills" \ - ".gemini/skills" ".codex/skills" ".github/skills" ".windsurf/skills" \ + ".gemini/skills" ".codex/skills" ".zcode/skills" ".github/skills" ".windsurf/skills" \ ".augment/skills" ".cline/skills" ".amp/skills" ".kiro/skills" \ ".trae/skills" ".openclaw/skills" ".hermes/skills" do @@ -693,6 +693,7 @@ install_skills_to_homes() { ".qoderwork/skills" \ ".gemini/skills" \ ".codex/skills" \ + ".zcode/skills" \ ".github/skills" \ ".windsurf/skills" \ ".augment/skills" \ @@ -788,7 +789,7 @@ install_multi_skills_to_homes() { specific_agents=0 for specific_dir in \ ".claude/skills" ".cursor/skills" ".qoder/skills" ".qoderwork/skills" \ - ".gemini/skills" ".codex/skills" ".github/skills" ".windsurf/skills" \ + ".gemini/skills" ".codex/skills" ".zcode/skills" ".github/skills" ".windsurf/skills" \ ".augment/skills" ".cline/skills" ".amp/skills" ".kiro/skills" \ ".trae/skills" ".openclaw/skills" ".hermes/skills" do @@ -802,6 +803,7 @@ install_multi_skills_to_homes() { ".qoderwork/skills" \ ".gemini/skills" \ ".codex/skills" \ + ".zcode/skills" \ ".github/skills" \ ".windsurf/skills" \ ".augment/skills" \ diff --git a/scripts/release/verify-package-managers.sh b/scripts/release/verify-package-managers.sh index 5944a7c5..631286d7 100755 --- a/scripts/release/verify-package-managers.sh +++ b/scripts/release/verify-package-managers.sh @@ -61,6 +61,7 @@ HOME_AGENT_PARENTS=" .qoderwork .gemini .codex +.zcode .github .windsurf .augment @@ -79,6 +80,7 @@ HOME_SKILL_BASES=" .qoderwork/skills .gemini/skills .codex/skills +.zcode/skills .github/skills .windsurf/skills .augment/skills diff --git a/test/scripts/install_js_smoke.mjs b/test/scripts/install_js_smoke.mjs index 6bf29d82..b94bb679 100644 --- a/test/scripts/install_js_smoke.mjs +++ b/test/scripts/install_js_smoke.mjs @@ -225,6 +225,33 @@ scenario("Codex uses its canonical root without a generic duplicate", () => { } }); +scenario("ZCode uses its canonical root without a generic duplicate", () => { + const { tmp, pkg, home } = stagePkg({ + "mono/SKILL.md": "# mono fixture\n", + "multi/dingtalk-chat/SKILL.md": "# dingtalk-chat\n", + }); + try { + writeFile(path.join(home, ".zcode", "v2", "config.json"), "{}\n"); + writeFile( + path.join(home, ".agents", "skills", "dws", "multi", "dingtalk-chat", "SKILL.md"), + "old nested duplicate\n", + ); + + const res = runInstall(pkg, home, "multi"); + assert.equal(res.status, 0, `exit=${res.status}\nstdout=${res.stdout}\nstderr=${res.stderr}`); + assert.ok( + fs.existsSync(path.join(home, ".zcode", "skills", "dingtalk-chat", "SKILL.md")), + "ZCode canonical Skill installed", + ); + assert.ok( + !fs.existsSync(path.join(home, ".agents", "skills", "dws")), + "legacy generic duplicate retired", + ); + } finally { + fs.rmSync(tmp, { recursive: true, force: true }); + } +}); + scenario("empty multi/ tree falls back to mono and keeps the old multi cache", () => { const { tmp, pkg, home } = stagePkg({ "SKILL.md": "# mono root copy\n", diff --git a/test/scripts/install_script_test.go b/test/scripts/install_script_test.go index c6295d06..d6e299a5 100644 --- a/test/scripts/install_script_test.go +++ b/test/scripts/install_script_test.go @@ -1292,6 +1292,51 @@ func TestInstallerShellPrefersCodexRootWithoutGenericDuplicate(t *testing.T) { } } +func TestInstallerShellPrefersZCodeRootWithoutGenericDuplicate(t *testing.T) { + for _, scriptName := range []string{"install.sh", "install-skills.sh"} { + t.Run(scriptName, func(t *testing.T) { + scriptPath, err := filepath.Abs(filepath.Join("..", "..", "scripts", scriptName)) + if err != nil { + t.Fatal(err) + } + data, err := os.ReadFile(scriptPath) + if err != nil { + t.Fatal(err) + } + cut := strings.LastIndex(string(data), "\nmain\n") + if cut < 0 { + t.Fatalf("%s final main invocation not found", scriptName) + } + library := filepath.Join(t.TempDir(), scriptName+"-lib.sh") + mustWriteFile(t, library, data[:cut], 0o755) + + home := t.TempDir() + source := filepath.Join(t.TempDir(), "multi") + mustWriteFile(t, filepath.Join(home, ".zcode", "v2", "config.json"), []byte("{}\n"), 0o644) + mustWriteFile(t, filepath.Join(home, ".agents", "skills", "dws", "multi", "dingtalk-chat", "SKILL.md"), []byte("old nested\n"), 0o644) + mustWriteFile(t, filepath.Join(source, "dingtalk-chat", "SKILL.md"), []byte("new chat\n"), 0o644) + + installCall := `install_multi_skills_to_homes "$DWS_TEST_SOURCE"` + if scriptName == "install-skills.sh" { + installCall = `install_multi_skills_to_root "$DWS_TEST_SOURCE" "$HOME"` + } + cmd := exec.Command("sh", "-c", `. "$DWS_TEST_LIBRARY" +`+installCall+` +`) + cmd.Env = append(os.Environ(), "HOME="+home, "DWS_TEST_LIBRARY="+library, "DWS_TEST_SOURCE="+source) + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%s ZCode-root harness failed: %v\n%s", scriptName, err, output) + } + if _, err := os.Stat(filepath.Join(home, ".zcode", "skills", "dingtalk-chat", "SKILL.md")); err != nil { + t.Fatalf("%s canonical ZCode Skill missing: %v", scriptName, err) + } + if _, err := os.Stat(filepath.Join(home, ".agents", "skills", "dws")); !os.IsNotExist(err) { + t.Fatalf("%s generic duplicate remains: %v", scriptName, err) + } + }) + } +} + func TestInstallPowerShellPrefersCodexRootWithoutGenericDuplicate(t *testing.T) { pwsh, err := exec.LookPath("pwsh") if err != nil { @@ -1341,6 +1386,55 @@ exit 0 } } +func TestInstallPowerShellPrefersZCodeRootWithoutGenericDuplicate(t *testing.T) { + pwsh, err := exec.LookPath("pwsh") + if err != nil { + if runtime.GOOS == "windows" { + pwsh, err = exec.LookPath("powershell") + } + if err != nil { + t.Skip("PowerShell is not available") + } + } + scriptPath, err := filepath.Abs(filepath.Join("..", "..", "scripts", "install.ps1")) + if err != nil { + t.Fatal(err) + } + data, err := os.ReadFile(scriptPath) + if err != nil { + t.Fatal(err) + } + cut := strings.LastIndex(string(data), "# ── Main") + if cut < 0 { + t.Fatal("install.ps1 main section not found") + } + prefix := strings.ReplaceAll(string(data[:cut]), "$HOME", "$env:DWS_TEST_HOME") + prefix += ` +if (!(Install-MultiSkillsToHomes -MultiSrc $env:DWS_TEST_MULTI -Root $env:DWS_TEST_HOME)) { exit 2 } +exit 0 +` + harnessPath := filepath.Join(t.TempDir(), "install-zcode-root.ps1") + mustWriteFile(t, harnessPath, []byte(prefix), 0o644) + + home := t.TempDir() + multi := filepath.Join(t.TempDir(), "multi") + mustWriteFile(t, filepath.Join(home, ".zcode", "v2", "config.json"), []byte("{}\n"), 0o644) + mustWriteFile(t, filepath.Join(home, ".agents", "skills", "dws", "multi", "dingtalk-chat", "SKILL.md"), []byte("old nested\n"), 0o644) + mustWriteFile(t, filepath.Join(multi, "dingtalk-chat", "SKILL.md"), []byte("new chat\n"), 0o644) + + cmd := exec.Command(pwsh, "-NoProfile", "-NonInteractive", "-File", harnessPath) + cmd.Env = append(os.Environ(), "DWS_TEST_HOME="+home, "DWS_TEST_MULTI="+multi) + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("PowerShell ZCode-root harness failed: %v\n%s", err, output) + } + if _, err := os.Stat(filepath.Join(home, ".zcode", "skills", "dingtalk-chat", "SKILL.md")); err != nil { + t.Fatalf("PowerShell canonical ZCode Skill missing: %v", err) + } + if _, err := os.Stat(filepath.Join(home, ".agents", "skills", "dws")); !os.IsNotExist(err) { + t.Fatalf("PowerShell generic duplicate remains: %v", err) + } +} + func TestInstallPowerShellBackupFailureWritesNoMultiSkills(t *testing.T) { pwsh, err := exec.LookPath("pwsh") if err != nil { diff --git a/test/scripts/package_script_test.go b/test/scripts/package_script_test.go index 91ee2cbd..d02d5634 100644 --- a/test/scripts/package_script_test.go +++ b/test/scripts/package_script_test.go @@ -24,6 +24,7 @@ var expectedPackagedSkillTargets = []string{ ".qoderwork/skills/dingtalk-shared", ".gemini/skills/dingtalk-shared", ".codex/skills/dingtalk-shared", + ".zcode/skills/dingtalk-shared", ".github/skills/dingtalk-shared", ".windsurf/skills/dingtalk-shared", ".augment/skills/dingtalk-shared", From b31eaec78daa9ac02623e896d2bbb553d936f726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Wed, 12 Aug 2026 15:47:39 +0800 Subject: [PATCH 5/5] docs: remove ZCode release fragment --- .changes/zcode-skill-root.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changes/zcode-skill-root.md diff --git a/.changes/zcode-skill-root.md b/.changes/zcode-skill-root.md deleted file mode 100644 index f3623960..00000000 --- a/.changes/zcode-skill-root.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: Added ---- - -- **ZCode Skill installation** — installs bundled and marketplace Skills into ZCode's canonical `~/.zcode/skills` directory.