Merge remote-tracking branch 'origin/main' into codex/im-page-all
This commit is contained in:
@@ -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 |
|
||||
|
||||
|
||||
+1
-1
@@ -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/` |
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ const AGENT_DIRS = [
|
||||
".qoderwork/skills",
|
||||
".gemini/skills",
|
||||
".codex/skills",
|
||||
".zcode/skills",
|
||||
".github/skills",
|
||||
".windsurf/skills",
|
||||
".augment/skills",
|
||||
|
||||
@@ -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 或任何产品接口变化**。
|
||||
|
||||
## 两阶段迁移与回执清理
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
".",
|
||||
|
||||
@@ -29,6 +29,7 @@ var skillSetupAgentHomes = []string{
|
||||
".qoderwork/skills",
|
||||
".gemini/skills",
|
||||
".codex/skills",
|
||||
".zcode/skills",
|
||||
".github/skills",
|
||||
".windsurf/skills",
|
||||
".augment/skills",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -47,6 +47,7 @@ var knownSkillDirs = []string{
|
||||
".qoderwork/skills",
|
||||
".gemini/skills",
|
||||
".codex/skills",
|
||||
".zcode/skills",
|
||||
".github/skills",
|
||||
".windsurf/skills",
|
||||
".augment/skills",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -61,6 +61,7 @@ $AgentDirs = @(
|
||||
".qoderwork\skills",
|
||||
".gemini\skills",
|
||||
".codex\skills",
|
||||
".zcode\skills",
|
||||
".github\skills",
|
||||
".windsurf\skills",
|
||||
".augment\skills",
|
||||
|
||||
+4
-2
@@ -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" \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user