@templmf/temp-solf-lmf 0.0.43 → 0.0.45

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.
Files changed (46) hide show
  1. package/ai-gateway/.env +42 -0
  2. package/ai-gateway/README.md +295 -0
  3. package/ai-gateway/package-lock.json +1370 -0
  4. package/ai-gateway/package.json +18 -0
  5. package/ai-gateway/src/index.js +132 -0
  6. package/ai-gateway/src/middleware/auth.js +45 -0
  7. package/ai-gateway/src/middleware/rateLimit.js +87 -0
  8. package/ai-gateway/src/routes/chat.js +657 -0
  9. package/ai-gateway/src/skills/detector.js +145 -0
  10. package/ai-gateway/src/skills/html.md +18 -0
  11. package/ai-gateway/src/skills/markdown.md +18 -0
  12. package/ai-gateway/src/skills/react.md +27 -0
  13. package/ai-gateway/src/skills/registry.js +441 -0
  14. package/ai-gateway/src/skills/skill-creator/LICENSE.txt +202 -0
  15. package/ai-gateway/src/skills/skill-creator/SKILL.md +485 -0
  16. package/ai-gateway/src/skills/skill-creator/agents/analyzer.md +274 -0
  17. package/ai-gateway/src/skills/skill-creator/agents/comparator.md +202 -0
  18. package/ai-gateway/src/skills/skill-creator/agents/grader.md +223 -0
  19. package/ai-gateway/src/skills/skill-creator/assets/eval_review.html +146 -0
  20. package/ai-gateway/src/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  21. package/ai-gateway/src/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  22. package/ai-gateway/src/skills/skill-creator/references/schemas.md +430 -0
  23. package/ai-gateway/src/skills/skill-creator/scripts/__init__.py +0 -0
  24. package/ai-gateway/src/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  25. package/ai-gateway/src/skills/skill-creator/scripts/generate_report.py +326 -0
  26. package/ai-gateway/src/skills/skill-creator/scripts/improve_description.py +247 -0
  27. package/ai-gateway/src/skills/skill-creator/scripts/package_skill.py +136 -0
  28. package/ai-gateway/src/skills/skill-creator/scripts/quick_validate.py +103 -0
  29. package/ai-gateway/src/skills/skill-creator/scripts/run_eval.py +310 -0
  30. package/ai-gateway/src/skills/skill-creator/scripts/run_loop.py +328 -0
  31. package/ai-gateway/src/skills/skill-creator/scripts/utils.py +47 -0
  32. package/ai-gateway/src/skills/skill-creator/skill-creator.skill +0 -0
  33. package/ai-gateway/src/skills/ticket.md +36 -0
  34. package/ai-gateway/src/skills/vue.md +31 -0
  35. package/ai-gateway/src/utils/logger.js +21 -0
  36. package/ai-gateway/src/utils/retry.js +90 -0
  37. package/ai-gateway/src/utils/sessionManager.js +159 -0
  38. package/ai-gateway/src/utils/structuredResponse.js +144 -0
  39. package/ai-gateway/src/utils/toolAdapter.js +151 -0
  40. package/package.json +1 -1
  41. package//345/216/213/347/274/251/345/220/216/347/232/204/346/226/207/344/273/266.7z +0 -0
  42. package/skill-mcp/README.md +0 -74
  43. package/skill-mcp/index.ts +0 -336
  44. package/skill-mcp/package (1).json +0 -19
  45. package/skill-mcp/tsconfig.json +0 -16
  46. package//347/247/273/345/212/250/345/272/224/347/224/250/345/217/260/350/264/246/347/247/273/344/272/244/346/270/205/345/215/225.xlsx +0 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 会话上下文管理器
3
+ *
4
+ * 支持多轮对话的服务端会话存储。
5
+ * 客户端通过 session_id 关联多次请求,网关自动拼接历史消息。
6
+ *
7
+ * 存储结构(内存 Map,生产环境替换为 Redis):
8
+ * sessionId -> {
9
+ * messages: [...], 完整对话历史
10
+ * createdAt: number,
11
+ * updatedAt: number,
12
+ * clientId: string,
13
+ * metadata: {} 业务元数据(如 ticket 草稿)
14
+ * }
15
+ */
16
+
17
+ import { v4 as uuidv4 } from "uuid";
18
+ import { logger } from "../utils/logger.js";
19
+
20
+ const SESSION_TTL_MS = parseInt(process.env.SESSION_TTL_MS || String(24 * 60 * 60 * 1000)); // 默认 24h
21
+ const MAX_HISTORY_MESSAGES = parseInt(process.env.MAX_HISTORY_MESSAGES || "50");
22
+
23
+ // 内存会话存储
24
+ const sessions = new Map();
25
+
26
+ // 定期清理过期会话
27
+ setInterval(() => {
28
+ const now = Date.now();
29
+ let cleaned = 0;
30
+ for (const [id, session] of sessions.entries()) {
31
+ if (now - session.updatedAt > SESSION_TTL_MS) {
32
+ sessions.delete(id);
33
+ cleaned++;
34
+ }
35
+ }
36
+ if (cleaned > 0) {
37
+ logger.info(`Session GC: cleaned ${cleaned} expired sessions`);
38
+ }
39
+ }, 5 * 60 * 1000); // 每 5 分钟清理一次
40
+
41
+ // ─────────────────────────────────────────────────────
42
+ // 创建新会话
43
+ // ─────────────────────────────────────────────────────
44
+ export function createSession(clientId, metadata = {}) {
45
+ const sessionId = uuidv4();
46
+ const now = Date.now();
47
+ sessions.set(sessionId, {
48
+ messages: [],
49
+ createdAt: now,
50
+ updatedAt: now,
51
+ clientId,
52
+ metadata
53
+ });
54
+ return sessionId;
55
+ }
56
+
57
+ // ─────────────────────────────────────────────────────
58
+ // 获取会话
59
+ // ─────────────────────────────────────────────────────
60
+ export function getSession(sessionId) {
61
+ const session = sessions.get(sessionId);
62
+ if (!session) return null;
63
+
64
+ // 检查是否过期
65
+ if (Date.now() - session.updatedAt > SESSION_TTL_MS) {
66
+ sessions.delete(sessionId);
67
+ return null;
68
+ }
69
+
70
+ return session;
71
+ }
72
+
73
+ // ─────────────────────────────────────────────────────
74
+ // 追加消息到会话历史
75
+ // ─────────────────────────────────────────────────────
76
+ export function appendMessages(sessionId, messages) {
77
+ const session = sessions.get(sessionId);
78
+ if (!session) return false;
79
+
80
+ session.messages.push(...messages);
81
+ session.updatedAt = Date.now();
82
+
83
+ // 超过最大历史消息数时,保留最近的(保留偶数条以维持 user/assistant 交替)
84
+ if (session.messages.length > MAX_HISTORY_MESSAGES) {
85
+ const overflow = session.messages.length - MAX_HISTORY_MESSAGES;
86
+ session.messages = session.messages.slice(overflow);
87
+ }
88
+
89
+ return true;
90
+ }
91
+
92
+ // ─────────────────────────────────────────────────────
93
+ // 更新会话元数据(用于提单草稿等业务状态)
94
+ // ─────────────────────────────────────────────────────
95
+ export function updateSessionMetadata(sessionId, metadata) {
96
+ const session = sessions.get(sessionId);
97
+ if (!session) return false;
98
+ session.metadata = { ...session.metadata, ...metadata };
99
+ session.updatedAt = Date.now();
100
+ return true;
101
+ }
102
+
103
+ // ─────────────────────────────────────────────────────
104
+ // 获取会话的完整消息历史(用于拼入上游请求)
105
+ // ─────────────────────────────────────────────────────
106
+ export function getSessionMessages(sessionId) {
107
+ const session = getSession(sessionId);
108
+ return session ? session.messages : [];
109
+ }
110
+
111
+ // ─────────────────────────────────────────────────────
112
+ // 上下文压缩:当 token 估算超阈值时,保留首条 + 最近 N 条
113
+ // 简单估算:1 token ≈ 4 字符
114
+ // ─────────────────────────────────────────────────────
115
+ export function compressIfNeeded(messages, maxTokenEstimate = 6000) {
116
+ const totalChars = messages.reduce((sum, m) => {
117
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
118
+ return sum + content.length;
119
+ }, 0);
120
+
121
+ const estimatedTokens = Math.ceil(totalChars / 4);
122
+ if (estimatedTokens <= maxTokenEstimate) {
123
+ return { messages, compressed: false };
124
+ }
125
+
126
+ // 保留首条用户消息(通常包含最重要的背景) + 最近 10 条
127
+ const recent = messages.slice(-10);
128
+ const first = messages[0];
129
+
130
+ const compressed = [first, ...recent].filter(
131
+ (m, i, arr) => arr.indexOf(m) === i // 去重(避免 first 和 recent 重叠)
132
+ );
133
+
134
+ logger.info("Context compressed", {
135
+ originalMessages: messages.length,
136
+ compressedMessages: compressed.length,
137
+ estimatedTokens
138
+ });
139
+
140
+ return { messages: compressed, compressed: true };
141
+ }
142
+
143
+ // ─────────────────────────────────────────────────────
144
+ // 删除会话
145
+ // ─────────────────────────────────────────────────────
146
+ export function deleteSession(sessionId) {
147
+ return sessions.delete(sessionId);
148
+ }
149
+
150
+ // ─────────────────────────────────────────────────────
151
+ // 统计(用于健康检查)
152
+ // ─────────────────────────────────────────────────────
153
+ export function getSessionStats() {
154
+ return {
155
+ activeSessions: sessions.size,
156
+ ttlMs: SESSION_TTL_MS,
157
+ maxHistoryMessages: MAX_HISTORY_MESSAGES
158
+ };
159
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * 结构化响应协议处理器
3
+ *
4
+ * 客户端可在请求中通过 `x-response-format` header 或 `response_format` 字段
5
+ * 指定期望的结构化输出类型,网关负责注入对应 prompt 并解析/校验响应。
6
+ *
7
+ * 支持的格式:
8
+ * - json_object 通用 JSON 对象
9
+ * - json_schema 按指定 Schema 返回 JSON
10
+ * - markdown Markdown 文档
11
+ * - html HTML 片段或完整页面
12
+ * - ticket_form 提单助手专用:动态表单草稿 + 缺失字段
13
+ */
14
+
15
+ // ─────────────────────────────────────────────────────
16
+ // 根据 response_format 注入 system prompt 片段
17
+ // ─────────────────────────────────────────────────────
18
+ export function buildResponseFormatPrompt(responseFormat) {
19
+ if (!responseFormat) return "";
20
+
21
+ const type = responseFormat.type || responseFormat;
22
+
23
+ switch (type) {
24
+
25
+ case "json_object":
26
+ return `\n\n【输出格式要求】只返回合法的 JSON 对象,不要有任何 Markdown 代码块包裹,不要有解释文字。`;
27
+
28
+ case "json_schema": {
29
+ const schema = responseFormat.schema || responseFormat.json_schema?.schema;
30
+ if (!schema) return buildResponseFormatPrompt("json_object");
31
+ return `\n\n【输出格式要求】严格按照以下 JSON Schema 返回,不要有任何多余内容:
32
+ \`\`\`json
33
+ ${JSON.stringify(schema, null, 2)}
34
+ \`\`\`
35
+ 直接输出符合 Schema 的 JSON,不要包裹在代码块中。`;
36
+ }
37
+
38
+ case "markdown":
39
+ return `\n\n【输出格式要求】使用标准 Markdown 格式输出,支持 GFM(表格、代码块、任务列表),不要输出 HTML 标签。`;
40
+
41
+ case "html":
42
+ return `\n\n【输出格式要求】输出完整可渲染的 HTML 片段。要求:
43
+ - 使用语义化标签
44
+ - 内联 CSS 样式(不依赖外部文件)
45
+ - 禁止包含 <script> 标签
46
+ - 直接输出 HTML,不要用代码块包裹`;
47
+
48
+ case "ticket_form":
49
+ return `\n\n【输出格式要求】你是一个提单助手。每次回复必须严格返回如下 JSON 结构,不要有任何额外文字:
50
+ {
51
+ "reply": "对用户说的话(追问缺失字段或确认完成)",
52
+ "form_draft": { /* 当前已收集到的字段 key-value */ },
53
+ "missing_fields": ["字段名1", "字段名2"],
54
+ "is_complete": false
55
+ }
56
+ 当所有必填字段都已收集完毕时,将 is_complete 设为 true,并在 reply 中请用户确认。`;
57
+
58
+ case "react_component":
59
+ return `\n\n【输出格式要求】返回一个完整可运行的 React 函数式组件,格式如下:
60
+ \`\`\`jsx
61
+ // 直接输出 JSX 代码,不要 import React(已全局注入)
62
+ // 使用 Hooks、Tailwind className
63
+ export default function Component() {
64
+ return ( /* JSX */ );
65
+ }
66
+ \`\`\``;
67
+
68
+ case "vue_component":
69
+ return `\n\n【输出格式要求】返回一个完整的 Vue 3 单文件组件(SFC),格式:
70
+ \`\`\`vue
71
+ <template>...</template>
72
+ <script setup>...</script>
73
+ <style scoped>...</style>
74
+ \`\`\``;
75
+
76
+ default:
77
+ return "";
78
+ }
79
+ }
80
+
81
+ // ─────────────────────────────────────────────────────
82
+ // 解析并校验模型返回内容
83
+ // ─────────────────────────────────────────────────────
84
+ export function parseStructuredResponse(content, responseFormat) {
85
+ if (!responseFormat) return { success: true, data: content };
86
+
87
+ const type = responseFormat.type || responseFormat;
88
+
89
+ // Markdown / HTML / 组件代码:直接返回原始文本
90
+ if (["markdown", "html", "react_component", "vue_component"].includes(type)) {
91
+ return { success: true, data: content };
92
+ }
93
+
94
+ // JSON 类格式:解析并校验
95
+ if (["json_object", "json_schema", "ticket_form"].includes(type)) {
96
+ try {
97
+ // 尝试直接解析
98
+ const parsed = JSON.parse(content.trim());
99
+ return { success: true, data: parsed };
100
+ } catch {
101
+ // 尝试从 Markdown 代码块中提取
102
+ const match = content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
103
+ if (match) {
104
+ try {
105
+ const parsed = JSON.parse(match[1].trim());
106
+ return { success: true, data: parsed };
107
+ } catch {
108
+ // 提取失败
109
+ }
110
+ }
111
+ // 解析失败:返回原始文本,标记失败
112
+ return {
113
+ success: false,
114
+ data: content,
115
+ error: "响应不是合法 JSON,已返回原始文本"
116
+ };
117
+ }
118
+ }
119
+
120
+ return { success: true, data: content };
121
+ }
122
+
123
+ // ─────────────────────────────────────────────────────
124
+ // 将解析结果包装为统一的响应 choice 格式
125
+ // ─────────────────────────────────────────────────────
126
+ export function wrapStructuredChoice(choice, parseResult, responseFormat) {
127
+ if (!responseFormat) return choice;
128
+
129
+ const type = responseFormat.type || responseFormat;
130
+ const isJsonType = ["json_object", "json_schema", "ticket_form"].includes(type);
131
+
132
+ return {
133
+ ...choice,
134
+ message: {
135
+ ...choice.message,
136
+ content: isJsonType && parseResult.success
137
+ ? JSON.stringify(parseResult.data) // 序列化回字符串,保持 OpenAI 格式兼容
138
+ : choice.message.content,
139
+ // 额外字段:方便客户端直接使用
140
+ parsed: parseResult.success ? parseResult.data : undefined,
141
+ parse_error: parseResult.error
142
+ }
143
+ };
144
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Tool Use 适配层
3
+ *
4
+ * 不同模型对 function calling / tool_use 的支持程度不同:
5
+ * - qwen3-coder:支持 tool_call,但部分版本需要特殊处理
6
+ * - qwen2.5 系列:标准 OpenAI function calling 格式
7
+ * - 不支持 tool 的模型:fallback 到 JSON mode prompt 工程
8
+ */
9
+
10
+ // ─────────────────────────────────────────────────────
11
+ // 模型能力配置表
12
+ // ─────────────────────────────────────────────────────
13
+ const MODEL_CAPABILITIES = {
14
+ // Qwen3-Coder 系列:支持 tool_call,需要设置 tool_choice
15
+ "qwen3-coder": {
16
+ toolSupport: "native",
17
+ toolChoiceFormat: "openai", // {type: "function", function: {name}}
18
+ requiresThinkingBlock: true, // qwen3 有 thinking 模式
19
+ maxTokens: 32768
20
+ },
21
+ "qwen3-coder-plus": {
22
+ toolSupport: "native",
23
+ toolChoiceFormat: "openai",
24
+ requiresThinkingBlock: true,
25
+ maxTokens: 32768
26
+ },
27
+ // Qwen2.5 系列:标准 OpenAI 格式
28
+ "qwen2.5-72b-instruct": {
29
+ toolSupport: "native",
30
+ toolChoiceFormat: "openai",
31
+ requiresThinkingBlock: false,
32
+ maxTokens: 8192
33
+ },
34
+ "qwen2.5-7b-instruct": {
35
+ toolSupport: "native",
36
+ toolChoiceFormat: "openai",
37
+ requiresThinkingBlock: false,
38
+ maxTokens: 8192
39
+ },
40
+ // 通用兜底:不确定能力时用 prompt 工程模拟
41
+ "default": {
42
+ toolSupport: "prompt", // 降级:通过 prompt 要求返回 JSON
43
+ toolChoiceFormat: null,
44
+ requiresThinkingBlock: false,
45
+ maxTokens: 4096
46
+ }
47
+ };
48
+
49
+ // ─────────────────────────────────────────────────────
50
+ // 获取模型能力配置(前缀匹配)
51
+ // ─────────────────────────────────────────────────────
52
+ export function getModelCapabilities(modelName) {
53
+ if (!modelName) return MODEL_CAPABILITIES["default"];
54
+
55
+ const lower = modelName.toLowerCase();
56
+
57
+ // 精确匹配
58
+ if (MODEL_CAPABILITIES[lower]) return MODEL_CAPABILITIES[lower];
59
+
60
+ // 前缀匹配:qwen3-coder-xxx → qwen3-coder
61
+ for (const [key, caps] of Object.entries(MODEL_CAPABILITIES)) {
62
+ if (key !== "default" && lower.startsWith(key)) return caps;
63
+ }
64
+
65
+ return MODEL_CAPABILITIES["default"];
66
+ }
67
+
68
+ // ─────────────────────────────────────────────────────
69
+ // 将客户端传入的 tools 转换为目标模型格式
70
+ // ─────────────────────────────────────────────────────
71
+ export function adaptTools(tools, modelName) {
72
+ const caps = getModelCapabilities(modelName);
73
+
74
+ if (!tools || tools.length === 0) return { tools: undefined, toolChoice: undefined };
75
+
76
+ if (caps.toolSupport === "native") {
77
+ // 原样透传(已经是 OpenAI 格式)
78
+ return { tools, toolChoice: undefined };
79
+ }
80
+
81
+ // toolSupport === "prompt":模型不支持 native tool,转为 prompt 工程
82
+ // 把 tools schema 注入 system prompt,要求返回 JSON
83
+ return {
84
+ tools: undefined,
85
+ toolChoice: undefined,
86
+ toolFallbackPrompt: buildToolFallbackPrompt(tools)
87
+ };
88
+ }
89
+
90
+ // ─────────────────────────────────────────────────────
91
+ // 降级时用 prompt 模拟 tool_use
92
+ // ─────────────────────────────────────────────────────
93
+ function buildToolFallbackPrompt(tools) {
94
+ const toolDescriptions = tools.map(t => {
95
+ const fn = t.function || t;
96
+ return `### ${fn.name}\n描述:${fn.description}\n参数 Schema:\n\`\`\`json\n${JSON.stringify(fn.parameters, null, 2)}\n\`\`\``;
97
+ }).join("\n\n");
98
+
99
+ return `\n\n你必须通过调用以下工具来完成任务。请严格按照 JSON 格式返回,不要有任何多余文字:
100
+ \`\`\`json
101
+ {
102
+ "tool_call": {
103
+ "name": "<工具名>",
104
+ "arguments": { <参数> }
105
+ }
106
+ }
107
+ \`\`\`
108
+
109
+ 可用工具:\n${toolDescriptions}`;
110
+ }
111
+
112
+ // ─────────────────────────────────────────────────────
113
+ // 解析降级模式下模型返回的 JSON tool call
114
+ // ─────────────────────────────────────────────────────
115
+ export function parseToolFallbackResponse(content) {
116
+ try {
117
+ const jsonMatch = content.match(/```json\s*([\s\S]*?)\s*```/);
118
+ const jsonStr = jsonMatch ? jsonMatch[1] : content;
119
+ const parsed = JSON.parse(jsonStr.trim());
120
+
121
+ if (parsed.tool_call) {
122
+ return {
123
+ role: "assistant",
124
+ content: null,
125
+ tool_calls: [{
126
+ id: `call_${Date.now()}`,
127
+ type: "function",
128
+ function: {
129
+ name: parsed.tool_call.name,
130
+ arguments: JSON.stringify(parsed.tool_call.arguments)
131
+ }
132
+ }]
133
+ };
134
+ }
135
+ } catch {
136
+ // 解析失败返回原始内容
137
+ }
138
+ return null;
139
+ }
140
+
141
+ // ─────────────────────────────────────────────────────
142
+ // Qwen3-Coder 的 thinking block 处理
143
+ // 模型返回 <think>...</think> 块,需要决定是否透传给客户端
144
+ // ─────────────────────────────────────────────────────
145
+ export function stripThinkingBlock(content, keepThinking = false) {
146
+ if (!content || typeof content !== "string") return content;
147
+ if (keepThinking) return content;
148
+
149
+ // 移除 <think>...</think> 块(qwen3 系列的 CoT 思考过程)
150
+ return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
151
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@templmf/temp-solf-lmf",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,74 +0,0 @@
1
- # Skills MCP
2
-
3
- 让 Roo Code 通过 MCP 工具管理和读取 Skills,彻底解决 Windows 路径环境变量问题。
4
-
5
- ## 安装
6
-
7
- ```bash
8
- # 1. 进入项目目录
9
- cd skills-mcp
10
-
11
- # 2. 安装依赖
12
- npm install
13
-
14
- # 3. 编译 TypeScript
15
- npm run build
16
- ```
17
-
18
- ## 配置到 Roo Code
19
-
20
- 在 Roo Code 的 MCP 配置文件中添加(`%APPDATA%\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_mcp_settings.json`):
21
-
22
- ```json
23
- {
24
- "mcpServers": {
25
- "skills-mcp": {
26
- "command": "node",
27
- "args": ["C:\\path\\to\\skills-mcp\\dist\\index.js"],
28
- "env": {
29
- "SKILLS_EXTRA_DIRS": "C:\\Users\\你的用户名\\.roo\\skills"
30
- }
31
- }
32
- }
33
- }
34
- ```
35
-
36
- > `SKILLS_EXTRA_DIRS` 是可选的,用于追加额外的 Skills 目录,多个目录用分号 `;` 分隔。
37
-
38
- ## 可用工具
39
-
40
- | 工具名 | 说明 | 参数 |
41
- |--------|------|------|
42
- | `list_skills` | 列出所有可用 Skills | 无 |
43
- | `read_skill` | 读取指定 Skill 的完整内容 | `name`: skill名称 |
44
- | `search_skills` | 模糊搜索 Skills(匹配名称或描述) | `query`: 搜索词 |
45
- | `list_skill_files` | 列出 Skill 目录下的所有附属文件 | `name`: skill名称 |
46
-
47
- ## Skills 路径查找顺序
48
-
49
- 1. `%USERPROFILE%\.roo\skills\`(全局,自动解析,不再依赖环境变量字面量)
50
- 2. `<当前工作目录>\.roo\skills\`(项目级,优先级更高,会覆盖同名全局 Skill)
51
- 3. `SKILLS_EXTRA_DIRS` 环境变量中指定的路径(补充)
52
-
53
- ## 与 Slash Commands 配合使用
54
-
55
- 在 `.roo\commands\skill.md` 中:
56
-
57
- ```markdown
58
- ---
59
- description: 调用指定 Skill 的指引来完成任务
60
- argument-hint: <skill-name>
61
- ---
62
-
63
- 请使用 MCP 工具 read_skill 读取名为 {参数} 的 Skill 内容,然后按其指引执行任务。
64
- ```
65
-
66
- 在 `.roo\commands\skills.md` 中:
67
-
68
- ```markdown
69
- ---
70
- description: 列出所有可用的 Skills
71
- ---
72
-
73
- 请使用 MCP 工具 list_skills 列出所有可用的 Skills。
74
- ```