create-zhin-app 1.0.33 → 1.0.35
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-zhin-app",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Create a new zhin bot project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@types/node": "^24.3.0",
|
|
21
21
|
"@types/fs-extra": "^11.0.4",
|
|
22
22
|
"@types/inquirer": "^9.0.7",
|
|
23
|
-
"typescript": "^5.3
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": "^20.19.0 || >=22.12.0"
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
**参数语法:** `<必需:类型>`、`[可选:类型=默认值]`、`[...可变:类型]`
|
|
3
|
-
|
|
4
|
-
### 2. 中间件
|
|
5
|
-
|
|
6
|
-
```typescript
|
|
7
|
-
const { addMiddleware } = usePlugin()
|
|
8
|
-
|
|
9
|
-
// 洋葱模型:before → next → after
|
|
10
|
-
addMiddleware(async (message, next) => {
|
|
11
|
-
const start = Date.now()
|
|
12
|
-
// 前置逻辑
|
|
13
|
-
await next()
|
|
14
|
-
// 后置逻辑
|
|
15
|
-
console.log(`耗时 ${Date.now() - start}ms`)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
// 拦截消息(不调 next)
|
|
19
|
-
addMiddleware(async (message, next) => {
|
|
20
|
-
if (message.$raw.includes('广告')) {
|
|
21
|
-
await message.$recall()
|
|
22
|
-
return // 中断后续处理
|
|
23
|
-
}
|
|
24
|
-
await next()
|
|
25
|
-
})
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### 3. 事件监听
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
const plugin = usePlugin()
|
|
32
|
-
|
|
33
|
-
// 消息事件
|
|
34
|
-
plugin.on('message.receive', (message) => { /* ... */ })
|
|
35
|
-
plugin.on('message.private.receive', (message) => { /* ... */ })
|
|
36
|
-
plugin.on('message.group.receive', (message) => { /* ... */ })
|
|
37
|
-
|
|
38
|
-
// 发送前钩子(可修改发送内容)
|
|
39
|
-
plugin.root.on('before.sendMessage', (options) => {
|
|
40
|
-
// 统一在消息尾部加签名
|
|
41
|
-
options.content += '\n-- bot'
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
// 通知事件
|
|
45
|
-
plugin.on('notice.receive', (notice) => { /* ... */ })
|
|
46
|
-
plugin.on('request.receive', (request) => { /* ... */ })
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 4. 定时任务
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
import { usePlugin, Cron } from 'zhin.js'
|
|
53
|
-
const { addCron } = usePlugin()
|
|
54
|
-
|
|
55
|
-
// Cron 表达式:分 时 日 月 周
|
|
56
|
-
addCron(new Cron('0 9 * * 1-5', async () => {
|
|
57
|
-
// 工作日早上 9 点
|
|
58
|
-
}))
|
|
59
|
-
|
|
60
|
-
addCron(new Cron('*/30 * * * *', async () => {
|
|
61
|
-
// 每 30 分钟
|
|
62
|
-
}))
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 5. AI 工具(ZhinTool)
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
import { usePlugin, ZhinTool } from 'zhin.js'
|
|
69
|
-
const { addTool } = usePlugin()
|
|
70
|
-
|
|
71
|
-
addTool(new ZhinTool('my_tool')
|
|
72
|
-
.desc('工具描述(供 AI 理解)')
|
|
73
|
-
.keyword('关键词1', '关键词2') // 帮助 AI 匹配
|
|
74
|
-
.tag('分类')
|
|
75
|
-
.param('arg1', { type: 'string', description: '参数描述' }, true) // 必需
|
|
76
|
-
.param('arg2', { type: 'number', description: '可选参数' }) // 可选
|
|
77
|
-
.execute(async (args) => {
|
|
78
|
-
// AI 调用时执行
|
|
79
|
-
return { result: 'ok' }
|
|
80
|
-
})
|
|
81
|
-
.action(async (message, result) => {
|
|
82
|
-
// 用户命令调用时执行(可选)
|
|
83
|
-
return '执行结果'
|
|
84
|
-
})
|
|
85
|
-
)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### 6. 组件
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { usePlugin, defineComponent } from 'zhin.js'
|
|
92
|
-
const { addComponent } = usePlugin()
|
|
93
|
-
|
|
94
|
-
addComponent(defineComponent(async function UserCard(
|
|
95
|
-
props: { userId: string; name: string }
|
|
96
|
-
) {
|
|
97
|
-
return `👤 ${props.name} (ID: ${props.userId})`
|
|
98
|
-
}))
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### 7. 数据库
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
const { useContext } = usePlugin()
|
|
105
|
-
|
|
106
|
-
useContext('database', (db) => {
|
|
107
|
-
const users = db.models.get('users')
|
|
108
|
-
|
|
109
|
-
// CRUD
|
|
110
|
-
// await users.create({ name: 'Alice', age: 25 })
|
|
111
|
-
// const all = await users.select()
|
|
112
|
-
// await users.update({ age: 26 }, { name: 'Alice' })
|
|
113
|
-
// await users.delete({ name: 'Alice' })
|
|
114
|
-
|
|
115
|
-
return () => {
|
|
116
|
-
// 清理资源
|
|
117
|
-
}
|
|
118
|
-
})
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 8. HTTP 路由
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
const { useContext } = usePlugin()
|
|
125
|
-
|
|
126
|
-
useContext('router', (router) => {
|
|
127
|
-
router.get('/pub/my-api/status', (ctx) => {
|
|
128
|
-
ctx.body = { status: 'ok' }
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
router.post('/api/my-api/action', (ctx) => {
|
|
132
|
-
// /api/ 路径需要 Token 认证
|
|
133
|
-
ctx.body = { success: true }
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### 9. Web 控制台页面
|
|
139
|
-
|
|
140
|
-
**服务端注册入口:**
|
|
141
|
-
```typescript
|
|
142
|
-
import path from 'node:path'
|
|
143
|
-
const { useContext } = usePlugin()
|
|
144
|
-
|
|
145
|
-
useContext('web', (web) => {
|
|
146
|
-
const entry = path.resolve(import.meta.dirname, '../client/index.tsx')
|
|
147
|
-
const dispose = web.addEntry(entry)
|
|
148
|
-
return dispose
|
|
149
|
-
})
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**客户端 client/index.tsx:**
|
|
153
|
-
```tsx
|
|
154
|
-
import { addPage } from '@zhin.js/client'
|
|
155
|
-
|
|
156
|
-
addPage({
|
|
157
|
-
key: 'my-plugin',
|
|
158
|
-
path: '/plugins/my-plugin',
|
|
159
|
-
title: '我的插件',
|
|
160
|
-
icon: <span>📦</span>,
|
|
161
|
-
element: <MyPage />
|
|
162
|
-
})
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### 10. Context 注册(服务型插件)
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const plugin = usePlugin()
|
|
169
|
-
|
|
170
|
-
// 注册异步 Context
|
|
171
|
-
plugin.provide({
|
|
172
|
-
name: 'myService',
|
|
173
|
-
description: '我的服务',
|
|
174
|
-
mounted: async (p) => {
|
|
175
|
-
const service = await createService()
|
|
176
|
-
return service
|
|
177
|
-
},
|
|
178
|
-
dispose: async (service) => {
|
|
179
|
-
await service.cleanup()
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
// 类型声明
|
|
184
|
-
declare module 'zhin.js' {
|
|
185
|
-
namespace Plugin {
|
|
186
|
-
interface Contexts {
|
|
187
|
-
myService: MyServiceType
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## 编码规范
|
|
194
|
-
|
|
195
|
-
1. **导入路径**:TS 文件间使用 `.js` 扩展名 → `import { foo } from './bar.js'`
|
|
196
|
-
2. **框架 API**:统一从 `zhin.js` 导入
|
|
197
|
-
3. **usePlugin()**:只在模块顶层调用,不在 async 函数内
|
|
198
|
-
4. **清理资源**:`useContext()` 回调返回清理函数
|
|
199
|
-
5. **参数读取**:从 `result.params` 读取,不自行解析 `message.$raw`
|
|
200
|
-
6. **类型扩展**:通过 `declare module 'zhin.js'` 扩展 `Plugin.Contexts`
|
|
201
|
-
|
|
202
|
-
## 检查清单
|
|
203
|
-
|
|
204
|
-
- [ ] 功能代码放在正确的子目录(commands/、middlewares/ 等)
|
|
205
|
-
- [ ] 入口文件只负责装配,不堆业务逻辑
|
|
206
|
-
- [ ] 所有注册的资源有对应的清理路径
|
|
207
|
-
- [ ] useContext 回调正确返回清理函数
|
|
208
|
-
- [ ] 新增命令有参数类型声明和描述
|
|
209
|
-
- [ ] 新增功能有对应测试用例
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: skill-creator
|
|
3
|
-
description: Create or update AgentSkills. Use when designing skill structure, writing SKILL.md, configuring tool associations, or organizing skill resources.
|
|
4
|
-
keywords: ["skill", "SKILL.md", "create skill", "skill development"]
|
|
5
|
-
tags: ["development", "skill-creation", "documentation"]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Skill Creator
|
|
9
|
-
|
|
10
|
-
Guide for creating high-quality AgentSkills.
|
|
11
|
-
|
|
12
|
-
## What Skills Provide
|
|
13
|
-
|
|
14
|
-
1. **Specialized workflows** — multi-step processes for specific domains
|
|
15
|
-
2. **Tool integration** — usage instructions for file formats or APIs
|
|
16
|
-
3. **Domain expertise** — company-specific knowledge, data structures, business logic
|
|
17
|
-
4. **Bundled resources** — scripts, reference docs, and assets for complex tasks
|
|
18
|
-
|
|
19
|
-
## Core Principles
|
|
20
|
-
|
|
21
|
-
- **Concise**: Tell the model *what to do*, not *how to be an AI*
|
|
22
|
-
- **Secure**: If the skill uses `exec`, never allow arbitrary command injection from untrusted input
|
|
23
|
-
- **Testable**: Verify AI understands and executes as expected
|
|
24
|
-
- **Context-aware**: Full content loads only on `activate_skill`, so `description` must be clear enough for matching
|
|
25
|
-
|
|
26
|
-
## SKILL.md Anatomy
|
|
27
|
-
|
|
28
|
-
### 1. Frontmatter (YAML header)
|
|
29
|
-
|
|
30
|
-
```yaml
|
|
31
|
-
---
|
|
32
|
-
name: skill-name # Required, kebab-case
|
|
33
|
-
description: What it does # Required — AI matches skills by this field
|
|
34
|
-
keywords: [keyword1, keyword2] # Optional, improves matching
|
|
35
|
-
tags: [category] # Optional, for grouping
|
|
36
|
-
tools: [tool_name] # Optional, associated tools
|
|
37
|
-
compatibility:
|
|
38
|
-
os: [darwin, linux]
|
|
39
|
-
deps: [git, node]
|
|
40
|
-
---
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Key: `name` + `description` are the only required fields. AI decides activation based on these alone.
|
|
44
|
-
|
|
45
|
-
### 2. Body (Markdown)
|
|
46
|
-
|
|
47
|
-
Loaded only after `activate_skill` is called. Recommended structure:
|
|
48
|
-
|
|
49
|
-
```markdown
|
|
50
|
-
# Skill Name
|
|
51
|
-
|
|
52
|
-
Brief purpose.
|
|
53
|
-
|
|
54
|
-
## Workflow
|
|
55
|
-
|
|
56
|
-
### Step 1: Title
|
|
57
|
-
Instructions...
|
|
58
|
-
|
|
59
|
-
### Step 2: Title
|
|
60
|
-
Instructions...
|
|
61
|
-
|
|
62
|
-
## Notes
|
|
63
|
-
- Edge cases, security, etc.
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Creating a Skill
|
|
67
|
-
|
|
68
|
-
1. **Scope**: What problem does it solve? Who uses it? What tools are needed?
|
|
69
|
-
2. **Create directory**: `mkdir -p skills/your-skill-name`
|
|
70
|
-
3. **Write SKILL.md**: Clear frontmatter + actionable body
|
|
71
|
-
4. **Test**: Discovery → Activation → Full execution
|
|
72
|
-
5. **Iterate**: Refine description/keywords based on real usage
|
|
73
|
-
|
|
74
|
-
## Best Practices
|
|
75
|
-
|
|
76
|
-
### Good description
|
|
77
|
-
```yaml
|
|
78
|
-
description: Database migration tool. Use when creating, applying, or rolling back migrations. Supports SQLite, MySQL, PostgreSQL.
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Bad description
|
|
82
|
-
```yaml
|
|
83
|
-
description: Database operations
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Security for exec-based skills
|
|
87
|
-
|
|
88
|
-
```markdown
|
|
89
|
-
## Security
|
|
90
|
-
- Only operate within the project directory
|
|
91
|
-
- Confirm with user before any destructive action
|
|
92
|
-
- On permission error, prompt user to run manually
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Multi-file Skills
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
skills/your-skill/
|
|
99
|
-
├── SKILL.md
|
|
100
|
-
├── examples/
|
|
101
|
-
├── scripts/
|
|
102
|
-
└── references/
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Reference sub-files from SKILL.md as needed.
|
|
106
|
-
|
|
107
|
-
## Skill Locations
|
|
108
|
-
|
|
109
|
-
- `<project>/skills/` — project-level skills
|
|
110
|
-
- `<project>/data/skills/` — user-defined skills
|
|
111
|
-
|
|
112
|
-
## FAQ
|
|
113
|
-
|
|
114
|
-
**Q: How long should a skill be?**
|
|
115
|
-
A: Body should be 500–2000 lines. Too short lacks guidance; too long hurts comprehension. Split complex flows into multiple skills.
|
|
116
|
-
|
|
117
|
-
**Q: When is a skill activated?**
|
|
118
|
-
A: Agent evaluates `description` + `keywords` against user messages. High-confidence matches trigger `activate_skill`.
|
|
119
|
-
|
|
120
|
-
**Q: Can skills call other skills?**
|
|
121
|
-
A: Not directly, but you can suggest "activate skill X if you need feature Y" in the instructions.
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: summarize
|
|
3
|
-
description: "Summarize web articles, documents, or long text into concise key points. Supports brief summary, bullet points, and structured formats."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Summarize
|
|
7
|
-
|
|
8
|
-
Condense URLs, local files, or user-provided long text into readable summaries.
|
|
9
|
-
|
|
10
|
-
## Summarize a URL
|
|
11
|
-
|
|
12
|
-
1. Use `web_fetch` to retrieve the page content
|
|
13
|
-
2. Extract and summarize key points
|
|
14
|
-
|
|
15
|
-
## Summarize a Local File
|
|
16
|
-
|
|
17
|
-
1. Use `read_file` to load the content
|
|
18
|
-
2. Summarize it
|
|
19
|
-
|
|
20
|
-
## Output Formats
|
|
21
|
-
|
|
22
|
-
- **Brief**: 2–3 sentences capturing the essence
|
|
23
|
-
- **Key Points**: 5–7 bullet points with important figures and quotes
|
|
24
|
-
- **Structured**: Sections — TL;DR, key findings, details, action items
|
|
25
|
-
- **Report**: For business/technical docs — overview, findings, recommendations, next steps
|
|
26
|
-
|
|
27
|
-
## Notes
|
|
28
|
-
|
|
29
|
-
- Default to the source language; translate if the user specifies a language
|
|
30
|
-
- Preserve important numbers, dates, and names
|
|
31
|
-
- For long content, summarize in sections and note the content type (news, research, blog, etc.)
|