新增 `dws skill setup` 交互式安装命令,支持单产品(mono)和多产品(multi,标记为试验版)两种 skill 布局,并与开源 dws v1.0.30 命令对齐。 主要变更: - 骨架:skills/ 拆为 skills/mono/ 和 skills/multi/(29 个产品 skill),git rename 保留历史 - 命令:新增 `skill setup` cobra 子命令,支持 --mode、--target、--source、--yes,huh 交互 - multi 模式支持 -s/--skill、-x/--exclude 按产品选择 - 覆盖全部 14 个 agent home 目录(与 install.sh 一致) - 发布:install.sh / install.ps1 / post-goreleaser.sh 改读 skills/mono;dws-skills.zip 同时包含 mono 和 multi 内容;~/.dws 本地缓存 - 内容对齐:删除 8 个仅内部使用的产品 skill、dingtalk-a2a;修正 mono/multi 共 15+ 处命令引用以匹配 OSS dws v1.0.30;chat skill 从 dws-wukong 回移更新 - 漂移清理:物理删除 skill 文档中 OSS 未暴露的命令,true-fail 命令转为 caveat;--help diff 100% 覆盖审计 - 测试:新增 Layer A 静态命令调度验证 + Layer B 端到端 mock 调度验证 - 同步 main:drive 9 个命令同步到 mono/multi skill
197 lines
6.1 KiB
JavaScript
Executable File
197 lines
6.1 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
"use strict";
|
|
|
|
const fs = require("fs");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const childProcess = require("child_process");
|
|
|
|
// Canonical list: keep scripts/install.sh, scripts/install.ps1, scripts/install-skills.sh in sync.
|
|
const AGENT_DIRS = [
|
|
".agents/skills",
|
|
".claude/skills",
|
|
".cursor/skills",
|
|
".gemini/skills",
|
|
".codex/skills",
|
|
".github/skills",
|
|
".windsurf/skills",
|
|
".augment/skills",
|
|
".cline/skills",
|
|
".amp/skills",
|
|
".kiro/skills",
|
|
".trae/skills",
|
|
".openclaw/skills",
|
|
".hermes/skills",
|
|
];
|
|
|
|
const PLATFORM_MAP = {
|
|
"darwin-x64": "dws-darwin-amd64.tar.gz",
|
|
"darwin-arm64": "dws-darwin-arm64.tar.gz",
|
|
"linux-x64": "dws-linux-amd64.tar.gz",
|
|
"linux-arm64": "dws-linux-arm64.tar.gz",
|
|
"win32-x64": "dws-windows-amd64.zip",
|
|
"win32-arm64": "dws-windows-arm64.zip",
|
|
};
|
|
|
|
function run(command, args) {
|
|
childProcess.execFileSync(command, args, { stdio: "inherit" });
|
|
}
|
|
|
|
function ensureCleanDir(dir) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
|
|
function findBinary(root) {
|
|
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
const entryPath = path.join(root, entry.name);
|
|
if (entry.isDirectory()) {
|
|
const nested = findBinary(entryPath);
|
|
if (nested) {
|
|
return nested;
|
|
}
|
|
continue;
|
|
}
|
|
if (entry.name === "dws" || entry.name === "dws.exe") {
|
|
return entryPath;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function extractArchive(archivePath, destDir) {
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dws-npm-bin-"));
|
|
try {
|
|
if (archivePath.endsWith(".tar.gz")) {
|
|
run("tar", ["-xzf", archivePath, "-C", tmpDir]);
|
|
} else if (process.platform === "win32") {
|
|
run("powershell.exe", [
|
|
"-NoLogo",
|
|
"-NoProfile",
|
|
"-Command",
|
|
`Expand-Archive -Path '${archivePath.replace(/'/g, "''")}' -DestinationPath '${tmpDir.replace(/'/g, "''")}' -Force`,
|
|
]);
|
|
} else {
|
|
run("unzip", ["-q", archivePath, "-d", tmpDir]);
|
|
}
|
|
|
|
const binaryPath = findBinary(tmpDir);
|
|
if (!binaryPath) {
|
|
throw new Error(`dws binary not found in archive ${archivePath}`);
|
|
}
|
|
|
|
ensureCleanDir(destDir);
|
|
const targetName = process.platform === "win32" ? "dws.exe" : "dws";
|
|
const targetPath = path.join(destDir, targetName);
|
|
fs.copyFileSync(binaryPath, targetPath);
|
|
if (process.platform !== "win32") {
|
|
fs.chmodSync(targetPath, 0o755);
|
|
}
|
|
} finally {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
function extractSkills(zipPath, destDir) {
|
|
ensureCleanDir(destDir);
|
|
if (process.platform === "win32") {
|
|
run("powershell.exe", [
|
|
"-NoLogo",
|
|
"-NoProfile",
|
|
"-Command",
|
|
`Expand-Archive -Path '${zipPath.replace(/'/g, "''")}' -DestinationPath '${destDir.replace(/'/g, "''")}' -Force`,
|
|
]);
|
|
return;
|
|
}
|
|
run("unzip", ["-q", zipPath, "-d", destDir]);
|
|
}
|
|
|
|
function copyChildren(srcDir, destDir) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
for (const entry of fs.readdirSync(srcDir)) {
|
|
fs.cpSync(path.join(srcDir, entry), path.join(destDir, entry), { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
function installSkillsToHomes(skillRoot) {
|
|
const homeDir = os.homedir();
|
|
let installed = 0;
|
|
|
|
AGENT_DIRS.forEach((agentDir, index) => {
|
|
const baseDir = path.join(homeDir, agentDir);
|
|
const parentGate = path.dirname(baseDir);
|
|
if (index > 0 && !fs.existsSync(parentGate)) {
|
|
return;
|
|
}
|
|
const destDir = path.join(baseDir, "dws");
|
|
fs.rmSync(destDir, { recursive: true, force: true });
|
|
copyChildren(skillRoot, destDir);
|
|
installed += 1;
|
|
});
|
|
|
|
if (installed === 0) {
|
|
copyChildren(skillRoot, path.join(homeDir, ".agents", "skills", "dws"));
|
|
}
|
|
}
|
|
|
|
// cacheUserSkills copies the mono and multi trees out of the freshly extracted
|
|
// dws-skills.zip into ~/.dws/skills/{mono,multi}/ so that `dws skill setup`
|
|
// can fall back to a user-local cache when --source is not provided. mono is
|
|
// already installed into agent homes by installSkillsToHomes; the cache is
|
|
// purely a source-of-truth for the setup command.
|
|
function cacheUserSkills(extractedSkillsRoot) {
|
|
const cacheBase = path.join(os.homedir(), ".dws", "skills");
|
|
|
|
const monoSource = fs.existsSync(path.join(extractedSkillsRoot, "mono", "SKILL.md"))
|
|
? path.join(extractedSkillsRoot, "mono")
|
|
: extractedSkillsRoot;
|
|
const monoCache = path.join(cacheBase, "mono");
|
|
fs.rmSync(monoCache, { recursive: true, force: true });
|
|
copyChildren(monoSource, monoCache);
|
|
|
|
const multiSource = path.join(extractedSkillsRoot, "multi");
|
|
if (fs.existsSync(multiSource) && fs.statSync(multiSource).isDirectory()) {
|
|
const multiCache = path.join(cacheBase, "multi");
|
|
fs.rmSync(multiCache, { recursive: true, force: true });
|
|
copyChildren(multiSource, multiCache);
|
|
}
|
|
}
|
|
|
|
function main() {
|
|
const packageRoot = __dirname;
|
|
const assetsDir = path.join(packageRoot, "assets");
|
|
const vendorDir = path.join(packageRoot, "vendor");
|
|
// Extract dws-skills.zip into a staging directory so we can split mono/
|
|
// (installed to agent homes) from multi/ (cached for later setup use).
|
|
const skillsStaging = path.join(packageRoot, "share", "skills");
|
|
const assetName = PLATFORM_MAP[`${process.platform}-${process.arch}`];
|
|
if (!assetName) {
|
|
throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
|
|
}
|
|
|
|
const archivePath = path.join(assetsDir, assetName);
|
|
const skillsPath = path.join(assetsDir, "dws-skills.zip");
|
|
if (!fs.existsSync(archivePath)) {
|
|
throw new Error(`missing platform archive: ${archivePath}`);
|
|
}
|
|
if (!fs.existsSync(skillsPath)) {
|
|
throw new Error(`missing skills archive: ${skillsPath}`);
|
|
}
|
|
|
|
extractArchive(archivePath, vendorDir);
|
|
extractSkills(skillsPath, skillsStaging);
|
|
|
|
// For backward compatibility, the zip root carries a copy of mono content
|
|
// (SKILL.md + references/ + scripts/). Prefer the explicit mono/ subdir
|
|
// when present; fall back to the staging root otherwise.
|
|
const monoRoot = fs.existsSync(path.join(skillsStaging, "mono", "SKILL.md"))
|
|
? path.join(skillsStaging, "mono")
|
|
: skillsStaging;
|
|
installSkillsToHomes(monoRoot);
|
|
cacheUserSkills(skillsStaging);
|
|
}
|
|
|
|
main();
|