#!/usr/bin/env node // 技能安装器(npx 入口): 将本包内容安装/更新到 ~/.claude/skills/smartbadge-ops // 保护文件: .env/.token/out/__pycache__ 不覆盖(已配置凭据与登录态在升级时保留) // 用法: // npx --yes --package= smartbadge-ops # 安装/更新 // node scripts/install.mjs --dest <路径> # 自定义安装目录(测试用) import fs from 'fs'; import os from 'os'; import path from 'path'; import { fileURLToPath } from 'url'; const PKG_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); const args = process.argv.slice(2); const argVal = (name) => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : undefined; }; const TARGET = argVal('--dest') || path.join(os.homedir(), '.claude', 'skills', 'smartbadge-ops'); const PRODUCTS = ['SKILL.md', 'README.md', '.env.example', '.gitignore', 'scripts', 'templates']; const PROTECTED = new Set(['.env', '.env.local', '.token', 'out', '__pycache__', '.git']); function copyEntry(src, dest) { if (PROTECTED.has(path.basename(src))) return; const stat = fs.statSync(src); if (stat.isDirectory()) { fs.mkdirSync(dest, { recursive: true }); for (const name of fs.readdirSync(src)) copyEntry(path.join(src, name), path.join(dest, name)); } else { fs.mkdirSync(path.dirname(dest), { recursive: true }); fs.copyFileSync(src, dest); } } const existed = fs.existsSync(TARGET); for (const entry of PRODUCTS) { const src = path.join(PKG_ROOT, entry); if (fs.existsSync(src)) copyEntry(src, path.join(TARGET, entry)); } const hasEnv = fs.existsSync(path.join(TARGET, '.env')); console.log(existed ? `已更新: ${TARGET}` : `已安装: ${TARGET}`); console.log(hasEnv ? '凭据/登录态已保留(.env/.token 未被覆盖)' : '下一步: 复制 .env.example 为 .env 并填入真实值(或配同名环境变量)'); if (existed) console.log('提示: 升级后重跑 node scripts/check-online.mjs 验证; 若技能在运行中, 重启 Claude 会话以加载新 SKILL.md');