zhiqu 0.1.0 → 0.2.0

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/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # zhiqu
2
2
 
3
3
  知趣·象限([zhiqustudy.com](https://zhiqustudy.com))的命令行 coding agent。**循环和工具跑在你的电脑上**(读、搜、改文件、跑命令都在本机的工作区里),
4
- **模型、登录和你的数据(任务、知识 Wiki、记忆)在服务器上**(模型的 API Key 只存在服务器,命令行拿不到)。
4
+ **模型、登录和你的数据(任务、知识 Wiki、记忆)在服务器上**(默认情况下模型的 API Key 只存在服务器,命令行拿不到;直连模式例外,密钥在本机)。
5
+
6
+ 不想经过服务器也行:**直连自己的模型**(内网 vLLM、公司网关、任何 OpenAI 兼容或 Anthropic 接口),配一次就不用登录,
7
+ 见下面[「直连自己的模型」](#直连自己的模型内网也行不用登录)。
5
8
 
6
9
  ## 安装
7
10
 
@@ -34,8 +37,11 @@ npm install -g zhiqu --registry=https://registry.npmmirror.com
34
37
  zhiqu
35
38
  ```
36
39
 
37
- 默认连的是 `https://zhiqustudy.com`。用本机的知趣·象限桌面应用:`zhiqu login --server http://127.0.0.1:47615`;
38
- 自己部署的服务器同理。
40
+ 默认连的是 `https://zhiqustudy.com`,Mac 上的知趣·象限桌面应用打开的也是它(同一个账号、同一份数据)。
41
+ 连本机的 Windows 桌面应用(还带着后端):`zhiqu login --server http://127.0.0.1:47615`;自己部署的服务器同理。
42
+
43
+ 不想注册、也不想让请求经过服务器:跳过上面两步,直接 `zhiqu model set --url … --name …` 连自己的模型,
44
+ 见[「直连自己的模型」](#直连自己的模型内网也行不用登录)。
39
45
 
40
46
  ## 三档权限
41
47
 
@@ -47,7 +53,7 @@ npm install -g zhiqu --registry=https://registry.npmmirror.com
47
53
 
48
54
  三档的安全规则一样,`auto` 免的是「问」,不是「规则」:
49
55
 
50
- - **系统沙箱**(macOS 的 Seatbelt、Linux 的 bubblewrap):命令只能写工作区(`.git`、`.zhiqu` 除外),默认断网,读不到家目录里的东西。
56
+ - **系统沙箱**(macOS 的 Seatbelt、Linux 的 bubblewrap):命令只能写工作区(`.git`、`.zhiqu` 除外),读不到家目录里的东西;联网默认开(`--sandbox-network off` 全断、`local` 只连本机)。
51
57
  没有沙箱的平台(Windows)退回白名单:只能跑认得的命令,不接受行内代码。
52
58
  - 路径不出工作区(`../`、绝对路径、中间某段是指向外面的软链都拒);不读 `.env`、`*.pem`、`id_rsa` 这类文件。
53
59
  - 改一个已存在的文件之前必须先读过它;读过之后被你在编辑器里改了就要重读。
@@ -63,20 +69,24 @@ zhiqu --resume [id] 挑一段会话接着做
63
69
  zhiqu --goal "目标" 一直做到目标达成并核对通过
64
70
  zhiqu exec "要做的事" 给脚本和 CI 用:stdout 只有结论,默认只读(要写给 --mode auto);--json、--output-schema、-o
65
71
  zhiqu review [说明] 只读地审查改动,按 P0–P3 列出真问题
66
- zhiqu login [--server URL] 登录(浏览器里确认);--token zqp_… 用网页里签的个人访问令牌
72
+ zhiqu login [--server URL] 登录(浏览器里确认);--token zqp_… 用网页里签的个人访问令牌;登录完说一句有没有新版本
73
+ zhiqu model 看模型走服务器网关还是直连;`set` 配直连、`off` 切回服务器(见下面)
74
+ zhiqu update 更新到 npm 上的最新版(npm 全局装的才替你装;没权限不替你 sudo)
67
75
  zhiqu logout | whoami | models | help | --version
68
76
  zhiqu mcp 当 MCP 服务器,见下面
69
77
  ```
70
78
 
71
79
  常用选项:`--mode plan|ask|auto`、`--model <id>`、`--effort instant|medium|high|extra|max`(思考档位)、
72
- `--sandbox-network off|local|on`(沙箱里的命令能不能联网,默认 off)。完整的用 `zhiqu help` 看。
80
+ `--sandbox-network on|local|off`(沙箱里的命令能不能联网,默认 on;`local` 只能连本机,`off` 全断)。完整的用 `zhiqu help` 看。
73
81
 
74
82
  会话里输入 `/` 会弹出命令菜单:`/goal`、`/mode`、`/model`、`/effort`、`/window`、`/ps`、`/stop`、`/resume`、`/new`、`/compact`、
75
- `/init`、`/review`、`/skills`、`/mcp`、`/system`、`/verbose`、`/usage`、`/help`、`/exit`。↑↓ 选、回车执行、Tab 补全、Esc 关掉。
83
+ `/init`、`/review`、`/agents`、`/skills`、`/mcp`、`/system`、`/verbose`、`/usage`、`/help`、`/exit`。↑↓ 选、回车执行、Tab 补全、Esc 关掉。
76
84
 
77
85
  本地工具:`list_files`、`read_file`、`search`(正则、按文件类型过滤、跳过 `.gitignore` 的)、`write_file`、`edit_file`、
78
86
  `apply_patch`(一次改多个文件,全成或全不成)、`delete_file`(挪进 `.zhiqu/trash/`,能恢复)、`shell`(在系统沙箱里;
79
- 可以放到后台,用 `/ps`、`/stop` 管)。行尾加 `\` 换行接着输入;Ctrl+C 打断正在做的事;它干活时可以接着打下一句,回车排队。
87
+ 可以放到后台,用 `/ps`、`/stop` 管)。
88
+
89
+ **子 agent**:说「派两个 agent 分头查」这类话时,它能派子 agent 在单独的上下文里并行做事,做完只交回一段结论 —— explorer 只读(读、搜、跑只读命令),worker 和主 agent 同一档;子 agent 不能再派子 agent。子 agent 也能起后台命令,但只管得了自己起的,它停下之前这些命令会先停掉(要一直跑的 dev server 让主 agent 起)。同时最多 3 个、一句话最多 8 个、一句话里子 agent 一共最多花 200 万 token(到了就全停)。`/agents` 看派过的、`/agents <编号>` 看结论。`~/.zhiqu/config.json` 里 `"agents": false` 关掉,或者 `"agents": { "maxConcurrent": 2 }` 这样调(项目里的 `.zhiqu/settings.json` 调不了,克隆下来的仓库不能替你多花钱)。行尾加 `\` 换行接着输入;Ctrl+C 打断正在做的事;它干活时可以接着打下一句,回车排队。
80
90
 
81
91
  ## goal 模式
82
92
 
@@ -92,7 +102,7 @@ zhiqu --goal "修好所有失败的测试" --mode auto < /dev/null 无人值
92
102
  ## 文件放在哪
93
103
 
94
104
  ```
95
- ~/.zhiqu/config.json 服务器、令牌(600 权限)、默认模型、默认档位
105
+ ~/.zhiqu/config.json 服务器、令牌(600 权限)、默认模型、默认档位、直连配置(direct)
96
106
  ~/.zhiqu/system.md 系统内容:第一次运行时写一份,之后以它为准,可以改
97
107
  ~/.zhiqu/ZHIQU.md 你自己的通用约定(每个项目都会带上)
98
108
  ~/.zhiqu/skills/<名字>/SKILL.md
@@ -110,6 +120,12 @@ zhiqu --goal "修好所有失败的测试" --mode auto < /dev/null 无人值
110
120
  任务对得上时模型用 `load_skill` 取正文;正文里提到的参考文件、脚本,真用到时再按需取。
111
121
  所以 `SKILL.md` 可以写得短,把大段细节放进同目录的其它文件。
112
122
 
123
+ **内置 skill**:包里带着八个常用的 —— `debug`(排障)、`test-first`(先写测试)、`refactor`(安全重构)、
124
+ `commit`(整理提交)、`code-audit`(体检现有代码)、`security-review`(安全审查)、`perf`(性能)、
125
+ `codebase-tour`(读陌生代码库)。`/skills` 里标「内置」。想改就放一份同名的到项目级或用户级目录,整个盖掉,不用改包。
126
+
127
+ 审查**一次改动**用 `/review`(或者 `zhiqu review`),那不是 skill —— 它在单独的上下文里取 diff、按固定结构出结论。`code-audit` 管的是另一半:没有 diff、只想问「这个模块现在有没有问题」。
128
+
113
129
  **MCP**:`.zhiqu/mcp.json` 里 `mcpServers` 下每项要么是 `{"command", "args", "env"}`(stdio),要么是
114
130
  `{"url", "headers"}`(HTTP);值里可以写 `${环境变量}`。工具名会加前缀 `mcp__服务__工具`;每个工具第一次用都会问你
115
131
  (`auto` 也问);返回的内容一律当作数据,不当作指令。
@@ -117,11 +133,30 @@ zhiqu --goal "修好所有失败的测试" --mode auto < /dev/null 无人值
117
133
  **长对话**:用到模型上下文窗口的约 80% 时自动把较早的对话压成摘要(目标、决定、改过的文件、待办),也可以 `/compact`。
118
134
  窗口大小在网页「设置 → AI 模型」里按模型设置。
119
135
 
136
+ ## 直连自己的模型(内网也行,不用登录)
137
+
138
+ 默认模型请求走服务器网关。要连自己的模型(内网 vLLM / one-api、公司网关、任何 OpenAI 兼容或 Anthropic 接口),配一次就行:
139
+
140
+ ```bash
141
+ zhiqu model set --url http://10.0.0.5:8000/v1/chat/completions --name qwen2.5-72b --key sk-xxx
142
+ zhiqu model set --url https://gateway.internal/anthropic/v1/messages --name claude-x --protocol anthropic --key ak-xxx
143
+ zhiqu model # 看现在走哪条路
144
+ zhiqu model off # 关掉,回到服务器网关
145
+ ```
146
+
147
+ - **地址原样用**:不拼路径、不补 `/v1`、不校验域名,`http://` 和 `https://` 都行 —— 填什么就请求什么。
148
+ - **配了就不用登录**:完全不连 zhiqustudy.com,只有本地工具(读写、搜索、跑命令)。代价是任务、Wiki、长期记忆、网页存档用不了。
149
+ - 密钥存在 `~/.zhiqu/config.json`(600 权限),只认用户级配置 —— 项目里的 `.zhiqu/settings.json` 改不了它(克隆下来的仓库不能把你的模型地址换成它的服务器)。
150
+ - `--protocol` 只有 `openai`(默认)和 `anthropic` 两种。上下文窗口默认按 64k 算,模型更大就加 `--context-window 128000`(小了会提前压缩,大了会超窗口被拒)。
151
+ - 会话里的 `/model`、`/window` 在直连模式下不生效(它们改的是服务器上的配置),要改就重新跑一次 `zhiqu model set` 或者直接改 `~/.zhiqu/config.json`;`/usage` 只报本地算的 token 数。
152
+
120
153
  ## 对话在网页里也看得到
121
154
 
122
155
  每段命令行会话在网页「AI 助手」里是一个 Notebook(「命令行 · 项目名 · 标题」)。让它排计划、改任务状态、记长期记忆、写知识 Wiki
123
156
  时,除了改任务状态是直接生效,其余生成的都是草稿,在网页里确认之后才生效。
124
157
 
158
+ 直连模式下没有这一段 —— 不连服务器,也就没有网页上的 Notebook 和草稿。
159
+
125
160
  ## 让别的 AI 也用上你的 Wiki 和任务:`zhiqu mcp`
126
161
 
127
162
  `zhiqu mcp` 是一个 MCP 服务器(stdio)。接上之后,Claude Code、Claude 桌面版、Cursor 这些客户端能用你在知趣·象限里的东西:
@@ -136,7 +171,7 @@ zhiqu --goal "修好所有失败的测试" --mode auto < /dev/null 无人值
136
171
  | `read_memory` / `propose_memory` | 读长期记忆 / 提议新的(草稿) |
137
172
 
138
173
  先 `zhiqu login` 登录一次(令牌存在 `~/.zhiqu/config.json`,`zhiqu mcp` 用的就是它)。连的是登录时的那台服务器 —— 默认是 `https://zhiqustudy.com`;
139
- 连本机的桌面应用就 `zhiqu login --server http://127.0.0.1:47615`,那就要开着桌面应用。
174
+ 连本机的 Windows 桌面应用就 `zhiqu login --server http://127.0.0.1:47615`,那就要开着它(Mac 版桌面应用打开的就是网站,不用另连)。
140
175
 
141
176
  **Claude Code**(所有项目都能用):
142
177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhiqu",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "知趣·象限的命令行 coding agent:循环与工具跑在你的电脑上,模型、登录、学习数据在服务器上",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "bin",
11
11
  "src",
12
+ "skills",
12
13
  "README.md"
13
14
  ],
14
15
  "engines": {
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: code-audit
3
+ description: 要看某个文件或模块本身有没有问题、能不能上线时用(不是看一次改动)。
4
+ ---
5
+
6
+ # 模块体检
7
+
8
+ 审查**已经存在的代码**。它和 `/review` 分工不同,先认清走哪条:
9
+
10
+ - 用户要看的是**一次改动**(刚改完、未提交、某个分支的 diff)→ 让他跑 `/review` 或 `zhiqu review`。那条路自己取 diff、在单独的上下文里按固定结构出结论,比在这里做准,而且能接着说「修第 1 条」。
11
+ - 用户问的是「这个模块 / 这个文件现在有没有问题」→ 用这个 skill。`/review` 的规矩里明确写了不报改动之前就有的问题,这里报的正是那些。
12
+
13
+ 两件事不要混着做,也不要两边各报一遍。
14
+
15
+ ## 怎么读
16
+
17
+ - 读完整文件,连同调用方和测试一起看。一段代码对不对,取决于它周围的错误处理、锁、事务、生命周期。
18
+ - 先弄清这段代码的契约:输入从哪来、可不可能为空 / 空集合 / 超大、谁负责释放、失败了谁收尾。契约不清就先说「这里没有明确契约」—— 那本身就是问题。
19
+ - 项目自己的约定优先于你的偏好:看相邻文件怎么写(错误类型、日志、返回结构),不符合它自己的约定才算问题。
20
+
21
+ ## 找什么(和 diff 审查不同的地方)
22
+
23
+ 1. **错误处理缺口**:每个 `catch`、`if err`、返回码,都问一句「出错了之后,状态还一致吗」。吞掉异常、只打日志不返回、把具体错误降级成通用 500,都算。
24
+ 2. **资源与生命周期**:文件、连接、事务是不是在所有路径上都关掉了(包括提前 return 和抛异常);缓存有没有上限和失效。
25
+ 3. **并发与共享状态**:可变字段被多线程 / 多请求共用、先查后改没有原子性、锁的粒度盖不住读改写、异步回调里用了闭包捕获的旧值。
26
+ 4. **边界与校验**:外部输入在进入核心逻辑前有没有校验;空集合、只有一项、重复、超长、负数、时区、精度。
27
+ 5. **重复与分叉**:同一段逻辑出现在两处,改一处漏一处;复制粘贴之后只改了其中一份的条件。
28
+ 6. **死代码与误导**:永远进不去的分支、注释和代码说的不是一回事、名字和实际行为不符。这类算 P3,但它会让人改错地方,值得说。
29
+
30
+ 安全类问题(注入、越权、密钥、路径穿越)不在这里展开,见 `security-review` skill;性能问题见 `perf` skill。
31
+
32
+ ## 每条怎么写
33
+
34
+ ```
35
+ [P1] src/order/service.js:142 — 退款失败时订单状态不回滚
36
+ 为什么:refund() 抛异常后,前面已经写了 status=REFUNDING,没有事务包住,订单会卡在这个状态,用户既退不了款也取消不了。
37
+ 怎么改:把这两步放进同一个事务,或者失败时显式回写原状态。
38
+ ```
39
+
40
+ - 优先级和 `/review` 用同一套:P0 必须马上修(丢数据、安全漏洞、主流程崩)、P1 合并前要修(常见场景出错)、P2 应该修(边界条件)、P3 小问题但确实是错的。
41
+ - 行号要准。说错一个行号,后面的话都会被打折。
42
+ - 「为什么」要给一个**具体场景**(什么输入、什么顺序会出事),不要写「可能有问题」。
43
+ - 「怎么改」要能直接动手,不要写「建议优化」。
44
+
45
+ ## 收尾
46
+
47
+ - 按 P0 → P3 排序。
48
+ - 一个问题都没有就说「没发现会导致故障的问题」,并说清看了哪些文件、哪些路径没覆盖到。**不要为了显得认真而凑数。**
49
+ - 不要顺手改代码 —— 先出结论,等用户点头。用户本来就说了「顺便修掉」的,按结论修,但先说清要动哪几处。
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: codebase-tour
3
+ description: 要快速读懂陌生代码库、接手别人的项目、问「这项目怎么跑起来」时用。
4
+ ---
5
+
6
+ # 读陌生代码库
7
+
8
+ 目标不是「读完所有文件」,是能回答三个问题:**怎么跑起来、一条请求怎么走、要改的地方在哪。**
9
+
10
+ ## 1. 先看骨架(不要一上来读源码)
11
+
12
+ 按顺序读,每一步停下来记一句结论:
13
+
14
+ 1. `README.md` 和 `docs/` 里的上手文档 —— 项目自己怎么介绍自己。
15
+ 2. 构建与依赖清单:`package.json`、`pom.xml`、`build.gradle`、`pyproject.toml`、`go.mod`、`Cargo.toml`。看**依赖**就能猜出技术栈和它解决的问题。
16
+ 3. `list_files` 看两层目录,认出哪些是源码、哪些是生成物(`target/`、`dist/`、`node_modules/` 不用看)。
17
+ 4. 配置文件:`application.yml`、`.env.example`、`docker-compose.yml`、CI 配置 —— 知道它连什么、怎么启动、怎么测。
18
+
19
+ ## 2. 找到入口
20
+
21
+ - 服务端:`main` 函数、`@SpringBootApplication`、`app.listen`、`func main`。
22
+ - 前端:`index.html` 引的第一个脚本、路由表。
23
+ - 命令行:`bin` 字段、`console_scripts`、`main` 包。
24
+ - 定时任务和消息消费者:`@Scheduled`、`cron`、`consumer`、`subscribe` —— 这些是没有 HTTP 入口的隐形入口,最容易漏。
25
+
26
+ ## 3. 顺一条真实路径走到底
27
+
28
+ 挑一条**用户最常用的**操作(登录、下单、列表页),从入口一路跟到数据库再回来,每层记一句:
29
+
30
+ ```
31
+ HTTP POST /api/order → OrderController.create
32
+ → OrderService.create(事务边界在这)
33
+ → 校验 + 扣库存(InventoryClient,远程调用,会失败)
34
+ → OrderMapper.insert → MySQL
35
+ → 返回 { id }
36
+ ```
37
+
38
+ 这一条走完,比读二十个文件有用:分层方式、错误处理、事务、日志、鉴权在哪一层,全都露出来了。
39
+
40
+ ## 4. 代码库大时分头看
41
+
42
+ 超过几百个文件时,用 `spawn_agent` 派只读探索的子 agent(`role: "explorer"`),一人一块,最后 `wait_agent` 收结论:
43
+
44
+ - 一个看**构建与运行**:怎么编译、怎么跑测试、有哪些脚本。
45
+ - 一个看**数据层**:表结构、迁移、ORM 映射、缓存。
46
+ - 一个看**接口层**:路由清单、鉴权中间件、错误码约定。
47
+ - 一个看**前端**(如果有):页面怎么组织、状态管理、接口调用封装。
48
+
49
+ 派的时候把「看哪一块、回答什么问题」写清楚,不要只说「看看这个项目」。子 agent 之间不要改同一个文件(explorer 本来也改不了)。
50
+
51
+ 没有 `spawn_agent`(用户把子 agent 关了)就自己按上面的顺序一块一块看,不要假装派了。
52
+
53
+ ## 5. 产出
54
+
55
+ 用户没要文件就只在回答里给,不要自己建文档。**用户要的是项目说明文件**(写进 `ZHIQU.md` 给以后的会话看)的话,那是 `/init` 的活 —— 告诉他跑 `/init`,不要在这里另写一份。
56
+
57
+ 结构:
58
+
59
+ 1. **一句话**:这是什么、用什么写的、跑起来要什么。
60
+ 2. **目录地图**:`src/` 下每个目录一行,说职责,不说文件名。
61
+ 3. **一条路径**:上面那条调用链。
62
+ 4. **约定与坑**:命名、错误处理、事务、测试怎么写、哪些地方看起来能改其实不能(生成代码、迁移文件、第三方 vendored 代码)。
63
+ 5. **要改 X 应该动哪里**:如果用户是为了某个任务才让你读的,直接给落点。
64
+
65
+ 不确定的地方标出来(「这块没跟到底」),不要编。
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: commit
3
+ description: 要提交、整理提交、写 commit message、或问「这些改动怎么提交」时用。
4
+ ---
5
+
6
+ # 整理提交
7
+
8
+ ## 前提:这里不能自己提交
9
+
10
+ `shell` 跑在系统沙箱里,**写不了 `.git`**:
11
+
12
+ ```
13
+ $ git add a.txt
14
+ fatal: Unable to create '…/.git/index.lock': Operation not permitted
15
+ ```
16
+
17
+ `git add`、`git commit`、`git checkout` 都是这个结果。`run_command` 的 git 也只放 `status` / `diff` / `log`。
18
+
19
+ 看到 `Operation not permitted` 要知道这是**沙箱边界**,不是文件权限没设对 —— 不要试 `chmod`、`sudo`、换目录、写 `.git/index` 绕过,那些只会白花几轮。
20
+
21
+ 所以你的产出是**给用户执行的命令和提交说明**,不是真的提交。做完说清这一点,不要让用户以为已经提交了。
22
+
23
+ ## 1. 先看清有什么
24
+
25
+ ```
26
+ git status
27
+ git diff
28
+ git diff --staged
29
+ ```
30
+
31
+ `git status` 报 `not a git repository` 就直说「这个目录还不是 git 仓库」,然后问用户要不要 `git init` —— 不要自己建。
32
+
33
+ `git status` 里出现这些,先停下问用户,不要自作主张提交:
34
+
35
+ - `.env`、`*.pem`、`id_rsa`、`credentials*`、`*.p12`、带 token 的配置文件
36
+ - 构建产物和依赖目录:`target/`、`dist/`、`node_modules/`、`*.class`、`*.log`
37
+ - 大文件、二进制、数据库文件
38
+ - 和这次任务无关的改动(可能是别人正在改的文件)
39
+
40
+ 已经提交进历史的密钥,删掉文件不算解决 —— 要提醒用户轮换密钥。
41
+
42
+ ## 2. 按意图分组
43
+
44
+ 一个提交 = 一个「为什么改」。判断标准:如果这个提交被单独回滚,项目还是自洽的吗?
45
+
46
+ - 修 bug 的改动 + 它的回归测试 → 同一个提交。
47
+ - 重构 + 功能 → 分开。
48
+ - 格式化 + 逻辑 → 分开(除非格式化就是这次的目的)。
49
+ - 三个不相关的顺手修复 → 三个提交,或者只提交和任务相关的那一个。
50
+
51
+ ## 3. 写 message
52
+
53
+ ```
54
+ <类型>: <做了什么,50 字以内,祈使句,句尾不加句号>
55
+
56
+ <为什么:原来的问题是什么、为什么这样改。每行 72 字以内。>
57
+ ```
58
+
59
+ 类型用项目已有的:先 `git log --oneline -20` 看一眼邻居怎么写的,跟着来。没有约定时用 `feat` / `fix` / `refactor` / `test` / `docs` / `chore` / `perf`。
60
+
61
+ 不要写「更新代码」「修复问题」「按需求修改」—— 等于没写。要写清改之前是什么行为、改之后是什么行为。
62
+
63
+ ## 4. 交给用户
64
+
65
+ 按分组给出可以直接粘贴的命令:
66
+
67
+ ```bash
68
+ git add src/a.js src/a.test.js
69
+ git commit -m "fix: 空标题的任务不再写入日历" -m "标题为空时前端仍然提交,后端直接落库。改为在 service 层校验并返回 400。"
70
+ ```
71
+
72
+ 多个提交就按顺序列出来。最后提醒一句:`git push` 之前自己再看一眼 `git status`。
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: debug
3
+ description: 排查 bug、测试失败、行为不对、报错看不懂时用;也用于「改了没生效」「偶现」「本地好的线上坏」这类问题。
4
+ ---
5
+
6
+ # 排查 bug
7
+
8
+ 顺序不能跳。跳步的典型表现是:看了一眼报错就猜一个原因,改一处跑一次,改到第五处才发现方向全错。
9
+
10
+ ## 1. 先把现象变成一条可重复的命令
11
+
12
+ - 拿到确切的触发方式:哪条命令、什么输入、哪个环境。用户只说「打不开」时,先问清,或者自己找到入口。
13
+ - 复现一次,把完整输出留下来。**复现不了就先解决这件事** —— 缺的是环境、数据、顺序、平台,还是缓存?把缺的条件找出来,不要凭想象改代码。
14
+ - 报错里最先要看的是**最内层的原因**(Java 是第一条 `Caused by`,Python / JS 是堆栈最底下那条),以及调用栈里**你自己代码**最靠上的那一行。框架内部的帧先跳过。
15
+
16
+ ## 2. 定位到具体位置
17
+
18
+ - 用 `search` 找报错原文、函数名、配置键,确认是哪一份文件(同一个名字可能有好几处)。
19
+ - `read_file` 把那一段连同上下文读完,不要只读报错指向的那一行。
20
+ - 需要知道运行时真实的值:临时加一行打印,或者写一个最小的脚本单独跑那条路径。临时文件用完用 `delete_file` 删掉。
21
+
22
+ ## 3. 缩小范围,而不是猜
23
+
24
+ 每次只动一个变量,按顺序试:
25
+
26
+ 1. **二分输入**:把出问题的输入砍一半,看还坏不坏。数据问题通常三步内能定位到具体那条。
27
+ 2. **二分代码**:在中间位置加打印或者直接返回,判断问题在之前还是之后。
28
+ 3. **最小例子**:只留必要的依赖。最小例子还能复现,问题就在它里面;不能复现,问题在环境或调用顺序。
29
+
30
+ ## 4. 形成假设,用一次能推翻它的实验去验
31
+
32
+ - 说得出「如果是 X,那么 Y 应该是 Z」才叫假设。说不出就先别改代码。
33
+ - 实验要能**证伪**:跑出来和预期一样,不代表假设成立;跑出来和预期不一样,假设就错了,立刻换。
34
+ - 同一处改到第二次还不对,停下 —— 重读代码,检查那个前提:这段代码真的在跑吗?跑的是刚改的版本吗(构建缓存、旧的 class 文件、没重启的服务、装的是全局那份而不是本地那份)?
35
+
36
+ ## 5. 修根因
37
+
38
+ - 修的是「为什么会这样」,不是「让这个报错消失」。加 `try/catch` 吞掉异常、加一个空判断挡住崩溃,只是把问题推给下一个调用方。
39
+ - 改动尽量小,不要顺手重构 —— 那会让「是不是我改坏的」变难判断。
40
+
41
+ ## 6. 留一条会变红的回归测试
42
+
43
+ - 先确认这条测试在**修之前是红的**(把修复临时撤掉跑一次,或者心里清楚它断言的就是刚修的行为)。
44
+ - 断言写在用户能看到的行为上,不要断言内部实现细节。
45
+ - 最后跑一遍完整测试,确认没碰坏别的。
46
+
47
+ ## 收尾
48
+
49
+ 回答里说清:根因是什么(一句话)、改在哪个文件哪一行、怎么验证的、还有没有同类地方没改(用 `search` 找过一遍再说没有)。
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: perf
3
+ description: 要查性能、接口慢、页面卡、查询慢、内存涨时用。
4
+ ---
5
+
6
+ # 性能
7
+
8
+ 没有测量的优化都是在赌。顺序:测量 → 定位热点 → 改一处 → 再测。
9
+
10
+ ## 1. 先把「慢」变成数字
11
+
12
+ - 问清楚:哪个操作慢、多慢算慢、数据量多大、在什么环境。用户说「很慢」时,先量出来是 200ms 还是 20s —— 差两个数量级,方向完全不同。
13
+ - 计时要在**用户抱怨的那一层**量:接口慢就量接口,不要量一个内部函数然后说「它很快」。
14
+ - 跑三次取中位数,避开冷启动和 JIT。第一次总是慢的。
15
+
16
+ ## 2. 找热点
17
+
18
+ 按命中率从高到低看:
19
+
20
+ 1. **N+1 查询**:循环里查库、调接口。`search` 找循环体内的 `query`、`find`、`select`、`fetch`、`http`。
21
+ 2. **算法**:嵌套循环、循环里 `contains` / `indexOf` / `filter`(列表转 set 通常就够)、重复排序。
22
+ 3. **重复计算**:同一个值在循环里反复算、正则每次重新编译、配置每次重读。
23
+ 4. **I/O**:一次读一行、每行一次写、小包多次网络请求、没分页的查询。
24
+ 5. **内存**:一次性把大结果集全读进内存、缓存没有上限、大对象长期持有。
25
+
26
+ 工具优先用项目里已经有的:`EXPLAIN`(SQL)、`console.time` / `time.perf_counter`、语言自带的 profiler。不要为了测一次性能去装一堆依赖。
27
+
28
+ ## 3. 改一处,再测
29
+
30
+ - 一次只改一个地方。改三处然后变快了,你不知道是哪一处起作用,也不知道另外两处是不是白改。
31
+ - 改完用同样的输入、同样的环境再量一次,把**前后数字**写出来。
32
+ - 变慢了或者没变,就退回去 —— 留一个没用的「优化」比不优化更糟。
33
+
34
+ ## 4. 别做的事
35
+
36
+ - 在没有测量的情况下加缓存。缓存会带来失效、内存、并发一致性问题,通常比原来的慢更麻烦。
37
+ - 为了快牺牲正确性(去掉校验、放宽事务、把写操作改成异步)—— 这属于改行为,要单独和用户确认。
38
+ - 微优化(少一次函数调用、字符串拼接换 `StringBuilder`)在热点不在这里时毫无意义。
39
+
40
+ ## 收尾
41
+
42
+ 回答里给:原来多少、现在多少、改的是哪里、为什么这里是热点、引入了什么取舍(内存换时间、缓存一致性)。
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: refactor
3
+ description: 要重构、改名、拆文件、消除重复,或用户说「代码太乱整理一下」时用。
4
+ ---
5
+
6
+ # 重构
7
+
8
+ 重构的定义:**外部行为一点不变**,只改内部结构。要顺带改行为,就先重构、验证、再改行为 —— 两次分开做,出问题才知道是哪一次。
9
+
10
+ ## 1. 先建基线
11
+
12
+ - 跑现有测试,把结果记下来(几条通过、哪几条本来就红)。
13
+ - 没有测试覆盖要动的地方:先写几条**特征测试**(characterization test)—— 不判断对错,只把当前输出钉住,比如拿几个真实输入跑一遍,把结果写成断言。它和 `test-first` 那种「先写一条会红的测试」不是一回事:这里要的是**现状**,不是期望。
14
+ - 至少要能回答:「改完之后,我怎么知道行为没变?」
15
+
16
+ ## 2. 找出全部受影响的地方
17
+
18
+ - `search` 找符号名,**包括字符串、配置、模板、文档、测试**里出现的。只改定义不改调用处,是最常见的半成品。
19
+ - 动态引用(反射、`getattr`、`eval`、依赖注入按名字、路由表、SQL 里的列名)`search` 找不到,要单独想一遍。
20
+
21
+ ## 3. 小步走,每步都绿
22
+
23
+ 一步 = 一个能独立解释的改动:
24
+
25
+ - 只改名,不改结构;
26
+ - 只提取函数,不改逻辑;
27
+ - 只移动文件,不改内容。
28
+
29
+ 每步之后:编译、跑测试、至少跑一次相关的那几条。**红了就退回这一步**,不要「先往下改,最后一起修」—— 到那时已经分不清是哪一步坏的。
30
+
31
+ ## 4. 别在同一轮里做的事
32
+
33
+ - 重命名 + 重排格式 + 改逻辑。三件事混在一起,review 的人看不出哪一行是行为变化。
34
+ - 顺手升级依赖、顺手改无关文件的风格。这些单独说,用户同意再做。
35
+
36
+ ## 5. 收尾
37
+
38
+ - 跑完整测试套件。
39
+ - 读一遍自己的 diff(`git diff` 是只读命令,可以跑):有没有留下注释掉的旧代码、调试打印、没用到的导入、改了又改回去的空行。
40
+ - 回答里说清:结构上改了什么、行为为什么没变、靠什么验证的。
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: security-review
3
+ description: 要做安全审查、担心注入/越权/密钥泄露/路径穿越时用。
4
+ ---
5
+
6
+ # 安全审查
7
+
8
+ 只报**说得清怎么被利用**的问题。说不出利用路径的,放进「存疑」,并标明需要什么条件才能确认。
9
+
10
+ 这里做的是**对一个入口或模块做系统排查**。用户只想看「这次改动有没有安全问题」→ 让他跑 `/review`(P0 里本来就包含安全漏洞),不要两边各报一遍。
11
+
12
+ ## 逐项核对
13
+
14
+ 对每个入口(HTTP 接口、命令行参数、文件上传、消息队列、第三方回调、Webhook)问三件事:**谁能碰到它、它信任了什么、信任错了会怎样。**
15
+
16
+ ### 1. 注入
17
+
18
+ - SQL:字符串拼接、模板字符串进查询。`search` 找 `"select`、`+ "where`、`${` 靠近 SQL 的地方;ORM 的 `raw` / `query` / `execute` 也要看。
19
+ - 命令:`exec` / `system` / `Runtime.exec` / `child_process` 里拼了外部输入。参数化的 `execFile` / `spawn` 传数组才安全。
20
+ - 模板与表达式:用户输入进了模板引擎、`eval`、`Function`、SpEL、OGNL、`pickle`。
21
+ - 日志注入:用户输入直接进日志,换行符能伪造日志行。
22
+
23
+ ### 2. 鉴权与越权
24
+
25
+ - 每个「按 id 取数据」的接口:有没有校验这条数据属于当前用户?只查 `where id = ?` 不查 `user_id`,就是水平越权。
26
+ - 管理接口有没有独立鉴权,还是只靠前端不显示按钮。
27
+ - 批量接口(一次传多个 id)要逐个校验,不能只校验第一个。
28
+
29
+ ### 3. 密钥与凭据
30
+
31
+ - 仓库里搜 `password`、`secret`、`token`、`api_key`、`AKIA`、`-----BEGIN`。硬编码的一律报。
32
+ - 密钥有没有进日志、进异常堆栈、进前端 bundle、进 URL 查询参数。
33
+ - 默认口令、默认密钥、示例配置里的占位值(`changeme`、`admin/admin`)有没有被带进生产。
34
+
35
+ ### 4. 路径与文件
36
+
37
+ - 用户给的文件名拼进路径:`../`、绝对路径、软链、Windows 的 `..\`、URL 编码过的 `%2e%2e`。
38
+ - 上传:类型只看扩展名、大小没上限、存到能被执行或能被直接访问的目录。
39
+ - 下载与解压:zip slip(压缩包条目名里带 `../`)。
40
+
41
+ ### 5. 反序列化与外部请求
42
+
43
+ - 不可信数据进 `ObjectInputStream`、`pickle.loads`、`yaml.load`(不是 `safe_load`);`JSON.parse` 之后的原型污染(`__proto__`、`constructor`)。
44
+ - SSRF:用户能给 URL 的地方,有没有拦内网地址(`127.0.0.1`、`169.254.169.254`、`10.`、`192.168.`)、有没有跟随重定向。
45
+
46
+ ### 6. 前端
47
+
48
+ - `innerHTML`、`dangerouslySetInnerHTML`、`v-html` 里有没有用户内容。
49
+ - 敏感数据放进了 localStorage,或者放进了前端可见的接口返回。
50
+ - 跨域配置是不是 `*` 加 `credentials`。
51
+
52
+ ### 7. 依赖与配置
53
+
54
+ - 依赖版本有没有明显过旧、锁文件里的实际版本和清单里写的是不是一回事。
55
+ - **不要凭记忆报 CVE 编号和「这个版本有某某漏洞」** —— 这里通常没有外网,编出来的编号比不报更糟。版本明显老就说「版本停在 X,去查一下有没有已知漏洞」,把判断交回用户。
56
+ - 生产配置里 `debug`、详细错误页、actuator / 管理端点有没有对外。
57
+
58
+ ## 每条怎么写
59
+
60
+ ```
61
+ [高危] src/file/DownloadController.java:63 — 文件名直接拼进路径
62
+ 利用:请求 ?name=../../../../etc/passwd 能读到工作区外的文件。
63
+ 修法:先 normalize,再校验结果仍在允许的根目录下(startsWith 要带分隔符)。
64
+ ```
65
+
66
+ - 严重度:高危(能直接拿到数据或执行代码)/ 中危(需要登录或特定条件)/ 低危(信息泄露、加固建议)。
67
+ - 修法要给出具体位置和判断条件,不要只说「加校验」。
68
+
69
+ ## 收尾
70
+
71
+ 按严重度排序。一条都没有时,说清核对了哪几类、看了哪些入口。**不要用「建议加强安全意识」凑数。**
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: test-first
3
+ description: 要写新功能、给已有代码补测试、或用户问「有没有测试」时用。
4
+ ---
5
+
6
+ # 先写测试
7
+
8
+ 用在新功能和补测试上。**修 bug** 时的回归测试在 `debug` skill 的最后一步,那边管的是先复现再修。
9
+
10
+ ## 0. 先认项目用的是什么
11
+
12
+ 不要引入新框架。按这个顺序找:
13
+
14
+ - `read_file` 读 `package.json` 的 `scripts.test`、`pom.xml` 的 surefire 配置、`pyproject.toml`、`Makefile`、`go.mod`。
15
+ - `list_files` 看测试放在哪:`test/`、`src/test/`、`__tests__/`、`tests/`、`*_test.go`。
16
+ - 读一个**已有的**测试文件,照它的写法来(导入方式、断言库、fixture、临时目录怎么建)。风格不一致的测试比没有测试更麻烦。
17
+
18
+ 跑一次现有测试,确认基线是绿的。基线本来就红,先告诉用户,不要在他的红上再叠一层。
19
+
20
+ ## 1. 先写测试,并且看着它红
21
+
22
+ - 一次只加一条,跑它,**确认它因为断言失败而红**。
23
+ - 因为 `import` 找不到、语法错、fixture 没建好而红,不算 —— 那说明测试本身还没写对,先修测试。
24
+ - 红的时候看一眼失败信息:它有没有说清「期望什么、实际什么」。说不清就改断言的消息,这条测试以后会救你一次。
25
+
26
+ ## 2. 再写实现
27
+
28
+ - 只写让这条测试变绿需要的东西,不要顺手把「以后可能要用」的也写了。
29
+ - 绿了之后先跑整个测试文件,再跑整个套件。
30
+
31
+ ## 3. 断言的质量
32
+
33
+ 一条测试只验证一件事,名字要说清「什么坏了」:
34
+
35
+ - 好:`拒绝空标题的任务`、`并发提交同一张单只会成功一次`
36
+ - 差:`test create`、`test2`
37
+
38
+ 要避免的假测试(跑了等于没跑):
39
+
40
+ - 没有断言,只调用一下看它不抛异常。
41
+ - 断言恒真:`assert.ok(true)`、`assert(x === x)`、把期望值写成被测函数算出来的值。
42
+ - 全部用 mock:mock 返回什么就断言什么,把被测代码删掉测试照样绿。
43
+ - 只断言「调用了几次」,不断言结果对不对。
44
+
45
+ 边界至少覆盖:空、只有一项、重复、超长、出错路径。用户提过的那个具体例子,必须有一条测试就是它。
46
+
47
+ ## 4. 收尾
48
+
49
+ - 测试文件放在项目已有的位置,命名跟邻居一致。
50
+ - 回答里给出跑测试的命令和实际输出(几条通过、几条失败),不要只说「测试通过了」。