@luban-code/luban-code 1.66.1 → 1.100.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/LICENSE +204 -204
- package/README.md +82 -8
- package/README.zh.md +84 -10
- package/dist/acp.js +1045 -0
- package/dist/cli-plugin.js +225 -0
- package/dist/cli.js +447 -320
- package/dist/extension.js +579 -279
- package/package.json +767 -551
- package/registry/README.md +160 -0
- package/registry/entry.schema.json +115 -0
- package/registry/keys/dev-1.pub +5 -0
- package/registry/keys/dev-2.pub +5 -0
- package/registry/keys/registry-ci.pub +5 -0
- package/registry/registry.json +178 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# registry/ —— Luban 插件 / 技能可信索引(L0 + L1)
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
registry/
|
|
5
|
+
registry.json 索引本体(entries[])
|
|
6
|
+
entry.schema.json 条目字段规范(L0 发现性字段 + L1 权限/签名/审核)
|
|
7
|
+
keys/<keyId>.pub 验签公钥(JSON: {keyId, algo, publicKey(base64 DER SPKI)})
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## 用法
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# 插件 / 技能源码仓(monorepo):https://github.com/eppen/luban-plugins
|
|
14
|
+
# 插件结构 plugins/<name>/,技能结构 skills/<name>/,tag 都是 <name>-v<version>
|
|
15
|
+
|
|
16
|
+
# 算 sourceSha256(目录的确定性哈希,任何人 clone 后都能复算出同一个值)
|
|
17
|
+
node scripts/registry-tool.mjs hash ../luban-plugins/plugins/demo
|
|
18
|
+
node scripts/registry-tool.mjs hash ../luban-plugins/skills/changelog-draft
|
|
19
|
+
|
|
20
|
+
# 生成签名密钥(私钥写到仓库外,勿提交;别放 /tmp,重启会被清掉)
|
|
21
|
+
node scripts/registry-tool.mjs keygen dev-1 --out '~/.luban-keys'
|
|
22
|
+
|
|
23
|
+
# 修改条目后重新签名(改 permissions/tools/install/sourceSha256 都必须重签)
|
|
24
|
+
node scripts/registry-tool.mjs sign demo --key '~/.luban-keys/dev-1.priv.json'
|
|
25
|
+
|
|
26
|
+
# 校验:字段规范 + 命名空间冲突 + 签名 + 与已装包一致性
|
|
27
|
+
node scripts/registry-tool.mjs verify # 离线:警告不阻断(断网 / 内网也能跑)
|
|
28
|
+
node scripts/registry-tool.mjs verify --strict # 除错误外,一个签名都没有也算失败
|
|
29
|
+
node scripts/registry-tool.mjs verify --remote # 按 install 来源拉包,比对包内清单与 sourceSha256
|
|
30
|
+
|
|
31
|
+
# 客户端:按索引装(验签 → 审核状态 → 按 tag 拉包 → 校对 sourceSha256 → 落盘)
|
|
32
|
+
luban plugin add demo --dry-run # 只校验不落盘
|
|
33
|
+
luban plugin add demo # 装进 ~/.luban/plugins/demo
|
|
34
|
+
luban plugin remove demo # 移进回收站,可还原
|
|
35
|
+
|
|
36
|
+
luban skill list # 只看 type=skill 的条目
|
|
37
|
+
luban skill add changelog-draft --dry-run # 技能同样先校验
|
|
38
|
+
luban skill add changelog-draft # 装进 ~/.luban/skills/changelog-draft
|
|
39
|
+
luban skill remove changelog-draft # 移进 ~/.luban/skills-trash/,可还原
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
> `luban plugin …` 与 `luban skill …` 是同一份索引的两个视角(等价于 `--type plugin` / `--type skill`)。
|
|
43
|
+
> 两个前缀都显式带上 `--type`,所以 `luban plugin add <技能名>` 会明确报「是技能,不是插件」——
|
|
44
|
+
> 不会出现「以为装插件、其实装进了技能目录」。
|
|
45
|
+
|
|
46
|
+
## 条目与源码仓的对应
|
|
47
|
+
|
|
48
|
+
插件源码不在本仓,在 [eppen/luban-plugins](https://github.com/eppen/luban-plugins):
|
|
49
|
+
|
|
50
|
+
| 字段 | 值 |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `repo` | `https://github.com/eppen/luban-plugins`(monorepo 根) |
|
|
53
|
+
| `install` | `{ type: "git", url: "git@github.com:eppen/luban-plugins.git", subdir: "plugins/<name>", target: "global" }` |
|
|
54
|
+
| tag | `<name>-v<version>`,例如 `demo-v0.1.0`、`changelog-draft-v0.1.0` |
|
|
55
|
+
| `sourceSha256` | 见下方算法;插件对 `plugins/<name>` 算、技能对 `skills/<name>` 算 |
|
|
56
|
+
|
|
57
|
+
技能条目把这些字段换掉即可(其余字段一致,验签逻辑完全相同):
|
|
58
|
+
|
|
59
|
+
| 字段 | 技能的值 |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `type` | `"skill"` |
|
|
62
|
+
| `install.subdir` | `skills/<name>` |
|
|
63
|
+
| `tools` | `[]`(技能不给模型新增工具) |
|
|
64
|
+
| `permissions` | `{}`(纯 Markdown,没有执行能力) |
|
|
65
|
+
| 包内清单 | `SKILL.md` 的 frontmatter:`name` 必须等于条目名、`version` 可选但写了就要一致 |
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
sourceSha256 算法(目录哈希,与机器 / 时间无关)
|
|
69
|
+
1. 遍历目录,跳过 node_modules / .git / .DS_Store,符号链接跳过并告警;
|
|
70
|
+
2. 文件按相对路径(用 / 分隔)的 UTF-8 字节序升序排序;
|
|
71
|
+
3. 对每个文件算 sha256 hex,向总哈希喂入 `<相对路径>\0<文件 sha256 hex>\n`;
|
|
72
|
+
4. 总哈希的 hex 即 sourceSha256。
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
复算方式(证明可复现):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone --depth 1 https://github.com/eppen/luban-plugins.git /tmp/lp
|
|
79
|
+
node scripts/registry-tool.mjs hash /tmp/lp/plugins/demo
|
|
80
|
+
# 应等于 registry.json 里 demo 条目的 sourceSha256
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 校验会挡住什么
|
|
84
|
+
|
|
85
|
+
| 检查 | 级别 | 说明 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 条目前提字段 / semver / ctxApi 区间 | error | 索引本身不可用 |
|
|
88
|
+
| `name` 重复 | error | 工具前缀 `pl__<name>__` 会冲突 |
|
|
89
|
+
| 工具名不合规(大写、连字符) | error | 生成出的工具名无法调用 |
|
|
90
|
+
| `permissions` 出现未知权限键 | error | 防止悄悄扩权绕过展示 |
|
|
91
|
+
| 签名与内容不符 | error | 改了权限却没重签 = 篡改 |
|
|
92
|
+
| 索引 tools ≠ 已安装 plugin.json | error | 防「先上架后换包」 |
|
|
93
|
+
| `type=skill` 却声明了 `tools` | error | 技能不新增工具,声明了说明字段用错 |
|
|
94
|
+
| `type=skill` 却声明了 `permissions` | warn | 忽略即可;技能没有执行能力 |
|
|
95
|
+
| 索引 name / version ≠ 已安装 `SKILL.md` 的 frontmatter | error | 技能侧的「先上架后换包」检查 |
|
|
96
|
+
| 未签名 / `review.status=pending` | warn | L0 可用,L1 未达标 |
|
|
97
|
+
| 工具全 readOnly 但 `shell/net/fsWrite` 为真 | warn | **需人工核对实现**,签名无法证明行为 |
|
|
98
|
+
| 缺 `sourceSha256` | warn | 安装不可复现 |
|
|
99
|
+
|
|
100
|
+
## 贡献者 vs 维护者:谁签名
|
|
101
|
+
|
|
102
|
+
签名是**维护者合并 PR 后补的工序**,不是贡献者的责任。私钥只在维护者手里,从不进仓库。
|
|
103
|
+
|
|
104
|
+
| 角色 | 做什么 | 需要私钥 |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| 贡献者 | fork luban-plugins → 加插件/技能 → 在本仓 `registry.json` 加条目(**不写 `signature`**)→ 提 PR | ❌ |
|
|
107
|
+
| 维护者 | 审核代码 → 合并 → `sign <name>` 补签名 → 推送 | ✅ |
|
|
108
|
+
|
|
109
|
+
贡献者侧(无私钥):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# 算好 sourceSha256 填进条目,条目里不要有 signature 字段
|
|
113
|
+
node scripts/registry-tool.mjs hash /path/to/their-plugin
|
|
114
|
+
# 提 PR。未签名条目在 verify 里只是 WARN,不阻断 CI
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
维护者侧(合并后补签):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git pull origin main
|
|
121
|
+
node scripts/registry-tool.mjs sign their-plugin --key '~/.luban-keys/dev-2.priv.json'
|
|
122
|
+
git add registry/registry.json
|
|
123
|
+
git commit -m "chore(registry): sign their-plugin"
|
|
124
|
+
git push origin main
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
未签名条目 `verify` 报 `WARN [xxx] 未签名(L0 可接受,L1 要求签名后才算可信)`,
|
|
128
|
+
退出码只看 `errors.length`,警告不影响 CI 通过。所以「贡献者没私钥」是正常且预期的。
|
|
129
|
+
|
|
130
|
+
多人维护时可按发布者分 `keyId`(`keygen maintainer-alice`),每个签名条目的
|
|
131
|
+
`signature.keyId` 指向对应维护者的公钥;吊销某维护者只需从 `signingKeys` 移除其 keyId,
|
|
132
|
+
不用动条目本身。
|
|
133
|
+
|
|
134
|
+
## 约定
|
|
135
|
+
|
|
136
|
+
- 签名对象:去掉 `signature` 后按键名递归排序的紧凑 JSON(见 `scripts/registry-tool.mjs` 的 `canonical()`)。
|
|
137
|
+
- 私钥永不入库;`keys/` 下只放公钥。轮换时新增 `keyId`,旧条目保留旧签名,不追溯重签。
|
|
138
|
+
- `review.status = revoked` 的下架条目保留在索引里(供已安装用户告警),不删除。
|
|
139
|
+
- 改任何内容(`permissions` / `tools` / `install` / `sourceSha256`)都要重签,否则 `verify` 报签名校验失败。
|
|
140
|
+
- `name` 建立索引即冻结:它决定工具前缀 `pl__<name>__`,换名等于换一个新插件。
|
|
141
|
+
|
|
142
|
+
## 注意事项
|
|
143
|
+
|
|
144
|
+
- `keygen --out` 与 `sign --key` 都会展开 `~`;路径含空格时用引号。
|
|
145
|
+
- `verify` 默认**离线**:`sourceSha256` 只校验格式,「索引 tools ≠ plugin.json」读的是**本机**
|
|
146
|
+
`~/.luban/plugins/<name>/plugin.json`(作者本机有装就比,没装就跳过;技能同理读
|
|
147
|
+
`~/.luban/skills/<name>/SKILL.md`)。这是为了「断网 / 内网 / CI 也能跑」。
|
|
148
|
+
- `verify --remote` 走网络:按 `install` 字段拉一次包(与 `luban plugin add` / `luban skill add`
|
|
149
|
+
同一条链路,共用 `registry-client.js` 的 `fetchSource`),比对包内清单(`plugin.json` 或
|
|
150
|
+
`SKILL.md` 的 frontmatter)、目录哈希与索引是否一致,不依赖作者本机有没有装。
|
|
151
|
+
**发版前、审核时、CI 里都应该跑这条。**
|
|
152
|
+
拉包失败(tag 不存在 / 仓库里没这个 `subdir` / 没有 git)会直接报 ERROR 而不是跳过。
|
|
153
|
+
- 技能装进 `~/.luban/skills/<name>`(项目级 `luban skill add --target project` → `<repo>/.luban/skills/<name>`),
|
|
154
|
+
**不需要信任表**:纯 Markdown 不给模型新增执行能力,装完下一轮就被 `skills.js` 扫到。
|
|
155
|
+
已装清单写在 `<skills 根>/.installed.json` 而**不写进技能目录** —— 写进去就成了包内容,
|
|
156
|
+
`sourceSha256` 复算必然不符(与插件回收站元数据写同级 `<条目>.json` 同一个理由)。
|
|
157
|
+
- 当前三个条目(`demo` / `changelog-draft` / `cc-safety-net`)都用开发密钥 `dev-2` 签名
|
|
158
|
+
(私钥在 `~/.luban-keys/dev-2.priv.json`,不入库)。
|
|
159
|
+
早期的 `dev-1` 私钥已丢失,其公钥 `keys/dev-1.pub` 仅作历史保留,不再列入 `signingKeys`。
|
|
160
|
+
面向公众发布前应重新 `keygen` 一个新 `keyId` 并重签。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://luban.local/schemas/registry-entry-v1.json",
|
|
4
|
+
"title": "Luban 插件 / 技能索引条目(L0 + L1)",
|
|
5
|
+
"description": "L0 = 可发现(名称/版本/来源/校验和);L1 = 可信任(声明权限 + 签名 + 审核状态)。",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["name", "displayName", "description", "type", "version", "author", "license", "install", "ctxApi"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"type": {
|
|
11
|
+
"enum": ["plugin", "skill"],
|
|
12
|
+
"description": "plugin = 会执行代码的本地插件;skill = 纯文本说明书,无需权限声明"
|
|
13
|
+
},
|
|
14
|
+
"name": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^[a-z0-9][a-z0-9-]{1,39}$",
|
|
17
|
+
"description": "唯一标识,也是工具名前缀(插件工具名 = pl__<name>__<tool>),建立索引即固定,不允许复用"
|
|
18
|
+
},
|
|
19
|
+
"displayName": { "type": "string", "minLength": 1, "maxLength": 60 },
|
|
20
|
+
"description": { "type": "string", "minLength": 8, "maxLength": 200 },
|
|
21
|
+
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$" },
|
|
22
|
+
"author": { "type": "string", "minLength": 1 },
|
|
23
|
+
"license": { "type": "string", "minLength": 1 },
|
|
24
|
+
"repo": { "type": "string", "format": "uri" },
|
|
25
|
+
"homepage": { "type": "string", "format": "uri" },
|
|
26
|
+
"tags": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"maxItems": 8,
|
|
29
|
+
"items": { "type": "string", "maxLength": 20 }
|
|
30
|
+
},
|
|
31
|
+
"install": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": ["type"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"type": { "enum": ["git", "tarball", "local"] },
|
|
37
|
+
"url": { "type": "string" },
|
|
38
|
+
"subdir": { "type": "string" },
|
|
39
|
+
"target": {
|
|
40
|
+
"enum": ["global", "project"],
|
|
41
|
+
"description": "global = ~/.luban/plugins/<name>;project = <repo>/.luban/plugins/<name>"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"sourceSha256": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"pattern": "^[a-f0-9]{64}$",
|
|
48
|
+
"description": "安装包 / 目录归档的 sha256。git 方式下为 tag 对应归档的哈希;缺失则视为不可复现安装"
|
|
49
|
+
},
|
|
50
|
+
"ctxApi": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "兼容的 ctx API 版本区间,例如 >=0.1 <1.0;宿主超出区间应拒绝加载并提示"
|
|
53
|
+
},
|
|
54
|
+
"tools": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"description": "工具清单,必须与包内 plugin.json 逐字一致(校验脚本比对)",
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["name", "description", "readOnly", "parameters"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"name": { "type": "string", "pattern": "^[a-z][a-z0-9_]{0,39}$" },
|
|
63
|
+
"description": { "type": "string", "minLength": 4 },
|
|
64
|
+
"readOnly": { "type": "boolean" },
|
|
65
|
+
"parameters": { "type": "object" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"permissions": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"description": "L1 核心:作者自报的实际能力。readOnly 只是工具语义,不能推翻这里;审核以本块为准",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"properties": {
|
|
74
|
+
"fsRead": { "type": "boolean" },
|
|
75
|
+
"fsWrite": { "type": "boolean" },
|
|
76
|
+
"shell": { "type": "boolean" },
|
|
77
|
+
"net": { "type": "boolean" },
|
|
78
|
+
"env": { "type": "boolean" }
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"review": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": ["status"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"status": { "enum": ["pending", "reviewed", "flagged", "revoked"] },
|
|
87
|
+
"reviewedBy": { "type": "string" },
|
|
88
|
+
"reviewedAt": { "type": "string", "format": "date-time" },
|
|
89
|
+
"notes": { "type": "string", "maxLength": 500 }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"signature": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"required": ["algo", "keyId", "sig"],
|
|
96
|
+
"properties": {
|
|
97
|
+
"algo": { "enum": ["ed25519"] },
|
|
98
|
+
"keyId": { "type": "string" },
|
|
99
|
+
"sig": { "type": "string", "description": "base64,签名对象 = 去掉 signature 字段后按键名排序的紧凑 JSON" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"allOf": [
|
|
104
|
+
{
|
|
105
|
+
"if": { "properties": { "type": { "const": "plugin" } }, "required": ["type"] },
|
|
106
|
+
"then": { "required": ["permissions", "tools", "review"] }
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"if": { "properties": { "type": { "const": "skill" } }, "required": ["type"] },
|
|
110
|
+
"then": {
|
|
111
|
+
"properties": { "permissions": { "const": {} }, "tools": { "maxItems": 0 } }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"registryVersion": 1,
|
|
3
|
+
"updatedAt": "2026-11-13T13:53:13Z",
|
|
4
|
+
"schema": "./entry.schema.json",
|
|
5
|
+
"signingKeys": [
|
|
6
|
+
{
|
|
7
|
+
"keyId": "dev-2",
|
|
8
|
+
"algo": "ed25519",
|
|
9
|
+
"publicKey": "keys/dev-2.pub"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"keyId": "registry-ci",
|
|
13
|
+
"algo": "ed25519",
|
|
14
|
+
"publicKey": "keys/registry-ci.pub"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"entries": [
|
|
18
|
+
{
|
|
19
|
+
"name": "demo",
|
|
20
|
+
"displayName": "Demo 小工具",
|
|
21
|
+
"description": "Luban 本地插件示例:本机时间与工作区磁盘剩余空间",
|
|
22
|
+
"type": "plugin",
|
|
23
|
+
"version": "0.1.0",
|
|
24
|
+
"author": "liuhualiang",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repo": "https://github.com/eppen/luban-plugins",
|
|
27
|
+
"tags": [
|
|
28
|
+
"demo",
|
|
29
|
+
"system"
|
|
30
|
+
],
|
|
31
|
+
"install": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git@github.com:eppen/luban-plugins.git",
|
|
34
|
+
"subdir": "plugins/demo",
|
|
35
|
+
"target": "global"
|
|
36
|
+
},
|
|
37
|
+
"sourceSha256": "f806fcc5d33aa49dda38b9dc19936d635adef498f0960987a08bd9b1d32c61cb",
|
|
38
|
+
"ctxApi": ">=0.1 <1.0",
|
|
39
|
+
"tools": [
|
|
40
|
+
{
|
|
41
|
+
"name": "now",
|
|
42
|
+
"description": "返回本机当前时间(ISO 8601 与时区)",
|
|
43
|
+
"readOnly": true,
|
|
44
|
+
"parameters": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "disk_free",
|
|
51
|
+
"description": "返回工作区所在磁盘的剩余空间",
|
|
52
|
+
"readOnly": true,
|
|
53
|
+
"parameters": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"permissions": {
|
|
60
|
+
"fsRead": false,
|
|
61
|
+
"fsWrite": false,
|
|
62
|
+
"shell": true,
|
|
63
|
+
"net": false,
|
|
64
|
+
"env": false
|
|
65
|
+
},
|
|
66
|
+
"review": {
|
|
67
|
+
"status": "reviewed",
|
|
68
|
+
"reviewedBy": "liuhualiang",
|
|
69
|
+
"reviewedAt": "2026-11-12T14:30:00Z",
|
|
70
|
+
"notes": "两个工具声明 readOnly,但 disk_free 通过 execFile 调用 df -k(只读取磁盘统计,未写盘/联网),与声明一致;shell=true 已如实声明。"
|
|
71
|
+
},
|
|
72
|
+
"signature": {
|
|
73
|
+
"algo": "ed25519",
|
|
74
|
+
"keyId": "registry-ci",
|
|
75
|
+
"sig": "rXVMOvPMaJVt5nLUHz+So/q3kYOMyKVNacH9AXUiyA69mwRzV3++kAO0Wvcw65EVBqfNuML/8f/xGlJtK2NfAg=="
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "changelog-draft",
|
|
80
|
+
"displayName": "CHANGELOG 起草",
|
|
81
|
+
"description": "按提交历史起草 CHANGELOG 条目:先读现有写法再取 git log,输出分类草稿,默认不改文件",
|
|
82
|
+
"type": "skill",
|
|
83
|
+
"version": "0.1.0",
|
|
84
|
+
"author": "liuhualiang",
|
|
85
|
+
"license": "MIT",
|
|
86
|
+
"repo": "https://github.com/eppen/luban-plugins",
|
|
87
|
+
"tags": [
|
|
88
|
+
"git",
|
|
89
|
+
"docs"
|
|
90
|
+
],
|
|
91
|
+
"install": {
|
|
92
|
+
"type": "git",
|
|
93
|
+
"url": "git@github.com:eppen/luban-plugins.git",
|
|
94
|
+
"subdir": "skills/changelog-draft",
|
|
95
|
+
"target": "global"
|
|
96
|
+
},
|
|
97
|
+
"sourceSha256": "807f066342c0c90b9accd011d6f009b5650c1e093eab9765585c1710eaee7a6b",
|
|
98
|
+
"ctxApi": ">=0.1 <1.0",
|
|
99
|
+
"tools": [],
|
|
100
|
+
"permissions": {},
|
|
101
|
+
"review": {
|
|
102
|
+
"status": "reviewed",
|
|
103
|
+
"reviewedBy": "liuhualiang",
|
|
104
|
+
"reviewedAt": "2026-11-12T14:56:13Z",
|
|
105
|
+
"notes": "技能无执行能力;正文只要求 read_file / git log / git show / git describe 这类只读命令,并明确禁止 reset/checkout/stash、禁止 commit/push、禁止在用户未确认时改文件。description 与正文一致。"
|
|
106
|
+
},
|
|
107
|
+
"signature": {
|
|
108
|
+
"algo": "ed25519",
|
|
109
|
+
"keyId": "registry-ci",
|
|
110
|
+
"sig": "kOGvBF7aNY2qmRyV8FS7UYqIHermnhVecumvVhmPgNgACxLbTLW4pubtccT0hGFlPHAJiK8Q9gHK8k+YaCt7DQ=="
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "cc-safety-net",
|
|
115
|
+
"displayName": "命令安全守卫",
|
|
116
|
+
"description": "执行前命令守卫:解析命令意图,拦截破坏性 Git/文件命令与敏感文件(SSH key / .env / ~/.aws)访问。移植自 kenryu42/cc-safety-net 的 checkCommand 库 API。",
|
|
117
|
+
"type": "plugin",
|
|
118
|
+
"version": "0.1.0",
|
|
119
|
+
"author": "liuhualiang",
|
|
120
|
+
"license": "MIT",
|
|
121
|
+
"repo": "https://github.com/eppen/luban-plugins",
|
|
122
|
+
"tags": [
|
|
123
|
+
"safety",
|
|
124
|
+
"git",
|
|
125
|
+
"shell"
|
|
126
|
+
],
|
|
127
|
+
"install": {
|
|
128
|
+
"type": "git",
|
|
129
|
+
"url": "git@github.com:eppen/luban-plugins.git",
|
|
130
|
+
"subdir": "plugins/cc-safety-net",
|
|
131
|
+
"target": "global"
|
|
132
|
+
},
|
|
133
|
+
"sourceSha256": "7fde042d9442b826d6b1d78ed1757dbb0be5ecb89867f7cd8ced9413c51c94c0",
|
|
134
|
+
"ctxApi": ">=0.1 <1.0",
|
|
135
|
+
"tools": [
|
|
136
|
+
{
|
|
137
|
+
"name": "check_command",
|
|
138
|
+
"description": "检查一条 shell 命令是否危险(破坏性 Git 操作、rm -rf、curl|bash、敏感文件访问等)。返回 allow 或 deny(带原因与规则 ID)。在真正执行前调用它做一次前置判断。",
|
|
139
|
+
"readOnly": true,
|
|
140
|
+
"parameters": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"properties": {
|
|
143
|
+
"command": {
|
|
144
|
+
"type": "string",
|
|
145
|
+
"description": "要检查的完整 shell 命令"
|
|
146
|
+
},
|
|
147
|
+
"cwd": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"description": "执行命令的工作目录(绝对路径),缺省用当前工作区根"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"required": [
|
|
153
|
+
"command"
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"permissions": {
|
|
159
|
+
"fsRead": false,
|
|
160
|
+
"fsWrite": false,
|
|
161
|
+
"shell": true,
|
|
162
|
+
"net": false,
|
|
163
|
+
"env": false
|
|
164
|
+
},
|
|
165
|
+
"review": {
|
|
166
|
+
"status": "reviewed",
|
|
167
|
+
"reviewedBy": "liuhualiang",
|
|
168
|
+
"reviewedAt": "2026-11-13T00:59:49Z",
|
|
169
|
+
"notes": "工具声明 readOnly,但 permissions.shell=true 已如实声明(实现通过 execFile 调用 git,只读检查不写盘/联网)。依赖外部 npm 包 cc-safety-net,安装时需 npm install cc-safety-net --prefix <插件目录>。"
|
|
170
|
+
},
|
|
171
|
+
"signature": {
|
|
172
|
+
"algo": "ed25519",
|
|
173
|
+
"keyId": "registry-ci",
|
|
174
|
+
"sig": "vvpXf4ApzGh1JpT7B7GuLe4n75s/71GAznRNijP2j3u4LZhkhBZBa36ZjqnhA2mvJrRYyBWSlthujANuelMABA=="
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|