@swifty.js/swifty 0.0.1 → 0.0.2

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/docs/ch11.md DELETED
@@ -1,163 +0,0 @@
1
- # Skill
2
-
3
- skill 是写给 AI 的 SOP (Standard Operation Procedure)
4
-
5
- 背景: ch10 的 Slash Command 中, prompt 类型的命令可以将硬编码的代码审查 prompt 发送给 LLM, 不独立、不可更新、不可移植
6
-
7
- 1. skill 是独立、可 (热) 更新、可移植
8
- 2. 可以显式指定 skill, 提高 LLM 的准确率
9
- 3. skill 可以携带资产:
10
- - SKILL.md: SOP
11
- - API reference、docs、example、scripts
12
- 4. 执行模式: inline 或 fork
13
- 5. 参数 $ARGUMENTS
14
-
15
- ## Frontmatter
16
-
17
- ```md
18
- ---
19
- name: skill-creator <!-- 不能和 Slash Comment 重名 -->
20
- description: Create new skills...
21
- allowed_tools: [] <!-- 可选的, 指定 skill 的工具白名单 -->
22
- mode: skill 执行模式, inline 或 fork
23
- model: 可选的, 指定 skill 使用的模型
24
- fork_context: 可选的, 仅在 mode: fork 时生效, 决定携带的上下文; 可以是 full 对话摘要、recent 最近的 5 条消息、none 不携带对话历史; inline 模式共享对话历史, fork_context 字段会被静默忽略 <!-- 源码: src/skills/catalog.ts -->
25
- ---
26
- ```
27
-
28
- ## skills 目录, 优先级从高到低
29
-
30
- 1. 项目级: ${workDir}/.swifty/skills/
31
- 2. 用户级: ~/.swifty/skills
32
- 3. 内置级: Swifty 内置的 skills
33
-
34
- ## skill 执行模式
35
-
36
- ### inline 模式
37
-
38
- 默认执行模式, 内联 skill 的 prompt 到用户消息, 参与 agent loop
39
-
40
- ### fork 模式
41
-
42
- 开启新对话, 调用 LLM API, 只将 LLM API 的响应返回给主对话, 不参与 agent loop
43
-
44
- fork 模式的实现:
45
-
46
- - 开启新对话
47
- - 根据 skill.fork_context
48
- - full 压缩上下文, 生成对话摘要, 携带对话摘要
49
- - recent 携带最近的 5 条消息
50
- - none 不携带对话历史 (默认)
51
- - 如果用户传递参数, 则替换或追加 skill prompt 中的参数 $ARGUMENTS
52
- - 使用 skill.model 创建一个临时 Agent, 拿到 LLM API 的响应
53
- - 将 LLM API 的响应作为 assistant 消息返回给主对话
54
-
55
- ## `$ARGUMENTS`
56
-
57
- ```md
58
- 请审查以下代码变更
59
- $ARGUMENTS
60
- 如果没有指定代码审查范围, 请审查 `git diff` 的所有变更
61
- ```
62
-
63
- 用户输入 `/review 重点关注类型问题` (replaceAll 字符串替换)
64
-
65
- ```md
66
- 请审查以下代码变更
67
- 重点关注类型问题
68
- 如果没有指定代码审查范围, 请审查 `git diff` 的所有变更
69
- ```
70
-
71
- fork 模式
72
-
73
- 如果用户有输入, 则在 prompt 末尾追加 `\n\nARGUMENTS: ${args}`
74
-
75
- ## 自动注册为 Slash Command (prompt 命令)
76
-
77
- skill 加载后自动注册为 Slash Command (prompt 命令)
78
-
79
- 热加载: 每次使用 skill 时都重新读文件
80
-
81
- ## 意图识别: 让 Agent 自己选择 skill
82
-
83
- ### 两阶段加载
84
-
85
- 第一阶段: 轻量启动
86
-
87
- Swifty 启动时, 扫描 skills 目录, 加载所有 skill 的 frontmatter 元信息 (name, description), 不加载完整 prompt 正文, 将 skill 元信息注入到 system prompt, 告诉 LLM 有哪些 skill
88
-
89
- 第二阶段: 按需加载
90
-
91
- LLM 判断用户意图匹配某个 skill 时, 调用 LoadSkill 工具
92
-
93
- LoadSkill 工具根据该 skill 的 name, 读取 SKILL.md 的 SOP 作为工具调用结果返回, LLM 在下一轮 agent loop turn 中可以看到该 skill 的 SOP
94
-
95
- ### LoadSkill
96
-
97
- - properties: name
98
- - metadata: 只读、非破坏性、分类 read
99
-
100
- ```js
101
- function LoadSkill(name) {
102
- const skill = this.catalog.get(name);
103
- if (!skill) {
104
- return {
105
- output: `Skill '${name}' not found. Available skills: ${available}`,
106
- isError: true,
107
- };
108
- }
109
- const body = runInline(skill, "", this.host);
110
- // 记录已激活
111
- // 用于上下文压缩, CLI 侧压缩恢复, 恢复关键上下文 skills
112
- return {
113
- output: `Skill '${name}' activated.\n\n${body}`,
114
- isError: false,
115
- };
116
- }
117
- ```
118
-
119
- ## 渐进式披露
120
-
121
- 渐进式披露: Agent 启动时, 扫描 skills 目录, 加载所有 skill 的 frontmatter 元信息 (name, description), 选择压力很低, LLM 判断用户意图匹配某个 skill 时, 才加载完整 SOP, 注意力集中在当前任务
122
-
123
- ## 目录型 skill
124
-
125
- - 单文件 skill 只有 SOP
126
- - 目录型 skill 可以携带资产: SOP、API reference、docs、example、scripts, 整套技能方便打包、移植
127
-
128
- ```txt
129
- $ tree ~/.claude/skills/skill-creator
130
- .claude/skills/skill-creator
131
- ├── agents
132
- │   └── xxx.md
133
- ├── assets
134
- │   └── xxx.html
135
- ├── docs
136
- │   └── ...
137
- ├── example
138
- │   └── xxx.md
139
- ├── LICENSE.txt
140
- ├── references
141
- │   └── xxx.md
142
- ├── scripts
143
- │   ├── xxx.py
144
- │   └── yyy.py
145
- └── SKILL.md
146
- ```
147
-
148
- ## 完整流程
149
-
150
- ```txt
151
- 1. 启动时扫描 skills 目录, 加载所有 skill 的 frontmatter 元信息 (name, description), 轻量启动
152
- 2. 将 skill 元信息注入到 system prompt, 告诉 LLM 有哪些 skill
153
- 3. skill 加载后自动注册为 Slash Command (prompt 命令)
154
- 4. 用户输入: 做一个 react 计数器 app
155
- 5. 意图识别: LLM 判断匹配 react-best-practices skill
156
- 6. Agent 请求调用工具 LoadSkill("react-best-practices")
157
- 7. SOP 作为工具调用结果返回, 注入到 messages
158
- 8. LLM 按 SKILL.md 的 critical/high/medium/low 规则、进行开发
159
- ```
160
-
161
- - MCP 负责接入
162
- - 工具调用负责调用
163
- - skill 负责编排工作流
package/docs/ch12.md DELETED
@@ -1,289 +0,0 @@
1
- # Hook
2
-
3
- 在 Agent 的生命周期事件上挂载自动化动作
4
-
5
- ## Hook 的三要素
6
-
7
- - 事件
8
- - 条件, 可以省略, 如果省略则无条件触发
9
- - 动作
10
-
11
- 配置:
12
-
13
- - ~/.swifty/config.yaml
14
- - ${projectRoot}/.swifty/config.yaml
15
- - ${projectRoot}/.swifty/config.local.yaml
16
-
17
- 3 个配置文件按顺序加载, 后加载的 hook 配置不会覆盖先加载的 hook 配置, 多个 hook 配置会被拼接; 一个事件有多个 hook 时, 按 yaml 中的顺序逐个执行
18
-
19
- <!-- 源码: src/config/config.ts HookConfigSchema -->
20
-
21
- ```yaml
22
- hooks:
23
- - event: post_tool_use
24
- condition: tool == "WriteFile"
25
- action:
26
- type: command
27
- command: "prettier -w $SWIFTY_FILE_PATH"
28
- ```
29
-
30
- <!-- 源码: src/hooks/hooks.ts 环境变量 SWIFTY_EVENT, SWIFTY_TOOL, SWIFTY_FILE_PATH -->
31
-
32
- 每当 Agent 调用 WriteFile 工具写文件后, 自动 format 代码
33
-
34
- - 事件: post_tool_use
35
- - 条件: tool == "WriteFile"
36
- - 动作: format 代码
37
-
38
- ## 事件
39
-
40
- <!-- 源码: src/hooks/hooks.ts EventName 类型定义 -->
41
-
42
- ### 会话级
43
-
44
- - session_start 会话开始时触发
45
- - session_end 会话结束时触发
46
-
47
- ### Agent Loop 轮次级
48
-
49
- - turn_start 用户发送新消息时触发, 一轮对话开始
50
- - turn_end LLM 退出 agent loop 时触发, 一轮对话结束
51
- - LLM 决定退出循环
52
- - 超过最大循环次数后强制退出循环
53
- - 用户按 esc 退出循环
54
-
55
- ### 工具级
56
-
57
- - pre_tool_use 工具调用前触发, 可以拦截工具调用
58
- - post_tool_use 工具调用后触发
59
-
60
- ### 消息级
61
-
62
- - pre_send 用户消息发送给 LLM API 前触发
63
- - post_receive 收到 LLM API 响应后触发
64
-
65
- ### 系统级
66
-
67
- - startup: Swifty 启动时触发
68
- - shutdown: Swifty 退出时触发
69
- - error: Swifty 发生错误时触发
70
- - compact: 上下文压缩时触发
71
- - permission_request: 权限请求时触发
72
- - file_change: 文件被修改时触发
73
- - command_execute: Slash Command 执行时触发
74
-
75
- ## pre_tool_use
76
-
77
- ```yaml
78
- hooks:
79
- - event: pre_tool_use
80
- condition: 'tool == "WriteFile" && path == "pnpm-lock.yaml"'
81
- action:
82
- type: command
83
- command: "echo 'Never manually edit pnpm-lock.yaml'"
84
- reject: true
85
-
86
- - event: pre_tool_use
87
- condition: 'tool == "Bash" && command =~ "rm\\s+-rf\\s+\\/"'
88
- action:
89
- type: command
90
- command: "echo 'Dangerous rm command detected'"
91
- reject: true
92
- ```
93
-
94
- <!-- 源码: src/hooks/hooks.ts -->
95
-
96
- reject: true 是 pre_tool_use 事件的特殊字段, reject: true 时, 工具调用被拒绝, CLI 将 hook 抛出的错误 reason 作为工具调用结果 `{ content: reason, isError: true }` 返回给 LLM, LLM 调整策略
97
-
98
- pre_tool_use 事件的动作默认同步执行 (`async: false`)、阻塞等待返回值, 检查是否拒绝
99
-
100
- <!-- 源码: src/hooks/hooks.ts reject 仅在 pre_tool_use 事件中断循环 -->
101
-
102
- ## 条件
103
-
104
- <!-- 源码: src/hooks/hooks.ts evaluateSingleCondition -->
105
-
106
- - == 等于, equal match
107
- - != 不等于, not equal match
108
- - =~ 正则匹配, regex match, 语法: `变量 =~ "正则表达式"`
109
- - =\* glob (filesystem 通配符语法) 匹配, glob match
110
- - 支持 && || 组合多个条件
111
-
112
- 变量名:
113
-
114
- - tool: 匹配 ctx.toolName
115
- - event: 匹配 ctx.event
116
- - file_path 匹配 ctx.filePath
117
- - message 匹配 ctx.message
118
- - 其他变量名在工具参数字典 ctx.args 中按 key 查找, 例如 path 匹配 args.path, command 匹配 args.command
119
-
120
- <!-- 源码: src/hooks/hooks.ts getContextValue -->
121
-
122
- ## 动作
123
-
124
- <!-- 源码: src/hooks/hooks.ts executeAction -->
125
-
126
- 四种动作执行器
127
-
128
- - command: 执行 shell 命令
129
- - prompt: 注入 system prompt
130
- - http: 发送 http 请求
131
- - agent: 启动 subagent
132
-
133
- ### command
134
-
135
- ```yaml
136
- action:
137
- type: command
138
- command: "prettier -w $SWIFTY_FILE_PATH"
139
- ```
140
-
141
- <!-- 源码: src/hooks/hooks.ts executeAction command 分支 -->
142
-
143
- - 执行 shell 命令, shell 命令执行前会设置环境变量 SWIFTY_EVENT, SWIFTY_TOOL, SWIFTY_FILE_PATH
144
- - 启动一个 shell 子进程执行命令, 拿到输出和退出码
145
- - timeout 字段指定命令超时时间
146
-
147
- <!-- 源码: src/hooks/hooks.ts timeout 硬编码 30000ms, HookConfigSchema 没有 timeout 字段 -->
148
-
149
- ### prompt
150
-
151
- <!-- 源码: src/hooks/hooks.ts executeAction prompt 分支 -->
152
-
153
- ```yaml
154
- action:
155
- type: prompt
156
- prompt: 先阅读 ARCHITECTURE.md 了解项目架构, 再开始工作
157
- ```
158
-
159
- - 使用 `<system-reminder />` 标签包裹, 作为一条 user 消息追加到对话历史, LLM 在下一轮 agent loop turn 请求中可以读到
160
- - system prompt 不变、prompt cache 前缀不变
161
- - 可以只在指定项目中注入, 或者只在第一轮对话中注入 (使用执行控制 `once: true`)
162
-
163
- ### http
164
-
165
- <!-- 源码: src/hooks/hooks.ts executeAction http 分支 -->
166
-
167
- ```yaml
168
- action:
169
- type: http
170
- url: https://api.example.com
171
- method: POST
172
- ```
173
-
174
- <!-- 源码: src/hooks/hooks.ts http 请求体固定为 JSON.stringify(context), HookConfigSchema 没有 body 字段 -->
175
-
176
- 发送 http 请求, 请求体是 HookContext 的 json 字符串 (包含 event, toolName, args, filePath, message 字段), 场景有: 发送 App 通知、日志收集、监控报警
177
-
178
- ### agent
179
-
180
- ```yaml
181
- action:
182
- type: agent
183
- prompt: "review 刚刚写入的 $SWIFTY_FILE_PATH 是否有安全漏洞"
184
- ```
185
-
186
- <!-- 源码: src/hooks/hooks.ts executeAction agent 分支 -->
187
-
188
- 启动 subagent, 见 ch13
189
-
190
- ## hook 执行控制
191
-
192
- - once: 只执行一次
193
- - async: 异步执行, 注意: pre_tool_use 事件的动作默认同步执行 (`async: false`)、阻塞等待返回值, 检查是否拒绝
194
- - on_error: hook 执行错误时的行为, 枚举值:
195
- - ignore: 默认, 忽略错误
196
- - fail 标记 hook 执行错误
197
- - reject 拒绝工具调用
198
- - 错误捕获: hook 执行错误不能中断 Agent
199
-
200
- <!-- 源码: src/hooks/hooks.ts on_error 处理逻辑 -->
201
-
202
- ```yaml
203
- hooks:
204
- - event: session_start
205
- action:
206
- type: prompt
207
- prompt: "使用英文注释"
208
- once: true
209
-
210
- - event: post_tool_use
211
- condition: tool == "WriteFile"
212
- action:
213
- type: http
214
- url: https://api.example.com
215
- method: POST
216
- async: true
217
- ```
218
-
219
- ## 上下文变量
220
-
221
- 事件触发时, 创建一个 HookContext 对象, 包含事件的上下文信息:
222
-
223
- <!-- 源码: src/hooks/hooks.ts HookContext 接口定义 -->
224
-
225
- HookContext 属性:
226
-
227
- - event 事件名 $SWIFTY_EVENT
228
- - toolName 工具名 $SWIFTY_TOOL
229
- - args 工具参数字典
230
- - filePath 文件路径 $SWIFTY_FILE_PATH
231
- - message 消息内容
232
-
233
- <!-- 源码: src/hooks/hooks.ts 暴露 SWIFTY_EVENT, SWIFTY_TOOL, SWIFTY_FILE_PATH 环境变量 -->
234
-
235
- ## 示例配置
236
-
237
- ```yaml
238
- hooks:
239
- - id: auto-format
240
- event: post_tool_use
241
- condition: 'tool == "WriteFile" && path =* "*.ts"' # glob match
242
- action:
243
- type: command
244
- command: "prettier -w $SWIFTY_FILE_PATH"
245
-
246
- - id: readonly-vendor
247
- event: pre_tool_use
248
- condition: 'tool == "WriteFile" && path =* "node_modules/*"'
249
- action:
250
- type: command
251
- command: "echo 'Never manually edit node_modules/*'"
252
- reject: true
253
-
254
- - id: project-context
255
- event: session_start
256
- action:
257
- type: prompt
258
- prompt: |
259
- - Tech Stack: TypeScript, React, Ink, Zod, Vitest, ESLint, oxfmt, pnpm workspace deps: `@swifty.js/glob-addon`, `@swifty.js/glob-wasm`.
260
- - Code Conventions: Enforced by `eslint.config.js`.
261
- - Reference: Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
262
- once: true
263
-
264
- - id: block-dangerous-rm
265
- event: pre_tool_use
266
- condition: 'tool == "Bash" && command =~ "rm\\s+-rf\\s+\\/"'
267
- action:
268
- type: command
269
- command: "echo 'Dangerous rm command detected'"
270
- reject: true
271
-
272
- - id: app-notify
273
- event: post_tool_use
274
- condition: 'tool == "WriteFile"'
275
- action:
276
- type: http
277
- url: https://api.example.com
278
- method: POST
279
- async: true
280
- ```
281
-
282
- ## 配置校验
283
-
284
- - reject 字段只能用于 pre_tool_use 事件
285
- - async 字段不能用于 pre_tool_use 事件
286
- - command 动作必须有 command 字段
287
- - prompt 动作必须有 prompt 字段
288
- - http 动作必须有 url 字段
289
- - agent 动作必须有 prompt 字段或 command 字段