foliko 1.1.88 → 1.1.90
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/.env.example +56 -56
- package/package.json +2 -2
- package/plugins/default-plugins.js +131 -19
- package/plugins/feishu-plugin.js +1 -1
- package/plugins/file-system-plugin.js +8 -7
- package/plugins/install-plugin.js +11 -6
- package/plugins/memory-plugin.js +4 -4
- package/plugins/plugin-manager-plugin.js +7 -5
- package/plugins/qq-plugin.js +9 -6
- package/plugins/rules-plugin.js +1 -1
- package/plugins/session-plugin.js +32 -23
- package/plugins/shell-executor-plugin.js +4 -1
- package/plugins/storage-plugin.js +1 -1
- package/plugins/subagent-plugin.js +1 -1
- package/plugins/telegram-plugin.js +2 -2
- package/plugins/weixin-plugin.js +9 -6
- package/skills/find-skills/SKILL.md +133 -133
- package/src/capabilities/skill-manager.js +3 -2
- package/src/capabilities/workflow-engine.js +2 -1
- package/src/core/agent-chat.js +4 -4
- package/src/core/agent.js +1 -1
- package/src/core/constants.js +4 -0
- package/src/core/framework.js +241 -2
- package/src/core/plugin-base.js +11 -0
- package/src/core/plugin-manager.js +18 -2
- package/website_v2/styles/animations.css +7 -7
|
@@ -16,7 +16,7 @@ const { buildSessionContext, EntryTypes } = require('../src/core/session-entry')
|
|
|
16
16
|
* Get default session directory for a cwd
|
|
17
17
|
*/
|
|
18
18
|
function getDefaultSessionDir(cwd) {
|
|
19
|
-
const agentDir = path.join(
|
|
19
|
+
const agentDir = path.join(cwd, '.foliko');
|
|
20
20
|
const sessionDir = path.join(agentDir, 'sessions');
|
|
21
21
|
return sessionDir;
|
|
22
22
|
}
|
|
@@ -54,10 +54,18 @@ class SessionPlugin extends Plugin {
|
|
|
54
54
|
return this;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* 获取当前工作目录(framework.getCwd() 优先, fallback 到 process.cwd())
|
|
59
|
+
*/
|
|
60
|
+
_defaultCwd() {
|
|
61
|
+
return this._framework?.getCwd?.() ?? process.cwd();
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
/**
|
|
58
65
|
* Get or create a SessionManager for a session
|
|
59
66
|
*/
|
|
60
|
-
_getSessionManager(sessionId, cwd
|
|
67
|
+
_getSessionManager(sessionId, cwd) {
|
|
68
|
+
cwd = cwd ?? this._defaultCwd();
|
|
61
69
|
if (this._sessionManagers.has(sessionId)) {
|
|
62
70
|
return this._sessionManagers.get(sessionId);
|
|
63
71
|
}
|
|
@@ -72,7 +80,7 @@ class SessionPlugin extends Plugin {
|
|
|
72
80
|
/**
|
|
73
81
|
* List all sessions for current cwd
|
|
74
82
|
*/
|
|
75
|
-
async listSessions(cwd =
|
|
83
|
+
async listSessions(cwd = this._defaultCwd()) {
|
|
76
84
|
const sessionDir = getDefaultSessionDir(cwd);
|
|
77
85
|
const sessions = [];
|
|
78
86
|
|
|
@@ -115,7 +123,7 @@ class SessionPlugin extends Plugin {
|
|
|
115
123
|
cwd: z.string().optional().describe('工作目录')
|
|
116
124
|
}),
|
|
117
125
|
execute: async (args, fw) => {
|
|
118
|
-
const cwd = args.cwd ||
|
|
126
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
119
127
|
const sessionDir = getDefaultSessionDir(cwd);
|
|
120
128
|
const manager = new SessionManager(cwd, sessionDir, undefined, true);
|
|
121
129
|
const sessionId = manager.getSessionId();
|
|
@@ -140,7 +148,7 @@ class SessionPlugin extends Plugin {
|
|
|
140
148
|
cwd: z.string().optional().describe('工作目录')
|
|
141
149
|
}),
|
|
142
150
|
execute: async (args) => {
|
|
143
|
-
const cwd = args.cwd ||
|
|
151
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
144
152
|
const manager = this._getSessionManager(args.sessionId, cwd);
|
|
145
153
|
const entries = manager.getEntries();
|
|
146
154
|
|
|
@@ -163,7 +171,7 @@ class SessionPlugin extends Plugin {
|
|
|
163
171
|
cwd: z.string().optional().describe('工作目录')
|
|
164
172
|
}),
|
|
165
173
|
execute: async (args) => {
|
|
166
|
-
const cwd = args.cwd ||
|
|
174
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
167
175
|
const sessions = await this.listSessions(cwd);
|
|
168
176
|
return {
|
|
169
177
|
success: true,
|
|
@@ -181,7 +189,7 @@ class SessionPlugin extends Plugin {
|
|
|
181
189
|
cwd: z.string().optional().describe('工作目录')
|
|
182
190
|
}),
|
|
183
191
|
execute: async (args) => {
|
|
184
|
-
const cwd = args.cwd ||
|
|
192
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
185
193
|
const manager = this._getSessionManager(args.sessionId, cwd);
|
|
186
194
|
|
|
187
195
|
if (manager.getSessionFile() && require('fs').existsSync(manager.getSessionFile())) {
|
|
@@ -203,7 +211,7 @@ class SessionPlugin extends Plugin {
|
|
|
203
211
|
limit: z.number().optional().describe('返回消息数量限制')
|
|
204
212
|
}),
|
|
205
213
|
execute: async (args) => {
|
|
206
|
-
const cwd = args.cwd ||
|
|
214
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
207
215
|
const manager = this._getSessionManager(args.sessionId, cwd);
|
|
208
216
|
const context = manager.buildSessionContext();
|
|
209
217
|
let messages = context.messages;
|
|
@@ -233,7 +241,7 @@ class SessionPlugin extends Plugin {
|
|
|
233
241
|
cwd: z.string().optional().describe('工作目录')
|
|
234
242
|
}),
|
|
235
243
|
execute: async (args) => {
|
|
236
|
-
const cwd = args.cwd ||
|
|
244
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
237
245
|
const manager = this._getSessionManager(args.sessionId, cwd);
|
|
238
246
|
|
|
239
247
|
try {
|
|
@@ -262,7 +270,7 @@ class SessionPlugin extends Plugin {
|
|
|
262
270
|
cwd: z.string().optional().describe('工作目录')
|
|
263
271
|
}),
|
|
264
272
|
execute: async (args) => {
|
|
265
|
-
const cwd = args.cwd ||
|
|
273
|
+
const cwd = args.cwd || this._defaultCwd();
|
|
266
274
|
const manager = this._getSessionManager(args.sessionId, cwd);
|
|
267
275
|
|
|
268
276
|
try {
|
|
@@ -307,7 +315,7 @@ class SessionPlugin extends Plugin {
|
|
|
307
315
|
/**
|
|
308
316
|
* Add message to session
|
|
309
317
|
*/
|
|
310
|
-
addMessage(sessionId, message, cwd =
|
|
318
|
+
addMessage(sessionId, message, cwd = this._defaultCwd()) {
|
|
311
319
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
312
320
|
const entryId = manager.appendMessage(message);
|
|
313
321
|
|
|
@@ -322,7 +330,7 @@ class SessionPlugin extends Plugin {
|
|
|
322
330
|
/**
|
|
323
331
|
* Get session context for LLM
|
|
324
332
|
*/
|
|
325
|
-
getSessionContext(sessionId, cwd =
|
|
333
|
+
getSessionContext(sessionId, cwd = this._defaultCwd()) {
|
|
326
334
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
327
335
|
return manager.buildSessionContext();
|
|
328
336
|
}
|
|
@@ -330,14 +338,14 @@ class SessionPlugin extends Plugin {
|
|
|
330
338
|
/**
|
|
331
339
|
* Get or create session (alias for getSessionContext)
|
|
332
340
|
*/
|
|
333
|
-
getOrCreateSession(sessionId, options = {}, cwd =
|
|
341
|
+
getOrCreateSession(sessionId, options = {}, cwd = this._defaultCwd()) {
|
|
334
342
|
return this.getSessionContext(sessionId, cwd);
|
|
335
343
|
}
|
|
336
344
|
|
|
337
345
|
/**
|
|
338
346
|
* Get specific entry
|
|
339
347
|
*/
|
|
340
|
-
getEntry(sessionId, entryId, cwd =
|
|
348
|
+
getEntry(sessionId, entryId, cwd = this._defaultCwd()) {
|
|
341
349
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
342
350
|
return manager.getEntry(entryId);
|
|
343
351
|
}
|
|
@@ -345,7 +353,7 @@ class SessionPlugin extends Plugin {
|
|
|
345
353
|
/**
|
|
346
354
|
* Get branch (path from root to leaf)
|
|
347
355
|
*/
|
|
348
|
-
getBranch(sessionId, fromId, cwd =
|
|
356
|
+
getBranch(sessionId, fromId, cwd = this._defaultCwd()) {
|
|
349
357
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
350
358
|
return manager.getBranch(fromId);
|
|
351
359
|
}
|
|
@@ -353,7 +361,7 @@ class SessionPlugin extends Plugin {
|
|
|
353
361
|
/**
|
|
354
362
|
* Append compaction entry
|
|
355
363
|
*/
|
|
356
|
-
appendCompaction(sessionId, summary, firstKeptEntryId, tokensBefore, details, cwd =
|
|
364
|
+
appendCompaction(sessionId, summary, firstKeptEntryId, tokensBefore, details, cwd = this._defaultCwd()) {
|
|
357
365
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
358
366
|
return manager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, false);
|
|
359
367
|
}
|
|
@@ -361,7 +369,7 @@ class SessionPlugin extends Plugin {
|
|
|
361
369
|
/**
|
|
362
370
|
* Append thinking level change
|
|
363
371
|
*/
|
|
364
|
-
appendThinkingLevelChange(sessionId, thinkingLevel, cwd =
|
|
372
|
+
appendThinkingLevelChange(sessionId, thinkingLevel, cwd = this._defaultCwd()) {
|
|
365
373
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
366
374
|
return manager.appendThinkingLevelChange(thinkingLevel);
|
|
367
375
|
}
|
|
@@ -369,7 +377,7 @@ class SessionPlugin extends Plugin {
|
|
|
369
377
|
/**
|
|
370
378
|
* Append model change
|
|
371
379
|
*/
|
|
372
|
-
appendModelChange(sessionId, provider, modelId, cwd =
|
|
380
|
+
appendModelChange(sessionId, provider, modelId, cwd = this._defaultCwd()) {
|
|
373
381
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
374
382
|
return manager.appendModelChange(provider, modelId);
|
|
375
383
|
}
|
|
@@ -377,7 +385,7 @@ class SessionPlugin extends Plugin {
|
|
|
377
385
|
/**
|
|
378
386
|
* Set session label
|
|
379
387
|
*/
|
|
380
|
-
setLabel(sessionId, entryId, label, cwd =
|
|
388
|
+
setLabel(sessionId, entryId, label, cwd = this._defaultCwd()) {
|
|
381
389
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
382
390
|
return manager.appendLabelChange(entryId, label);
|
|
383
391
|
}
|
|
@@ -385,7 +393,7 @@ class SessionPlugin extends Plugin {
|
|
|
385
393
|
/**
|
|
386
394
|
* Get session info (compatible with external callers)
|
|
387
395
|
*/
|
|
388
|
-
getSession(sessionId, cwd =
|
|
396
|
+
getSession(sessionId, cwd = this._defaultCwd()) {
|
|
389
397
|
const manager = this._getSessionManager(sessionId, cwd);
|
|
390
398
|
const entries = manager.getEntries();
|
|
391
399
|
return {
|
|
@@ -414,8 +422,9 @@ class SessionPlugin extends Plugin {
|
|
|
414
422
|
async _doCleanup() {
|
|
415
423
|
try {
|
|
416
424
|
const fs = require('fs');
|
|
417
|
-
const
|
|
418
|
-
|
|
425
|
+
const cwd = this._defaultCwd();
|
|
426
|
+
const sessionDir = getDefaultSessionDir(cwd);
|
|
427
|
+
|
|
419
428
|
if (!fs.existsSync(sessionDir)) {
|
|
420
429
|
return;
|
|
421
430
|
}
|
|
@@ -426,7 +435,7 @@ class SessionPlugin extends Plugin {
|
|
|
426
435
|
for (const file of files) {
|
|
427
436
|
const filePath = path.join(sessionDir, file);
|
|
428
437
|
try {
|
|
429
|
-
const manager = SessionManager.open(filePath, sessionDir,
|
|
438
|
+
const manager = SessionManager.open(filePath, sessionDir, cwd);
|
|
430
439
|
const entries = manager.getEntries();
|
|
431
440
|
|
|
432
441
|
// Clean up if exceeds maxHistoryLength
|
|
@@ -19,7 +19,7 @@ class ShellExecutorPlugin extends Plugin {
|
|
|
19
19
|
|
|
20
20
|
this.config = {
|
|
21
21
|
timeout: config.timeout || 60000,
|
|
22
|
-
workingDir: config.workingDir
|
|
22
|
+
workingDir: config.workingDir
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
this._framework = null
|
|
@@ -27,6 +27,9 @@ class ShellExecutorPlugin extends Plugin {
|
|
|
27
27
|
|
|
28
28
|
install(framework) {
|
|
29
29
|
this._framework = framework
|
|
30
|
+
if (!this.config.workingDir) {
|
|
31
|
+
this.config.workingDir = framework?.getCwd?.() ?? process.cwd()
|
|
32
|
+
}
|
|
30
33
|
return this
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -44,7 +44,7 @@ class StoragePlugin extends Plugin {
|
|
|
44
44
|
_initStorage() {
|
|
45
45
|
if (this._storageManager) return;
|
|
46
46
|
|
|
47
|
-
const baseDir = path.resolve(process.cwd(), this.config.path);
|
|
47
|
+
const baseDir = path.resolve(this._framework?.getCwd?.() ?? process.cwd(), this.config.path);
|
|
48
48
|
const filePath = path.join(baseDir, `${this.config.namespace}.jsonl`);
|
|
49
49
|
|
|
50
50
|
// 传递 compaction 配置
|
|
@@ -587,7 +587,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
587
587
|
*/
|
|
588
588
|
_loadAgentsFromDir() {
|
|
589
589
|
// 默认使用 .foliko/agents 目录
|
|
590
|
-
const agentsDir = this.config.agentsDir || path.join(process.cwd(), '.foliko', 'agents')
|
|
590
|
+
const agentsDir = this.config.agentsDir || path.join(this._framework?.getCwd?.() ?? process.cwd(), '.foliko', 'agents')
|
|
591
591
|
//log.info(' _loadAgentsFromDir called, agentsDir:', agentsDir)
|
|
592
592
|
if (!fs.existsSync(agentsDir)) {
|
|
593
593
|
//log.info(' agentsDir not found or does not exist')
|
|
@@ -254,7 +254,7 @@ class TelegramPlugin extends Plugin {
|
|
|
254
254
|
const agent = this._framework.createSessionAgent(`telegram_${chatId}`, {
|
|
255
255
|
systemPrompt: this.systemPrompt,
|
|
256
256
|
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
257
|
-
metadata: { WORK_DIR: process.cwd() }
|
|
257
|
+
metadata: { WORK_DIR: this._framework?.getCwd?.() ?? process.cwd() }
|
|
258
258
|
})
|
|
259
259
|
this._sessionAgents.set(chatId, agent)
|
|
260
260
|
|
|
@@ -477,7 +477,7 @@ class TelegramPlugin extends Plugin {
|
|
|
477
477
|
async _downloadFile(fileId, ext, subDir) {
|
|
478
478
|
const path = require('path')
|
|
479
479
|
const fs = require('fs')
|
|
480
|
-
const saveDir = path.join(process.cwd(), '.foliko', 'data', subDir)
|
|
480
|
+
const saveDir = path.join(this._framework?.getCwd?.() ?? process.cwd(), '.foliko', 'data', subDir)
|
|
481
481
|
if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true })
|
|
482
482
|
|
|
483
483
|
const fileName = `${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -63,17 +63,20 @@ class WeixinPlugin extends Plugin {
|
|
|
63
63
|
this._initialized = false
|
|
64
64
|
// 预创建的 sessionScope 监听器(避免每次消息都创建/销毁)
|
|
65
65
|
this._sessionScopes = new Map()
|
|
66
|
-
|
|
67
|
-
this.downloader=new FileDownloader({
|
|
68
|
-
retries: 3,
|
|
69
|
-
baseDir:saveDir
|
|
70
|
-
})
|
|
66
|
+
this.downloader = null
|
|
71
67
|
this.agent=null
|
|
72
68
|
this.sessionId=null
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
install(framework) {
|
|
76
72
|
this._framework = framework
|
|
73
|
+
if (!this.downloader) {
|
|
74
|
+
const saveDir = path.join(this._framework?.getCwd?.() ?? process.cwd(), '.foliko', 'data', this.name)
|
|
75
|
+
this.downloader = new FileDownloader({
|
|
76
|
+
retries: 3,
|
|
77
|
+
baseDir: saveDir
|
|
78
|
+
})
|
|
79
|
+
}
|
|
77
80
|
// 注册微信发送工具
|
|
78
81
|
this._registerTools()
|
|
79
82
|
return this
|
|
@@ -473,7 +476,7 @@ class WeixinPlugin extends Plugin {
|
|
|
473
476
|
name: `weixin_${userId}`,
|
|
474
477
|
systemPrompt: this.systemPrompt,
|
|
475
478
|
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
476
|
-
metadata: { WORK_DIR: process.cwd() }
|
|
479
|
+
metadata: { WORK_DIR: this._framework?.getCwd?.() ?? process.cwd() }
|
|
477
480
|
})
|
|
478
481
|
|
|
479
482
|
this._sessionAgents.set(userId, agent)
|
|
@@ -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
|
+
```
|
|
@@ -247,7 +247,7 @@ class Skill {
|
|
|
247
247
|
sessionId: framework?._currentSessionId || 'unknown',
|
|
248
248
|
agent: null,
|
|
249
249
|
framework: framework,
|
|
250
|
-
cwd: process.cwd(), // 工作目录,用于路径解析
|
|
250
|
+
cwd: framework?.getCwd?.() ?? process.cwd(), // 工作目录,用于路径解析
|
|
251
251
|
};
|
|
252
252
|
// 支持 argumentParser 先解析参数
|
|
253
253
|
let parsedArgs = toolArgs.command || '';
|
|
@@ -520,8 +520,9 @@ class SkillManagerPlugin extends Plugin {
|
|
|
520
520
|
let totalLoaded = 0;
|
|
521
521
|
|
|
522
522
|
// 1. 扫描 skillsDirs
|
|
523
|
+
const baseCwd = this._framework?.getCwd?.() ?? process.cwd();
|
|
523
524
|
for (const skillsDir of this._skillsDirs) {
|
|
524
|
-
const resolvedDir = path.resolve(
|
|
525
|
+
const resolvedDir = path.resolve(baseCwd, skillsDir);
|
|
525
526
|
|
|
526
527
|
if (!fs.existsSync(resolvedDir)) {
|
|
527
528
|
continue;
|
|
@@ -881,7 +881,8 @@ class WorkflowPlugin extends Plugin {
|
|
|
881
881
|
* 加载目录中的工作流定义
|
|
882
882
|
*/
|
|
883
883
|
_loadWorkflows() {
|
|
884
|
-
const
|
|
884
|
+
const cwd = this._framework?.getCwd?.() ?? process.cwd();
|
|
885
|
+
const dir = path.resolve(cwd, this._workflowsDir);
|
|
885
886
|
if (!fs.existsSync(dir)) {
|
|
886
887
|
fs.mkdirSync(dir, { recursive: true });
|
|
887
888
|
// log.info(` Created workflows directory: ${dir}`);
|
package/src/core/agent-chat.js
CHANGED
|
@@ -475,7 +475,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
475
475
|
if (!this._aiClient) {
|
|
476
476
|
throw new Error('AI client not configured.');
|
|
477
477
|
}
|
|
478
|
-
|
|
478
|
+
|
|
479
479
|
const systemPrompt = framework.getSystemPrompt();
|
|
480
480
|
//await fs.promises.writeFile(`.${sessionId}_systemPrompt.md`, systemPrompt); // 调试用:保存系统提示词
|
|
481
481
|
const tools = this._getAITools(aiTool);
|
|
@@ -580,7 +580,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
580
580
|
usage: usageData,
|
|
581
581
|
});
|
|
582
582
|
}
|
|
583
|
-
|
|
583
|
+
|
|
584
584
|
// yield usage 数据,让 UI 层能直接获取
|
|
585
585
|
yield {
|
|
586
586
|
type: 'usage',
|
|
@@ -658,7 +658,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
658
658
|
if (this._thinkingMode) {
|
|
659
659
|
delete apiOptions.temperature;
|
|
660
660
|
}
|
|
661
|
-
|
|
661
|
+
|
|
662
662
|
const agent = new ToolLoopAgent({
|
|
663
663
|
model: this._aiClient,
|
|
664
664
|
instructions: systemPrompt,
|
|
@@ -699,7 +699,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
699
699
|
messages.push(...result.response.messages);
|
|
700
700
|
const userMsg = messages[messages.length - result.response.messages.length - 1];
|
|
701
701
|
this.emit('message', { content: result.text, sessionId: sessionId, userMessage: userMsg });
|
|
702
|
-
|
|
702
|
+
|
|
703
703
|
return {
|
|
704
704
|
success: true,
|
|
705
705
|
message: cleanResponse(result.text || ''),
|
package/src/core/agent.js
CHANGED
package/src/core/constants.js
CHANGED
|
@@ -122,6 +122,9 @@ const SYSTEM_PLUGINS = new Set([
|
|
|
122
122
|
/** Agent 配置目录名 */
|
|
123
123
|
const AGENT_DIR = '.foliko';
|
|
124
124
|
|
|
125
|
+
/** 用户主目录下 Agent 配置目录名 */
|
|
126
|
+
const HOME_AGENT_DIR_NAME = '.foliko';
|
|
127
|
+
|
|
125
128
|
// ============================================================================
|
|
126
129
|
// Session 存储
|
|
127
130
|
// ============================================================================
|
|
@@ -186,6 +189,7 @@ module.exports = {
|
|
|
186
189
|
DEFAULT_PLUGIN_PRIORITY,
|
|
187
190
|
SYSTEM_PLUGINS,
|
|
188
191
|
AGENT_DIR,
|
|
192
|
+
HOME_AGENT_DIR_NAME,
|
|
189
193
|
|
|
190
194
|
// Storage
|
|
191
195
|
DEFAULT_SESSION_STORAGE_TYPE,
|