moluoxixi-ai-rules 0.1.0
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/LICENSE +21 -0
- package/README-zh.md +246 -0
- package/README.md +246 -0
- package/dist/constants/hosts.d.ts +34 -0
- package/dist/constants/hosts.js +71 -0
- package/dist/constants/skills.d.ts +91 -0
- package/dist/constants/skills.js +156 -0
- package/dist/scripts/cli.d.ts +2 -0
- package/dist/scripts/cli.js +145 -0
- package/dist/scripts/host-setup.d.ts +2 -0
- package/dist/scripts/host-setup.js +147 -0
- package/dist/scripts/lib/install.d.ts +78 -0
- package/dist/scripts/lib/install.js +367 -0
- package/dist/scripts/lib/links.d.ts +8 -0
- package/dist/scripts/lib/links.js +18 -0
- package/dist/scripts/lib/skill-projection.d.ts +14 -0
- package/dist/scripts/lib/skill-projection.js +54 -0
- package/dist/scripts/lib/tool.d.ts +40 -0
- package/dist/scripts/lib/tool.js +125 -0
- package/dist/scripts/lib/vendor-sync.d.ts +3 -0
- package/dist/scripts/lib/vendor-sync.js +129 -0
- package/dist/scripts/lib/vendors.d.ts +29 -0
- package/dist/scripts/lib/vendors.js +155 -0
- package/dist/scripts/lib/verify.d.ts +7 -0
- package/dist/scripts/lib/verify.js +84 -0
- package/dist/scripts/sync-vendors.d.ts +1 -0
- package/dist/scripts/sync-vendors.js +173 -0
- package/dist/scripts/verify-host.d.ts +2 -0
- package/dist/scripts/verify-host.js +42 -0
- package/package.json +60 -0
- package/rules/AGENTS.md +30 -0
- package/skills/skill-validation-standard/SKILL.md +45 -0
- package/skills/skill-validation-standard/__test__/verify-rules.test.ts +108 -0
- package/skills/skill-validation-standard/scripts/verify-rules.mjs +186 -0
- package/skills/workflow/frontend-code-standard/SKILL.md +58 -0
- package/skills/workflow/frontend-code-standard/__test__/verify-rules.test.ts +227 -0
- package/skills/workflow/frontend-code-standard/scripts/verify-rules.mjs +537 -0
- package/skills/workflow/java-code-standard/SKILL.md +58 -0
- package/skills/workflow/java-code-standard/__test__/verify-rules.test.ts +48 -0
- package/skills/workflow/java-code-standard/scripts/verify-rules.mjs +215 -0
- package/skills/workflow/nestjs-code-standard/SKILL.md +61 -0
- package/skills/workflow/nestjs-code-standard/__test__/verify-rules.test.ts +48 -0
- package/skills/workflow/nestjs-code-standard/scripts/verify-rules.mjs +219 -0
- package/skills/workflow/node-code-standard/SKILL.md +62 -0
- package/skills/workflow/node-code-standard/__test__/verify-rules.test.ts +48 -0
- package/skills/workflow/node-code-standard/scripts/verify-rules.mjs +213 -0
- package/skills/workflow/project-knowledge-read-workflow/SKILL.md +102 -0
- package/skills/workflow/project-knowledge-refresh-workflow/SKILL.md +149 -0
- package/skills/workflow/software-development-workflow/SKILL.md +85 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安装前置命令必须以结构化参数声明,避免把配置内容拼进 shell 字符串。
|
|
3
|
+
*/
|
|
4
|
+
export interface SetupCommand {
|
|
5
|
+
command: string;
|
|
6
|
+
args?: string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 单个 skill 的详细配置(适用于需要重命名或前置安装命令的场景)
|
|
10
|
+
*/
|
|
11
|
+
export interface SkillConfig {
|
|
12
|
+
/** 仓库内源目录名 */
|
|
13
|
+
name: string;
|
|
14
|
+
/** 安装后目录名,默认与 name 相同 */
|
|
15
|
+
output?: string;
|
|
16
|
+
/**
|
|
17
|
+
* 该 skill 的安装前置命令。
|
|
18
|
+
* 在 skill 链接建立后执行,例如安装对应的全局 CLI 工具。
|
|
19
|
+
* 例如:[{ command: 'some-cli-installer', args: ['--global'] }]
|
|
20
|
+
* 命令按顺序执行,任一失败均会抛出错误并中断整体流程。
|
|
21
|
+
*/
|
|
22
|
+
setup?: SetupCommand[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 技能定义:
|
|
26
|
+
* - 字符串:简写形式,源目录名 === 安装后目录名,无 setup
|
|
27
|
+
* - SkillConfig:对象形式,支持重命名和 per-skill setup 命令
|
|
28
|
+
*/
|
|
29
|
+
export type SkillDef = string | SkillConfig;
|
|
30
|
+
/**
|
|
31
|
+
* 单个供应商仓库内的一条安装投影规则。
|
|
32
|
+
* namespace 用于递归扫描目录中的叶子 skills;skills 用于按 skill 精确投影。
|
|
33
|
+
* 两者安装到 vendor/skills 时都只保留叶子 skill 名称,不继承源目录层级。
|
|
34
|
+
*/
|
|
35
|
+
export type VendorProjection = {
|
|
36
|
+
kind: 'namespace';
|
|
37
|
+
/** 仓库内要递归扫描的目录 */
|
|
38
|
+
sourceDir: string;
|
|
39
|
+
/** 清单中的占位名;实际 vendor 目录由叶子 skill 名称决定 */
|
|
40
|
+
output: string;
|
|
41
|
+
/** namespace 级安装前置命令 */
|
|
42
|
+
setup?: SetupCommand[];
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'skills';
|
|
45
|
+
/** 仓库内技能所在的基准目录 */
|
|
46
|
+
sourceBaseDir: string;
|
|
47
|
+
/** 需要精确安装的技能列表 */
|
|
48
|
+
skills: SkillDef[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* 代表一个外部供应商的技能仓库
|
|
52
|
+
*/
|
|
53
|
+
export interface VendorRepo {
|
|
54
|
+
/** 供应商名称,也是克隆到本地后的目录名 */
|
|
55
|
+
name: string;
|
|
56
|
+
/** 是否为官方仓库 */
|
|
57
|
+
official: boolean;
|
|
58
|
+
/** Git 仓库地址 */
|
|
59
|
+
source: string;
|
|
60
|
+
/** 从该仓库投影到 vendor/skills 的安装规则列表 */
|
|
61
|
+
projections: VendorProjection[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 技能节点:可以是一个具体的 VendorRepo 实例,也可以是一个包含多个节点的分类对象。
|
|
65
|
+
* 这种递归结构允许在数组中直接混合使用"扁平技能"和"嵌套分类"。
|
|
66
|
+
*/
|
|
67
|
+
export type VendorNode = VendorRepo | {
|
|
68
|
+
[category: string]: VendorNode[];
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* 供应商配置:必须是一个 VendorNode 数组。
|
|
72
|
+
* 这种结构提供了极大的灵活性:
|
|
73
|
+
* - 如果直接放入 VendorRepo,安装时会扁平化到顶级。
|
|
74
|
+
* - 如果放入 { "category": [...] },category 只作为配置分组,不进入 vendor/skills 路径。
|
|
75
|
+
*/
|
|
76
|
+
export type VendorsConfig = VendorNode[];
|
|
77
|
+
/**
|
|
78
|
+
* @see https://github.com/vercel/next.js.git next.js官方仓库
|
|
79
|
+
* @see https://github.com/vercel/next.js/tree/canary/.claude-plugin/plugins/cache-components/skills next.js官方用于claude的skills
|
|
80
|
+
* @see https://github.com/facebook/react.git react官方仓库
|
|
81
|
+
* @see https://github.com/antfu/skills.git antfu的技能仓库,收集了很多前端技能
|
|
82
|
+
* @see https://github.com/facebook/react/tree/main/.claude/skills react官方用于claude的skills
|
|
83
|
+
* @see https://github.com/vercel-labs/agent-skills.git Vercel官方面向AI代理的React/React Native技能仓库
|
|
84
|
+
* @see https://github.com/vercel-labs/web-interface-guidelines.git Vercel官方Web界面规范
|
|
85
|
+
* @see https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/awesome_agent_skills awesome-agent-skills仓库,收集了很多技能
|
|
86
|
+
* @see https://github.com/anthropics/skills.git anthropic(claude)官方技能仓库
|
|
87
|
+
* @see https://github.com/google-gemini/gemini-cli.git gemini官方技能仓库
|
|
88
|
+
* @see https://github.com/openai/skills.git openai官方技能仓库
|
|
89
|
+
* @see https://github.com/obra/superpowers.git superpowers官方技能仓库
|
|
90
|
+
*/
|
|
91
|
+
export declare const vendors: VendorsConfig;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/vercel/next.js.git next.js官方仓库
|
|
3
|
+
* @see https://github.com/vercel/next.js/tree/canary/.claude-plugin/plugins/cache-components/skills next.js官方用于claude的skills
|
|
4
|
+
* @see https://github.com/facebook/react.git react官方仓库
|
|
5
|
+
* @see https://github.com/antfu/skills.git antfu的技能仓库,收集了很多前端技能
|
|
6
|
+
* @see https://github.com/facebook/react/tree/main/.claude/skills react官方用于claude的skills
|
|
7
|
+
* @see https://github.com/vercel-labs/agent-skills.git Vercel官方面向AI代理的React/React Native技能仓库
|
|
8
|
+
* @see https://github.com/vercel-labs/web-interface-guidelines.git Vercel官方Web界面规范
|
|
9
|
+
* @see https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/awesome_agent_skills awesome-agent-skills仓库,收集了很多技能
|
|
10
|
+
* @see https://github.com/anthropics/skills.git anthropic(claude)官方技能仓库
|
|
11
|
+
* @see https://github.com/google-gemini/gemini-cli.git gemini官方技能仓库
|
|
12
|
+
* @see https://github.com/openai/skills.git openai官方技能仓库
|
|
13
|
+
* @see https://github.com/obra/superpowers.git superpowers官方技能仓库
|
|
14
|
+
*/
|
|
15
|
+
export const vendors = [
|
|
16
|
+
{
|
|
17
|
+
name: 'gemini',
|
|
18
|
+
official: true,
|
|
19
|
+
source: 'https://github.com/google-gemini/gemini-cli.git',
|
|
20
|
+
projections: [
|
|
21
|
+
{
|
|
22
|
+
kind: 'skills',
|
|
23
|
+
sourceBaseDir: '.gemini/skills',
|
|
24
|
+
skills: ['code-reviewer', 'pr-creator'],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'vercelLabs',
|
|
30
|
+
official: true,
|
|
31
|
+
source: 'https://github.com/vercel-labs/skills.git',
|
|
32
|
+
projections: [
|
|
33
|
+
{
|
|
34
|
+
kind: 'skills',
|
|
35
|
+
sourceBaseDir: 'skills',
|
|
36
|
+
skills: ['find-skills'],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'vercelAgentSkills',
|
|
42
|
+
official: true,
|
|
43
|
+
source: 'https://github.com/vercel-labs/agent-skills.git',
|
|
44
|
+
projections: [
|
|
45
|
+
{
|
|
46
|
+
kind: 'skills',
|
|
47
|
+
sourceBaseDir: 'skills',
|
|
48
|
+
skills: [
|
|
49
|
+
{
|
|
50
|
+
name: 'react-best-practices',
|
|
51
|
+
output: 'vercel-react-best-practices',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'react-native-skills',
|
|
55
|
+
output: 'vercel-react-native-skills',
|
|
56
|
+
},
|
|
57
|
+
'web-design-guidelines',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'anthropic',
|
|
64
|
+
official: true,
|
|
65
|
+
source: 'https://github.com/anthropics/skills.git',
|
|
66
|
+
projections: [
|
|
67
|
+
{
|
|
68
|
+
kind: 'skills',
|
|
69
|
+
sourceBaseDir: 'skills',
|
|
70
|
+
skills: [
|
|
71
|
+
'frontend-design',
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'antfu',
|
|
78
|
+
official: true,
|
|
79
|
+
source: 'https://github.com/antfu/skills.git',
|
|
80
|
+
projections: [
|
|
81
|
+
{
|
|
82
|
+
kind: 'skills',
|
|
83
|
+
sourceBaseDir: 'skills',
|
|
84
|
+
skills: [
|
|
85
|
+
// 只保留具体技术栈技能,避免顶层个人偏好规则抢占代码库第一方 workflow。
|
|
86
|
+
'pnpm',
|
|
87
|
+
'slidev',
|
|
88
|
+
'tsdown',
|
|
89
|
+
'turborepo',
|
|
90
|
+
'vitest',
|
|
91
|
+
'nuxt',
|
|
92
|
+
'pinia',
|
|
93
|
+
'unocss',
|
|
94
|
+
'vite',
|
|
95
|
+
'vitepress',
|
|
96
|
+
'vue',
|
|
97
|
+
'vue-best-practices',
|
|
98
|
+
'vue-router-best-practices',
|
|
99
|
+
'vue-testing-best-practices',
|
|
100
|
+
'vueuse-functions',
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'openai',
|
|
107
|
+
official: true,
|
|
108
|
+
source: 'https://github.com/openai/skills.git',
|
|
109
|
+
projections: [
|
|
110
|
+
{
|
|
111
|
+
kind: 'skills',
|
|
112
|
+
sourceBaseDir: 'skills/.curated',
|
|
113
|
+
skills: ['playwright'],
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'superpowers',
|
|
119
|
+
official: true,
|
|
120
|
+
source: 'https://github.com/obra/superpowers.git',
|
|
121
|
+
projections: [
|
|
122
|
+
{
|
|
123
|
+
kind: 'skills',
|
|
124
|
+
sourceBaseDir: 'skills',
|
|
125
|
+
// 投影代码库常用工作流子集;TDD、子代理执行与分支收尾仍由第一方 workflow 按风险显式触发。
|
|
126
|
+
skills: [
|
|
127
|
+
'systematic-debugging',
|
|
128
|
+
'verification-before-completion',
|
|
129
|
+
'receiving-code-review',
|
|
130
|
+
'writing-skills',
|
|
131
|
+
'using-git-worktrees',
|
|
132
|
+
'writing-plans',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'moluoxixi',
|
|
139
|
+
official: true,
|
|
140
|
+
source: 'https://github.com/moluoxixi/AIRules.git',
|
|
141
|
+
projections: [
|
|
142
|
+
{
|
|
143
|
+
kind: 'namespace',
|
|
144
|
+
sourceDir: 'skills/workflow',
|
|
145
|
+
output: 'workflow',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
kind: 'skills',
|
|
149
|
+
sourceBaseDir: 'skills',
|
|
150
|
+
skills: [
|
|
151
|
+
'skill-validation-standard',
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
|
+
import kleur from 'kleur';
|
|
7
|
+
import { addLocalSkill, getDefaultMoluoHome, syncToHosts, verifyHosts, } from './lib/tool.js';
|
|
8
|
+
function findPackageRoot(fromFileUrl) {
|
|
9
|
+
let currentDir = path.dirname(fileURLToPath(fromFileUrl));
|
|
10
|
+
while (!existsSync(path.join(currentDir, 'package.json'))) {
|
|
11
|
+
const parentDir = path.dirname(currentDir);
|
|
12
|
+
if (parentDir === currentDir) {
|
|
13
|
+
throw new Error(`Unable to locate package.json from ${fromFileUrl}`);
|
|
14
|
+
}
|
|
15
|
+
currentDir = parentDir;
|
|
16
|
+
}
|
|
17
|
+
return currentDir;
|
|
18
|
+
}
|
|
19
|
+
const PACKAGE_ROOT = findPackageRoot(import.meta.url);
|
|
20
|
+
function printHelp() {
|
|
21
|
+
console.log(`Usage:
|
|
22
|
+
airules sync [--host <name|all>] [--home <dir>] [--user-home <dir>] [--skip-vendors] [--no-verify]
|
|
23
|
+
airules add <skill-dir> [--name <skill-name>] [--host <name|all>] [--home <dir>] [--user-home <dir>] [--overwrite] [--skip-sync] [--sync-vendors]
|
|
24
|
+
airules verify [--host <name|all>] [--home <dir>] [--user-home <dir>]
|
|
25
|
+
|
|
26
|
+
Commands:
|
|
27
|
+
sync 同步内置、用户自定义和第三方 skills 到宿主
|
|
28
|
+
add 添加包含 SKILL.md 的本地 skill 目录,并默认同步到宿主
|
|
29
|
+
verify 校验宿主 skills 链接完整性
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
function parseCommandArgs(args) {
|
|
33
|
+
return parseArgs({
|
|
34
|
+
args,
|
|
35
|
+
allowPositionals: true,
|
|
36
|
+
options: {
|
|
37
|
+
'help': { type: 'boolean', short: 'h' },
|
|
38
|
+
'home': { type: 'string' },
|
|
39
|
+
'host': { type: 'string' },
|
|
40
|
+
'name': { type: 'string' },
|
|
41
|
+
'overwrite': { type: 'boolean' },
|
|
42
|
+
'no-verify': { type: 'boolean' },
|
|
43
|
+
'repo-root': { type: 'string' },
|
|
44
|
+
'skip-sync': { type: 'boolean' },
|
|
45
|
+
'skip-vendors': { type: 'boolean' },
|
|
46
|
+
'sync-vendors': { type: 'boolean' },
|
|
47
|
+
'user-home': { type: 'string' },
|
|
48
|
+
},
|
|
49
|
+
strict: true,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function commonOptions(values) {
|
|
53
|
+
return {
|
|
54
|
+
home: String(values.home ?? getDefaultMoluoHome()),
|
|
55
|
+
host: String(values.host ?? 'all'),
|
|
56
|
+
repoRoot: String(values['repo-root'] ?? PACKAGE_ROOT),
|
|
57
|
+
userHome: values['user-home'] === undefined ? undefined : String(values['user-home']),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function runSync(args) {
|
|
61
|
+
const { values } = parseCommandArgs(args);
|
|
62
|
+
if (values.help === true) {
|
|
63
|
+
printHelp();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const options = commonOptions(values);
|
|
67
|
+
const result = await syncToHosts({
|
|
68
|
+
...options,
|
|
69
|
+
skipVendors: values['skip-vendors'] === true,
|
|
70
|
+
verify: values['no-verify'] !== true,
|
|
71
|
+
});
|
|
72
|
+
console.log(kleur.green(`[sync] 完成: ${result.projectedHosts.join(', ') || '无宿主投影'}`));
|
|
73
|
+
console.log(`[home] ${result.moluoHome}`);
|
|
74
|
+
if (result.skippedHosts.length > 0) {
|
|
75
|
+
console.log(`[skip] 宿主目录不存在: ${result.skippedHosts.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function runAdd(args) {
|
|
79
|
+
const { positionals, values } = parseCommandArgs(args);
|
|
80
|
+
if (values.help === true) {
|
|
81
|
+
printHelp();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const options = commonOptions(values);
|
|
85
|
+
const sourceDir = positionals[0];
|
|
86
|
+
if (sourceDir === undefined) {
|
|
87
|
+
throw new Error('Missing <skill-dir> for add command');
|
|
88
|
+
}
|
|
89
|
+
const added = addLocalSkill({
|
|
90
|
+
sourceDir,
|
|
91
|
+
moluoHome: options.home,
|
|
92
|
+
name: values.name === undefined ? undefined : String(values.name),
|
|
93
|
+
overwrite: values.overwrite === true,
|
|
94
|
+
});
|
|
95
|
+
console.log(kleur.green(`[add] ${added.skillName} -> ${added.targetDir}`));
|
|
96
|
+
if (values['skip-sync'] === true) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const result = await syncToHosts({
|
|
100
|
+
...options,
|
|
101
|
+
skipVendors: values['sync-vendors'] !== true,
|
|
102
|
+
verify: values['no-verify'] !== true,
|
|
103
|
+
});
|
|
104
|
+
console.log(kleur.green(`[sync] 完成: ${result.projectedHosts.join(', ') || '无宿主投影'}`));
|
|
105
|
+
if (result.skippedHosts.length > 0) {
|
|
106
|
+
console.log(`[skip] 宿主目录不存在: ${result.skippedHosts.join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function runVerify(args) {
|
|
110
|
+
const { values } = parseCommandArgs(args);
|
|
111
|
+
if (values.help === true) {
|
|
112
|
+
printHelp();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const options = commonOptions(values);
|
|
116
|
+
const targets = await verifyHosts({
|
|
117
|
+
home: options.home,
|
|
118
|
+
host: options.host,
|
|
119
|
+
});
|
|
120
|
+
console.log(kleur.green(`[verify] 通过: ${targets.join(', ')}`));
|
|
121
|
+
}
|
|
122
|
+
async function main() {
|
|
123
|
+
const [command, ...commandArgs] = process.argv.slice(2);
|
|
124
|
+
if (command === undefined || command === 'help' || command === '--help' || command === '-h') {
|
|
125
|
+
printHelp();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (command === 'sync') {
|
|
129
|
+
await runSync(commandArgs);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (command === 'add') {
|
|
133
|
+
await runAdd(commandArgs);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (command === 'verify') {
|
|
137
|
+
await runVerify(commandArgs);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`Unknown command: ${command}`);
|
|
141
|
+
}
|
|
142
|
+
main().catch((error) => {
|
|
143
|
+
console.error(kleur.red(String(error)));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import kleur from 'kleur';
|
|
5
|
+
import { ALL_HOST_IDS } from '../constants/hosts.js';
|
|
6
|
+
import { ensureGlobalSkillLink, ensureInstallRoot, getDefaultInstallPaths, linkHostBaseline, projectHostById, rebuildVendorSkillLinks, runSkillSetupCommands, syncFirstPartySkillsToVendor, syncFirstPartyToHome, } from './lib/install.js';
|
|
7
|
+
import { ensureVendorRepo } from './lib/vendor-sync.js';
|
|
8
|
+
import { loadVendorManifest } from './lib/vendors.js';
|
|
9
|
+
import { verifyHost } from './lib/verify.js';
|
|
10
|
+
function printHelp() {
|
|
11
|
+
const hostsLine = ALL_HOST_IDS.join('\n ');
|
|
12
|
+
console.log(`Usage: npx tsx scripts/host-setup.ts --host <name|all> [--mode <install|upgrade>] [--home <dir>] [--user-home <dir>] [--skip-vendors]
|
|
13
|
+
|
|
14
|
+
Hosts:
|
|
15
|
+
all (安装到所有支持的代理)
|
|
16
|
+
${hostsLine}
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const args = {
|
|
21
|
+
host: '',
|
|
22
|
+
mode: 'install', // 默认安装
|
|
23
|
+
home: path.join(os.homedir(), '.moluoxixi'),
|
|
24
|
+
skipVendors: false,
|
|
25
|
+
help: false,
|
|
26
|
+
};
|
|
27
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
28
|
+
const arg = argv[index];
|
|
29
|
+
if (arg === '--help') {
|
|
30
|
+
args.help = true;
|
|
31
|
+
}
|
|
32
|
+
else if (arg === '--host') {
|
|
33
|
+
args.host = argv[index + 1];
|
|
34
|
+
index += 1;
|
|
35
|
+
}
|
|
36
|
+
else if (arg === '--mode') {
|
|
37
|
+
args.mode = argv[index + 1];
|
|
38
|
+
index += 1;
|
|
39
|
+
}
|
|
40
|
+
else if (arg === '--home') {
|
|
41
|
+
args.home = argv[index + 1];
|
|
42
|
+
index += 1;
|
|
43
|
+
}
|
|
44
|
+
else if (arg === '--user-home') {
|
|
45
|
+
args.userHome = argv[index + 1];
|
|
46
|
+
index += 1;
|
|
47
|
+
}
|
|
48
|
+
else if (arg === '--skip-vendors') {
|
|
49
|
+
args.skipVendors = true;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return args;
|
|
56
|
+
}
|
|
57
|
+
function assertRequiredArgs(args) {
|
|
58
|
+
if (!args.host) {
|
|
59
|
+
throw new Error('Missing required --host argument');
|
|
60
|
+
}
|
|
61
|
+
if (!args.mode || !['install', 'upgrade'].includes(args.mode)) {
|
|
62
|
+
throw new Error('Missing or invalid --mode argument (expected install or upgrade)');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function syncVendorsIfNeeded(homeDir, repoRoot, skipVendors) {
|
|
66
|
+
if (skipVendors) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const manifest = await loadVendorManifest(path.join(repoRoot, 'constants', 'skills.js'));
|
|
70
|
+
for (const vendor of Object.values(manifest.vendors ?? {})) {
|
|
71
|
+
ensureVendorRepo(homeDir, vendor);
|
|
72
|
+
}
|
|
73
|
+
// 执行各 skill 的安装前置命令
|
|
74
|
+
runSkillSetupCommands(manifest);
|
|
75
|
+
}
|
|
76
|
+
async function main() {
|
|
77
|
+
const args = parseArgs(process.argv.slice(2));
|
|
78
|
+
if (args.help) {
|
|
79
|
+
printHelp();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
assertRequiredArgs(args);
|
|
83
|
+
const repoRoot = process.cwd();
|
|
84
|
+
const userHome = path.resolve(args.userHome ?? os.homedir());
|
|
85
|
+
const paths = getDefaultInstallPaths(userHome);
|
|
86
|
+
paths.moluoHome = path.resolve(args.home);
|
|
87
|
+
paths.repoRoot = repoRoot;
|
|
88
|
+
ensureInstallRoot(paths);
|
|
89
|
+
ensureGlobalSkillLink(paths);
|
|
90
|
+
syncFirstPartyToHome(repoRoot, paths.moluoHome);
|
|
91
|
+
await syncVendorsIfNeeded(paths.moluoHome, repoRoot, args.skipVendors);
|
|
92
|
+
await rebuildVendorSkillLinks({
|
|
93
|
+
homeDir: paths.moluoHome,
|
|
94
|
+
manifestPath: path.join(repoRoot, 'constants', 'skills.js'),
|
|
95
|
+
});
|
|
96
|
+
syncFirstPartySkillsToVendor(repoRoot, paths.moluoHome);
|
|
97
|
+
syncFirstPartySkillsToVendor(paths.moluoHome, paths.moluoHome);
|
|
98
|
+
const targets = args.host === 'all' ? ALL_HOST_IDS : [args.host];
|
|
99
|
+
const failedHosts = [];
|
|
100
|
+
const installErrors = [];
|
|
101
|
+
for (const host of targets) {
|
|
102
|
+
try {
|
|
103
|
+
const { success } = projectHostById(host, userHome, paths.moluoHome);
|
|
104
|
+
if (success) {
|
|
105
|
+
const baselineTarget = linkHostBaseline({
|
|
106
|
+
moluoHome: paths.moluoHome,
|
|
107
|
+
host,
|
|
108
|
+
userHome,
|
|
109
|
+
});
|
|
110
|
+
console.log(`[host] ${host} - 配置完成`);
|
|
111
|
+
if (baselineTarget) {
|
|
112
|
+
console.log(`[baseline] ${baselineTarget}`);
|
|
113
|
+
}
|
|
114
|
+
// 自动执行校验逻辑
|
|
115
|
+
const verified = await verifyHost(host, paths.moluoHome, userHome);
|
|
116
|
+
if (!verified) {
|
|
117
|
+
failedHosts.push(host);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
installErrors.push(host);
|
|
123
|
+
console.error(`[error] ${host} 安装过程中发生异常:`);
|
|
124
|
+
console.error(String(error));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const allFailures = [...installErrors, ...failedHosts];
|
|
128
|
+
const allPassed = allFailures.length === 0;
|
|
129
|
+
if (allPassed) {
|
|
130
|
+
console.log(`\n${kleur.green('✅ 所有宿主验证通过,安装/更新流程已完成。')}`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.error(`\n${kleur.red('❌ 以下宿主未通过验证,请检查上述错误信息:')}`);
|
|
134
|
+
for (const host of installErrors) {
|
|
135
|
+
console.error(` ${kleur.red(`• ${host} — 安装异常`)}`);
|
|
136
|
+
}
|
|
137
|
+
for (const host of failedHosts) {
|
|
138
|
+
console.error(` ${kleur.red(`• ${host} — 验证失败`)}`);
|
|
139
|
+
}
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
}
|
|
142
|
+
console.log(`[home] ${paths.moluoHome}`);
|
|
143
|
+
}
|
|
144
|
+
main().catch((error) => {
|
|
145
|
+
console.error(error);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { LinkEntry } from './links.js';
|
|
2
|
+
import type { VendorManifest } from './vendors.js';
|
|
3
|
+
/**
|
|
4
|
+
* 执行各 skill 的安装前置命令(per-skill 精度)。
|
|
5
|
+
* 遍历 manifest 中所有 vendor 的 links,对具有 setup 字段的 link 执行其命令。
|
|
6
|
+
* 任一命令失败都会抛出错误,避免安装流程伪装成功。
|
|
7
|
+
* @param manifest 已解析的 VendorManifest
|
|
8
|
+
*/
|
|
9
|
+
export declare function runSkillSetupCommands(manifest: VendorManifest): void;
|
|
10
|
+
export declare function isSamePath(p1: string, p2: string): boolean;
|
|
11
|
+
export declare function replaceWithSymlink(source: string, target: string, type: 'junction' | 'dir' | 'file'): void;
|
|
12
|
+
export interface InstallPaths {
|
|
13
|
+
userHome: string;
|
|
14
|
+
moluoHome: string;
|
|
15
|
+
repoRoot: string;
|
|
16
|
+
moluoBaselineFile: string;
|
|
17
|
+
globalAgentSkillsHome: string;
|
|
18
|
+
[key: string]: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function getDefaultInstallPaths(userHome?: string): InstallPaths;
|
|
21
|
+
export declare function ensureInstallRoot(paths: InstallPaths): void;
|
|
22
|
+
/**
|
|
23
|
+
* 确保全局 Agent 技能目录 (~/.agents/skills) 的链接正确。
|
|
24
|
+
* ~/.agents 是行业标准共享层,始终存在。
|
|
25
|
+
* 直接从 vendor/skills 映射,无中间层。
|
|
26
|
+
* 遵循层级自愈同步逻辑。
|
|
27
|
+
*/
|
|
28
|
+
export declare function ensureGlobalSkillLink(paths: InstallPaths): void;
|
|
29
|
+
/**
|
|
30
|
+
* 同步展平的技能软链接,并清理过时链接。
|
|
31
|
+
* @param sourceDir 源技能目录
|
|
32
|
+
* @param targetDir 目标链接目录
|
|
33
|
+
* @param moluoHome moluoxixi 根目录(用于识别自愈时需要删除的内部链接)
|
|
34
|
+
*/
|
|
35
|
+
export declare function syncFlattenedSkills(sourceDir: string, targetDir: string, moluoHome: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* 同步第一方(当前仓库内)的 agents 和基线文件到本地 moluoxixi 主目录。
|
|
38
|
+
* skills 统一走 clone → vendor/skills 流程,不在此处理。
|
|
39
|
+
*
|
|
40
|
+
* rules/AGENTS.md 始终复制到 vendor/ 目录下,作为所有宿主基线软链接的统一源。
|
|
41
|
+
* 即使 repoRoot === moluoHome(仓库本身就是安装目录),也需要执行此步骤,
|
|
42
|
+
* 因为软链接最终指向的是 vendor/AGENTS.md。
|
|
43
|
+
*/
|
|
44
|
+
export declare function syncFirstPartyToHome(repoRoot: string, moluoHome: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* 将第一方 skills 源目录投影到 vendor/skills,作为第三方 vendor 后的本地覆盖层。
|
|
47
|
+
* 该函数只清理曾经指向同一 source skills 根目录的过时链接,不会删除第三方 vendor 技能。
|
|
48
|
+
*/
|
|
49
|
+
export declare function syncFirstPartySkillsToVendor(sourceRoot: string, moluoHome: string): void;
|
|
50
|
+
export declare function rebuildVendorSkillLinks({ homeDir, manifestPath }: {
|
|
51
|
+
homeDir: string;
|
|
52
|
+
manifestPath: string;
|
|
53
|
+
}): Promise<LinkEntry[]>;
|
|
54
|
+
/**
|
|
55
|
+
* 将所有技能投影到宿主软件目录(如 .claude 或 .cursor)。
|
|
56
|
+
* 链路:vendor/skills → ~/.agents/skills → 宿主/skills
|
|
57
|
+
* ~/.agents 是行业标准共享层,始终存在(不存在则创建)。
|
|
58
|
+
*/
|
|
59
|
+
export declare function projectSkillsToHost(userHome: string, moluoHome: string, hostSkillsHome: string): void;
|
|
60
|
+
export declare function projectToHost({ userHome, moluoHome, hostHome, hostBaselineFile, customSkillsDirName, }: {
|
|
61
|
+
userHome: string;
|
|
62
|
+
moluoHome: string;
|
|
63
|
+
hostHome: string;
|
|
64
|
+
hostBaselineFile: string;
|
|
65
|
+
customSkillsDirName?: string;
|
|
66
|
+
}): void;
|
|
67
|
+
export declare function linkHostBaseline({ moluoHome, host, userHome }: {
|
|
68
|
+
moluoHome: string;
|
|
69
|
+
host: string;
|
|
70
|
+
userHome?: string;
|
|
71
|
+
}): string;
|
|
72
|
+
/**
|
|
73
|
+
* 将技能和基线投影到指定宿主,并返回是否成功(宿主目录不存在则跳过)。
|
|
74
|
+
*/
|
|
75
|
+
export declare function projectHostById(host: string, userHome: string, moluoHome: string): {
|
|
76
|
+
success: boolean;
|
|
77
|
+
hostBaselineFile: string;
|
|
78
|
+
};
|