foliko 2.0.3 → 2.0.5
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/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/docs/public-api.md +924 -6
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/default/bootstrap.js +21 -0
- package/plugins/core/skill-manager/index.js +158 -0
- package/plugins/core/workflow/index.js +24 -0
- package/plugins/executors/extension/index.js +1 -1
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/chat.js +67 -5
- package/src/agent/prompt-registry.js +95 -89
- package/src/agent/sub.js +1 -1
- package/src/cli/ui/chat-ui.js +20 -16
- package/src/common/queue.js +1 -1
- package/src/framework/framework.js +649 -2
- package/src/plugin/base.js +5 -5
- package/src/session/chat.js +1 -1
- package/src/tool/executor.js +1 -1
- package/tests/core/plugin-prompts.test.js +13 -13
- package/tests/core/prompt-registry.test.js +59 -51
|
@@ -1006,6 +1006,164 @@ class SkillManagerPlugin extends Plugin {
|
|
|
1006
1006
|
return this._skills.has(name);
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* 注册一个新 skill(程序化注册,不依赖文件)
|
|
1011
|
+
* @param {string} name - 技能名称
|
|
1012
|
+
* @param {string} content - 技能内容(markdown)
|
|
1013
|
+
* @param {Object} [metadata] - 技能元数据
|
|
1014
|
+
* @param {Object} [options] - 选项
|
|
1015
|
+
* @param {Function[]} [options.commands] - 命令定义数组 [{ name, description, options, execute }]
|
|
1016
|
+
* @param {string} [options.path] - 技能路径(用于 references/scripts 解析)
|
|
1017
|
+
* @returns {Object} 注册的 skill 对象
|
|
1018
|
+
*/
|
|
1019
|
+
register(name, content, metadata = {}, options = {}) {
|
|
1020
|
+
const skillMetadata = new SkillMetadata({
|
|
1021
|
+
name: name,
|
|
1022
|
+
description: metadata.description || '',
|
|
1023
|
+
license: metadata.license || null,
|
|
1024
|
+
compatibility: metadata.compatibility || null,
|
|
1025
|
+
metadata: metadata.metadata || {},
|
|
1026
|
+
allowedTools: metadata['allowed-tools'] || metadata.allowedTools || [],
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
const skill = new Skill(skillMetadata, content);
|
|
1030
|
+
skill._skillPath = options.path || null;
|
|
1031
|
+
skill.install(this._framework);
|
|
1032
|
+
|
|
1033
|
+
// 注册命令
|
|
1034
|
+
if (Array.isArray(options.commands) && options.commands.length > 0) {
|
|
1035
|
+
for (const cmd of options.commands) {
|
|
1036
|
+
if (!cmd || !cmd.name) continue;
|
|
1037
|
+
|
|
1038
|
+
const handler = typeof cmd.execute === 'function' ? cmd.execute : null;
|
|
1039
|
+
if (!handler) continue;
|
|
1040
|
+
|
|
1041
|
+
const fullName = `${name}:${cmd.name}`;
|
|
1042
|
+
|
|
1043
|
+
// 获取参数解析器
|
|
1044
|
+
let argumentParser = cmd.argumentParser;
|
|
1045
|
+
if (!argumentParser && Array.isArray(cmd.options) && cmd.options.length > 0) {
|
|
1046
|
+
argumentParser = (rawArgs) => {
|
|
1047
|
+
try {
|
|
1048
|
+
const program = new Command();
|
|
1049
|
+
program.exitOverride();
|
|
1050
|
+
program.configureOutput({ writeErr: () => {} });
|
|
1051
|
+
for (const opt of cmd.options) {
|
|
1052
|
+
program.option(opt.flags, opt.description, opt.defaultValue);
|
|
1053
|
+
}
|
|
1054
|
+
program.arguments('[args...]');
|
|
1055
|
+
program.parse(['node', 'cmd', ...parseShellArgs(rawArgs)]);
|
|
1056
|
+
return { ...program.opts(), args: program.args };
|
|
1057
|
+
} catch (err) {
|
|
1058
|
+
return { args: rawArgs };
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// 构建命令描述
|
|
1064
|
+
let commandHint = '';
|
|
1065
|
+
if (cmd.options && cmd.options.length > 0) {
|
|
1066
|
+
const optDescs = cmd.options.map(formatOptionForHelp);
|
|
1067
|
+
commandHint = `\n 可用选项:\n ${optDescs.join('\n ')}`;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const toolDef = {
|
|
1071
|
+
name: fullName,
|
|
1072
|
+
description: `${cmd.description || 'Skill command'}\n\n**重要**: args.command 是**字符串**,不是对象!\n${commandHint}`,
|
|
1073
|
+
inputSchema: z.object({
|
|
1074
|
+
command: z.string().optional().describe(
|
|
1075
|
+
cmd.options && cmd.options.length > 0
|
|
1076
|
+
? `**字符串**,空格分隔,如: ${cmd.options.map(formatOptionForExample).join(' ')}`
|
|
1077
|
+
: `**字符串**,命令行参数`
|
|
1078
|
+
),
|
|
1079
|
+
}),
|
|
1080
|
+
_options: cmd.options || null,
|
|
1081
|
+
execute: async (toolArgs, framework) => {
|
|
1082
|
+
const ctx = {
|
|
1083
|
+
sessionId: framework?._currentSessionId || 'unknown',
|
|
1084
|
+
agent: null,
|
|
1085
|
+
framework: framework,
|
|
1086
|
+
cwd: framework?.getCwd?.() ?? process.cwd(),
|
|
1087
|
+
};
|
|
1088
|
+
let parsedArgs = toolArgs.command || '';
|
|
1089
|
+
if (typeof argumentParser === 'function') {
|
|
1090
|
+
parsedArgs = argumentParser(toolArgs.command || '');
|
|
1091
|
+
}
|
|
1092
|
+
return await handler(parsedArgs, ctx);
|
|
1093
|
+
},
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const extExecutor = this._framework?.pluginManager?.get('extension-executor');
|
|
1097
|
+
if (extExecutor) {
|
|
1098
|
+
extExecutor.registerTool('skill', {
|
|
1099
|
+
name: name,
|
|
1100
|
+
description: skillMetadata.description || `Skill: ${name}`,
|
|
1101
|
+
version: skillMetadata.compatibility || '1.0.0',
|
|
1102
|
+
}, toolDef);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
skill.tools[fullName] = toolDef;
|
|
1106
|
+
skill._commands.push(fullName);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// 扫描 references 和 scripts
|
|
1111
|
+
const references = skill._skillPath ? this._scanReferences(skill._skillPath) : new Map();
|
|
1112
|
+
const scripts = skill._skillPath ? this._scanScripts(skill._skillPath) : new Map();
|
|
1113
|
+
|
|
1114
|
+
const skillData = {
|
|
1115
|
+
name: name,
|
|
1116
|
+
metadata: skillMetadata,
|
|
1117
|
+
content: content,
|
|
1118
|
+
instance: skill,
|
|
1119
|
+
path: skill._skillPath,
|
|
1120
|
+
references,
|
|
1121
|
+
scripts,
|
|
1122
|
+
fromPlugin: false,
|
|
1123
|
+
registered: true, // 标记为程序化注册
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
this._skills.set(name, skillData);
|
|
1127
|
+
|
|
1128
|
+
// 更新 SkillManagerPlugin.tools
|
|
1129
|
+
Object.assign(this.tools, skill.tools);
|
|
1130
|
+
|
|
1131
|
+
return skillData;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* 移除 skill
|
|
1136
|
+
* @param {string} name 技能名称
|
|
1137
|
+
* @returns {boolean} 是否成功移除
|
|
1138
|
+
*/
|
|
1139
|
+
remove(name) {
|
|
1140
|
+
if (!this._skills.has(name)) {
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const skill = this._skills.get(name);
|
|
1145
|
+
|
|
1146
|
+
// 清理命令(从 extension-executor 移除)
|
|
1147
|
+
if (skill.instance?._commands) {
|
|
1148
|
+
const extExecutor = this._framework?.pluginManager?.get('extension-executor');
|
|
1149
|
+
if (extExecutor) {
|
|
1150
|
+
for (const cmd of skill.instance._commands) {
|
|
1151
|
+
extExecutor._extensions.delete(`${name}:${cmd}`);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// 从 SkillManagerPlugin.tools 中移除
|
|
1157
|
+
if (skill.instance?.tools) {
|
|
1158
|
+
for (const toolName of Object.keys(skill.instance.tools)) {
|
|
1159
|
+
delete this.tools[toolName];
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
this._skills.delete(name);
|
|
1164
|
+
return true;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1009
1167
|
/**
|
|
1010
1168
|
* 获取 skill 数量
|
|
1011
1169
|
*/
|
|
@@ -981,6 +981,30 @@ class WorkflowPlugin extends Plugin {
|
|
|
981
981
|
// log.info(` Registered tool: ${toolName}`);
|
|
982
982
|
}
|
|
983
983
|
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* 移除工作流
|
|
987
|
+
* @param {string} name 工作流名称
|
|
988
|
+
* @returns {boolean} 是否成功移除
|
|
989
|
+
*/
|
|
990
|
+
remove(name) {
|
|
991
|
+
if (!this._workflows.has(name)) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// 从工作流定义中删除
|
|
996
|
+
this._workflows.delete(name);
|
|
997
|
+
|
|
998
|
+
// 清除已注册的工具
|
|
999
|
+
const toolName = this._workflowTools.get(name);
|
|
1000
|
+
if (toolName) {
|
|
1001
|
+
// 工具注销需要框架支持,这里只清理内部状态
|
|
1002
|
+
this._workflowTools.delete(name);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
984
1008
|
/**
|
|
985
1009
|
* 重载工作流
|
|
986
1010
|
*/
|
|
@@ -302,7 +302,7 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
302
302
|
ext.tools.push(toolEntry);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
log.debug(` Registered tool '${toolDef.name}' for extension '${pluginName}'`);
|
|
305
|
+
//log.debug(` Registered tool '${toolDef.name}' for extension '${pluginName}'`);
|
|
306
306
|
|
|
307
307
|
if (this._framework && this._framework._ready) {
|
|
308
308
|
this._invalidatePromptCaches(this._framework);
|
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: find-skills
|
|
3
|
-
description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Find Skills
|
|
7
|
-
|
|
8
|
-
This skill helps you discover and install skills from the open agent skills ecosystem.
|
|
9
|
-
|
|
10
|
-
## When to Use This Skill
|
|
11
|
-
|
|
12
|
-
Use this skill when the user:
|
|
13
|
-
|
|
14
|
-
- Asks "how do I do X" where X might be a common task with an existing skill
|
|
15
|
-
- Says "find a skill for X" or "is there a skill for X"
|
|
16
|
-
- Asks "can you do X" where X is a specialized capability
|
|
17
|
-
- Expresses interest in extending agent capabilities
|
|
18
|
-
- Wants to search for tools, templates, or workflows
|
|
19
|
-
- Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
|
|
20
|
-
|
|
21
|
-
## What is the Skills CLI?
|
|
22
|
-
|
|
23
|
-
The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
|
|
24
|
-
|
|
25
|
-
**Key commands:**
|
|
26
|
-
|
|
27
|
-
- `npx skills find [query]` - Search for skills interactively or by keyword
|
|
28
|
-
- `npx skills add <package>` - Install a skill from GitHub or other sources
|
|
29
|
-
- `npx skills check` - Check for skill updates
|
|
30
|
-
- `npx skills update` - Update all installed skills
|
|
31
|
-
|
|
32
|
-
**Browse skills at:** https://skills.sh/
|
|
33
|
-
|
|
34
|
-
## How to Help Users Find Skills
|
|
35
|
-
|
|
36
|
-
### Step 1: Understand What They Need
|
|
37
|
-
|
|
38
|
-
When a user asks for help with something, identify:
|
|
39
|
-
|
|
40
|
-
1. The domain (e.g., React, testing, design, deployment)
|
|
41
|
-
2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
|
|
42
|
-
3. Whether this is a common enough task that a skill likely exists
|
|
43
|
-
|
|
44
|
-
### Step 2: Search for Skills
|
|
45
|
-
|
|
46
|
-
Run the find command with a relevant query:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npx skills find [query]
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
For example:
|
|
53
|
-
|
|
54
|
-
- User asks "how do I make my React app faster?" → `npx skills find react performance`
|
|
55
|
-
- User asks "can you help me with PR reviews?" → `npx skills find pr review`
|
|
56
|
-
- User asks "I need to create a changelog" → `npx skills find changelog`
|
|
57
|
-
|
|
58
|
-
The command will return results like:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
Install with npx skills add <owner/repo@skill>
|
|
62
|
-
|
|
63
|
-
vercel-labs/agent-skills@vercel-react-best-practices
|
|
64
|
-
└ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Step 3: Present Options to the User
|
|
68
|
-
|
|
69
|
-
When you find relevant skills, present them to the user with:
|
|
70
|
-
|
|
71
|
-
1. The skill name and what it does
|
|
72
|
-
2. The install command they can run
|
|
73
|
-
3. A link to learn more at skills.sh
|
|
74
|
-
|
|
75
|
-
Example response:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
I found a skill that might help! The "vercel-react-best-practices" skill provides
|
|
79
|
-
React and Next.js performance optimization guidelines from Vercel Engineering.
|
|
80
|
-
|
|
81
|
-
To install it:
|
|
82
|
-
npx skills add vercel-labs/agent-skills@vercel-react-best-practices
|
|
83
|
-
|
|
84
|
-
Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Step 4: Offer to Install
|
|
88
|
-
|
|
89
|
-
If the user wants to proceed, you can install the skill for them:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npx skills add <owner/repo@skill> -g -y
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
|
|
96
|
-
|
|
97
|
-
## Common Skill Categories
|
|
98
|
-
|
|
99
|
-
When searching, consider these common categories:
|
|
100
|
-
|
|
101
|
-
| Category | Example Queries |
|
|
102
|
-
| --------------- | ---------------------------------------- |
|
|
103
|
-
| Web Development | react, nextjs, typescript, css, tailwind |
|
|
104
|
-
| Testing | testing, jest, playwright, e2e |
|
|
105
|
-
| DevOps | deploy, docker, kubernetes, ci-cd |
|
|
106
|
-
| Documentation | docs, readme, changelog, api-docs |
|
|
107
|
-
| Code Quality | review, lint, refactor, best-practices |
|
|
108
|
-
| Design | ui, ux, design-system, accessibility |
|
|
109
|
-
| Productivity | workflow, automation, git |
|
|
110
|
-
|
|
111
|
-
## Tips for Effective Searches
|
|
112
|
-
|
|
113
|
-
1. **Use specific keywords**: "react testing" is better than just "testing"
|
|
114
|
-
2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
|
|
115
|
-
3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
|
|
116
|
-
|
|
117
|
-
## When No Skills Are Found
|
|
118
|
-
|
|
119
|
-
If no relevant skills exist:
|
|
120
|
-
|
|
121
|
-
1. Acknowledge that no existing skill was found
|
|
122
|
-
2. Offer to help with the task directly using your general capabilities
|
|
123
|
-
3. Suggest the user could create their own skill with `npx skills init`
|
|
124
|
-
|
|
125
|
-
Example:
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
I searched for skills related to "xyz" but didn't find any matches.
|
|
129
|
-
I can still help you with this task directly! Would you like me to proceed?
|
|
130
|
-
|
|
131
|
-
If this is something you do often, you could create your own skill:
|
|
132
|
-
npx skills init my-xyz-skill
|
|
133
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: find-skills
|
|
3
|
+
description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Find Skills
|
|
7
|
+
|
|
8
|
+
This skill helps you discover and install skills from the open agent skills ecosystem.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use this skill when the user:
|
|
13
|
+
|
|
14
|
+
- Asks "how do I do X" where X might be a common task with an existing skill
|
|
15
|
+
- Says "find a skill for X" or "is there a skill for X"
|
|
16
|
+
- Asks "can you do X" where X is a specialized capability
|
|
17
|
+
- Expresses interest in extending agent capabilities
|
|
18
|
+
- Wants to search for tools, templates, or workflows
|
|
19
|
+
- Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
|
|
20
|
+
|
|
21
|
+
## What is the Skills CLI?
|
|
22
|
+
|
|
23
|
+
The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
|
|
24
|
+
|
|
25
|
+
**Key commands:**
|
|
26
|
+
|
|
27
|
+
- `npx skills find [query]` - Search for skills interactively or by keyword
|
|
28
|
+
- `npx skills add <package>` - Install a skill from GitHub or other sources
|
|
29
|
+
- `npx skills check` - Check for skill updates
|
|
30
|
+
- `npx skills update` - Update all installed skills
|
|
31
|
+
|
|
32
|
+
**Browse skills at:** https://skills.sh/
|
|
33
|
+
|
|
34
|
+
## How to Help Users Find Skills
|
|
35
|
+
|
|
36
|
+
### Step 1: Understand What They Need
|
|
37
|
+
|
|
38
|
+
When a user asks for help with something, identify:
|
|
39
|
+
|
|
40
|
+
1. The domain (e.g., React, testing, design, deployment)
|
|
41
|
+
2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
|
|
42
|
+
3. Whether this is a common enough task that a skill likely exists
|
|
43
|
+
|
|
44
|
+
### Step 2: Search for Skills
|
|
45
|
+
|
|
46
|
+
Run the find command with a relevant query:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx skills find [query]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For example:
|
|
53
|
+
|
|
54
|
+
- User asks "how do I make my React app faster?" → `npx skills find react performance`
|
|
55
|
+
- User asks "can you help me with PR reviews?" → `npx skills find pr review`
|
|
56
|
+
- User asks "I need to create a changelog" → `npx skills find changelog`
|
|
57
|
+
|
|
58
|
+
The command will return results like:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Install with npx skills add <owner/repo@skill>
|
|
62
|
+
|
|
63
|
+
vercel-labs/agent-skills@vercel-react-best-practices
|
|
64
|
+
└ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3: Present Options to the User
|
|
68
|
+
|
|
69
|
+
When you find relevant skills, present them to the user with:
|
|
70
|
+
|
|
71
|
+
1. The skill name and what it does
|
|
72
|
+
2. The install command they can run
|
|
73
|
+
3. A link to learn more at skills.sh
|
|
74
|
+
|
|
75
|
+
Example response:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
I found a skill that might help! The "vercel-react-best-practices" skill provides
|
|
79
|
+
React and Next.js performance optimization guidelines from Vercel Engineering.
|
|
80
|
+
|
|
81
|
+
To install it:
|
|
82
|
+
npx skills add vercel-labs/agent-skills@vercel-react-best-practices
|
|
83
|
+
|
|
84
|
+
Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 4: Offer to Install
|
|
88
|
+
|
|
89
|
+
If the user wants to proceed, you can install the skill for them:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx skills add <owner/repo@skill> -g -y
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
|
|
96
|
+
|
|
97
|
+
## Common Skill Categories
|
|
98
|
+
|
|
99
|
+
When searching, consider these common categories:
|
|
100
|
+
|
|
101
|
+
| Category | Example Queries |
|
|
102
|
+
| --------------- | ---------------------------------------- |
|
|
103
|
+
| Web Development | react, nextjs, typescript, css, tailwind |
|
|
104
|
+
| Testing | testing, jest, playwright, e2e |
|
|
105
|
+
| DevOps | deploy, docker, kubernetes, ci-cd |
|
|
106
|
+
| Documentation | docs, readme, changelog, api-docs |
|
|
107
|
+
| Code Quality | review, lint, refactor, best-practices |
|
|
108
|
+
| Design | ui, ux, design-system, accessibility |
|
|
109
|
+
| Productivity | workflow, automation, git |
|
|
110
|
+
|
|
111
|
+
## Tips for Effective Searches
|
|
112
|
+
|
|
113
|
+
1. **Use specific keywords**: "react testing" is better than just "testing"
|
|
114
|
+
2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
|
|
115
|
+
3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
|
|
116
|
+
|
|
117
|
+
## When No Skills Are Found
|
|
118
|
+
|
|
119
|
+
If no relevant skills exist:
|
|
120
|
+
|
|
121
|
+
1. Acknowledge that no existing skill was found
|
|
122
|
+
2. Offer to help with the task directly using your general capabilities
|
|
123
|
+
3. Suggest the user could create their own skill with `npx skills init`
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
I searched for skills related to "xyz" but didn't find any matches.
|
|
129
|
+
I can still help you with this task directly! Would you like me to proceed?
|
|
130
|
+
|
|
131
|
+
If this is something you do often, you could create your own skill:
|
|
132
|
+
npx skills init my-xyz-skill
|
|
133
|
+
```
|
package/src/agent/chat.js
CHANGED
|
@@ -473,7 +473,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
473
473
|
const { messageStore, messages } = await this._prepareSession(message, sessionId);
|
|
474
474
|
|
|
475
475
|
if (!this._aiClient) {
|
|
476
|
-
throw new Error('AI
|
|
476
|
+
throw new Error('AI 客户端未配置');
|
|
477
477
|
}
|
|
478
478
|
this._validateToolCalls(messages);
|
|
479
479
|
const systemPrompt = framework.getSystemPrompt();
|
|
@@ -497,7 +497,12 @@ class AgentChatHandler extends EventEmitter {
|
|
|
497
497
|
let reasoningContent = '';
|
|
498
498
|
|
|
499
499
|
const result = await framework.runInSession(sessionId, {}, async () => {
|
|
500
|
-
return agent.stream({
|
|
500
|
+
return agent.stream({
|
|
501
|
+
messages,
|
|
502
|
+
...this.providerOptions,
|
|
503
|
+
onError: handleSDKError,
|
|
504
|
+
abortSignal: framework.getAbortSignal?.(),
|
|
505
|
+
});
|
|
501
506
|
});
|
|
502
507
|
|
|
503
508
|
const stream = result.fullStream;
|
|
@@ -595,7 +600,10 @@ class AgentChatHandler extends EventEmitter {
|
|
|
595
600
|
// 统一错误处理:提取简洁消息并通过 yield 传递
|
|
596
601
|
const friendlyMessage = err?.message?.split('\n')[0] || '未知错误';
|
|
597
602
|
|
|
598
|
-
|
|
603
|
+
// 用户主动中断(framework.stop())不上报 message:error,避免与上游重复
|
|
604
|
+
if (err.name !== 'AbortError') {
|
|
605
|
+
this.emit('message:error', { sessionId, error: friendlyMessage, originalError: err });
|
|
606
|
+
}
|
|
599
607
|
yield { type: 'error', error: friendlyMessage };
|
|
600
608
|
} finally {
|
|
601
609
|
const messageStore = this._getSessionMessageStore(sessionId);
|
|
@@ -648,7 +656,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
648
656
|
const { messageStore, messages } = await this._prepareSession(message, sessionId);
|
|
649
657
|
|
|
650
658
|
if (!this._aiClient) {
|
|
651
|
-
throw new Error('AI
|
|
659
|
+
throw new Error('AI 客户端未配置');
|
|
652
660
|
}
|
|
653
661
|
this._validateToolCalls(messages);
|
|
654
662
|
const systemPrompt = framework.getSystemPrompt();
|
|
@@ -669,7 +677,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
669
677
|
});
|
|
670
678
|
|
|
671
679
|
const result = await framework.runInSession(sessionId, {}, async () => {
|
|
672
|
-
return agent.generate({ messages, ...apiOptions });
|
|
680
|
+
return agent.generate({ messages, ...apiOptions, abortSignal: framework.getAbortSignal?.() });
|
|
673
681
|
});
|
|
674
682
|
|
|
675
683
|
if (result.usage && (result.usage.promptTokens > 0 || result.usage.completionTokens > 0)) {
|
|
@@ -780,6 +788,9 @@ class AgentChatHandler extends EventEmitter {
|
|
|
780
788
|
messages.push(userMessage);
|
|
781
789
|
this._validateToolCalls(messages);
|
|
782
790
|
|
|
791
|
+
// 最终兜底:暴力清理所有 orphaned tool 消息,确保 AI SDK 不报错
|
|
792
|
+
this._stripOrphanedToolMessages(messages);
|
|
793
|
+
|
|
783
794
|
return { messageStore, messages };
|
|
784
795
|
}
|
|
785
796
|
|
|
@@ -832,6 +843,57 @@ class AgentChatHandler extends EventEmitter {
|
|
|
832
843
|
messageStore._cacheMessageCount = messageStore.messages.length;
|
|
833
844
|
}
|
|
834
845
|
|
|
846
|
+
/**
|
|
847
|
+
* 最终兜底:暴力清理所有 orphaned tool 消息
|
|
848
|
+
* 遍历所有 tool 角色消息,删除没有对应 tool-call 的
|
|
849
|
+
* 处理数组格式(AI SDK)、字符串格式(OpenAI 兼容)、tool_call_id 格式
|
|
850
|
+
*/
|
|
851
|
+
_stripOrphanedToolMessages(messages) {
|
|
852
|
+
const validIds = new Set();
|
|
853
|
+
for (const msg of messages) {
|
|
854
|
+
if (msg.role !== 'assistant') continue;
|
|
855
|
+
if (Array.isArray(msg.content)) {
|
|
856
|
+
for (const item of msg.content) {
|
|
857
|
+
if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) validIds.add(item.toolCallId);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
861
|
+
for (const tc of msg.tool_calls) { if (tc.id) validIds.add(tc.id); }
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
let removed = 0;
|
|
866
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
867
|
+
const msg = messages[i];
|
|
868
|
+
if (msg.role !== 'tool') continue;
|
|
869
|
+
|
|
870
|
+
let isOrphaned = false;
|
|
871
|
+
if (Array.isArray(msg.content)) {
|
|
872
|
+
msg.content = msg.content.filter(item => {
|
|
873
|
+
if (item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId && !validIds.has(item.toolCallId)) {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
return true;
|
|
877
|
+
});
|
|
878
|
+
if (msg.content.length === 0) isOrphaned = true;
|
|
879
|
+
} else if (msg.tool_call_id && !validIds.has(msg.tool_call_id)) {
|
|
880
|
+
isOrphaned = true;
|
|
881
|
+
} else if (!msg.tool_call_id && validIds.size === 0) {
|
|
882
|
+
// 没有任何 tool-call,所有 tool 消息都是 orphaned
|
|
883
|
+
isOrphaned = true;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (isOrphaned) {
|
|
887
|
+
messages.splice(i, 1);
|
|
888
|
+
removed++;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (removed > 0) {
|
|
893
|
+
logger.debug(`[_stripOrphanedToolMessages] removed ${removed} orphaned tool message(s)`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
835
897
|
/**
|
|
836
898
|
* 计算工具列表缓存 key
|
|
837
899
|
* @private
|