feat: 重组为标准 skill 包结构 + 修复 verify.sh bash 3.2 兼容

- 新增 .claude/skills/new-project/SKILL.md(标准 skill 格式)
- install-skill.sh 安装时组装 SKILL.md + verify.sh,单一源无重复
- verify.sh 改用索引数组替代 declare -A,兼容 macOS bash 3.2
- log_pass/log_skip 显式 return 0,避免 set -e 下非 verbose 误退出
- README 改为 skill 优先;补全 copier.yml/scripts/template 入库
This commit is contained in:
baozaotumao
2026-06-01 21:19:45 -07:00
parent 167fa10dde
commit 39fe248f9f
53 changed files with 3744 additions and 55 deletions
+11
View File
@@ -0,0 +1,11 @@
# Frontend 配置模板 —— 复制为 .env 后按需修改
# Vite 规则:只有 VITE_ 前缀的变量才会被注入浏览器端代码
# 切勿在此放任何密钥(前端变量对用户完全可见)
{%- if include_backend %}
# Backend WebSocket 地址
VITE_BACKEND_WS_URL=ws://localhost:{{ backend_port }}
# Backend HTTP 地址(文件下载 / 预览)
VITE_BACKEND_HTTP_URL=http://localhost:{{ backend_port }}
{%- endif %}
+7
View File
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
+38
View File
@@ -0,0 +1,38 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.strict],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2022,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
// 禁 console.log(允许 warn/error
"no-console": ["error", { allow: ["warn", "error"] }],
// 禁 any
"@typescript-eslint/no-explicit-any": "error",
// 命名约定
"@typescript-eslint/naming-convention": [
"error",
{ selector: "variable", format: ["camelCase", "UPPER_CASE", "PascalCase"] },
{ selector: "function", format: ["camelCase", "PascalCase"] },
{ selector: "typeLike", format: ["PascalCase"] },
],
// 单文件行数上限
"max-lines": ["warn", { max: 200, skipBlankLines: true, skipComments: true }],
},
}
);
+43
View File
@@ -0,0 +1,43 @@
{
"name": "{{ project_slug }}-frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"test": "vitest",
"test:run": "vitest run"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"zustand": "^5.0.0",
"@tanstack/react-query": "^5.0.0",
"zod": "^3.23.0",
"sonner": "^1.5.0"
},
"devDependencies": {
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.0",
"vite": "^5.4.0",
"typescript": "^5.5.0",
"eslint": "^9.11.0",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.0",
"prettier": "^3.3.0",
"vitest": "^2.1.0",
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.5.0",
"@vitest/coverage-v8": "^2.1.0",
"tailwindcss": "^3.4.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"jsdom": "^25.0.0"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: {{ frontend_port }},
},
});
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
coverage: {
provider: "v8",
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
},
});