foliko 2.0.17 → 2.0.19

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.
@@ -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
@@ -674,9 +674,16 @@ class AgentChatHandler extends EventEmitter {
674
674
  async *chatStream(message, options = {}) {
675
675
  const sessionId = options.sessionId || this.getDefaultSessionId();
676
676
  const framework = this.agent.framework;
677
+ // ★ 提到外层作用域,catch 中要回滚 userMessage 时需要访问
678
+ let messageStore = null;
679
+ let messages = null;
680
+ let userMessage = null;
677
681
 
678
682
  try {
679
- const { messageStore, messages } = await this._prepareSession(message, sessionId);
683
+ const prepared = await this._prepareSession(message, sessionId);
684
+ messageStore = prepared.messageStore;
685
+ messages = prepared.messages;
686
+ userMessage = prepared.userMessage;
680
687
 
681
688
  if (!this._aiClient) {
682
689
  throw new Error('AI 客户端未配置');
@@ -806,7 +813,43 @@ class AgentChatHandler extends EventEmitter {
806
813
 
807
814
  const userMsg = messages[messages.length - finishMessages.length - 1];
808
815
  this.emit('message', { content: fullText, sessionId: sessionId, userMessage: userMsg });
816
+
817
+ // ★ 修复:成功路径才持久化。AI 失败时若 finally 也 save,
818
+ // 会把刚 push 的 userMessage 写盘 → 下次请求 loadHistory 再次
819
+ // load 到,_prepareSession 再 push 一份 → 连续两条相同 user
820
+ // → AI SDK 抛 "Invalid prompt: ModelMessage[] schema"
821
+ try {
822
+ await messageStore.save();
823
+ } catch (err) {
824
+ logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
825
+ }
809
826
  } catch (err) {
827
+ // ★ 修复:失败时打印 InvalidPromptError 的 cause(含 Zod issue 路径),
828
+ // 方便定位是哪条消息、哪个字段不匹配 ModelMessage[] schema
829
+ logger.error('[AgentChat] chatStream error:', err.message);
830
+ if (err.cause) {
831
+ const cause = err.cause;
832
+ if (cause.issues) {
833
+ for (const issue of cause.issues.slice(0, 5)) {
834
+ logger.error(
835
+ `[AgentChat] schema issue: path=${JSON.stringify(issue.path)} code=${issue.code} message=${issue.message}`
836
+ );
837
+ }
838
+ } else {
839
+ logger.error(`[AgentChat] error cause: ${cause.message || String(cause)}`);
840
+ }
841
+ }
842
+
843
+ // ★ 修复:失败时 ROLLBACK 掉本轮 push 的 userMessage
844
+ try {
845
+ if (messages && userMessage) {
846
+ const last = messages[messages.length - 1];
847
+ if (last && last.role === 'user' && last === userMessage) {
848
+ messages.pop();
849
+ }
850
+ }
851
+ } catch { /* ignore */ }
852
+
810
853
  // 统一错误处理:提取简洁消息并通过 yield 传递
811
854
  const friendlyMessage = err?.message?.split('\n')[0] || '未知错误';
812
855
 
@@ -815,11 +858,6 @@ class AgentChatHandler extends EventEmitter {
815
858
  this.emit('message:error', { sessionId, error: friendlyMessage, originalError: err });
816
859
  }
817
860
  yield { type: 'error', error: friendlyMessage };
818
- } finally {
819
- const messageStore = this._getSessionMessageStore(sessionId);
820
- messageStore.save().catch((err) => {
821
- logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
822
- });
823
861
  }
824
862
  }
825
863
 
@@ -869,9 +907,16 @@ class AgentChatHandler extends EventEmitter {
869
907
  */
870
908
  async _processMessage(sessionId, message, options) {
871
909
  const framework = this.agent.framework;
910
+ // ★ 提到外层作用域,catch 中要回滚 userMessage 时需要访问
911
+ let messageStore = null;
912
+ let messages = null;
913
+ let userMessage = null;
872
914
 
873
915
  try {
874
- const { messageStore, messages } = await this._prepareSession(message, sessionId);
916
+ const prepared = await this._prepareSession(message, sessionId);
917
+ messageStore = prepared.messageStore;
918
+ messages = prepared.messages;
919
+ userMessage = prepared.userMessage;
875
920
 
876
921
  if (!this._aiClient) {
877
922
  throw new Error('AI 客户端未配置');
@@ -927,28 +972,57 @@ class AgentChatHandler extends EventEmitter {
927
972
  const userMsg = messages[messages.length - result.response.messages.length - 1];
928
973
  this.emit('message', { content: result.text, sessionId: sessionId, userMessage: userMsg });
929
974
 
975
+ // ★ 修复:AI 调用成功后才持久化(之前 finally 总是 save,
976
+ // 会把刚 push 的 userMessage 写盘;下次请求 loadHistory 再次 load 到,
977
+ // _prepareSession 又 push 一份 → 连续两条相同 user 消息 →
978
+ // AI SDK 抛 "Invalid prompt: ModelMessage[] schema")
979
+ try {
980
+ await messageStore.save();
981
+ } catch (err) {
982
+ logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
983
+ }
984
+
930
985
  return {
931
986
  success: true,
932
987
  message: cleanResponse(result.text || ''),
933
988
  stepCount: result.stepCount || 1,
934
989
  };
935
990
  } catch (err) {
991
+ // ★ 修复:失败时打印 InvalidPromptError 的 cause(含 Zod issue 路径),
992
+ // 方便定位是哪条消息、哪个字段不匹配 ModelMessage[] schema
936
993
  logger.error('[AgentChat] _processMessage error:', err.message);
994
+ if (err.cause) {
995
+ const cause = err.cause;
996
+ if (cause.issues) {
997
+ // ZodError:逐条打印
998
+ for (const issue of cause.issues.slice(0, 5)) {
999
+ logger.error(
1000
+ `[AgentChat] schema issue: path=${JSON.stringify(issue.path)} code=${issue.code} message=${issue.message}`
1001
+ );
1002
+ }
1003
+ } else {
1004
+ logger.error(`[AgentChat] error cause: ${cause.message || String(cause)}`);
1005
+ }
1006
+ }
937
1007
  const errorMsg = err.message || String(err);
938
1008
 
1009
+ // ★ 修复:失败时 ROLLBACK 掉本轮 push 的 userMessage,
1010
+ // 避免下次请求把它从 jsonl load 出来再 push 一遍
1011
+ try {
1012
+ if (messages && userMessage) {
1013
+ const last = messages[messages.length - 1];
1014
+ if (last && last.role === 'user' && last === userMessage) {
1015
+ messages.pop();
1016
+ }
1017
+ }
1018
+ } catch { /* ignore */ }
1019
+
939
1020
  return {
940
1021
  success: false,
941
1022
  message: 'AI 服务暂时不可用,请稍后重试。',
942
1023
  error: errorMsg,
943
1024
  stepCount: 0,
944
1025
  };
945
- } finally {
946
- const messageStore = this._getSessionMessageStore(sessionId);
947
- try {
948
- await messageStore.save();
949
- } catch (err) {
950
- logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
951
- }
952
1026
  }
953
1027
  }
954
1028
 
@@ -1006,9 +1080,44 @@ class AgentChatHandler extends EventEmitter {
1006
1080
  messages.length = 0;
1007
1081
  messages.push(...validated);
1008
1082
  }
1083
+
1084
+ // ★ 修复:去重末尾重复的 user 消息。
1085
+ // 场景:AI 失败时上一轮已把 userMessage 持久化到 jsonl(或留在 messageStore 内存),
1086
+ // 本次 _prepareSession 再次 loadHistory 又会拿到它;
1087
+ // 如果再 push 一次,消息数组末尾出现两条相同 user,
1088
+ // AI SDK 抛 "Invalid prompt: ModelMessage[] schema"。
1089
+ // 仅对"末尾连续 user"做去重;中间重复历史由 validateAll 处理。
1090
+ if (messages.length > 0) {
1091
+ const last = messages[messages.length - 1];
1092
+ if (
1093
+ last &&
1094
+ last.role === 'user' &&
1095
+ userMessage &&
1096
+ userMessage.role === 'user' &&
1097
+ this._isSameUserContent(last.content, userMessage.content)
1098
+ ) {
1099
+ logger.debug(`[_prepareSession] 去重末尾 user 消息(避免连续两条相同 user)`);
1100
+ messages.pop();
1101
+ }
1102
+ }
1009
1103
  messages.push(userMessage);
1010
1104
 
1011
- return { messageStore, messages };
1105
+ // 返回 userMessage 引用,调用方(_processMessage)失败时需 rollback
1106
+ return { messageStore, messages, userMessage };
1107
+ }
1108
+
1109
+ /**
1110
+ * 判断两个 user content 是否等价(用于末尾 user 去重)
1111
+ * @private
1112
+ */
1113
+ _isSameUserContent(a, b) {
1114
+ if (a === b) return true;
1115
+ if (typeof a === 'string' && typeof b === 'string') return a === b;
1116
+ try {
1117
+ return JSON.stringify(a) === JSON.stringify(b);
1118
+ } catch {
1119
+ return false;
1120
+ }
1012
1121
  }
1013
1122
 
1014
1123
  /**
@@ -54,7 +54,7 @@ class SessionManager {
54
54
  this.labelTimestampsById = new Map();
55
55
  this.leafId = null;
56
56
  this.sessionId = '';
57
-
57
+ this.fileName=''
58
58
  if (persist && sessionDir && !fs.existsSync(sessionDir)) {
59
59
  fs.mkdirSync(sessionDir, { recursive: true });
60
60
  }
@@ -84,6 +84,7 @@ class SessionManager {
84
84
  }
85
85
 
86
86
  const header = this.fileEntries.find(e => e.type === 'session');
87
+ this.fileName = path.basename(sessionFile);
87
88
  this.sessionId = header?.id || createSessionId();
88
89
 
89
90
  this._buildIndex();