moluoxixi-ai-rules 0.2.6 → 0.2.7
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-en.md +2 -0
- package/README.md +2 -0
- package/capabilities/README.md +2 -2
- package/dist/scripts/cli.js +1 -1
- package/dist/scripts/lib/capabilities.d.ts +3 -3
- package/dist/scripts/lib/hosts.d.ts +12 -0
- package/dist/scripts/lib/hosts.js +182 -0
- package/dist/scripts/lib/install.d.ts +1 -1
- package/dist/scripts/lib/install.js +1 -1
- package/dist/scripts/lib/tool.js +1 -1
- package/dist/scripts/lib/{capability-types.d.ts → types/capabilities.d.ts} +1 -1
- package/dist/{constants → scripts/lib/types}/hosts.d.ts +8 -16
- package/dist/scripts/lib/types/hosts.js +1 -0
- package/dist/scripts/lib/vendors.d.ts +2 -2
- package/dist/scripts/lib/vendors.js +1 -1
- package/dist/scripts/lib/verify.js +1 -1
- package/hosts/README.md +58 -0
- package/hosts/hosts.yaml +134 -0
- package/package.json +2 -1
- package/dist/constants/hosts.js +0 -167
- /package/dist/scripts/lib/{capability-types.js → types/capabilities.js} +0 -0
- /package/dist/scripts/lib/{manifest-types.d.ts → types/manifest.d.ts} +0 -0
- /package/dist/scripts/lib/{manifest-types.js → types/manifest.js} +0 -0
package/README-en.md
CHANGED
|
@@ -301,6 +301,8 @@ Role source: [`roles/trellis`](roles/trellis).
|
|
|
301
301
|
|
|
302
302
|
Shared skills, MCP catalogs, and vendor declarations live together in `capabilities/<capability>/`, such as `capabilities/common/skills/`. Each capability is a data-only directory containing `capability.yaml`, skills, MCP catalogs, and documentation; roles select capabilities in `role.yaml`. The runtime loader lives in `scripts/lib/capabilities.ts`, composes vendors in declaration order, merges compatible projections, deduplicates identical projections, and rejects source or target conflicts. See [capabilities/README.md](capabilities/README.md) for the layout and complete mapping.
|
|
303
303
|
|
|
304
|
+
Host directories, aliases, skill projection switches, and MCP formats are declared in [hosts/hosts.yaml](hosts/hosts.yaml). `scripts/lib/hosts.ts` loads and validates the declaration and resolves paths; declaration types live in `scripts/lib/types/`. Adding a host only requires a YAML entry. See [hosts/README.md](hosts/README.md) for fields and maintenance guidance.
|
|
305
|
+
|
|
304
306
|
```text
|
|
305
307
|
role manifest
|
|
306
308
|
-> pinned vendor checkout / package setup
|
package/README.md
CHANGED
|
@@ -308,6 +308,8 @@ Plan 阶段创建并确认 PRD,复杂任务补齐 design/implement 后 `task.p
|
|
|
308
308
|
|
|
309
309
|
共享 skills、MCP 清单和供应商声明按能力集中在 `capabilities/<能力>/`,例如 `capabilities/common/skills/`。每个能力目录是纯数据目录,只包含 `capability.yaml`、skills、MCP 清单和文档;Role 在 `role.yaml` 中选择 capability。运行时加载器位于 `scripts/lib/capabilities.ts`,负责按声明顺序组合 vendor,合并兼容投影、去重完全相同的 projection,并拒绝来源或目标冲突。目录结构与完整映射见 [capabilities/README.md](capabilities/README.md)。
|
|
310
310
|
|
|
311
|
+
宿主目录、别名、skills 投影开关和 MCP 格式统一声明在 [hosts/hosts.yaml](hosts/hosts.yaml)。`scripts/lib/hosts.ts` 负责加载、校验和路径解析,声明类型放在 `scripts/lib/types/`。新增宿主只需更新 YAML;字段与维护说明见 [hosts/README.md](hosts/README.md)。
|
|
312
|
+
|
|
311
313
|
分发链路为:
|
|
312
314
|
|
|
313
315
|
```text
|
package/capabilities/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`capabilities/` 集中维护跨角色复用的供应商声明、skills 和 MCP 配置。每个能力目录同时容纳资产与安装声明;角色在 `roles/<role>/role.yaml` 中选择能力。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
能力、角色和[宿主声明](../hosts/README.md)都使用 YAML,因为它们是安装器、校验器和其它分发工具共享的配置数据,而不是执行逻辑。新增能力、角色或宿主只需更新声明,不需要编写 TypeScript 注册表。声明类型集中在 `scripts/lib/types/`;旧角色的 `constants/skills.ts` 仅作为兼容回退,不是权威来源。
|
|
6
6
|
|
|
7
7
|
## 目录与职责
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@ capabilities/
|
|
|
28
28
|
└── engineering/capability.yaml # Matt Pocock engineering 上游声明
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
每个能力目录的 `capability.yaml` 描述从哪个 Git 仓库、哪个固定 commit、哪个路径获取资产,以及投影到哪里;目录中的 `skills/` 和 `mcps.json` 是能力实际携带的资产。`scripts/lib/capabilities.ts` 是安装器使用的通用加载器,按角色选择顺序组合这些声明,合并相同供应商并拒绝配置冲突;`scripts/lib/
|
|
31
|
+
每个能力目录的 `capability.yaml` 描述从哪个 Git 仓库、哪个固定 commit、哪个路径获取资产,以及投影到哪里;目录中的 `skills/` 和 `mcps.json` 是能力实际携带的资产。`scripts/lib/capabilities.ts` 是安装器使用的通用加载器,按角色选择顺序组合这些声明,合并相同供应商并拒绝配置冲突;`scripts/lib/types/capabilities.ts` 只存放声明类型。新增能力只需新增目录和 `capability.yaml`,无需修改 TypeScript 注册表。
|
|
32
32
|
|
|
33
33
|
AIRules 自有共享 skills 实际存放在 `capabilities/<能力>/skills/`。第三方 skills 保留在上游,安装时按声明的固定 commit 远程同步,不在此目录复制源码。AIRules 自有资产也从 AIRules 的远程 checkout 投影,符合本项目的远程分发机制。
|
|
34
34
|
|
package/dist/scripts/cli.js
CHANGED
|
@@ -4,8 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
6
|
import kleur from 'kleur';
|
|
7
|
-
import { HOST_IDS } from '../constants/hosts.js';
|
|
8
7
|
import { compareContractFiles, createContractErrorAudit, serializeContractAudit, writeContractAudit, } from './lib/contract-diff.js';
|
|
8
|
+
import { HOST_IDS } from './lib/hosts.js';
|
|
9
9
|
import { requireRoleName } from './lib/role-assets.js';
|
|
10
10
|
import { getDefaultMoluoHome, syncToHosts, verifyHosts, } from './lib/tool.js';
|
|
11
11
|
function findPackageRoot(fromFileUrl) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CapabilityDefinition, CapabilityName, CapabilitySelection, ComposeCapabilitiesOptions } from './
|
|
2
|
-
import type { VendorRepo } from './manifest
|
|
3
|
-
export type { CapabilityDefinition, CapabilityName, CapabilitySelection, ComposeCapabilitiesOptions } from './
|
|
1
|
+
import type { CapabilityDefinition, CapabilityName, CapabilitySelection, ComposeCapabilitiesOptions } from './types/capabilities.js';
|
|
2
|
+
import type { VendorRepo } from './types/manifest.js';
|
|
3
|
+
export type { CapabilityDefinition, CapabilityName, CapabilitySelection, ComposeCapabilitiesOptions } from './types/capabilities.js';
|
|
4
4
|
export declare const CAPABILITY_NAMES: readonly string[];
|
|
5
5
|
export declare const capabilityRegistry: Readonly<Record<CapabilityName, CapabilityDefinition>>;
|
|
6
6
|
export declare function loadCapabilityDefinition(name: CapabilityName): CapabilityDefinition;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HostConfig, HostDeclaration, ResolvedHostPaths } from './types/hosts.js';
|
|
2
|
+
/** 所有角色都必须获得的 canonical skills 公共层,不是可选宿主。 */
|
|
3
|
+
export declare const GLOBAL_AGENT_SKILLS: import("./types/hosts.js").AgentSkillsConfig;
|
|
4
|
+
export declare const HOST_CONFIGS: HostConfig[];
|
|
5
|
+
/** 所有已登记宿主 ID,供显式 --host 校验与帮助输出使用。 */
|
|
6
|
+
export declare const HOST_IDS: string[];
|
|
7
|
+
/** 校验 YAML 后转换成安装器使用的类型;配置错误在文件投影前失败。 */
|
|
8
|
+
export declare function loadHostDeclaration(declarationPath?: string): HostDeclaration;
|
|
9
|
+
export declare function findHostConfig(id: string): HostConfig | undefined;
|
|
10
|
+
export declare function resolveHostId(id: string): string | undefined;
|
|
11
|
+
export declare function resolveGlobalAgentSkillsPath(userHome: string): string;
|
|
12
|
+
export declare function resolveHostPaths(config: HostConfig, userHome: string): ResolvedHostPaths;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { parseDocument } from 'yaml';
|
|
5
|
+
const declaration = loadHostDeclaration();
|
|
6
|
+
/** 所有角色都必须获得的 canonical skills 公共层,不是可选宿主。 */
|
|
7
|
+
export const GLOBAL_AGENT_SKILLS = declaration.globalAgentSkills;
|
|
8
|
+
export const HOST_CONFIGS = declaration.hosts;
|
|
9
|
+
/** 所有已登记宿主 ID,供显式 --host 校验与帮助输出使用。 */
|
|
10
|
+
export const HOST_IDS = HOST_CONFIGS.map(host => host.id);
|
|
11
|
+
/** 从源码或 npm 包自身读取声明,不依赖 CLI 的工作目录。 */
|
|
12
|
+
function resolveHostDeclarationPath() {
|
|
13
|
+
const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const candidates = [
|
|
15
|
+
path.resolve(moduleDirectory, '..', '..', 'hosts', 'hosts.yaml'),
|
|
16
|
+
path.resolve(moduleDirectory, '..', '..', '..', 'hosts', 'hosts.yaml'),
|
|
17
|
+
];
|
|
18
|
+
const declarationPath = candidates.find(candidate => fs.existsSync(candidate));
|
|
19
|
+
if (!declarationPath)
|
|
20
|
+
throw new Error(`Unable to locate hosts/hosts.yaml from ${moduleDirectory}`);
|
|
21
|
+
return declarationPath;
|
|
22
|
+
}
|
|
23
|
+
/** 校验 YAML 后转换成安装器使用的类型;配置错误在文件投影前失败。 */
|
|
24
|
+
export function loadHostDeclaration(declarationPath = resolveHostDeclarationPath()) {
|
|
25
|
+
const location = `Host declaration "${declarationPath}"`;
|
|
26
|
+
const document = parseDocument(fs.readFileSync(declarationPath, 'utf8'), {
|
|
27
|
+
merge: false,
|
|
28
|
+
prettyErrors: true,
|
|
29
|
+
strict: true,
|
|
30
|
+
uniqueKeys: true,
|
|
31
|
+
});
|
|
32
|
+
if (document.errors.length > 0)
|
|
33
|
+
throw new Error(`${location} is invalid: ${document.errors.map(error => error.message).join('; ')}`);
|
|
34
|
+
let value;
|
|
35
|
+
try {
|
|
36
|
+
value = document.toJS({ maxAliasCount: 0 });
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new Error(`${location} is invalid: ${String(error)}`, { cause: error });
|
|
40
|
+
}
|
|
41
|
+
const record = requireRecord(value, location, ['schema_version', 'global_agent_skills', 'hosts']);
|
|
42
|
+
if (record.schema_version !== 1)
|
|
43
|
+
throw new Error(`${location} must use schema_version: 1`);
|
|
44
|
+
const globalLocation = `${location}.global_agent_skills`;
|
|
45
|
+
const global = requireRecord(record.global_agent_skills, globalLocation, ['home_rel_path', 'skills_dir_name']);
|
|
46
|
+
if (!Array.isArray(record.hosts) || record.hosts.length === 0)
|
|
47
|
+
throw new TypeError(`${location}.hosts must be a non-empty array`);
|
|
48
|
+
const hosts = record.hosts.map((host, index) => readHost(host, `${location}.hosts[${index}]`));
|
|
49
|
+
const names = new Set(['all']);
|
|
50
|
+
for (const host of hosts) {
|
|
51
|
+
for (const name of [host.id, ...host.aliases ?? []]) {
|
|
52
|
+
if (names.has(name))
|
|
53
|
+
throw new Error(`${location} has a duplicate or reserved host id/alias: "${name}"`);
|
|
54
|
+
names.add(name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
globalAgentSkills: {
|
|
59
|
+
homeRelPath: requireRelativePath(global.home_rel_path, `${globalLocation}.home_rel_path`),
|
|
60
|
+
skillsDirName: requirePathSegment(global.skills_dir_name, `${globalLocation}.skills_dir_name`),
|
|
61
|
+
},
|
|
62
|
+
hosts,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function requireRecord(value, location, allowedKeys) {
|
|
66
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
67
|
+
throw new TypeError(`${location} must be a mapping`);
|
|
68
|
+
const record = value;
|
|
69
|
+
const unknownKey = allowedKeys && Object.keys(record).find(key => !allowedKeys.includes(key));
|
|
70
|
+
if (unknownKey !== undefined)
|
|
71
|
+
throw new Error(`${location} has an unknown field "${unknownKey}"`);
|
|
72
|
+
return record;
|
|
73
|
+
}
|
|
74
|
+
function requireString(value, location) {
|
|
75
|
+
if (typeof value !== 'string' || value.trim().length === 0 || value.trim() !== value)
|
|
76
|
+
throw new TypeError(`${location} must be a non-empty string without surrounding whitespace`);
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function requireRelativePath(value, location) {
|
|
80
|
+
const configured = requireString(value, location);
|
|
81
|
+
const normalized = configured.replaceAll('\\', '/');
|
|
82
|
+
if (path.posix.isAbsolute(normalized) || path.win32.isAbsolute(configured) || /^[A-Za-z]:/u.test(normalized) || normalized.includes('\0') || normalized.split('/').includes('..'))
|
|
83
|
+
throw new Error(`${location} must be a relative path without parent traversal`);
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
function requirePathSegment(value, location) {
|
|
87
|
+
const configured = requireRelativePath(value, location);
|
|
88
|
+
if (configured === '.' || configured.includes('/'))
|
|
89
|
+
throw new Error(`${location} must be a single file or directory name`);
|
|
90
|
+
return configured;
|
|
91
|
+
}
|
|
92
|
+
function optionalBoolean(value, location) {
|
|
93
|
+
if (value !== undefined && typeof value !== 'boolean')
|
|
94
|
+
throw new TypeError(`${location} must be boolean`);
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function optionalStringArray(value, location) {
|
|
98
|
+
if (value === undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
if (!Array.isArray(value))
|
|
101
|
+
throw new TypeError(`${location} must be an array of strings`);
|
|
102
|
+
return value.map((entry, index) => requireString(entry, `${location}[${index}]`));
|
|
103
|
+
}
|
|
104
|
+
function optionalRecord(value, location) {
|
|
105
|
+
return value === undefined ? undefined : requireRecord(value, location);
|
|
106
|
+
}
|
|
107
|
+
function readHost(value, location) {
|
|
108
|
+
const record = requireRecord(value, location, ['id', 'aliases', 'home_rel_path', 'skills_dir_name', 'excluded_skills', 'project_skills', 'mcp']);
|
|
109
|
+
const id = requireString(record.id, `${location}.id`);
|
|
110
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(id))
|
|
111
|
+
throw new Error(`${location}.id must contain lowercase letters, digits, and hyphens`);
|
|
112
|
+
const excludedSkills = optionalStringArray(record.excluded_skills, `${location}.excluded_skills`)
|
|
113
|
+
?.map((skill, index) => requirePathSegment(skill, `${location}.excluded_skills[${index}]`));
|
|
114
|
+
return {
|
|
115
|
+
id,
|
|
116
|
+
aliases: optionalStringArray(record.aliases, `${location}.aliases`),
|
|
117
|
+
homeRelPath: requireRelativePath(record.home_rel_path, `${location}.home_rel_path`),
|
|
118
|
+
skillsDirName: record.skills_dir_name === undefined ? undefined : requirePathSegment(record.skills_dir_name, `${location}.skills_dir_name`),
|
|
119
|
+
excludedSkills,
|
|
120
|
+
projectSkills: optionalBoolean(record.project_skills, `${location}.project_skills`),
|
|
121
|
+
mcp: record.mcp === undefined ? undefined : readMcpProjection(record.mcp, `${location}.mcp`),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function readMcpProjection(value, location) {
|
|
125
|
+
const record = requireRecord(value, location, [
|
|
126
|
+
'home_rel_path',
|
|
127
|
+
'rel_dir',
|
|
128
|
+
'file_name',
|
|
129
|
+
'servers_key',
|
|
130
|
+
'format',
|
|
131
|
+
'require_host_home',
|
|
132
|
+
'default_top_level',
|
|
133
|
+
'server_defaults',
|
|
134
|
+
'server_overrides',
|
|
135
|
+
'server_command_format',
|
|
136
|
+
]);
|
|
137
|
+
const format = record.format;
|
|
138
|
+
if (format !== 'json' && format !== 'toml')
|
|
139
|
+
throw new Error(`${location}.format must be "json" or "toml"`);
|
|
140
|
+
const serverCommandFormat = record.server_command_format;
|
|
141
|
+
if (serverCommandFormat !== undefined && serverCommandFormat !== 'command-and-args' && serverCommandFormat !== 'command-array')
|
|
142
|
+
throw new Error(`${location}.server_command_format must be "command-and-args" or "command-array"`);
|
|
143
|
+
const overrides = optionalRecord(record.server_overrides, `${location}.server_overrides`);
|
|
144
|
+
return {
|
|
145
|
+
homeRelPath: record.home_rel_path === undefined ? undefined : requireRelativePath(record.home_rel_path, `${location}.home_rel_path`),
|
|
146
|
+
relDir: requireRelativePath(record.rel_dir, `${location}.rel_dir`),
|
|
147
|
+
fileName: requirePathSegment(record.file_name, `${location}.file_name`),
|
|
148
|
+
serversKey: requireString(record.servers_key, `${location}.servers_key`),
|
|
149
|
+
format,
|
|
150
|
+
requireHostHome: optionalBoolean(record.require_host_home, `${location}.require_host_home`),
|
|
151
|
+
defaultTopLevel: optionalRecord(record.default_top_level, `${location}.default_top_level`),
|
|
152
|
+
serverDefaults: optionalRecord(record.server_defaults, `${location}.server_defaults`),
|
|
153
|
+
serverOverrides: overrides === undefined
|
|
154
|
+
? undefined
|
|
155
|
+
: Object.fromEntries(Object.entries(overrides).map(([name, override]) => [name, requireRecord(override, `${location}.server_overrides.${name}`)])),
|
|
156
|
+
serverCommandFormat,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export function findHostConfig(id) {
|
|
160
|
+
const canonical = resolveHostId(id);
|
|
161
|
+
return canonical ? HOST_CONFIGS.find(host => host.id === canonical) : undefined;
|
|
162
|
+
}
|
|
163
|
+
export function resolveHostId(id) {
|
|
164
|
+
return HOST_CONFIGS.find(host => host.id === id || host.aliases?.includes(id))?.id;
|
|
165
|
+
}
|
|
166
|
+
function resolveUserRelativePath(userHome, relPath) {
|
|
167
|
+
return path.join(userHome, ...relPath.split(/[\\/]+/u).filter(Boolean));
|
|
168
|
+
}
|
|
169
|
+
export function resolveGlobalAgentSkillsPath(userHome) {
|
|
170
|
+
return path.join(resolveUserRelativePath(userHome, GLOBAL_AGENT_SKILLS.homeRelPath), GLOBAL_AGENT_SKILLS.skillsDirName);
|
|
171
|
+
}
|
|
172
|
+
export function resolveHostPaths(config, userHome) {
|
|
173
|
+
const hostHome = resolveUserRelativePath(userHome, config.homeRelPath);
|
|
174
|
+
return {
|
|
175
|
+
hostHome,
|
|
176
|
+
skillsDirName: config.skillsDirName ?? 'skills',
|
|
177
|
+
excludedSkills: config.excludedSkills ?? [],
|
|
178
|
+
projectSkills: config.projectSkills ?? true,
|
|
179
|
+
mcpHome: config.mcp?.homeRelPath ? resolveUserRelativePath(userHome, config.mcp.homeRelPath) : hostHome,
|
|
180
|
+
mcp: config.mcp,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { McpProjection } from '../../constants/hosts.js';
|
|
2
1
|
import type { LinkEntry } from './links.js';
|
|
2
|
+
import type { McpProjection } from './types/hosts.js';
|
|
3
3
|
import type { VendorManifest } from './vendors.js';
|
|
4
4
|
export declare function resolveSetupCommandExecutable(command: string, windowsCommandShim?: boolean): string;
|
|
5
5
|
export declare function shouldUseShellForSetupCommand(command: string, windowsCommandShim?: boolean): boolean;
|
|
@@ -2,8 +2,8 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, symlinkSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { findHostConfig, resolveGlobalAgentSkillsPath, resolveHostPaths } from '../../constants/hosts.js';
|
|
6
5
|
import { areSamePaths, canonicalPath, canonicalPathKey, isPathInside } from './canonical-path.js';
|
|
6
|
+
import { findHostConfig, resolveGlobalAgentSkillsPath, resolveHostPaths } from './hosts.js';
|
|
7
7
|
import { buildLinkPlan } from './links.js';
|
|
8
8
|
import { loadMcpCatalog, validateMcpServerNames } from './mcp-catalog.js';
|
|
9
9
|
import { requireRoleName } from './role-assets.js';
|
package/dist/scripts/lib/tool.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { HOST_IDS, resolveHostId } from '
|
|
4
|
+
import { HOST_IDS, resolveHostId } from './hosts.js';
|
|
5
5
|
import { ensureGlobalSkillLink, ensureInstallRoot, getDefaultInstallPaths, projectHostById, runSkillSetupCommands, } from './install.js';
|
|
6
6
|
import { resolveRoleManifestPath } from './roles.js';
|
|
7
7
|
import { cleanupEmptyVendorSkillDirectories, rebuildVendorAssets } from './vendor-staging.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VendorProjection, VendorRepo } from './manifest
|
|
1
|
+
import type { VendorProjection, VendorRepo } from './manifest.js';
|
|
2
2
|
/** Capability ids are discovered from capability directories containing capability.yaml. */
|
|
3
3
|
export type CapabilityName = string;
|
|
4
4
|
export interface CapabilityDefinition {
|
|
@@ -30,20 +30,14 @@ export interface HostConfig {
|
|
|
30
30
|
/** 角色可选 MCP 中性源到该宿主配置的投影规则。 */
|
|
31
31
|
mcp?: McpProjection;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*/
|
|
42
|
-
export declare const HOST_CONFIGS: HostConfig[];
|
|
43
|
-
/** 所有已登记宿主 ID,供显式 `--host` 校验与帮助输出使用。 */
|
|
44
|
-
export declare const HOST_IDS: string[];
|
|
45
|
-
export declare function findHostConfig(id: string): HostConfig | undefined;
|
|
46
|
-
export declare function resolveHostId(id: string): string | undefined;
|
|
33
|
+
export interface AgentSkillsConfig {
|
|
34
|
+
homeRelPath: string;
|
|
35
|
+
skillsDirName: string;
|
|
36
|
+
}
|
|
37
|
+
export interface HostDeclaration {
|
|
38
|
+
globalAgentSkills: AgentSkillsConfig;
|
|
39
|
+
hosts: HostConfig[];
|
|
40
|
+
}
|
|
47
41
|
export interface ResolvedHostPaths {
|
|
48
42
|
hostHome: string;
|
|
49
43
|
skillsDirName: string;
|
|
@@ -52,5 +46,3 @@ export interface ResolvedHostPaths {
|
|
|
52
46
|
mcpHome: string;
|
|
53
47
|
mcp?: McpProjection;
|
|
54
48
|
}
|
|
55
|
-
export declare function resolveGlobalAgentSkillsPath(userHome: string): string;
|
|
56
|
-
export declare function resolveHostPaths(config: HostConfig, userHome: string): ResolvedHostPaths;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { RolePackageConfig, SetupCommand, Vendor, VendorManifest } from './manifest
|
|
2
|
-
export type { RolePackageConfig, RolePackageInstall, SetupCommand, SkillConfig, SkillDef, Vendor, VendorLink, VendorManifest, VendorNode, VendorProjection, VendorRepo, VendorsConfig, } from './manifest
|
|
1
|
+
import type { RolePackageConfig, SetupCommand, Vendor, VendorManifest } from './types/manifest.js';
|
|
2
|
+
export type { RolePackageConfig, RolePackageInstall, SetupCommand, SkillConfig, SkillDef, Vendor, VendorLink, VendorManifest, VendorNode, VendorProjection, VendorRepo, VendorsConfig, } from './types/manifest.js';
|
|
3
3
|
export declare function normalizePath(value: string): string;
|
|
4
4
|
export declare function rolePackageSetupCommands(packages?: RolePackageConfig[]): SetupCommand[];
|
|
5
5
|
/**
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath, pathToFileURL, URL } from 'node:url';
|
|
4
4
|
import { parseDocument } from 'yaml';
|
|
5
|
-
import { HOST_IDS } from '../../constants/hosts.js';
|
|
6
5
|
import { CAPABILITY_NAMES, composeCapabilities } from './capabilities.js';
|
|
6
|
+
import { HOST_IDS } from './hosts.js';
|
|
7
7
|
import { flattenedSkillName, flattenedVendorSkillTarget } from './skill-projection.js';
|
|
8
8
|
const vendorNamePattern = /^[A-Za-z0-9][\w-]*$/u;
|
|
9
9
|
const npmPackageNamePattern = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
@@ -2,7 +2,7 @@ import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import kleur from 'kleur';
|
|
5
|
-
import { findHostConfig, resolveGlobalAgentSkillsPath, resolveHostPaths } from '
|
|
5
|
+
import { findHostConfig, resolveGlobalAgentSkillsPath, resolveHostPaths } from './hosts.js';
|
|
6
6
|
import { applyMcpServerProjection, readInstalledMcpServers, readTomlMcpServerNames } from './install.js';
|
|
7
7
|
import { DEFAULT_ROLE } from './roles.js';
|
|
8
8
|
/** Verify the mandatory canonical skills layer shared by every role and host. */
|
package/hosts/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 宿主声明
|
|
2
|
+
|
|
3
|
+
`hosts.yaml` 是公共安装器的宿主配置来源,与角色的 `role.yaml`、能力的 `capability.yaml` 一样采用 YAML。这里维护配置数据;`scripts/lib/hosts.ts` 负责加载、校验、别名查找和路径解析,类型位于 `scripts/lib/types/hosts.ts`。
|
|
4
|
+
|
|
5
|
+
声明随 npm 包分发。源码运行和打包后的 CLI 都从自身所在的包定位声明,不依赖用户的当前工作目录。字段使用 `snake_case`,加载器转换为运行时使用的 `camelCase`。
|
|
6
|
+
|
|
7
|
+
## 添加宿主
|
|
8
|
+
|
|
9
|
+
在 `hosts.yaml` 的 `hosts` 数组中追加条目;数组顺序也是帮助输出和 `--host all` 的处理顺序。角色的 `hosts: all` 自动包含新增宿主,显式列出的角色需自行加入新 ID。
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
- id: example
|
|
13
|
+
aliases: [example desktop]
|
|
14
|
+
home_rel_path: .example
|
|
15
|
+
project_skills: false
|
|
16
|
+
mcp:
|
|
17
|
+
rel_dir: .
|
|
18
|
+
file_name: mcp.json
|
|
19
|
+
servers_key: mcpServers
|
|
20
|
+
format: json
|
|
21
|
+
server_defaults:
|
|
22
|
+
type: stdio
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`id` 使用小写字母、数字和连字符;别名仅供 CLI 输入,角色声明必须使用 ID。所有 ID 和别名必须唯一,`all` 为保留名称。
|
|
26
|
+
|
|
27
|
+
## 字段
|
|
28
|
+
|
|
29
|
+
顶层必须包含 `schema_version: 1`、`global_agent_skills` 和非空的 `hosts`。`global_agent_skills` 声明所有角色共享的 skills 目录,当前为 `~/.agents/skills`。
|
|
30
|
+
|
|
31
|
+
| 宿主字段 | 含义 |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `id` | 宿主 ID,必填 |
|
|
34
|
+
| `aliases` | 可选的 CLI 别名列表 |
|
|
35
|
+
| `home_rel_path` | 相对用户 home 的宿主目录,必填 |
|
|
36
|
+
| `skills_dir_name` | 私有 skills 目录名,默认 `skills` |
|
|
37
|
+
| `excluded_skills` | 最终宿主投影时排除的 skill 名称列表 |
|
|
38
|
+
| `project_skills` | 是否创建私有 skills 投影,默认 `true`;`false` 表示宿主复用 canonical skills |
|
|
39
|
+
| `mcp` | 可选的 MCP 投影配置 |
|
|
40
|
+
|
|
41
|
+
| MCP 字段 | 含义 |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `home_rel_path` | MCP 配置的根目录,相对用户 home;默认使用宿主目录 |
|
|
44
|
+
| `rel_dir` | 配置文件相对 MCP 根目录的位置,必填;`.` 表示根目录 |
|
|
45
|
+
| `file_name` | 配置文件名,必填 |
|
|
46
|
+
| `servers_key` | 宿主使用的 MCP servers 字段名,必填 |
|
|
47
|
+
| `format` | `json` 或 `toml`,必填 |
|
|
48
|
+
| `require_host_home` | 为 `true` 时,只有宿主目录存在才写入 MCP |
|
|
49
|
+
| `default_top_level` | 新配置的顶层默认字段,不覆盖已有用户字段 |
|
|
50
|
+
| `server_defaults` | 每个 server 的默认字段,不覆盖源配置 |
|
|
51
|
+
| `server_overrides` | 按 server 名称指定宿主适配字段 |
|
|
52
|
+
| `server_command_format` | `command-and-args`(默认)或 `command-array` |
|
|
53
|
+
|
|
54
|
+
路径统一使用 `/`,运行时按系统转换。目录路径必须相对且不含 `..`;文件名、skills 目录名和排除的 skill 名称必须为单个路径段。加载器拒绝未知字段、错误类型、重复 YAML key、YAML alias 和冲突的 ID/别名。
|
|
55
|
+
|
|
56
|
+
宿主原生 agents、commands、hooks、settings 由角色的项目初始化器安装。这里负责用户级 skills 和 MCP 的公共投影。
|
|
57
|
+
|
|
58
|
+
修改后运行 `pnpm run lint:check`、`pnpm run typecheck`、`pnpm test` 和 `pnpm run verify:packed-airules`,验证声明、安装行为及 npm 包中的加载路径。
|
package/hosts/hosts.yaml
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
|
|
3
|
+
# 所有角色共享的 canonical skills 目录。
|
|
4
|
+
global_agent_skills:
|
|
5
|
+
home_rel_path: .agents
|
|
6
|
+
skills_dir_name: skills
|
|
7
|
+
|
|
8
|
+
# 路径相对于用户 home,统一使用 /;顺序决定 --host all 和帮助输出顺序。
|
|
9
|
+
# 宿主原生 agents、commands、hooks、settings 由角色的项目初始化器安装。
|
|
10
|
+
hosts:
|
|
11
|
+
- id: claude
|
|
12
|
+
home_rel_path: .claude
|
|
13
|
+
mcp:
|
|
14
|
+
home_rel_path: .
|
|
15
|
+
rel_dir: .
|
|
16
|
+
file_name: .claude.json
|
|
17
|
+
servers_key: mcpServers
|
|
18
|
+
format: json
|
|
19
|
+
require_host_home: true
|
|
20
|
+
server_defaults:
|
|
21
|
+
type: stdio
|
|
22
|
+
server_overrides:
|
|
23
|
+
codegraph:
|
|
24
|
+
args: [serve, --mcp]
|
|
25
|
+
|
|
26
|
+
- id: codex
|
|
27
|
+
home_rel_path: .codex
|
|
28
|
+
project_skills: false
|
|
29
|
+
mcp:
|
|
30
|
+
rel_dir: .
|
|
31
|
+
file_name: config.toml
|
|
32
|
+
servers_key: mcp_servers
|
|
33
|
+
format: toml
|
|
34
|
+
server_overrides:
|
|
35
|
+
codegraph:
|
|
36
|
+
args: [serve, --mcp]
|
|
37
|
+
|
|
38
|
+
- id: hermes
|
|
39
|
+
aliases: [hermes desktop]
|
|
40
|
+
home_rel_path: AppData/Local/hermes
|
|
41
|
+
project_skills: false
|
|
42
|
+
|
|
43
|
+
- id: cursor
|
|
44
|
+
home_rel_path: .cursor
|
|
45
|
+
skills_dir_name: skills-cursor
|
|
46
|
+
project_skills: false
|
|
47
|
+
mcp:
|
|
48
|
+
rel_dir: .
|
|
49
|
+
file_name: mcp.json
|
|
50
|
+
servers_key: mcpServers
|
|
51
|
+
format: json
|
|
52
|
+
server_defaults:
|
|
53
|
+
type: stdio
|
|
54
|
+
|
|
55
|
+
- id: qoderwork
|
|
56
|
+
home_rel_path: .qoderwork
|
|
57
|
+
|
|
58
|
+
- id: trae
|
|
59
|
+
home_rel_path: .trae
|
|
60
|
+
project_skills: false
|
|
61
|
+
mcp:
|
|
62
|
+
home_rel_path: AppData/Roaming/Trae/User
|
|
63
|
+
rel_dir: .
|
|
64
|
+
file_name: mcp.json
|
|
65
|
+
servers_key: mcpServers
|
|
66
|
+
format: json
|
|
67
|
+
default_top_level:
|
|
68
|
+
inputs: []
|
|
69
|
+
|
|
70
|
+
- id: trae-cn
|
|
71
|
+
home_rel_path: .trae-cn
|
|
72
|
+
project_skills: false
|
|
73
|
+
mcp:
|
|
74
|
+
home_rel_path: AppData/Roaming/Trae CN/User
|
|
75
|
+
rel_dir: .
|
|
76
|
+
file_name: mcp.json
|
|
77
|
+
servers_key: mcpServers
|
|
78
|
+
format: json
|
|
79
|
+
default_top_level:
|
|
80
|
+
inputs: []
|
|
81
|
+
|
|
82
|
+
- id: trae-solo
|
|
83
|
+
home_rel_path: .trae-solo
|
|
84
|
+
project_skills: false
|
|
85
|
+
mcp:
|
|
86
|
+
home_rel_path: AppData/Roaming/TRAE SOLO/User
|
|
87
|
+
rel_dir: .
|
|
88
|
+
file_name: mcp.json
|
|
89
|
+
servers_key: mcpServers
|
|
90
|
+
format: json
|
|
91
|
+
default_top_level:
|
|
92
|
+
inputs: []
|
|
93
|
+
|
|
94
|
+
- id: trae-solo-cn
|
|
95
|
+
home_rel_path: .trae-solo-cn
|
|
96
|
+
project_skills: false
|
|
97
|
+
mcp:
|
|
98
|
+
home_rel_path: AppData/Roaming/TRAE SOLO CN/User
|
|
99
|
+
rel_dir: .
|
|
100
|
+
file_name: mcp.json
|
|
101
|
+
servers_key: mcpServers
|
|
102
|
+
format: json
|
|
103
|
+
default_top_level:
|
|
104
|
+
inputs: []
|
|
105
|
+
|
|
106
|
+
- id: qoder
|
|
107
|
+
home_rel_path: .qoder
|
|
108
|
+
project_skills: false
|
|
109
|
+
mcp:
|
|
110
|
+
home_rel_path: AppData/Roaming/Qoder/SharedClientCache
|
|
111
|
+
rel_dir: .
|
|
112
|
+
file_name: mcp.json
|
|
113
|
+
servers_key: mcpServers
|
|
114
|
+
format: json
|
|
115
|
+
server_defaults:
|
|
116
|
+
type: stdio
|
|
117
|
+
|
|
118
|
+
- id: opencode
|
|
119
|
+
home_rel_path: .config/opencode
|
|
120
|
+
project_skills: false
|
|
121
|
+
mcp:
|
|
122
|
+
rel_dir: .
|
|
123
|
+
file_name: opencode.json
|
|
124
|
+
servers_key: mcp
|
|
125
|
+
format: json
|
|
126
|
+
default_top_level:
|
|
127
|
+
$schema: https://opencode.ai/config.json
|
|
128
|
+
server_defaults:
|
|
129
|
+
type: local
|
|
130
|
+
enabled: true
|
|
131
|
+
server_overrides:
|
|
132
|
+
codegraph:
|
|
133
|
+
args: [serve, --mcp]
|
|
134
|
+
server_command_format: command-array
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moluoxixi-ai-rules",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.7",
|
|
5
5
|
"description": "Remote AI role asset distribution for supported hosts.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "https://github.com/moluoxixi/AIRules",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"bin",
|
|
25
25
|
"capabilities",
|
|
26
26
|
"dist",
|
|
27
|
+
"hosts",
|
|
27
28
|
"roles/*/role.yaml"
|
|
28
29
|
],
|
|
29
30
|
"scripts": {
|
package/dist/constants/hosts.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
/** 所有角色都必须获得的 canonical skills 公共层,不是可选宿主。 */
|
|
3
|
-
export const GLOBAL_AGENT_SKILLS = {
|
|
4
|
-
homeRelPath: '.agents',
|
|
5
|
-
skillsDirName: 'skills',
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* 公共分发层登记 canonical skills 与角色可选 MCP 的宿主投影规则。
|
|
9
|
-
* Agents、commands、hooks、settings 等宿主原生资产由角色的项目初始化器安装。
|
|
10
|
-
*/
|
|
11
|
-
export const HOST_CONFIGS = [
|
|
12
|
-
{
|
|
13
|
-
id: 'claude',
|
|
14
|
-
homeRelPath: '.claude',
|
|
15
|
-
mcp: {
|
|
16
|
-
homeRelPath: '.',
|
|
17
|
-
relDir: '.',
|
|
18
|
-
fileName: '.claude.json',
|
|
19
|
-
serversKey: 'mcpServers',
|
|
20
|
-
format: 'json',
|
|
21
|
-
requireHostHome: true,
|
|
22
|
-
serverDefaults: { type: 'stdio' },
|
|
23
|
-
serverOverrides: { codegraph: { args: ['serve', '--mcp'] } },
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: 'codex',
|
|
28
|
-
homeRelPath: '.codex',
|
|
29
|
-
projectSkills: false,
|
|
30
|
-
mcp: {
|
|
31
|
-
relDir: '.',
|
|
32
|
-
fileName: 'config.toml',
|
|
33
|
-
serversKey: 'mcp_servers',
|
|
34
|
-
format: 'toml',
|
|
35
|
-
serverOverrides: { codegraph: { args: ['serve', '--mcp'] } },
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: 'hermes',
|
|
40
|
-
aliases: ['hermes desktop'],
|
|
41
|
-
homeRelPath: path.join('AppData', 'Local', 'hermes'),
|
|
42
|
-
projectSkills: false,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: 'cursor',
|
|
46
|
-
homeRelPath: '.cursor',
|
|
47
|
-
skillsDirName: 'skills-cursor',
|
|
48
|
-
projectSkills: false,
|
|
49
|
-
mcp: {
|
|
50
|
-
relDir: '.',
|
|
51
|
-
fileName: 'mcp.json',
|
|
52
|
-
serversKey: 'mcpServers',
|
|
53
|
-
format: 'json',
|
|
54
|
-
serverDefaults: { type: 'stdio' },
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
id: 'qoderwork',
|
|
59
|
-
homeRelPath: '.qoderwork',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: 'trae',
|
|
63
|
-
homeRelPath: '.trae',
|
|
64
|
-
projectSkills: false,
|
|
65
|
-
mcp: {
|
|
66
|
-
homeRelPath: path.join('AppData', 'Roaming', 'Trae', 'User'),
|
|
67
|
-
relDir: '.',
|
|
68
|
-
fileName: 'mcp.json',
|
|
69
|
-
serversKey: 'mcpServers',
|
|
70
|
-
format: 'json',
|
|
71
|
-
defaultTopLevel: { inputs: [] },
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
id: 'trae-cn',
|
|
76
|
-
homeRelPath: '.trae-cn',
|
|
77
|
-
projectSkills: false,
|
|
78
|
-
mcp: {
|
|
79
|
-
homeRelPath: path.join('AppData', 'Roaming', 'Trae CN', 'User'),
|
|
80
|
-
relDir: '.',
|
|
81
|
-
fileName: 'mcp.json',
|
|
82
|
-
serversKey: 'mcpServers',
|
|
83
|
-
format: 'json',
|
|
84
|
-
defaultTopLevel: { inputs: [] },
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: 'trae-solo',
|
|
89
|
-
homeRelPath: '.trae-solo',
|
|
90
|
-
projectSkills: false,
|
|
91
|
-
mcp: {
|
|
92
|
-
homeRelPath: path.join('AppData', 'Roaming', 'TRAE SOLO', 'User'),
|
|
93
|
-
relDir: '.',
|
|
94
|
-
fileName: 'mcp.json',
|
|
95
|
-
serversKey: 'mcpServers',
|
|
96
|
-
format: 'json',
|
|
97
|
-
defaultTopLevel: { inputs: [] },
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
id: 'trae-solo-cn',
|
|
102
|
-
homeRelPath: '.trae-solo-cn',
|
|
103
|
-
projectSkills: false,
|
|
104
|
-
mcp: {
|
|
105
|
-
homeRelPath: path.join('AppData', 'Roaming', 'TRAE SOLO CN', 'User'),
|
|
106
|
-
relDir: '.',
|
|
107
|
-
fileName: 'mcp.json',
|
|
108
|
-
serversKey: 'mcpServers',
|
|
109
|
-
format: 'json',
|
|
110
|
-
defaultTopLevel: { inputs: [] },
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
id: 'qoder',
|
|
115
|
-
homeRelPath: '.qoder',
|
|
116
|
-
projectSkills: false,
|
|
117
|
-
mcp: {
|
|
118
|
-
homeRelPath: path.join('AppData', 'Roaming', 'Qoder', 'SharedClientCache'),
|
|
119
|
-
relDir: '.',
|
|
120
|
-
fileName: 'mcp.json',
|
|
121
|
-
serversKey: 'mcpServers',
|
|
122
|
-
format: 'json',
|
|
123
|
-
serverDefaults: { type: 'stdio' },
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
id: 'opencode',
|
|
128
|
-
homeRelPath: path.join('.config', 'opencode'),
|
|
129
|
-
projectSkills: false,
|
|
130
|
-
mcp: {
|
|
131
|
-
relDir: '.',
|
|
132
|
-
fileName: 'opencode.json',
|
|
133
|
-
serversKey: 'mcp',
|
|
134
|
-
format: 'json',
|
|
135
|
-
defaultTopLevel: { $schema: 'https://opencode.ai/config.json' },
|
|
136
|
-
serverDefaults: { type: 'local', enabled: true },
|
|
137
|
-
serverOverrides: { codegraph: { args: ['serve', '--mcp'] } },
|
|
138
|
-
serverCommandFormat: 'command-array',
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
];
|
|
142
|
-
/** 所有已登记宿主 ID,供显式 `--host` 校验与帮助输出使用。 */
|
|
143
|
-
export const HOST_IDS = HOST_CONFIGS.map(host => host.id);
|
|
144
|
-
export function findHostConfig(id) {
|
|
145
|
-
const canonical = resolveHostId(id);
|
|
146
|
-
return canonical ? HOST_CONFIGS.find(host => host.id === canonical) : undefined;
|
|
147
|
-
}
|
|
148
|
-
export function resolveHostId(id) {
|
|
149
|
-
return HOST_CONFIGS.find(host => host.id === id || host.aliases?.includes(id))?.id;
|
|
150
|
-
}
|
|
151
|
-
function resolveUserRelativePath(userHome, relPath) {
|
|
152
|
-
return path.join(userHome, ...relPath.split(/[\\/]+/u).filter(Boolean));
|
|
153
|
-
}
|
|
154
|
-
export function resolveGlobalAgentSkillsPath(userHome) {
|
|
155
|
-
return path.join(resolveUserRelativePath(userHome, GLOBAL_AGENT_SKILLS.homeRelPath), GLOBAL_AGENT_SKILLS.skillsDirName);
|
|
156
|
-
}
|
|
157
|
-
export function resolveHostPaths(config, userHome) {
|
|
158
|
-
const hostHome = resolveUserRelativePath(userHome, config.homeRelPath);
|
|
159
|
-
return {
|
|
160
|
-
hostHome,
|
|
161
|
-
skillsDirName: config.skillsDirName ?? 'skills',
|
|
162
|
-
excludedSkills: config.excludedSkills ?? [],
|
|
163
|
-
projectSkills: config.projectSkills ?? true,
|
|
164
|
-
mcpHome: config.mcp?.homeRelPath ? resolveUserRelativePath(userHome, config.mcp.homeRelPath) : hostHome,
|
|
165
|
-
mcp: config.mcp,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|