claude-pangu 1.0.7 → 1.0.9
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/.claude-plugin/plugin.json +1 -1
- package/README.md +95 -29
- package/README_EN.md +97 -21
- package/agents/baozheng.md +29 -0
- package/agents/cangjie.md +29 -0
- package/agents/weizheng.md +29 -0
- package/hooks/keyword-detector.sh +11 -6
- package/hooks/progress-notifier.sh +4 -4
- package/hooks/todo-enforcer.sh +14 -13
- package/package.json +1 -1
- package/scripts/cli.js +412 -75
- package/scripts/install.ps1 +6 -6
- package/scripts/install.sh +6 -6
- package/scripts/logger.js +60 -0
- package/scripts/postinstall.js +2 -6
- package/scripts/sync-version.js +2 -26
- package/skills/bilingual/skill.json +1 -1
- package/skills/progress/skill.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://code.claude.com/plugin-schema.json",
|
|
3
3
|
"name": "oh-my-claude",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.9",
|
|
5
5
|
"description": "基于中国传统文化的 Claude Code 智能编排插件 - A Claude Code plugin inspired by Chinese traditional culture",
|
|
6
6
|
"author": "ZDragon17",
|
|
7
7
|
"contributors": [
|
package/README.md
CHANGED
|
@@ -175,20 +175,86 @@ claude plugins install .
|
|
|
175
175
|
### 验证安装
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
|
-
# 在 Claude Code
|
|
179
|
-
/
|
|
178
|
+
# 在 Claude Code 中输入
|
|
179
|
+
/yishan 你好
|
|
180
180
|
|
|
181
181
|
# 如果看到愚公移山模式的响应,说明安装成功
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
### 更新插件
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# npm 方式更新(推荐)
|
|
188
|
+
npx claude-pangu@latest update
|
|
189
|
+
|
|
190
|
+
# 或重新运行一键安装脚本(会自动覆盖旧版本)
|
|
191
|
+
# macOS / Linux
|
|
192
|
+
curl -fsSL https://raw.githubusercontent.com/ZDragon17/oh-my-claude/main/scripts/install.sh | bash
|
|
193
|
+
|
|
194
|
+
# Windows (PowerShell)
|
|
195
|
+
irm https://raw.githubusercontent.com/ZDragon17/oh-my-claude/main/scripts/install.ps1 | iex
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> ⚠️ **重要**: 更新后请**完全退出**并重新启动 Claude Code(仅关闭窗口可能不够)。macOS 使用 `Cmd+Q` 完全退出,Windows 确保从任务管理器中关闭所有进程。
|
|
199
|
+
|
|
200
|
+
### 卸载插件
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# npm 方式卸载
|
|
204
|
+
npx claude-pangu uninstall
|
|
205
|
+
|
|
206
|
+
# 或手动删除
|
|
207
|
+
# macOS / Linux
|
|
208
|
+
rm -rf ~/.claude/commands/{yishan,yugong,zhuge,bianque,luban,wukong}.md # 等其他命令文件
|
|
209
|
+
rm -rf ~/.claude/plugins/oh-my-claude
|
|
210
|
+
|
|
211
|
+
# Windows (PowerShell)
|
|
212
|
+
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\oh-my-claude"
|
|
213
|
+
# 命令文件需要单独删除
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 故障排除
|
|
217
|
+
|
|
218
|
+
如果安装后命令无法使用,请按以下步骤排查:
|
|
219
|
+
|
|
220
|
+
#### 1. 完全重启 Claude Code
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# macOS: 使用 Cmd+Q 完全退出,不只是关闭窗口
|
|
224
|
+
# Windows: 确保从任务管理器中关闭所有 Claude 进程
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### 2. 验证文件是否安装成功
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# 检查命令文件是否存在
|
|
231
|
+
ls ~/.claude/commands/yishan.md
|
|
232
|
+
|
|
233
|
+
# 查看所有已安装的命令
|
|
234
|
+
ls ~/.claude/commands/
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### 3. 清除缓存(macOS 特定问题)
|
|
238
|
+
|
|
239
|
+
macOS 上存在已知的命令发现 Bug ([Issue #13906](https://github.com/anthropics/claude-code/issues/13906)),清除缓存可能解决问题:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# 清除 Claude Code 缓存
|
|
243
|
+
rm ~/.claude.json
|
|
244
|
+
|
|
245
|
+
# 然后完全重启 Claude Code
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### 4. 检查 /help 输出
|
|
249
|
+
|
|
250
|
+
在 Claude Code 中输入 `/help`,查看是否显示 `yishan`、`zhuge` 等命令。
|
|
185
251
|
|
|
186
252
|
## 🚀 快速开始
|
|
187
253
|
|
|
188
254
|
### 1. 愚公移山 - 大规模任务
|
|
189
255
|
|
|
190
256
|
```bash
|
|
191
|
-
/
|
|
257
|
+
/yishan 重构整个用户模块,包括:
|
|
192
258
|
- 用户注册
|
|
193
259
|
- 用户登录
|
|
194
260
|
- 密码重置
|
|
@@ -204,7 +270,7 @@ claude plugins install .
|
|
|
204
270
|
### 2. 诸葛顾问 - 架构设计
|
|
205
271
|
|
|
206
272
|
```bash
|
|
207
|
-
/
|
|
273
|
+
/zhuge 我们的用户系统应该如何设计?需要考虑:
|
|
208
274
|
- 多租户支持
|
|
209
275
|
- 权限管理
|
|
210
276
|
- 高可用性
|
|
@@ -219,7 +285,7 @@ claude plugins install .
|
|
|
219
285
|
### 3. 扁鹊诊断 - Bug 修复
|
|
220
286
|
|
|
221
287
|
```bash
|
|
222
|
-
/
|
|
288
|
+
/bianque TypeError: Cannot read property 'name' of undefined
|
|
223
289
|
at UserService.getProfile (user.service.ts:42)
|
|
224
290
|
```
|
|
225
291
|
|
|
@@ -232,7 +298,7 @@ claude plugins install .
|
|
|
232
298
|
### 4. 悟空侦察 - 代码探索
|
|
233
299
|
|
|
234
300
|
```bash
|
|
235
|
-
/
|
|
301
|
+
/wukong 找到所有处理用户认证的代码
|
|
236
302
|
```
|
|
237
303
|
|
|
238
304
|
悟空会快速:
|
|
@@ -243,7 +309,7 @@ claude plugins install .
|
|
|
243
309
|
### 5. 鲁班巧工 - 精密实现
|
|
244
310
|
|
|
245
311
|
```bash
|
|
246
|
-
/
|
|
312
|
+
/luban 实现一个带动画效果的 Toast 组件
|
|
247
313
|
```
|
|
248
314
|
|
|
249
315
|
鲁班会精心:
|
|
@@ -316,37 +382,37 @@ oh-my-claude/
|
|
|
316
382
|
|
|
317
383
|
## 🎯 命令速查
|
|
318
384
|
|
|
319
|
-
> 💡
|
|
385
|
+
> 💡 直接使用命令名即可,如 `/yishan`、`/zhuge`
|
|
320
386
|
|
|
321
387
|
### Agent 命令
|
|
322
388
|
|
|
323
389
|
| 命令 | 功能 |
|
|
324
390
|
|------|------|
|
|
325
|
-
| `/
|
|
326
|
-
| `/
|
|
327
|
-
| `/
|
|
328
|
-
| `/
|
|
329
|
-
| `/
|
|
330
|
-
| `/
|
|
331
|
-
| `/
|
|
332
|
-
| `/
|
|
333
|
-
| `/
|
|
334
|
-
| `/
|
|
335
|
-
| `/
|
|
336
|
-
| `/
|
|
337
|
-
| `/
|
|
338
|
-
| `/
|
|
339
|
-
| `/
|
|
340
|
-
| `/
|
|
341
|
-
| `/
|
|
342
|
-
| `/
|
|
391
|
+
| `/yugong` 或 `/yishan` | 启动愚公移山模式 |
|
|
392
|
+
| `/zhuge` | 召唤诸葛顾问 |
|
|
393
|
+
| `/luban` | 召唤鲁班巧匠 |
|
|
394
|
+
| `/wukong` | 召唤悟空侦察 |
|
|
395
|
+
| `/bianque` | 召唤扁鹊诊断 |
|
|
396
|
+
| `/mozi` | 召唤墨子安全 |
|
|
397
|
+
| `/sunzi` | 召唤孙子性能 |
|
|
398
|
+
| `/simaqian` | 召唤司马迁史官 |
|
|
399
|
+
| `/zhenghe` | 召唤郑和 API |
|
|
400
|
+
| `/zhangheng` | 召唤张衡监控 |
|
|
401
|
+
| `/libing` | 召唤李冰 DevOps |
|
|
402
|
+
| `/laozi` | 召唤老子简洁大师 |
|
|
403
|
+
| `/baozheng` | 召唤包拯测试专家 |
|
|
404
|
+
| `/weizheng` | 召唤魏征代码审查 |
|
|
405
|
+
| `/cangjie` | 召唤仓颉数据库专家 |
|
|
406
|
+
| `/libai` | 召唤李白需求炼金师 |
|
|
407
|
+
| `/gukaizhi` | 召唤顾恺之界面美学师 |
|
|
408
|
+
| `/change` | 召唤嫦娥云端仙子 |
|
|
343
409
|
|
|
344
410
|
### 工具命令
|
|
345
411
|
|
|
346
412
|
| 命令 | 功能 |
|
|
347
413
|
|------|------|
|
|
348
|
-
| `/
|
|
349
|
-
| `/
|
|
414
|
+
| `/progress` | 📊 可视化进度面板 |
|
|
415
|
+
| `/team` | 🤝 多 Agent 团队协作 |
|
|
350
416
|
|
|
351
417
|
### 关键词触发
|
|
352
418
|
|
package/README_EN.md
CHANGED
|
@@ -174,12 +174,82 @@ claude plugins install .
|
|
|
174
174
|
### Verify Installation
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
# In Claude Code, type
|
|
177
|
+
# In Claude Code, type (use / prefix)
|
|
178
178
|
/yishan hello
|
|
179
179
|
|
|
180
180
|
# If you see YuGong mode response, installation is successful
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
+
> 💡 **Command Prefix**: After installation, all commands use the `/` prefix, e.g., `/yishan`, `/zhuge`
|
|
184
|
+
|
|
185
|
+
### Update Plugin
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# npm update (recommended)
|
|
189
|
+
npx claude-pangu@latest update
|
|
190
|
+
|
|
191
|
+
# Or re-run the one-line install script (will overwrite old version)
|
|
192
|
+
# macOS / Linux
|
|
193
|
+
curl -fsSL https://raw.githubusercontent.com/ZDragon17/oh-my-claude/main/scripts/install.sh | bash
|
|
194
|
+
|
|
195
|
+
# Windows (PowerShell)
|
|
196
|
+
irm https://raw.githubusercontent.com/ZDragon17/oh-my-claude/main/scripts/install.ps1 | iex
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
> ⚠️ **Important**: After updating, please **completely quit** and restart Claude Code (just closing the window may not be enough). On macOS use `Cmd+Q`, on Windows ensure all processes are closed via Task Manager.
|
|
200
|
+
|
|
201
|
+
### Uninstall Plugin
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# npm uninstall
|
|
205
|
+
npx claude-pangu uninstall
|
|
206
|
+
|
|
207
|
+
# Or manual removal
|
|
208
|
+
# macOS / Linux
|
|
209
|
+
rm -rf ~/.claude/commands/{yishan,yugong,zhuge,bianque,luban,wukong}.md # and other command files
|
|
210
|
+
rm -rf ~/.claude/plugins/oh-my-claude
|
|
211
|
+
|
|
212
|
+
# Windows (PowerShell)
|
|
213
|
+
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\oh-my-claude"
|
|
214
|
+
# Command files need to be deleted separately
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Troubleshooting
|
|
218
|
+
|
|
219
|
+
If commands don't work after installation, follow these steps:
|
|
220
|
+
|
|
221
|
+
#### 1. Fully Restart Claude Code
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# macOS: Use Cmd+Q to completely quit, not just close the window
|
|
225
|
+
# Windows: Ensure all Claude processes are closed via Task Manager
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### 2. Verify Files Are Installed
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Check if command files exist
|
|
232
|
+
ls ~/.claude/commands/yishan.md
|
|
233
|
+
|
|
234
|
+
# View all installed commands
|
|
235
|
+
ls ~/.claude/commands/
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### 3. Clear Cache (macOS-specific Issue)
|
|
239
|
+
|
|
240
|
+
There's a known command discovery bug on macOS ([Issue #13906](https://github.com/anthropics/claude-code/issues/13906)). Clearing cache may help:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Clear Claude Code cache
|
|
244
|
+
rm ~/.claude.json
|
|
245
|
+
|
|
246
|
+
# Then completely restart Claude Code
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### 4. Check /help Output
|
|
250
|
+
|
|
251
|
+
Type `/help` in Claude Code to see if `yishan`, `zhuge`, and other commands are listed.
|
|
252
|
+
|
|
183
253
|
## 🚀 Quick Start
|
|
184
254
|
|
|
185
255
|
### 1. YuGong Mode - Large-scale Tasks
|
|
@@ -313,31 +383,37 @@ oh-my-claude/
|
|
|
313
383
|
|
|
314
384
|
## 🎯 Command Reference
|
|
315
385
|
|
|
386
|
+
> 💡 All commands use the `/` prefix
|
|
387
|
+
|
|
316
388
|
### Agent Commands
|
|
317
389
|
|
|
318
|
-
| Command |
|
|
319
|
-
|
|
320
|
-
| `/yugong`
|
|
321
|
-
| `/zhuge` |
|
|
322
|
-
| `/luban` |
|
|
323
|
-
| `/wukong` |
|
|
324
|
-
| `/bianque` |
|
|
325
|
-
| `/mozi` |
|
|
326
|
-
| `/sunzi` |
|
|
327
|
-
| `/simaqian` |
|
|
328
|
-
| `/zhenghe` |
|
|
329
|
-
| `/zhangheng` |
|
|
330
|
-
| `/libing` |
|
|
331
|
-
| `/laozi` |
|
|
332
|
-
| `/baozheng` |
|
|
333
|
-
| `/weizheng` |
|
|
334
|
-
| `/cangjie` |
|
|
390
|
+
| Command | Function |
|
|
391
|
+
|---------|----------|
|
|
392
|
+
| `/yugong` or `/yishan` | Start YuGong mode |
|
|
393
|
+
| `/zhuge` | Summon ZhuGe advisor |
|
|
394
|
+
| `/luban` | Summon LuBan craftsman |
|
|
395
|
+
| `/wukong` | Summon WuKong scout |
|
|
396
|
+
| `/bianque` | Summon BianQue diagnostician |
|
|
397
|
+
| `/mozi` | Summon MoZi security expert |
|
|
398
|
+
| `/sunzi` | Summon SunZi performance expert |
|
|
399
|
+
| `/simaqian` | Summon SimaQian historian |
|
|
400
|
+
| `/zhenghe` | Summon ZhengHe API expert |
|
|
401
|
+
| `/zhangheng` | Summon ZhangHeng monitor |
|
|
402
|
+
| `/libing` | Summon LiBing DevOps |
|
|
403
|
+
| `/laozi` | Summon LaoZi simplicity master |
|
|
404
|
+
| `/baozheng` | Summon BaoZheng testing expert |
|
|
405
|
+
| `/weizheng` | Summon WeiZheng code reviewer |
|
|
406
|
+
| `/cangjie` | Summon CangJie database expert |
|
|
407
|
+
| `/libai` | Summon LiBai requirements analyst |
|
|
408
|
+
| `/gukaizhi` | Summon GuKaiZhi UI/UX designer |
|
|
409
|
+
| `/change` | Summon ChangE cloud architect |
|
|
335
410
|
|
|
336
411
|
### Tool Commands
|
|
337
412
|
|
|
338
|
-
| Command |
|
|
339
|
-
|
|
340
|
-
| `/progress` |
|
|
413
|
+
| Command | Function |
|
|
414
|
+
|---------|----------|
|
|
415
|
+
| `/progress` | 📊 Visual progress dashboard |
|
|
416
|
+
| `/team` | 🤝 Multi-agent team collaboration |
|
|
341
417
|
|
|
342
418
|
### Keyword Triggers
|
|
343
419
|
|
package/agents/baozheng.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: baozheng
|
|
3
|
+
description: |
|
|
4
|
+
包拯 (BaoZheng) - 测试专家
|
|
5
|
+
以北宋著名清官包拯(包青天)为原型,专注于测试设计和质量保证。
|
|
6
|
+
铁面无私,明察秋毫,确保代码质量。
|
|
7
|
+
|
|
8
|
+
使用场景:
|
|
9
|
+
- 单元测试设计和编写
|
|
10
|
+
- 集成测试和端到端测试
|
|
11
|
+
- 测试驱动开发 (TDD) 指导
|
|
12
|
+
- 测试覆盖率分析
|
|
13
|
+
- 测试策略制定
|
|
14
|
+
|
|
15
|
+
触发方式:
|
|
16
|
+
- 用户提及 "包拯"、"测试"、"test"、"tdd"
|
|
17
|
+
- 使用 /baozheng 或 /test 命令
|
|
18
|
+
allowed-tools:
|
|
19
|
+
- Read
|
|
20
|
+
- Write
|
|
21
|
+
- Edit
|
|
22
|
+
- Bash
|
|
23
|
+
- Grep
|
|
24
|
+
- Glob
|
|
25
|
+
- Task
|
|
26
|
+
- TodoWrite
|
|
27
|
+
model: sonnet
|
|
28
|
+
---
|
|
29
|
+
|
|
1
30
|
# 包拯 (BaoZheng) - 测试专家 ⚖️
|
|
2
31
|
|
|
3
32
|
> "公正无私,明察秋毫" - 包拯,北宋名臣
|
package/agents/cangjie.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cangjie
|
|
3
|
+
description: |
|
|
4
|
+
仓颉 (CangJie) - 数据库专家
|
|
5
|
+
以传说中的造字始祖仓颉为原型,专注于数据库设计和优化。
|
|
6
|
+
观察规律,创造结构,设计清晰高效的数据模型。
|
|
7
|
+
|
|
8
|
+
使用场景:
|
|
9
|
+
- 数据库表结构设计
|
|
10
|
+
- SQL 查询优化
|
|
11
|
+
- 数据库迁移策略
|
|
12
|
+
- 索引设计和性能调优
|
|
13
|
+
- 数据建模
|
|
14
|
+
|
|
15
|
+
触发方式:
|
|
16
|
+
- 用户提及 "仓颉"、"数据库"、"database"、"sql"
|
|
17
|
+
- 使用 /cangjie 或 /db 命令
|
|
18
|
+
allowed-tools:
|
|
19
|
+
- Read
|
|
20
|
+
- Write
|
|
21
|
+
- Edit
|
|
22
|
+
- Bash
|
|
23
|
+
- Grep
|
|
24
|
+
- Glob
|
|
25
|
+
- Task
|
|
26
|
+
- TodoWrite
|
|
27
|
+
model: sonnet
|
|
28
|
+
---
|
|
29
|
+
|
|
1
30
|
# 仓颉 (CangJie) - 数据库专家 📊
|
|
2
31
|
|
|
3
32
|
> "仰观天象,俯察鸟兽之迹,创造文字" - 仓颉造字传说
|
package/agents/weizheng.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weizheng
|
|
3
|
+
description: |
|
|
4
|
+
魏征 (WeiZheng) - 代码审查专家
|
|
5
|
+
以唐代著名谏臣魏征为原型,专注于代码审查和质量把控。
|
|
6
|
+
直言不讳,如镜照人,确保代码符合规范和最佳实践。
|
|
7
|
+
|
|
8
|
+
使用场景:
|
|
9
|
+
- 代码审查 (Code Review)
|
|
10
|
+
- 代码规范检查
|
|
11
|
+
- 设计模式评估
|
|
12
|
+
- 安全性审查
|
|
13
|
+
- PR 审查
|
|
14
|
+
|
|
15
|
+
触发方式:
|
|
16
|
+
- 用户提及 "魏征"、"审查"、"review"、"cr"
|
|
17
|
+
- 使用 /weizheng 或 /review 命令
|
|
18
|
+
allowed-tools:
|
|
19
|
+
- Read
|
|
20
|
+
- Write
|
|
21
|
+
- Edit
|
|
22
|
+
- Bash
|
|
23
|
+
- Grep
|
|
24
|
+
- Glob
|
|
25
|
+
- Task
|
|
26
|
+
- TodoWrite
|
|
27
|
+
model: sonnet
|
|
28
|
+
---
|
|
29
|
+
|
|
1
30
|
# 魏征 (WeiZheng) - 代码审查专家 🪞
|
|
2
31
|
|
|
3
32
|
> "以铜为镜,可以正衣冠;以人为镜,可以明得失" - 唐太宗论魏征
|
|
@@ -8,23 +8,28 @@ if command -v jq > /dev/null 2>&1; then
|
|
|
8
8
|
has_jq=1
|
|
9
9
|
fi
|
|
10
10
|
|
|
11
|
-
# 读取 stdin 中的 JSON
|
|
12
|
-
input=$(cat)
|
|
11
|
+
# 读取 stdin 中的 JSON 数据(带超时保护)
|
|
12
|
+
input=$(cat 2>/dev/null) || input=""
|
|
13
|
+
|
|
14
|
+
# 空输入检查
|
|
15
|
+
if [ -z "$input" ]; then
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
13
18
|
|
|
14
19
|
# 提取用户提示
|
|
15
20
|
if [ "$has_jq" -eq 1 ]; then
|
|
16
|
-
prompt=$(echo "$input" | jq -r '.prompt // empty' 2>/dev/null)
|
|
21
|
+
prompt=$(echo "$input" | jq -r '.prompt // empty' 2>/dev/null) || prompt=""
|
|
17
22
|
else
|
|
18
23
|
# 简单的字符串提取作为回退
|
|
19
|
-
prompt=$(echo "$input" | sed -n 's/.*"prompt"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
|
24
|
+
prompt=$(echo "$input" | sed -n 's/.*"prompt"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' 2>/dev/null) || prompt=""
|
|
20
25
|
fi
|
|
21
26
|
|
|
22
27
|
if [ -z "$prompt" ]; then
|
|
23
28
|
exit 0
|
|
24
29
|
fi
|
|
25
30
|
|
|
26
|
-
#
|
|
27
|
-
prompt_lower=$(echo "$prompt" | tr '[:upper:]' '[:lower:]')
|
|
31
|
+
# 转换为小写进行匹配(带错误保护)
|
|
32
|
+
prompt_lower=$(echo "$prompt" | tr '[:upper:]' '[:lower:]' 2>/dev/null) || prompt_lower="$prompt"
|
|
28
33
|
|
|
29
34
|
# 检测愚公移山关键词
|
|
30
35
|
if echo "$prompt_lower" | grep -qE '(ultra[-_]?work|ulw|移山|yi[-_]?shan|persist|愚公|yu[-_]?gong)'; then
|
|
@@ -8,11 +8,11 @@ if command -v jq > /dev/null 2>&1; then
|
|
|
8
8
|
has_jq=1
|
|
9
9
|
fi
|
|
10
10
|
|
|
11
|
-
# 读取 stdin 中的 JSON
|
|
12
|
-
input=$(cat)
|
|
11
|
+
# 读取 stdin 中的 JSON 数据(带错误保护)
|
|
12
|
+
input=$(cat 2>/dev/null) || input=""
|
|
13
13
|
|
|
14
|
-
# 如果没有 jq
|
|
15
|
-
if [ "$has_jq" -eq 0 ]; then
|
|
14
|
+
# 如果没有 jq 或输入为空,静默退出
|
|
15
|
+
if [ "$has_jq" -eq 0 ] || [ -z "$input" ]; then
|
|
16
16
|
exit 0
|
|
17
17
|
fi
|
|
18
18
|
|
package/hooks/todo-enforcer.sh
CHANGED
|
@@ -39,48 +39,49 @@ if [ -z "$transcript_path" ]; then
|
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
41
|
# 路径安全验证函数
|
|
42
|
+
# 注意: 避免使用 local 关键字以保持 POSIX 兼容性
|
|
42
43
|
validate_path() {
|
|
43
|
-
|
|
44
|
+
_vp_path="$1"
|
|
44
45
|
|
|
45
46
|
# 1. 检查是否为空
|
|
46
|
-
if [ -z "$
|
|
47
|
+
if [ -z "$_vp_path" ]; then
|
|
47
48
|
return 1
|
|
48
49
|
fi
|
|
49
50
|
|
|
50
51
|
# 2. 检查危险字符(命令注入防护)
|
|
51
|
-
case "$
|
|
52
|
+
case "$_vp_path" in
|
|
52
53
|
*[\;\&\|\`\$\(\)\{\}\[\]\<\>\!\*\?]*)
|
|
53
54
|
return 1
|
|
54
55
|
;;
|
|
55
56
|
esac
|
|
56
57
|
|
|
57
58
|
# 3. 检查文件是否存在且为普通文件(必须在规范化之前检查)
|
|
58
|
-
if [ ! -f "$
|
|
59
|
+
if [ ! -f "$_vp_path" ]; then
|
|
59
60
|
return 1
|
|
60
61
|
fi
|
|
61
62
|
|
|
62
63
|
# 4. 检查文件是否可读
|
|
63
|
-
if [ ! -r "$
|
|
64
|
+
if [ ! -r "$_vp_path" ]; then
|
|
64
65
|
return 1
|
|
65
66
|
fi
|
|
66
67
|
|
|
67
68
|
# 5. 规范化路径(消除 .. 和符号链接)
|
|
68
|
-
|
|
69
|
+
_vp_real_path=""
|
|
69
70
|
if command -v realpath > /dev/null 2>&1; then
|
|
70
|
-
|
|
71
|
+
_vp_real_path=$(realpath "$_vp_path" 2>/dev/null) || return 1
|
|
71
72
|
elif command -v readlink > /dev/null 2>&1; then
|
|
72
73
|
# macOS 和一些系统可能没有 realpath,使用 readlink -f
|
|
73
|
-
|
|
74
|
+
_vp_real_path=$(readlink -f "$_vp_path" 2>/dev/null) || {
|
|
74
75
|
# 如果 readlink -f 不支持,回退到原始路径检查
|
|
75
|
-
|
|
76
|
+
_vp_real_path="$_vp_path"
|
|
76
77
|
}
|
|
77
78
|
else
|
|
78
79
|
# 无规范化工具,使用基本检查
|
|
79
|
-
|
|
80
|
+
_vp_real_path="$_vp_path"
|
|
80
81
|
fi
|
|
81
82
|
|
|
82
83
|
# 6. 检查规范化后的路径是否包含路径遍历
|
|
83
|
-
case "$
|
|
84
|
+
case "$_vp_real_path" in
|
|
84
85
|
*..*)
|
|
85
86
|
return 1
|
|
86
87
|
;;
|
|
@@ -88,13 +89,13 @@ validate_path() {
|
|
|
88
89
|
|
|
89
90
|
# 7. 检查路径是否以期望的前缀开始(只允许用户目录或临时目录)
|
|
90
91
|
# 支持: Unix ($HOME, /tmp, /var/folders), WSL (/mnt/c/Users), Git Bash (/c/Users)
|
|
91
|
-
case "$
|
|
92
|
+
case "$_vp_real_path" in
|
|
92
93
|
"$HOME"/*|/tmp/*|/var/folders/*|/mnt/c/[Uu]sers/*|/c/[Uu]sers/*|/home/*)
|
|
93
94
|
# 允许的路径前缀(包括 Windows WSL 和 Git Bash 路径)
|
|
94
95
|
;;
|
|
95
96
|
*)
|
|
96
97
|
# 检查 Windows 原生路径格式 (如 C:\Users\... 或 C:\Temp\...)
|
|
97
|
-
if echo "$
|
|
98
|
+
if echo "$_vp_real_path" | grep -qiE '^[a-z]:\\(users|temp)\\'; then
|
|
98
99
|
# Windows 原生路径,允许
|
|
99
100
|
:
|
|
100
101
|
else
|
package/package.json
CHANGED
package/scripts/cli.js
CHANGED
|
@@ -12,11 +12,12 @@ const os = require('os');
|
|
|
12
12
|
|
|
13
13
|
// ==================== 常量配置 ====================
|
|
14
14
|
|
|
15
|
-
const VERSION = '1.0.
|
|
15
|
+
const VERSION = '1.0.9';
|
|
16
16
|
const PLUGIN_NAME = 'oh-my-claude';
|
|
17
17
|
|
|
18
|
-
// 路径配置
|
|
19
|
-
const
|
|
18
|
+
// 路径配置 - 使用用户主目录下的持久化日志目录
|
|
19
|
+
const LOG_DIR = path.join(os.homedir(), '.oh-my-claude', 'logs');
|
|
20
|
+
const ERROR_LOG_PATH = path.join(LOG_DIR, 'error.log');
|
|
20
21
|
|
|
21
22
|
// 时间配置(毫秒)
|
|
22
23
|
const LOCK_TIMEOUT_MS = 30000; // 锁等待超时:30秒
|
|
@@ -65,6 +66,11 @@ function sanitizeStackTrace(stack) {
|
|
|
65
66
|
// 记录错误到日志文件(带脱敏处理)
|
|
66
67
|
function logErrorToFile(err) {
|
|
67
68
|
try {
|
|
69
|
+
// 确保日志目录存在
|
|
70
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
71
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
const timestamp = new Date().toISOString();
|
|
69
75
|
const sanitizedStack = sanitizeStackTrace(err.stack);
|
|
70
76
|
const sanitizedMessage = sanitizeStackTrace(err.message);
|
|
@@ -114,24 +120,8 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
114
120
|
process.exit(1);
|
|
115
121
|
});
|
|
116
122
|
|
|
117
|
-
// 颜色输出
|
|
118
|
-
const colors =
|
|
119
|
-
reset: '\x1b[0m',
|
|
120
|
-
red: '\x1b[31m',
|
|
121
|
-
green: '\x1b[32m',
|
|
122
|
-
yellow: '\x1b[33m',
|
|
123
|
-
blue: '\x1b[34m',
|
|
124
|
-
cyan: '\x1b[36m',
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
function log(msg, color = 'reset') {
|
|
128
|
-
console.log(`${colors[color]}${msg}${colors.reset}`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function success(msg) { log(`✅ ${msg}`, 'green'); }
|
|
132
|
-
function error(msg) { log(`❌ ${msg}`, 'red'); }
|
|
133
|
-
function info(msg) { log(`ℹ️ ${msg}`, 'blue'); }
|
|
134
|
-
function warn(msg) { log(`⚠️ ${msg}`, 'yellow'); }
|
|
123
|
+
// 颜色输出 - 使用共享模块
|
|
124
|
+
const { colors, log, success, error, info, warn } = require('./logger');
|
|
135
125
|
|
|
136
126
|
// ==================== 进度反馈机制 ====================
|
|
137
127
|
|
|
@@ -289,6 +279,21 @@ function getPluginDir() {
|
|
|
289
279
|
return path.join(home, '.claude', 'plugins', PLUGIN_NAME);
|
|
290
280
|
}
|
|
291
281
|
|
|
282
|
+
// 获取 commands 安装路径
|
|
283
|
+
// 注意:直接安装到 commands 根目录,不使用子目录
|
|
284
|
+
// 因为 Claude Code 的子目录命名空间功能存在 Bug (Issue #2422)
|
|
285
|
+
// macOS 原生客户端不支持 /prefix:namespace:command 格式
|
|
286
|
+
function getCommandsDir() {
|
|
287
|
+
const home = os.homedir();
|
|
288
|
+
return path.join(home, '.claude', 'commands');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 获取 skills 安装路径
|
|
292
|
+
function getSkillsDir() {
|
|
293
|
+
const home = os.homedir();
|
|
294
|
+
return path.join(home, '.claude', 'skills');
|
|
295
|
+
}
|
|
296
|
+
|
|
292
297
|
// 获取当前包的路径
|
|
293
298
|
function getPackageDir() {
|
|
294
299
|
return path.resolve(__dirname, '..');
|
|
@@ -401,6 +406,169 @@ function setHookPermissions(pluginDir) {
|
|
|
401
406
|
}
|
|
402
407
|
}
|
|
403
408
|
|
|
409
|
+
/**
|
|
410
|
+
* 安装 slash commands 到 ~/.claude/commands/
|
|
411
|
+
* @param {string} packageDir - 源目录
|
|
412
|
+
* @returns {Object} 安装结果
|
|
413
|
+
*/
|
|
414
|
+
function installCommands(packageDir) {
|
|
415
|
+
const commandsDir = getCommandsDir();
|
|
416
|
+
const commandsSrc = path.join(packageDir, 'commands');
|
|
417
|
+
const stats = { count: 0, errors: [], cleaned: false };
|
|
418
|
+
|
|
419
|
+
info('正在安装 slash commands...');
|
|
420
|
+
|
|
421
|
+
// 清理旧版本的 zcf 子目录(1.0.8 及之前版本使用 /zcf:* 命令格式)
|
|
422
|
+
const legacyZcfDir = path.join(commandsDir, 'zcf');
|
|
423
|
+
if (fs.existsSync(legacyZcfDir)) {
|
|
424
|
+
try {
|
|
425
|
+
fs.rmSync(legacyZcfDir, { recursive: true, force: true });
|
|
426
|
+
stats.cleaned = true;
|
|
427
|
+
info('已清理旧版本 zcf 子目录');
|
|
428
|
+
} catch (err) {
|
|
429
|
+
warn(`清理旧版本目录失败: ${err.message}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// 创建 commands 目录
|
|
434
|
+
if (!fs.existsSync(commandsDir)) {
|
|
435
|
+
fs.mkdirSync(commandsDir, { recursive: true });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// 复制所有 .md 文件
|
|
439
|
+
if (fs.existsSync(commandsSrc)) {
|
|
440
|
+
const mdFiles = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
|
|
441
|
+
for (const file of mdFiles) {
|
|
442
|
+
try {
|
|
443
|
+
fs.copyFileSync(
|
|
444
|
+
path.join(commandsSrc, file),
|
|
445
|
+
path.join(commandsDir, file)
|
|
446
|
+
);
|
|
447
|
+
stats.count++;
|
|
448
|
+
} catch (err) {
|
|
449
|
+
stats.errors.push(`复制 ${file} 失败: ${err.message}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (stats.count > 0) {
|
|
455
|
+
success(`Slash commands 安装完成 (${stats.count} 个命令)`);
|
|
456
|
+
info(`Commands 位置: ${commandsDir}`);
|
|
457
|
+
if (stats.cleaned) {
|
|
458
|
+
warn('⚠️ 命令格式已更改:请使用 /yishan 而非 /zcf:yishan');
|
|
459
|
+
}
|
|
460
|
+
} else {
|
|
461
|
+
warn('未找到任何命令文件');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return stats;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* 安装 skills 到 ~/.claude/skills/
|
|
469
|
+
* @param {string} packageDir - 源目录
|
|
470
|
+
* @returns {Object} 安装结果
|
|
471
|
+
*/
|
|
472
|
+
function installSkills(packageDir) {
|
|
473
|
+
const skillsDir = getSkillsDir();
|
|
474
|
+
const skillsSrc = path.join(packageDir, 'skills');
|
|
475
|
+
const stats = { count: 0, errors: [] };
|
|
476
|
+
|
|
477
|
+
info('正在安装 skills...');
|
|
478
|
+
|
|
479
|
+
if (fs.existsSync(skillsSrc)) {
|
|
480
|
+
const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true })
|
|
481
|
+
.filter(d => d.isDirectory());
|
|
482
|
+
|
|
483
|
+
for (const skillDir of skillDirs) {
|
|
484
|
+
const skillName = skillDir.name;
|
|
485
|
+
const skillSrcDir = path.join(skillsSrc, skillName);
|
|
486
|
+
const skillDestDir = path.join(skillsDir, skillName);
|
|
487
|
+
|
|
488
|
+
try {
|
|
489
|
+
// 创建 skill 目录
|
|
490
|
+
if (!fs.existsSync(skillDestDir)) {
|
|
491
|
+
fs.mkdirSync(skillDestDir, { recursive: true });
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// 复制 SKILL.md (如果存在)
|
|
495
|
+
const skillMdSrc = path.join(skillSrcDir, 'SKILL.md');
|
|
496
|
+
if (fs.existsSync(skillMdSrc)) {
|
|
497
|
+
fs.copyFileSync(skillMdSrc, path.join(skillDestDir, 'SKILL.md'));
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// 复制其他支持文件(排除 skill.json)
|
|
501
|
+
const files = fs.readdirSync(skillSrcDir, { withFileTypes: true })
|
|
502
|
+
.filter(f => f.isFile() && f.name !== 'skill.json');
|
|
503
|
+
|
|
504
|
+
for (const file of files) {
|
|
505
|
+
try {
|
|
506
|
+
fs.copyFileSync(
|
|
507
|
+
path.join(skillSrcDir, file.name),
|
|
508
|
+
path.join(skillDestDir, file.name)
|
|
509
|
+
);
|
|
510
|
+
} catch {
|
|
511
|
+
// 忽略非关键文件的复制错误
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
stats.count++;
|
|
516
|
+
} catch (err) {
|
|
517
|
+
stats.errors.push(`安装 skill ${skillName} 失败: ${err.message}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (stats.count > 0) {
|
|
523
|
+
success(`Skills 安装完成 (${stats.count} 个 skill)`);
|
|
524
|
+
info(`Skills 位置: ${skillsDir}`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return stats;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 验证安装结果
|
|
532
|
+
* @returns {Object} 验证结果
|
|
533
|
+
*/
|
|
534
|
+
function verifyInstallation() {
|
|
535
|
+
info('验证安装...');
|
|
536
|
+
const commandsDir = getCommandsDir();
|
|
537
|
+
const result = { success: true, errors: [] };
|
|
538
|
+
|
|
539
|
+
// 检查关键命令文件
|
|
540
|
+
const yishanPath = path.join(commandsDir, 'yishan.md');
|
|
541
|
+
if (fs.existsSync(yishanPath)) {
|
|
542
|
+
success('✓ yishan.md 已安装');
|
|
543
|
+
} else {
|
|
544
|
+
warn('✗ yishan.md 未找到');
|
|
545
|
+
result.errors.push('yishan.md 未找到');
|
|
546
|
+
result.success = false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// 检查命令数量
|
|
550
|
+
if (fs.existsSync(commandsDir)) {
|
|
551
|
+
const cmdFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
|
|
552
|
+
if (cmdFiles.length > 0) {
|
|
553
|
+
success(`✓ 已安装 ${cmdFiles.length} 个命令`);
|
|
554
|
+
} else {
|
|
555
|
+
warn('✗ 未检测到任何命令文件');
|
|
556
|
+
result.errors.push('未检测到命令文件');
|
|
557
|
+
result.success = false;
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
warn('✗ commands 目录不存在');
|
|
561
|
+
result.errors.push('commands 目录不存在');
|
|
562
|
+
result.success = false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!result.success) {
|
|
566
|
+
warn('安装可能不完整,请检查上述警告');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
|
|
404
572
|
/**
|
|
405
573
|
* 注册插件到 Claude Code
|
|
406
574
|
* @param {string} pluginDir - 插件目录
|
|
@@ -459,7 +627,25 @@ function acquireLock(lockFile, timeout = LOCK_TIMEOUT_MS) {
|
|
|
459
627
|
if (err.code === 'EEXIST') {
|
|
460
628
|
// 锁文件已存在,检查是否过期
|
|
461
629
|
try {
|
|
462
|
-
const
|
|
630
|
+
const lockData = fs.readFileSync(lockFile, 'utf8');
|
|
631
|
+
let existingLock;
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
existingLock = JSON.parse(lockData);
|
|
635
|
+
} catch (parseErr) {
|
|
636
|
+
// JSON 解析失败,锁文件可能已损坏,清理后重试
|
|
637
|
+
warn('锁文件格式损坏,正在清理...');
|
|
638
|
+
fs.unlinkSync(lockFile);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// 验证锁内容的有效性
|
|
643
|
+
if (!existingLock || typeof existingLock.timestamp !== 'number') {
|
|
644
|
+
warn('锁文件内容无效,正在清理...');
|
|
645
|
+
fs.unlinkSync(lockFile);
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
|
|
463
649
|
const lockAge = Date.now() - existingLock.timestamp;
|
|
464
650
|
|
|
465
651
|
// 如果锁超过阈值,认为是陈旧锁,强制删除
|
|
@@ -476,18 +662,32 @@ function acquireLock(lockFile, timeout = LOCK_TIMEOUT_MS) {
|
|
|
476
662
|
|
|
477
663
|
// 其他进程持有锁,等待
|
|
478
664
|
info(`另一个 oh-my-claude 进程 (PID: ${existingLock.pid}) 正在运行,等待中...`);
|
|
479
|
-
} catch {
|
|
480
|
-
//
|
|
665
|
+
} catch (readErr) {
|
|
666
|
+
// 读取锁文件失败,可能已被删除或权限问题
|
|
667
|
+
if (readErr.code === 'ENOENT') {
|
|
668
|
+
// 文件已被删除,直接重试
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
// 其他读取错误,记录后继续
|
|
481
672
|
continue;
|
|
482
673
|
}
|
|
483
674
|
|
|
484
|
-
//
|
|
675
|
+
// 使用 spawnSync 实现非阻塞等待,避免 CPU 空转
|
|
485
676
|
const waitTime = Math.min(LOCK_RETRY_INTERVAL_MS, timeout - (Date.now() - startTime));
|
|
486
677
|
if (waitTime > 0) {
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
678
|
+
// 使用系统 sleep 命令等待,避免忙等待占用 CPU
|
|
679
|
+
try {
|
|
680
|
+
if (process.platform === 'win32') {
|
|
681
|
+
// Windows: 使用 ping localhost 实现延时
|
|
682
|
+
spawnSync('ping', ['-n', '2', '127.0.0.1'], { stdio: 'ignore', timeout: waitTime + 1000 });
|
|
683
|
+
} else {
|
|
684
|
+
// Unix: 使用 sleep 命令
|
|
685
|
+
spawnSync('sleep', [(waitTime / 1000).toFixed(1)], { stdio: 'ignore', timeout: waitTime + 1000 });
|
|
686
|
+
}
|
|
687
|
+
} catch {
|
|
688
|
+
// 如果 sleep 失败,使用短暂的忙等待作为后备
|
|
689
|
+
const endWait = Date.now() + Math.min(waitTime, 100);
|
|
690
|
+
while (Date.now() < endWait) { /* 短暂等待 */ }
|
|
491
691
|
}
|
|
492
692
|
}
|
|
493
693
|
} else {
|
|
@@ -508,14 +708,26 @@ function acquireLock(lockFile, timeout = LOCK_TIMEOUT_MS) {
|
|
|
508
708
|
function releaseLock(lockFile) {
|
|
509
709
|
try {
|
|
510
710
|
if (fs.existsSync(lockFile)) {
|
|
711
|
+
// 读取并验证是我们自己的锁
|
|
712
|
+
const lockData = fs.readFileSync(lockFile, 'utf8');
|
|
713
|
+
|
|
714
|
+
let lockContent;
|
|
715
|
+
try {
|
|
716
|
+
lockContent = JSON.parse(lockData);
|
|
717
|
+
} catch {
|
|
718
|
+
// JSON 解析失败,锁文件可能已损坏
|
|
719
|
+
// 安全删除损坏的锁文件
|
|
720
|
+
fs.unlinkSync(lockFile);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
511
724
|
// 验证是我们自己的锁
|
|
512
|
-
|
|
513
|
-
if (lockContent.pid === process.pid) {
|
|
725
|
+
if (lockContent && lockContent.pid === process.pid) {
|
|
514
726
|
fs.unlinkSync(lockFile);
|
|
515
727
|
}
|
|
516
728
|
}
|
|
517
729
|
} catch {
|
|
518
|
-
//
|
|
730
|
+
// 忽略释放锁时的错误(文件可能已被删除等)
|
|
519
731
|
}
|
|
520
732
|
}
|
|
521
733
|
|
|
@@ -631,14 +843,44 @@ function install() {
|
|
|
631
843
|
process.exit(1);
|
|
632
844
|
}
|
|
633
845
|
|
|
634
|
-
//
|
|
635
|
-
|
|
846
|
+
// 安装 commands 到 ~/.claude/commands/
|
|
847
|
+
installCommands(packageDir);
|
|
848
|
+
|
|
849
|
+
// 安装 skills 到 ~/.claude/skills/
|
|
850
|
+
installSkills(packageDir);
|
|
636
851
|
|
|
852
|
+
// 验证安装
|
|
853
|
+
verifyInstallation();
|
|
854
|
+
|
|
855
|
+
// 显示完成信息
|
|
637
856
|
log('\n🎉 安装完成!', 'green');
|
|
638
|
-
log('━'.repeat(DIVIDER_LENGTH), '
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
857
|
+
log('━'.repeat(DIVIDER_LENGTH), 'green');
|
|
858
|
+
log('');
|
|
859
|
+
warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
|
|
860
|
+
warn(' (仅关闭窗口可能不够,需要完全退出应用)');
|
|
861
|
+
if (os.platform() === 'darwin') {
|
|
862
|
+
warn(' macOS: 使用 Cmd+Q 完全退出应用');
|
|
863
|
+
warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
|
|
864
|
+
}
|
|
865
|
+
log('');
|
|
866
|
+
log('快速开始:', 'cyan');
|
|
867
|
+
info(' /yishan - 愚公移山模式(大规模任务)');
|
|
868
|
+
info(' /zhuge - 诸葛顾问(架构设计)');
|
|
869
|
+
info(' /bianque - 扁鹊诊断(调试问题)');
|
|
870
|
+
info(' /luban - 鲁班巧工(前端开发)');
|
|
871
|
+
info(' /wukong - 悟空探索(代码搜索)');
|
|
872
|
+
log('');
|
|
873
|
+
log('安装位置:', 'cyan');
|
|
874
|
+
info(` Commands: ${getCommandsDir()}`);
|
|
875
|
+
info(` Skills: ${getSkillsDir()}`);
|
|
876
|
+
info(` Plugin: ${pluginDir}`);
|
|
877
|
+
log('');
|
|
878
|
+
log('故障排除:', 'cyan');
|
|
879
|
+
info(' 如果命令未出现在 /help 中:');
|
|
880
|
+
info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
|
|
881
|
+
info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
|
|
882
|
+
info(' 3. 清除缓存: rm ~/.claude.json && 重启');
|
|
883
|
+
log('');
|
|
642
884
|
}
|
|
643
885
|
|
|
644
886
|
// 执行卸载操作
|
|
@@ -763,6 +1005,40 @@ function update() {
|
|
|
763
1005
|
process.exit(1);
|
|
764
1006
|
}
|
|
765
1007
|
|
|
1008
|
+
// 更新 commands 到 ~/.claude/commands/
|
|
1009
|
+
const cmdStats = installCommands(packageDir);
|
|
1010
|
+
|
|
1011
|
+
// 更新 skills 到 ~/.claude/skills/
|
|
1012
|
+
installSkills(packageDir);
|
|
1013
|
+
|
|
1014
|
+
// 验证安装
|
|
1015
|
+
verifyInstallation();
|
|
1016
|
+
|
|
1017
|
+
// 显示完成信息
|
|
1018
|
+
log('\n🎉 更新完成!', 'green');
|
|
1019
|
+
log('━'.repeat(DIVIDER_LENGTH), 'green');
|
|
1020
|
+
log('');
|
|
1021
|
+
warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
|
|
1022
|
+
warn(' (仅关闭窗口可能不够,需要完全退出应用)');
|
|
1023
|
+
if (os.platform() === 'darwin') {
|
|
1024
|
+
warn(' macOS: 使用 Cmd+Q 完全退出应用');
|
|
1025
|
+
warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
|
|
1026
|
+
}
|
|
1027
|
+
log('');
|
|
1028
|
+
|
|
1029
|
+
// 如果清理了旧版本,显示命令格式变化提示
|
|
1030
|
+
if (cmdStats.cleaned) {
|
|
1031
|
+
log('📝 命令格式变更提示:', 'yellow');
|
|
1032
|
+
info(' 旧格式: /zcf:yishan, /zcf:zhuge, /zcf:bianque ...');
|
|
1033
|
+
info(' 新格式: /yishan, /zhuge, /bianque ...');
|
|
1034
|
+
log('');
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
log('故障排除:', 'cyan');
|
|
1038
|
+
info(' 如果命令未出现在 /help 中:');
|
|
1039
|
+
info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
|
|
1040
|
+
info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
|
|
1041
|
+
info(' 3. 清除缓存: rm ~/.claude.json && 重启');
|
|
766
1042
|
log('');
|
|
767
1043
|
}
|
|
768
1044
|
|
|
@@ -983,42 +1259,103 @@ function copyDir(src, dest, options = {}) {
|
|
|
983
1259
|
return stats;
|
|
984
1260
|
}
|
|
985
1261
|
|
|
986
|
-
//
|
|
987
|
-
const args = process.argv.slice(2);
|
|
988
|
-
const command = args[0] || 'help';
|
|
1262
|
+
// ==================== 模块导出(供测试使用) ====================
|
|
989
1263
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1264
|
+
/**
|
|
1265
|
+
* 导出内部函数供测试使用
|
|
1266
|
+
* 仅在测试环境下使用,生产环境下这些函数通过 CLI 入口调用
|
|
1267
|
+
*/
|
|
1268
|
+
module.exports = {
|
|
1269
|
+
// 常量
|
|
1270
|
+
VERSION,
|
|
1271
|
+
PLUGIN_NAME,
|
|
1272
|
+
LOCK_TIMEOUT_MS,
|
|
1273
|
+
LOCK_STALE_MS,
|
|
1274
|
+
LARGE_FILE_THRESHOLD_BYTES,
|
|
1275
|
+
LOG_DIR,
|
|
1276
|
+
ERROR_LOG_PATH,
|
|
1277
|
+
|
|
1278
|
+
// 安全函数
|
|
1279
|
+
sanitizeStackTrace,
|
|
1280
|
+
getUserFriendlyError,
|
|
1281
|
+
|
|
1282
|
+
// 文件操作
|
|
1283
|
+
safeReadFile,
|
|
1284
|
+
safeWriteFile,
|
|
1285
|
+
safeRemoveDir,
|
|
1286
|
+
safeCopyFile,
|
|
1287
|
+
copyDir,
|
|
1288
|
+
smartCopyFile,
|
|
1289
|
+
copyPluginFiles,
|
|
1290
|
+
|
|
1291
|
+
// 路径函数
|
|
1292
|
+
getPluginDir,
|
|
1293
|
+
getCommandsDir,
|
|
1294
|
+
getSkillsDir,
|
|
1295
|
+
getPackageDir,
|
|
1296
|
+
getLockFilePath,
|
|
1297
|
+
|
|
1298
|
+
// 锁机制
|
|
1299
|
+
acquireLock,
|
|
1300
|
+
releaseLock,
|
|
1301
|
+
|
|
1302
|
+
// 进度类
|
|
1303
|
+
ProgressIndicator,
|
|
1304
|
+
|
|
1305
|
+
// 执行器
|
|
1306
|
+
executeWithRollback,
|
|
1307
|
+
setHookPermissions,
|
|
1308
|
+
|
|
1309
|
+
// 验证
|
|
1310
|
+
verifyInstallation,
|
|
1311
|
+
|
|
1312
|
+
// 主要命令(供集成测试使用)
|
|
1313
|
+
install,
|
|
1314
|
+
uninstall,
|
|
1315
|
+
update,
|
|
1316
|
+
verify,
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
// ==================== CLI 入口 ====================
|
|
1320
|
+
|
|
1321
|
+
// 仅在直接运行时执行(非 require)
|
|
1322
|
+
if (require.main === module) {
|
|
1323
|
+
const args = process.argv.slice(2);
|
|
1324
|
+
const command = args[0] || 'help';
|
|
1325
|
+
|
|
1326
|
+
switch (command) {
|
|
1327
|
+
case 'install':
|
|
1328
|
+
case 'i':
|
|
1329
|
+
install();
|
|
1330
|
+
break;
|
|
1331
|
+
case 'uninstall':
|
|
1332
|
+
case 'remove':
|
|
1333
|
+
case 'rm':
|
|
1334
|
+
uninstall();
|
|
1335
|
+
break;
|
|
1336
|
+
case 'update':
|
|
1337
|
+
case 'upgrade':
|
|
1338
|
+
case 'up':
|
|
1339
|
+
update();
|
|
1340
|
+
break;
|
|
1341
|
+
case 'verify':
|
|
1342
|
+
case 'check':
|
|
1343
|
+
case 'doctor':
|
|
1344
|
+
verify();
|
|
1345
|
+
break;
|
|
1346
|
+
case 'version':
|
|
1347
|
+
case '-v':
|
|
1348
|
+
case '--version':
|
|
1349
|
+
showVersion();
|
|
1350
|
+
break;
|
|
1351
|
+
case 'help':
|
|
1352
|
+
case '-h':
|
|
1353
|
+
case '--help':
|
|
1354
|
+
showHelp();
|
|
1355
|
+
break;
|
|
1356
|
+
default:
|
|
1357
|
+
error(`未知命令: ${command}`);
|
|
1358
|
+
showHelp();
|
|
1359
|
+
process.exit(1);
|
|
1360
|
+
}
|
|
1024
1361
|
}
|
package/scripts/install.ps1
CHANGED
|
@@ -304,12 +304,12 @@ function Show-Success {
|
|
|
304
304
|
Write-Host "⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令" -ForegroundColor Yellow
|
|
305
305
|
Write-Host " (仅关闭窗口可能不够,需要完全退出应用)" -ForegroundColor Yellow
|
|
306
306
|
Write-Host ""
|
|
307
|
-
Write-Host "
|
|
308
|
-
Write-Host " /
|
|
309
|
-
Write-Host " /
|
|
310
|
-
Write-Host " /
|
|
311
|
-
Write-Host " /
|
|
312
|
-
Write-Host " /
|
|
307
|
+
Write-Host "快速开始:" -ForegroundColor Cyan
|
|
308
|
+
Write-Host " /yishan - 愚公移山模式(大规模任务)"
|
|
309
|
+
Write-Host " /zhuge - 诸葛顾问(架构设计)"
|
|
310
|
+
Write-Host " /bianque - 扁鹊诊断(调试问题)"
|
|
311
|
+
Write-Host " /luban - 鲁班巧工(前端开发)"
|
|
312
|
+
Write-Host " /wukong - 悟空探索(代码搜索)"
|
|
313
313
|
Write-Host ""
|
|
314
314
|
Write-Host "查看所有 Agent:" -ForegroundColor Cyan
|
|
315
315
|
Write-Host " https://github.com/$Repo#-agent-列表"
|
package/scripts/install.sh
CHANGED
|
@@ -259,12 +259,12 @@ show_success() {
|
|
|
259
259
|
echo -e "${YELLOW}⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令${NC}"
|
|
260
260
|
echo -e "${YELLOW} (仅关闭窗口可能不够,需要完全退出应用)${NC}"
|
|
261
261
|
echo ""
|
|
262
|
-
echo -e "${CYAN}
|
|
263
|
-
echo " /
|
|
264
|
-
echo " /
|
|
265
|
-
echo " /
|
|
266
|
-
echo " /
|
|
267
|
-
echo " /
|
|
262
|
+
echo -e "${CYAN}快速开始:${NC}"
|
|
263
|
+
echo " /yishan - 愚公移山模式(大规模任务)"
|
|
264
|
+
echo " /zhuge - 诸葛顾问(架构设计)"
|
|
265
|
+
echo " /bianque - 扁鹊诊断(调试问题)"
|
|
266
|
+
echo " /luban - 鲁班巧工(前端开发)"
|
|
267
|
+
echo " /wukong - 悟空探索(代码搜索)"
|
|
268
268
|
echo ""
|
|
269
269
|
echo -e "${CYAN}查看所有 Agent:${NC}"
|
|
270
270
|
echo " https://github.com/$REPO#-agent-列表"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oh-my-claude 共享日志模块
|
|
3
|
+
* 提供统一的颜色输出和日志函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ANSI 颜色码定义
|
|
7
|
+
const colors = {
|
|
8
|
+
reset: '\x1b[0m',
|
|
9
|
+
red: '\x1b[31m',
|
|
10
|
+
green: '\x1b[32m',
|
|
11
|
+
yellow: '\x1b[33m',
|
|
12
|
+
blue: '\x1b[34m',
|
|
13
|
+
cyan: '\x1b[36m',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 带颜色的日志输出
|
|
18
|
+
* @param {string} msg - 消息内容
|
|
19
|
+
* @param {string} color - 颜色名称,默认 'reset'
|
|
20
|
+
*/
|
|
21
|
+
function log(msg, color = 'reset') {
|
|
22
|
+
console.log(`${colors[color]}${msg}${colors.reset}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 成功消息(绿色 ✅)
|
|
27
|
+
*/
|
|
28
|
+
function success(msg) {
|
|
29
|
+
log(`✅ ${msg}`, 'green');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 错误消息(红色 ❌)
|
|
34
|
+
*/
|
|
35
|
+
function error(msg) {
|
|
36
|
+
log(`❌ ${msg}`, 'red');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 信息消息(蓝色 ℹ️)
|
|
41
|
+
*/
|
|
42
|
+
function info(msg) {
|
|
43
|
+
log(`ℹ️ ${msg}`, 'blue');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 警告消息(黄色 ⚠️)
|
|
48
|
+
*/
|
|
49
|
+
function warn(msg) {
|
|
50
|
+
log(`⚠️ ${msg}`, 'yellow');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
colors,
|
|
55
|
+
log,
|
|
56
|
+
success,
|
|
57
|
+
error,
|
|
58
|
+
info,
|
|
59
|
+
warn,
|
|
60
|
+
};
|
package/scripts/postinstall.js
CHANGED
|
@@ -5,12 +5,8 @@
|
|
|
5
5
|
* 在 npm install 后显示安装提示
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
cyan: '\x1b[36m',
|
|
11
|
-
green: '\x1b[32m',
|
|
12
|
-
yellow: '\x1b[33m',
|
|
13
|
-
};
|
|
8
|
+
// 使用共享日志模块
|
|
9
|
+
const { colors } = require('./logger');
|
|
14
10
|
|
|
15
11
|
console.log(`
|
|
16
12
|
${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}
|
package/scripts/sync-version.js
CHANGED
|
@@ -53,32 +53,8 @@ const FILES = [
|
|
|
53
53
|
},
|
|
54
54
|
];
|
|
55
55
|
|
|
56
|
-
// 颜色输出
|
|
57
|
-
const colors =
|
|
58
|
-
reset: '\x1b[0m',
|
|
59
|
-
red: '\x1b[31m',
|
|
60
|
-
green: '\x1b[32m',
|
|
61
|
-
yellow: '\x1b[33m',
|
|
62
|
-
blue: '\x1b[34m',
|
|
63
|
-
cyan: '\x1b[36m',
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
function log(msg, color = 'reset') {
|
|
67
|
-
console.log(`${colors[color]}${msg}${colors.reset}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function success(msg) {
|
|
71
|
-
log(`✅ ${msg}`, 'green');
|
|
72
|
-
}
|
|
73
|
-
function error(msg) {
|
|
74
|
-
log(`❌ ${msg}`, 'red');
|
|
75
|
-
}
|
|
76
|
-
function info(msg) {
|
|
77
|
-
log(`ℹ️ ${msg}`, 'blue');
|
|
78
|
-
}
|
|
79
|
-
function warn(msg) {
|
|
80
|
-
log(`⚠️ ${msg}`, 'yellow');
|
|
81
|
-
}
|
|
56
|
+
// 颜色输出 - 使用共享模块
|
|
57
|
+
const { colors, log, success, error, info, warn } = require('./logger');
|
|
82
58
|
|
|
83
59
|
// 验证版本号格式
|
|
84
60
|
function isValidVersion(version) {
|