pandax 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/bin/pandax.js +592 -0
- package/package.json +33 -0
- package/skills/pandax-frontend-api-mock/SKILL.md +62 -0
- package/skills/pandax-frontend-architecture/SKILL.md +1239 -0
- package/skills/pandax-frontend-component-review/SKILL.md +114 -0
- package/skills/pandax-frontend-requirement/SKILL.md +142 -0
- package/skills/pandax-frontend-standards/SKILL.md +468 -0
- package/skills/pandax-frontend-use-modal/SKILL.md +621 -0
- package/templates/pandax-monorepo/.editorconfig +16 -0
- package/templates/pandax-monorepo/.husky/commit-msg +2 -0
- package/templates/pandax-monorepo/.husky/pre-commit +2 -0
- package/templates/pandax-monorepo/.prettierrc.json +28 -0
- package/templates/pandax-monorepo/apps/base/env.d.ts +1 -0
- package/templates/pandax-monorepo/apps/base/index.html +13 -0
- package/templates/pandax-monorepo/apps/base/package.json +30 -0
- package/templates/pandax-monorepo/apps/base/src/App.vue +15 -0
- package/templates/pandax-monorepo/apps/base/src/layouts/default-layout/index.vue +23 -0
- package/templates/pandax-monorepo/apps/base/src/main.ts +31 -0
- package/templates/pandax-monorepo/apps/base/src/router/index.ts +22 -0
- package/templates/pandax-monorepo/apps/base/src/stores/index.ts +15 -0
- package/templates/pandax-monorepo/apps/base/src/styles/global.scss +19 -0
- package/templates/pandax-monorepo/apps/base/src/styles/variables.scss +4 -0
- package/templates/pandax-monorepo/apps/base/src/types/wujie.d.ts +16 -0
- package/templates/pandax-monorepo/apps/base/src/views/home/index.vue +21 -0
- package/templates/pandax-monorepo/apps/base/tsconfig.json +25 -0
- package/templates/pandax-monorepo/apps/base/tsconfig.node.json +11 -0
- package/templates/pandax-monorepo/apps/base/vite.config.ts +26 -0
- package/templates/pandax-monorepo/apps/main/env.d.ts +1 -0
- package/templates/pandax-monorepo/apps/main/index.html +13 -0
- package/templates/pandax-monorepo/apps/main/package.json +32 -0
- package/templates/pandax-monorepo/apps/main/src/App.vue +15 -0
- package/templates/pandax-monorepo/apps/main/src/layouts/MicroApp.vue +29 -0
- package/templates/pandax-monorepo/apps/main/src/layouts/default-layout/index.vue +48 -0
- package/templates/pandax-monorepo/apps/main/src/main.ts +27 -0
- package/templates/pandax-monorepo/apps/main/src/router/index.ts +22 -0
- package/templates/pandax-monorepo/apps/main/src/stores/index.ts +23 -0
- package/templates/pandax-monorepo/apps/main/src/styles/global.scss +23 -0
- package/templates/pandax-monorepo/apps/main/src/styles/variables.scss +8 -0
- package/templates/pandax-monorepo/apps/main/src/views/home/index.vue +25 -0
- package/templates/pandax-monorepo/apps/main/tsconfig.json +25 -0
- package/templates/pandax-monorepo/apps/main/tsconfig.node.json +11 -0
- package/templates/pandax-monorepo/apps/main/vite.config.ts +24 -0
- package/templates/pandax-monorepo/commitlint.config.js +38 -0
- package/templates/pandax-monorepo/eslint.config.js +61 -0
- package/templates/pandax-monorepo/package.json +50 -0
- package/templates/pandax-monorepo/packages/components/package.json +20 -0
- package/templates/pandax-monorepo/packages/components/src/PandaButton.vue +15 -0
- package/templates/pandax-monorepo/packages/components/src/PandaCard.vue +16 -0
- package/templates/pandax-monorepo/packages/components/src/index.ts +2 -0
- package/templates/pandax-monorepo/packages/core/package.json +20 -0
- package/templates/pandax-monorepo/packages/core/src/index.ts +2 -0
- package/templates/pandax-monorepo/packages/core/src/request.ts +24 -0
- package/templates/pandax-monorepo/packages/core/src/utils.ts +7 -0
- package/templates/pandax-monorepo/packages/hooks/package.json +19 -0
- package/templates/pandax-monorepo/packages/hooks/src/index.ts +2 -0
- package/templates/pandax-monorepo/packages/hooks/src/useCounter.ts +27 -0
- package/templates/pandax-monorepo/packages/hooks/src/useToggle.ts +19 -0
- package/templates/pandax-monorepo/packages/styles/package.json +19 -0
- package/templates/pandax-monorepo/packages/styles/src/index.ts +1 -0
- package/templates/pandax-monorepo/packages/styles/src/uno.ts +19 -0
- package/templates/pandax-monorepo/packages/styles/src/variables.scss +14 -0
- package/templates/pandax-monorepo/pnpm-workspace.yaml +32 -0
- package/templates/pandax-monorepo/turbo.json +23 -0
- package/templates/vue3/.eslintrc.cjs +16 -0
- package/templates/vue3/.prettierrc +7 -0
- package/templates/vue3/env.d.ts +1 -0
- package/templates/vue3/index.html +13 -0
- package/templates/vue3/package.json +34 -0
- package/templates/vue3/src/App.vue +14 -0
- package/templates/vue3/src/api/index.ts +27 -0
- package/templates/vue3/src/assets/.gitkeep +1 -0
- package/templates/vue3/src/components/.gitkeep +1 -0
- package/templates/vue3/src/composables/.gitkeep +1 -0
- package/templates/vue3/src/constants/.gitkeep +1 -0
- package/templates/vue3/src/directives/.gitkeep +1 -0
- package/templates/vue3/src/layouts/default-layout/index.vue +33 -0
- package/templates/vue3/src/locales/.gitkeep +1 -0
- package/templates/vue3/src/main.ts +17 -0
- package/templates/vue3/src/router/index.ts +17 -0
- package/templates/vue3/src/services/.gitkeep +1 -0
- package/templates/vue3/src/stores/index.ts +12 -0
- package/templates/vue3/src/styles/global.scss +18 -0
- package/templates/vue3/src/styles/variables.scss +17 -0
- package/templates/vue3/src/types/global.d.ts +9 -0
- package/templates/vue3/src/utils/format.ts +11 -0
- package/templates/vue3/src/views/home/index.vue +19 -0
- package/templates/vue3/tsconfig.json +25 -0
- package/templates/vue3/tsconfig.node.json +10 -0
- package/templates/vue3/vite.config.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# pandax
|
|
2
|
+
|
|
3
|
+
Pandax 全栈 AI 编程技能魔盒,参考 [superpowers-zh](https://www.npmjs.com/package/superpowers-zh) 的 `npx` 安装方式,将团队工程规范沉淀为可复用的 AI skills,未来还将扩展脚手架、项目模板等能力。
|
|
4
|
+
|
|
5
|
+
## 安装 Skills
|
|
6
|
+
|
|
7
|
+
在任意项目根目录执行:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx pandax
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
CLI 会自动检测当前项目使用的 AI 编程工具。如果检测失败,可手动指定:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx pandax --tool trae
|
|
17
|
+
npx pandax --tool cursor
|
|
18
|
+
npx pandax --tool claude
|
|
19
|
+
npx pandax --tool codebuddy
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
安装完成后重启对应的 AI 编程工具即可生效。
|
|
23
|
+
|
|
24
|
+
## 初始化项目模板
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx pandax --init vue3 # 初始化 Vue3 单应用模板
|
|
28
|
+
npx pandax --init pandax-monorepo # 初始化 Monorepo + 微前端模板
|
|
29
|
+
npx pandax --init vue3 --with-skills # 初始化模板并安装 skills
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 卸载
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx pandax --uninstall
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 包含的 Skills
|
|
39
|
+
|
|
40
|
+
### 前端
|
|
41
|
+
|
|
42
|
+
| Skill | 说明 |
|
|
43
|
+
|-------|------|
|
|
44
|
+
| [pandax-frontend-architecture](skills/pandax-frontend-architecture/SKILL.md) | 设计前端项目架构与技术方案 |
|
|
45
|
+
| [pandax-frontend-component-review](skills/pandax-frontend-component-review/SKILL.md) | 审查 Vue/React 组件代码 |
|
|
46
|
+
| [pandax-frontend-chinese-copywriting](skills/pandax-frontend-chinese-copywriting/SKILL.md) | 检查并修正项目中的中文文案排版 |
|
|
47
|
+
| [pandax-frontend-api-mock](skills/pandax-frontend-api-mock/SKILL.md) | 规范 API 对接与 Mock 数据管理 |
|
|
48
|
+
| [pandax-frontend-requirement](skills/pandax-frontend-requirement/SKILL.md) | 梳理前端需求并输出可执行方案 |
|
|
49
|
+
| [pandax-frontend-standards](skills/pandax-frontend-standards/SKILL.md) | 统一引用前端规范文档 |
|
|
50
|
+
| [pandax-frontend-use-modal](skills/pandax-frontend-use-modal/SKILL.md) | 生成 Vue 3 + Element Plus useModal Hook |
|
|
51
|
+
|
|
52
|
+
## 支持的 AI 工具
|
|
53
|
+
|
|
54
|
+
- Trae
|
|
55
|
+
- Cursor
|
|
56
|
+
- Claude Code
|
|
57
|
+
- Codex CLI
|
|
58
|
+
- OpenCode
|
|
59
|
+
- Cline
|
|
60
|
+
- CodeBuddy
|
|
61
|
+
|
|
62
|
+
## 环境要求
|
|
63
|
+
|
|
64
|
+
- Node.js >= 14.0.0
|
|
65
|
+
|
|
66
|
+
## 开发与发布
|
|
67
|
+
|
|
68
|
+
### 本地测试
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node bin/pandax.js --tool trae
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 发布到 npm
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm login
|
|
78
|
+
npm publish --access public
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 参考
|
|
82
|
+
|
|
83
|
+
- [superpowers-zh](https://www.npmjs.com/package/superpowers-zh)
|
package/bin/pandax.js
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
copyFileSync,
|
|
10
|
+
lstatSync,
|
|
11
|
+
realpathSync,
|
|
12
|
+
rmdirSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
} = require('fs');
|
|
15
|
+
const { resolve, dirname, join } = require('path');
|
|
16
|
+
const { homedir } = require('os');
|
|
17
|
+
const PKG = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
|
|
18
|
+
const SKILLS_SRC = resolve(__dirname, '..', 'skills');
|
|
19
|
+
const TEMPLATES_SRC = resolve(__dirname, '..', 'templates');
|
|
20
|
+
const PROJECT_DIR = process.cwd();
|
|
21
|
+
|
|
22
|
+
// 手动递归复制,避免 Windows npx 缓存 junction 问题
|
|
23
|
+
function copyDirSync(src, dest) {
|
|
24
|
+
let realSrc = src;
|
|
25
|
+
try { realSrc = realpathSync(src); } catch {}
|
|
26
|
+
|
|
27
|
+
mkdirSync(dest, { recursive: true });
|
|
28
|
+
const entries = readdirSync(realSrc, { withFileTypes: true });
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
if (entry.name === '.DS_Store') continue;
|
|
31
|
+
const srcPath = join(realSrc, entry.name);
|
|
32
|
+
const destPath = join(dest, entry.name);
|
|
33
|
+
let stat;
|
|
34
|
+
try { stat = lstatSync(srcPath); } catch { continue; }
|
|
35
|
+
if (stat.isSymbolicLink()) {
|
|
36
|
+
try {
|
|
37
|
+
const real = realpathSync(srcPath);
|
|
38
|
+
const realStat = lstatSync(real);
|
|
39
|
+
if (realStat.isDirectory()) copyDirSync(real, destPath);
|
|
40
|
+
else copyFileSync(real, destPath);
|
|
41
|
+
} catch {}
|
|
42
|
+
} else if (stat.isDirectory()) {
|
|
43
|
+
copyDirSync(srcPath, destPath);
|
|
44
|
+
} else if (stat.isFile()) {
|
|
45
|
+
copyFileSync(srcPath, destPath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 兼容 Node >= 14.0.0:fs.removeSync 直到 14.14.0 才引入
|
|
51
|
+
function removeSync(p) {
|
|
52
|
+
if (!existsSync(p)) return;
|
|
53
|
+
const stat = lstatSync(p);
|
|
54
|
+
if (stat.isDirectory()) {
|
|
55
|
+
for (const entry of readdirSync(p, { withFileTypes: true })) {
|
|
56
|
+
removeSync(join(p, entry.name));
|
|
57
|
+
}
|
|
58
|
+
rmdirSync(p);
|
|
59
|
+
} else {
|
|
60
|
+
unlinkSync(p);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function countDirs(dir) {
|
|
65
|
+
if (!existsSync(dir)) return 0;
|
|
66
|
+
return readdirSync(dir, { withFileTypes: true }).filter(e => e.isDirectory()).length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function scanSkillEntries(skillsDir) {
|
|
70
|
+
const entries = [];
|
|
71
|
+
if (!existsSync(skillsDir)) return entries;
|
|
72
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
73
|
+
if (!entry.isDirectory()) continue;
|
|
74
|
+
const skillFile = resolve(skillsDir, entry.name, 'SKILL.md');
|
|
75
|
+
if (!existsSync(skillFile)) continue;
|
|
76
|
+
const content = readFileSync(skillFile, 'utf8');
|
|
77
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
78
|
+
if (!fmMatch) continue;
|
|
79
|
+
const nameMatch = fmMatch[1].match(/^name:\s*(.+)$/m);
|
|
80
|
+
const categoryMatch = fmMatch[1].match(/^category:\s*(.+)$/m);
|
|
81
|
+
const descMatch = fmMatch[1].match(/^description:\s*["']?(.+?)["']?\s*$/m);
|
|
82
|
+
if (nameMatch) {
|
|
83
|
+
const stripQuotes = (s) => s.replace(/^["']|["']$/g, '');
|
|
84
|
+
entries.push({
|
|
85
|
+
name: stripQuotes(nameMatch[1].trim()),
|
|
86
|
+
category: stripQuotes((categoryMatch ? categoryMatch[1] : '通用').trim()),
|
|
87
|
+
desc: descMatch ? stripQuotes(descMatch[1].trim()) : '',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return entries;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function groupSkillsByCategory(entries) {
|
|
95
|
+
const groups = {};
|
|
96
|
+
for (const e of entries) {
|
|
97
|
+
const cat = e.category || '通用';
|
|
98
|
+
if (!groups[cat]) groups[cat] = [];
|
|
99
|
+
groups[cat].push(e);
|
|
100
|
+
}
|
|
101
|
+
return groups;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function categoryLabel(category) {
|
|
105
|
+
const labels = {
|
|
106
|
+
frontend: '前端',
|
|
107
|
+
backend: '后端',
|
|
108
|
+
common: '通用',
|
|
109
|
+
通用: '通用',
|
|
110
|
+
};
|
|
111
|
+
return labels[category] || category;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildSkillTablesByCategory(entries) {
|
|
115
|
+
const groups = groupSkillsByCategory(entries);
|
|
116
|
+
const order = ['frontend', 'backend', 'common', '通用'];
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
const parts = [];
|
|
119
|
+
for (const cat of order) {
|
|
120
|
+
if (!groups[cat] || groups[cat].length === 0) continue;
|
|
121
|
+
if (seen.has(cat)) continue;
|
|
122
|
+
seen.add(cat);
|
|
123
|
+
const rows = groups[cat].map(s => `| ${s.name} | ${s.desc} |`).join('\n');
|
|
124
|
+
parts.push(`### ${categoryLabel(cat)}\n\n| Skill | 触发条件 |\n|-------|---------|\n${rows}`);
|
|
125
|
+
}
|
|
126
|
+
for (const cat of Object.keys(groups)) {
|
|
127
|
+
if (seen.has(cat)) continue;
|
|
128
|
+
const rows = groups[cat].map(s => `| ${s.name} | ${s.desc} |`).join('\n');
|
|
129
|
+
parts.push(`### ${categoryLabel(cat)}\n\n| Skill | 触发条件 |\n|-------|---------|\n${rows}`);
|
|
130
|
+
}
|
|
131
|
+
return parts.join('\n\n');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const SENTINEL_BEGIN = '<!-- pandax:begin (do not edit between these markers) -->';
|
|
135
|
+
const SENTINEL_END = '<!-- pandax:end -->';
|
|
136
|
+
|
|
137
|
+
function wrapWithSentinel(body) {
|
|
138
|
+
return `${SENTINEL_BEGIN}\n${body.replace(/\n+$/, '')}\n${SENTINEL_END}\n`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function writeOrDelete(filePath, head, tail) {
|
|
142
|
+
const headTrim = head.replace(/\s+$/, '');
|
|
143
|
+
const tailTrim = tail.replace(/^\s+/, '');
|
|
144
|
+
let body = headTrim;
|
|
145
|
+
if (headTrim && tailTrim) body += '\n\n' + tailTrim;
|
|
146
|
+
else body += tailTrim;
|
|
147
|
+
body = body.replace(/\s+$/, '');
|
|
148
|
+
if (body.length === 0) {
|
|
149
|
+
removeSync(filePath);
|
|
150
|
+
} else {
|
|
151
|
+
writeFileSync(filePath, body + '\n', 'utf8');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function cleanBootstrapSection(filePath) {
|
|
156
|
+
if (!existsSync(filePath)) return false;
|
|
157
|
+
const content = readFileSync(filePath, 'utf8');
|
|
158
|
+
|
|
159
|
+
const sBegin = content.indexOf(SENTINEL_BEGIN);
|
|
160
|
+
if (sBegin !== -1) {
|
|
161
|
+
const sEnd = content.indexOf(SENTINEL_END, sBegin + SENTINEL_BEGIN.length);
|
|
162
|
+
if (sEnd !== -1) {
|
|
163
|
+
writeOrDelete(filePath, content.slice(0, sBegin), content.slice(sEnd + SENTINEL_END.length));
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function generateTraeBootstrapRule(projectDir, skillEntries) {
|
|
171
|
+
const rulesDir = resolve(projectDir, '.trae', 'rules');
|
|
172
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
173
|
+
|
|
174
|
+
const skillTables = buildSkillTablesByCategory(skillEntries);
|
|
175
|
+
|
|
176
|
+
const rule = `---
|
|
177
|
+
alwaysApply: true
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# Pandax 全栈 AI 编程技能魔盒
|
|
181
|
+
|
|
182
|
+
你已加载 Pandax 技能框架(${skillEntries.length} 个 skills)。
|
|
183
|
+
|
|
184
|
+
## 核心规则
|
|
185
|
+
|
|
186
|
+
1. **收到任务时,先检查是否有匹配的 Pandax skill** — 哪怕只有 1% 的可能性也要检查
|
|
187
|
+
2. **遵循 Pandax 工程规范** — 组件、样式、API、模板等按对应 skill 执行
|
|
188
|
+
3. **验证先于完成** — 声称完成前必须运行验证命令
|
|
189
|
+
|
|
190
|
+
## 可用 Skills
|
|
191
|
+
|
|
192
|
+
Skills 位于 \`.trae/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
|
|
193
|
+
|
|
194
|
+
${skillTables}
|
|
195
|
+
|
|
196
|
+
## 如何使用
|
|
197
|
+
|
|
198
|
+
当任务匹配某个 skill 的触发条件时,读取对应的 \`.trae/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
|
|
199
|
+
`;
|
|
200
|
+
|
|
201
|
+
const rulePath = resolve(rulesDir, 'pandax.md');
|
|
202
|
+
writeFileSync(rulePath, rule, 'utf8');
|
|
203
|
+
console.log(` ✅ Trae: bootstrap rule -> ${rulePath}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function buildSkillListsByCategory(entries) {
|
|
207
|
+
const groups = groupSkillsByCategory(entries);
|
|
208
|
+
const order = ['frontend', 'backend', 'common', '通用'];
|
|
209
|
+
const seen = new Set();
|
|
210
|
+
const parts = [];
|
|
211
|
+
for (const cat of order) {
|
|
212
|
+
if (!groups[cat] || groups[cat].length === 0) continue;
|
|
213
|
+
if (seen.has(cat)) continue;
|
|
214
|
+
seen.add(cat);
|
|
215
|
+
const items = groups[cat].map(s => `- **${s.name}**: ${s.desc}`).join('\n');
|
|
216
|
+
parts.push(`### ${categoryLabel(cat)}\n\n${items}`);
|
|
217
|
+
}
|
|
218
|
+
for (const cat of Object.keys(groups)) {
|
|
219
|
+
if (seen.has(cat)) continue;
|
|
220
|
+
const items = groups[cat].map(s => `- **${s.name}**: ${s.desc}`).join('\n');
|
|
221
|
+
parts.push(`### ${categoryLabel(cat)}\n\n${items}`);
|
|
222
|
+
}
|
|
223
|
+
return parts.join('\n\n');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function generateCodeBuddyBootstrap(baseDir, skillEntries) {
|
|
227
|
+
const skillList = buildSkillListsByCategory(skillEntries);
|
|
228
|
+
|
|
229
|
+
const content = `# Pandax 全栈 AI 编程技能魔盒
|
|
230
|
+
|
|
231
|
+
本项目已安装 Pandax 技能框架(${skillEntries.length} 个 skills)。
|
|
232
|
+
|
|
233
|
+
## 核心规则
|
|
234
|
+
|
|
235
|
+
1. **收到任务时,先检查是否有匹配的 Pandax skill** — 哪怕只有 1% 的可能性也要检查
|
|
236
|
+
2. **遵循 Pandax 工程规范** — 组件、样式、API、模板等按对应 skill 执行
|
|
237
|
+
3. **验证先于完成** — 声称完成前必须运行验证命令
|
|
238
|
+
|
|
239
|
+
## 可用 Skills
|
|
240
|
+
|
|
241
|
+
Skills 位于 \`.codebuddy/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
|
|
242
|
+
|
|
243
|
+
${skillList}
|
|
244
|
+
|
|
245
|
+
## 如何使用
|
|
246
|
+
|
|
247
|
+
当任务匹配某个 skill 时,读取对应的 \`.codebuddy/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
|
|
248
|
+
`;
|
|
249
|
+
|
|
250
|
+
const mdPath = resolve(baseDir, 'CODEBUDDY.md');
|
|
251
|
+
if (existsSync(mdPath)) {
|
|
252
|
+
const existing = readFileSync(mdPath, 'utf8');
|
|
253
|
+
if (!existing.includes('Pandax 全栈 AI 编程技能魔盒')) {
|
|
254
|
+
writeFileSync(mdPath, existing.replace(/\s+$/, '') + '\n\n' + wrapWithSentinel(content), 'utf8');
|
|
255
|
+
console.log(` ✅ CodeBuddy: 追加 skills 引用 -> ${mdPath}`);
|
|
256
|
+
} else {
|
|
257
|
+
console.log(` ✅ CodeBuddy: CODEBUDDY.md 已包含 Pandax skills 引用`);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
writeFileSync(mdPath, wrapWithSentinel(content), 'utf8');
|
|
261
|
+
console.log(` ✅ CodeBuddy: bootstrap -> ${mdPath}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 支持的目标工具
|
|
266
|
+
const TARGETS = [
|
|
267
|
+
{ name: 'Trae', dir: '.trae/skills', detect: '.trae', generateBootstrap: generateTraeBootstrapRule },
|
|
268
|
+
{ name: 'Cursor', dir: '.cursor/skills', detect: ['.cursor', '.cursorrules'] },
|
|
269
|
+
{ name: 'Claude Code', dir: '.claude/skills', detect: '.claude' },
|
|
270
|
+
{ name: 'Codex CLI', dir: '.codex/skills', detect: '.codex' },
|
|
271
|
+
{ name: 'OpenCode', dir: '.opencode/skills', detect: '.opencode' },
|
|
272
|
+
{ name: 'Cline', dir: '.cline/skills', detect: '.clinerules' },
|
|
273
|
+
{ name: 'CodeBuddy', dir: '.codebuddy/skills', detect: ['.codebuddy', 'CODEBUDDY.md'], generateBootstrap: generateCodeBuddyBootstrap },
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
const TOOL_ALIASES = {
|
|
277
|
+
trae: 'Trae',
|
|
278
|
+
cursor: 'Cursor',
|
|
279
|
+
claude: 'Claude Code',
|
|
280
|
+
'claude-code': 'Claude Code',
|
|
281
|
+
claudecode: 'Claude Code',
|
|
282
|
+
codex: 'Codex CLI',
|
|
283
|
+
opencode: 'OpenCode',
|
|
284
|
+
cline: 'Cline',
|
|
285
|
+
codebuddy: 'CodeBuddy',
|
|
286
|
+
'codebuddy-code': 'CodeBuddy',
|
|
287
|
+
codebuddycode: 'CodeBuddy',
|
|
288
|
+
'codebuddy-cn': 'CodeBuddy',
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
function installForTarget(target, baseDir, skillEntries) {
|
|
292
|
+
const dest = resolve(baseDir, target.dir);
|
|
293
|
+
const srcCount = countDirs(SKILLS_SRC);
|
|
294
|
+
mkdirSync(dest, { recursive: true });
|
|
295
|
+
copyDirSync(SKILLS_SRC, dest);
|
|
296
|
+
const totalAfter = countDirs(dest);
|
|
297
|
+
if (srcCount > 0 && totalAfter === 0) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`复制 skills 失败:源目录 ${SKILLS_SRC} 有 ${srcCount} 个 skill,但目标 ${dest} 为空。` +
|
|
300
|
+
`\n 请尝试:npm cache clean --force && npx ${PKG.name}`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
console.log(` ✅ ${target.name}: ${srcCount} 个 skills -> ${dest}`);
|
|
304
|
+
|
|
305
|
+
if (target.generateBootstrap) {
|
|
306
|
+
target.generateBootstrap(baseDir, skillEntries);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function isHomeDir(p) {
|
|
311
|
+
const home = homedir();
|
|
312
|
+
if (!home) return false;
|
|
313
|
+
try {
|
|
314
|
+
return realpathSync(p) === realpathSync(home);
|
|
315
|
+
} catch { return resolve(p) === resolve(home); }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function listTemplates() {
|
|
319
|
+
if (!existsSync(TEMPLATES_SRC)) return [];
|
|
320
|
+
return readdirSync(TEMPLATES_SRC, { withFileTypes: true })
|
|
321
|
+
.filter(e => e.isDirectory())
|
|
322
|
+
.map(e => e.name);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function copyTemplateSync(templateName, destDir) {
|
|
326
|
+
const src = resolve(TEMPLATES_SRC, templateName);
|
|
327
|
+
if (!existsSync(src)) {
|
|
328
|
+
throw new Error(`模板不存在: ${templateName}`);
|
|
329
|
+
}
|
|
330
|
+
copyDirSync(src, destDir);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function mergeFileIfExists(filePath, content) {
|
|
334
|
+
if (existsSync(filePath)) {
|
|
335
|
+
const existing = readFileSync(filePath, 'utf8');
|
|
336
|
+
if (existing.includes(content.trim())) return false;
|
|
337
|
+
writeFileSync(filePath, existing.replace(/\s+$/, '') + '\n\n' + content.trim() + '\n', 'utf8');
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
writeFileSync(filePath, content, 'utf8');
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function init(templateName, withSkills) {
|
|
345
|
+
console.log(`\n ${PKG.name} v${PKG.version} — 初始化 ${templateName} 模板\n`);
|
|
346
|
+
|
|
347
|
+
if (!existsSync(TEMPLATES_SRC)) {
|
|
348
|
+
console.error(' ❌ 错误:templates 源目录不存在。');
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const templates = listTemplates();
|
|
353
|
+
if (templates.length === 0) {
|
|
354
|
+
console.error(' ❌ 错误:没有可用的模板。');
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!templateName) {
|
|
359
|
+
console.error(` ❌ --init 需要指定模板名。可用模板: ${templates.join(', ')}\n`);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!templates.includes(templateName)) {
|
|
364
|
+
console.error(` ❌ 未知模板: ${templateName}`);
|
|
365
|
+
console.error(` 可用模板: ${templates.join(', ')}\n`);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!withSkills && isHomeDir(PROJECT_DIR)) {
|
|
370
|
+
console.error(
|
|
371
|
+
` ⚠️ 当前目录是用户主目录: ${PROJECT_DIR}
|
|
372
|
+
|
|
373
|
+
${PKG.name} --init 应该执行到具体项目目录。
|
|
374
|
+
请先 cd 到项目目录后重试:
|
|
375
|
+
cd /path/to/your/project && npx ${PKG.name} --init ${templateName}
|
|
376
|
+
`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
copyTemplateSync(templateName, PROJECT_DIR);
|
|
381
|
+
console.log(` ✅ ${templateName} 模板已初始化到: ${PROJECT_DIR}\n`);
|
|
382
|
+
|
|
383
|
+
if (withSkills) {
|
|
384
|
+
console.log(` 继续安装 skills...\n`);
|
|
385
|
+
install(undefined, true);
|
|
386
|
+
} else {
|
|
387
|
+
console.log(' 提示:如需同时安装 skills,可执行:\n');
|
|
388
|
+
console.log(` npx ${PKG.name} --init ${templateName} --with-skills\n`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function showHelp() {
|
|
393
|
+
console.log(`
|
|
394
|
+
${PKG.name} v${PKG.version} — Pandax 全栈 AI 编程技能魔盒
|
|
395
|
+
|
|
396
|
+
用法:
|
|
397
|
+
npx ${PKG.name} 项目级:自动检测工具并安装 skills
|
|
398
|
+
npx ${PKG.name} --tool trae 指定工具安装 skills
|
|
399
|
+
npx ${PKG.name} --init vue3 初始化 Vue3 项目模板
|
|
400
|
+
npx ${PKG.name} --init vue3 --with-skills 初始化 Vue3 模板并安装 skills
|
|
401
|
+
npx ${PKG.name} --uninstall 卸载当前项目 skills
|
|
402
|
+
npx ${PKG.name} --help 显示帮助
|
|
403
|
+
npx ${PKG.name} --version 显示版本
|
|
404
|
+
|
|
405
|
+
支持的工具名:
|
|
406
|
+
${Object.keys(TOOL_ALIASES).join(', ')}
|
|
407
|
+
|
|
408
|
+
可用模板:
|
|
409
|
+
${listTemplates().join(', ') || '(无)'}
|
|
410
|
+
|
|
411
|
+
说明:
|
|
412
|
+
项目级:把 ${countDirs(SKILLS_SRC)} 个 skills 装到当前项目对应目录(如 .trae/skills)。
|
|
413
|
+
`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function uninstall() {
|
|
417
|
+
const baseDir = PROJECT_DIR;
|
|
418
|
+
console.log(`\n ${PKG.name} v${PKG.version} — 卸载(项目级)\n`);
|
|
419
|
+
console.log(` 目标: ${baseDir}\n`);
|
|
420
|
+
|
|
421
|
+
if (!existsSync(SKILLS_SRC)) {
|
|
422
|
+
console.error(' ❌ 错误:skills 源目录不存在,无法识别要卸载的 skill 名单。');
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const srcSkillNames = new Set(
|
|
427
|
+
readdirSync(SKILLS_SRC, { withFileTypes: true })
|
|
428
|
+
.filter(e => e.isDirectory())
|
|
429
|
+
.map(e => e.name)
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
let totalSkills = 0;
|
|
433
|
+
for (const target of TARGETS) {
|
|
434
|
+
const dest = resolve(baseDir, target.dir);
|
|
435
|
+
if (!existsSync(dest)) continue;
|
|
436
|
+
let removed = 0;
|
|
437
|
+
for (const entry of readdirSync(dest, { withFileTypes: true })) {
|
|
438
|
+
if (entry.isDirectory() && srcSkillNames.has(entry.name)) {
|
|
439
|
+
removeSync(resolve(dest, entry.name), { recursive: true, force: true });
|
|
440
|
+
removed++;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (removed > 0) {
|
|
444
|
+
console.log(` ✅ ${target.name}: 移除 ${removed} 个 skills <- ${dest}`);
|
|
445
|
+
totalSkills += removed;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
if (existsSync(dest)) {
|
|
449
|
+
const left = readdirSync(dest).filter(n => n !== '.DS_Store');
|
|
450
|
+
if (left.length === 0) removeSync(dest, { recursive: true, force: true });
|
|
451
|
+
}
|
|
452
|
+
} catch {}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// 删除 Trae bootstrap rule
|
|
456
|
+
const rulePath = resolve(baseDir, '.trae', 'rules', 'pandax.md');
|
|
457
|
+
let bootstrapsRemoved = 0;
|
|
458
|
+
if (existsSync(rulePath)) {
|
|
459
|
+
removeSync(rulePath);
|
|
460
|
+
console.log(` ✅ 删除 bootstrap: ${rulePath}`);
|
|
461
|
+
bootstrapsRemoved++;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 清理 CodeBuddy CODEBUDDY.md 中的 sentinel 段落
|
|
465
|
+
const codeBuddyPath = resolve(baseDir, 'CODEBUDDY.md');
|
|
466
|
+
if (cleanBootstrapSection(codeBuddyPath)) {
|
|
467
|
+
console.log(` ✅ 清理 bootstrap: ${codeBuddyPath}`);
|
|
468
|
+
bootstrapsRemoved++;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (totalSkills === 0 && bootstrapsRemoved === 0) {
|
|
472
|
+
console.log(` ⚠️ 未在当前目录找到 ${PKG.name} 安装痕迹。`);
|
|
473
|
+
} else {
|
|
474
|
+
console.log(`\n 卸载完成。共移除 ${totalSkills} 个 skill 目录、${bootstrapsRemoved} 个 bootstrap 文件。\n`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function install(forceToolName, force) {
|
|
479
|
+
try {
|
|
480
|
+
console.log(`\n ${PKG.name} v${PKG.version} — Pandax 全栈 AI 编程技能魔盒\n`);
|
|
481
|
+
|
|
482
|
+
if (!existsSync(SKILLS_SRC)) {
|
|
483
|
+
console.error(' ❌ 错误:skills 源目录不存在。');
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (!force && isHomeDir(PROJECT_DIR)) {
|
|
488
|
+
console.error(
|
|
489
|
+
` ⚠️ 当前目录是用户主目录: ${PROJECT_DIR}
|
|
490
|
+
|
|
491
|
+
${PKG.name} 应该装到具体项目目录,而不是 ~/。
|
|
492
|
+
请先 cd 到项目目录后重试:
|
|
493
|
+
cd /path/to/your/project && npx ${PKG.name}
|
|
494
|
+
|
|
495
|
+
如果你确实要在主目录安装(不推荐),加 --force:
|
|
496
|
+
npx ${PKG.name} --force
|
|
497
|
+
`);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const skillEntries = scanSkillEntries(SKILLS_SRC);
|
|
502
|
+
|
|
503
|
+
console.log(` 源: ${skillEntries.length} 个 skills`);
|
|
504
|
+
console.log(` 模式: 项目级`);
|
|
505
|
+
console.log(` 目标: ${PROJECT_DIR}\n`);
|
|
506
|
+
|
|
507
|
+
if (forceToolName) {
|
|
508
|
+
const target = TARGETS.find(t => t.name === forceToolName);
|
|
509
|
+
if (!target) {
|
|
510
|
+
console.error(` ❌ 未知工具: ${forceToolName}`);
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
installForTarget(target, PROJECT_DIR, skillEntries);
|
|
514
|
+
console.log('\n 安装完成!重启你的 AI 编程工具即可生效。\n');
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
let installed = 0;
|
|
519
|
+
for (const target of TARGETS) {
|
|
520
|
+
const detectMarker = target.detect;
|
|
521
|
+
const detects = Array.isArray(detectMarker) ? detectMarker : [detectMarker];
|
|
522
|
+
const found = detects.some(d => existsSync(resolve(PROJECT_DIR, d)));
|
|
523
|
+
if (found) {
|
|
524
|
+
installForTarget(target, PROJECT_DIR, skillEntries);
|
|
525
|
+
installed++;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (installed === 0) {
|
|
530
|
+
console.log(` ⚠️ 未在当前目录检测到任何已知 AI 编程工具的标记。\n`);
|
|
531
|
+
console.log(' 为避免装错工具,未做任何安装。请用 --tool 显式指定你的工具,例如:\n');
|
|
532
|
+
console.log(` npx ${PKG.name} --tool trae # Trae`);
|
|
533
|
+
console.log(` npx ${PKG.name} --tool cursor # Cursor`);
|
|
534
|
+
console.log(` npx ${PKG.name} --tool claude # Claude Code\n`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
console.log('\n 安装完成!重启你的 AI 编程工具即可生效。\n');
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.error(` ❌ 安装失败:${err.message}`);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const args = process.argv.slice(2);
|
|
546
|
+
const helpIdx = args.findIndex(a => a === '--help' || a === '-h');
|
|
547
|
+
const versionIdx = args.findIndex(a => a === '--version' || a === '-v');
|
|
548
|
+
const toolIdx = args.findIndex(a => a === '--tool' || a === '-t');
|
|
549
|
+
const initIdx = args.findIndex(a => a === '--init' || a === '-i');
|
|
550
|
+
const uninstallIdx = args.findIndex(a => a === '--uninstall' || a === '-u');
|
|
551
|
+
const forceIdx = args.findIndex(a => a === '--force' || a === '-f');
|
|
552
|
+
const withSkillsIdx = args.findIndex(a => a === '--with-skills');
|
|
553
|
+
const force = forceIdx !== -1;
|
|
554
|
+
const withSkills = withSkillsIdx !== -1;
|
|
555
|
+
|
|
556
|
+
const KNOWN_FLAGS = new Set([
|
|
557
|
+
'--help', '-h', '--version', '-v', '--uninstall', '-u', '--force', '-f',
|
|
558
|
+
'--tool', '-t', '--init', '-i', '--with-skills'
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
if (helpIdx !== -1) {
|
|
562
|
+
showHelp();
|
|
563
|
+
} else if (versionIdx !== -1) {
|
|
564
|
+
console.log(PKG.version);
|
|
565
|
+
} else if (uninstallIdx !== -1) {
|
|
566
|
+
uninstall();
|
|
567
|
+
} else if (initIdx !== -1) {
|
|
568
|
+
const templateArg = args[initIdx + 1];
|
|
569
|
+
init(templateArg, withSkills);
|
|
570
|
+
} else if (toolIdx !== -1) {
|
|
571
|
+
const toolArg = args[toolIdx + 1];
|
|
572
|
+
if (!toolArg) {
|
|
573
|
+
console.error(' ❌ --tool 需要指定工具名,例如: --tool trae\n');
|
|
574
|
+
showHelp();
|
|
575
|
+
process.exit(1);
|
|
576
|
+
}
|
|
577
|
+
const toolName = TOOL_ALIASES[toolArg.toLowerCase()];
|
|
578
|
+
if (!toolName) {
|
|
579
|
+
console.error(` ❌ 未知工具: ${toolArg}`);
|
|
580
|
+
console.error(` 支持的工具: ${Object.keys(TOOL_ALIASES).join(', ')}\n`);
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
install(toolName, force);
|
|
584
|
+
} else {
|
|
585
|
+
const unknown = args.find(a => a.startsWith('-') && !KNOWN_FLAGS.has(a));
|
|
586
|
+
if (unknown) {
|
|
587
|
+
console.warn(` 未知参数: ${unknown}\n`);
|
|
588
|
+
showHelp();
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
install(undefined, force);
|
|
592
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pandax",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Pandax 全栈 AI 编程技能魔盒,支持通过 npx 安装 skills 到 Trae/Cursor/CodeBuddy 等工具",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pandax": "bin/pandax.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node bin/pandax.js --help"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"pandax",
|
|
13
|
+
"pandora",
|
|
14
|
+
"skills",
|
|
15
|
+
"ai-coding",
|
|
16
|
+
"trae",
|
|
17
|
+
"cursor",
|
|
18
|
+
"codebuddy",
|
|
19
|
+
"superpowers",
|
|
20
|
+
"frontend",
|
|
21
|
+
"backend"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"bin/",
|
|
25
|
+
"skills/",
|
|
26
|
+
"templates/"
|
|
27
|
+
],
|
|
28
|
+
"author": "Pandax Team",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=14.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|