rush-ai 0.19.0 → 0.21.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 +18 -9
- package/dist/{_codex-content-loader-Q7KBWZSB.js → _codex-content-loader-WAPA56U3.js} +2 -2
- package/dist/{chunk-X45FKY3L.js → chunk-22YQT6XF.js} +18 -7
- package/dist/chunk-22YQT6XF.js.map +1 -0
- package/dist/chunk-C7KJUHEF.js +830 -0
- package/dist/chunk-C7KJUHEF.js.map +1 -0
- package/dist/{chunk-RLKEUPBP.js → chunk-GPOA7SAG.js} +18 -3
- package/dist/chunk-GPOA7SAG.js.map +1 -0
- package/dist/index.js +1521 -346
- package/dist/index.js.map +1 -1
- package/dist/{mcp-UZYID3GG.js → mcp-TROKQRBF.js} +2 -2
- package/dist/{mirror-IHLOSPAS.js → mirror-SQ6GNVP5.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2AICQRQP.js +0 -447
- package/dist/chunk-2AICQRQP.js.map +0 -1
- package/dist/chunk-RLKEUPBP.js.map +0 -1
- package/dist/chunk-X45FKY3L.js.map +0 -1
- /package/dist/{_codex-content-loader-Q7KBWZSB.js.map → _codex-content-loader-WAPA56U3.js.map} +0 -0
- /package/dist/{mcp-UZYID3GG.js.map → mcp-TROKQRBF.js.map} +0 -0
- /package/dist/{mirror-IHLOSPAS.js.map → mirror-SQ6GNVP5.js.map} +0 -0
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ rush-ai task status <id> --json
|
|
|
124
124
|
- **多环境 profile**:在不同 Rush 环境之间切换
|
|
125
125
|
- **shell 补全**:bash / zsh / fish
|
|
126
126
|
- **CI 友好**:`--json` 输出、`--ci` 模式、出错返回非 0 退出码
|
|
127
|
-
- **插件分发**:`marketplace` + `plugin install` 一条命令把 Rush 生态的 skill / command / rule / MCP
|
|
127
|
+
- **插件分发**:`marketplace` + `plugin install` 一条命令把 Rush 生态的 skill / command / rule / MCP 默认装到 Claude Code、Codex、Cursor;Claude-3p 作为显式 target 用 `--target claude-3p` 安装;首次 `plugin install --target codex` 自动把 marketplace 完整目录镜像到 Codex 桌面端 Plugins 页(含未装的插件,无需先 `marketplace add`)
|
|
128
128
|
|
|
129
129
|
## 命令一览
|
|
130
130
|
|
|
@@ -201,7 +201,7 @@ registry,仍然可以直接使用独立的 `reskill` CLI。
|
|
|
201
201
|
| `marketplace add <source>` | 注册 marketplace(`github:owner/repo`、`directory:/abs/path`、`rush://<host>`),同时把 catalog 镜像到 Codex 桌面端 |
|
|
202
202
|
| `marketplace sync [<n>]` | 增量刷新 marketplace 镜像;`--all` 全部刷;`--target codex` 指定目标 |
|
|
203
203
|
| `marketplace list` / `remove` / `update` | 管理本地 marketplace 缓存;`remove` 同步清掉 Codex 端注册和镜像 |
|
|
204
|
-
| `plugin install <ref>` |
|
|
204
|
+
| `plugin install <ref>` | 默认同步装到 Claude Code + Codex + Cursor;`--target` 可指定单个或多个目标,Claude-3p 需显式 `--target claude-3p`;`--dry-run` / `--force` / `--secret KEY=VALUE` |
|
|
205
205
|
| `plugin list` / `uninstall` / `update` | 对称管理 |
|
|
206
206
|
|
|
207
207
|
`<ref>` 格式:`<name>` 或 `<name>@<marketplace>`(例 `my-plugin@rush`)。
|
|
@@ -212,8 +212,8 @@ registry,仍然可以直接使用独立的 `reskill` CLI。
|
|
|
212
212
|
- `--secret KEY=VALUE`(可重复)预设 MCP 凭证,跳过交互式输入
|
|
213
213
|
- `--force` 重新拉取 manifest 并更新 secrets
|
|
214
214
|
|
|
215
|
-
`
|
|
216
|
-
-
|
|
215
|
+
`@rush` 中的 Claude 官方镜像特性:
|
|
216
|
+
- Claude Code 官方 marketplace 镜像也统一使用 `@rush` 后缀;旧 `@claude-plugins-official` 后缀已废弃,CLI 会提示改用 `@rush`
|
|
217
217
|
- 支持 URL source 按需 clone:外部 git repo 的 plugin 自动 clone 到 `~/.rush/plugin-cache/`
|
|
218
218
|
- 支持 git-subdir 模式(一个 repo 内多个 plugin)
|
|
219
219
|
- `--force` 重新 clone 最新版本
|
|
@@ -232,7 +232,7 @@ registry,仍然可以直接使用独立的 `reskill` CLI。
|
|
|
232
232
|
|
|
233
233
|
`plugin install` 触发的同步发生在装完目标 plugin 之后(仅 `--target codex`
|
|
234
234
|
成功时),把 marketplace 里其它插件也一并镜像为 stub。所以从干净状态直接
|
|
235
|
-
跑一条 `rush-ai plugin install context7@
|
|
235
|
+
跑一条 `rush-ai plugin install context7@rush --target codex`
|
|
236
236
|
就能在 Codex Plugins 页同时拿到 225 个 plugin 的完整目录。
|
|
237
237
|
|
|
238
238
|
### 认证 / 配置 / 其他
|
|
@@ -291,11 +291,16 @@ rush-ai plugin install my-plugin@rush
|
|
|
291
291
|
# ✓ Codex (skills + MCP)
|
|
292
292
|
# ✓ Cursor (skills + MCP)
|
|
293
293
|
|
|
294
|
+
# Claude-3p 第一期不进默认 target,需要显式安装
|
|
295
|
+
rush-ai plugin install my-plugin@rush --target claude-3p
|
|
296
|
+
# ✓ Claude-3p (commands + skills + hooks + MCP)
|
|
297
|
+
|
|
294
298
|
# 带 MCP secrets(CI/非交互模式)
|
|
295
299
|
rush-ai plugin install my-plugin@rush --secret SHIMO_TOKEN=xxx --secret API_KEY=yyy
|
|
296
300
|
|
|
297
301
|
# 只装某一家
|
|
298
302
|
rush-ai plugin install my-plugin@rush --target claude-code
|
|
303
|
+
rush-ai plugin install my-plugin@rush --target claude-3p
|
|
299
304
|
|
|
300
305
|
# 更新 secrets / 刷新 skill
|
|
301
306
|
rush-ai plugin install my-plugin@rush --force --secret SHIMO_TOKEN=new-value
|
|
@@ -303,16 +308,20 @@ rush-ai plugin install my-plugin@rush --force --secret SHIMO_TOKEN=new-value
|
|
|
303
308
|
# 预览不落盘
|
|
304
309
|
rush-ai plugin install my-plugin@rush --dry-run
|
|
305
310
|
|
|
306
|
-
# Anthropic 官方 marketplace
|
|
307
|
-
rush-ai plugin install sentry@
|
|
308
|
-
rush-ai plugin install aikido@
|
|
311
|
+
# Anthropic 官方 marketplace 镜像(通过 @rush 统一访问,自动注册)
|
|
312
|
+
rush-ai plugin install sentry@rush
|
|
313
|
+
rush-ai plugin install aikido@rush --target claude-code
|
|
309
314
|
|
|
310
315
|
# GitHub marketplace 也支持
|
|
311
316
|
rush-ai marketplace add github:kanyun-inc/rush-plugin
|
|
312
317
|
rush-ai plugin install rush
|
|
313
318
|
```
|
|
314
319
|
|
|
315
|
-
|
|
320
|
+
Claude Code 侧与原生 plugin 清单互通;Codex、Cursor、Claude-3p 由
|
|
321
|
+
rush-ai 写入各自原生/适配目录。后续可用 `rush-ai plugin list` /
|
|
322
|
+
`rush-ai plugin uninstall` / `rush-ai plugin update` 统一管理;Claude-3p
|
|
323
|
+
安装后需新开对话生效。不用这条路径就忽略;日常的 `task create` /
|
|
324
|
+
`task push` 和插件分发完全独立。
|
|
316
325
|
|
|
317
326
|
### MCP 安装
|
|
318
327
|
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
buildRushPluginContentLoader,
|
|
5
5
|
pathExists,
|
|
6
6
|
pickContentLoader
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-C7KJUHEF.js";
|
|
8
8
|
import "./chunk-OIKYNVKO.js";
|
|
9
9
|
import "./chunk-T5S6NCHZ.js";
|
|
10
10
|
export {
|
|
@@ -13,4 +13,4 @@ export {
|
|
|
13
13
|
pathExists,
|
|
14
14
|
pickContentLoader
|
|
15
15
|
};
|
|
16
|
-
//# sourceMappingURL=_codex-content-loader-
|
|
16
|
+
//# sourceMappingURL=_codex-content-loader-WAPA56U3.js.map
|
|
@@ -21,24 +21,35 @@ function normalizeClaudeMcpServers(ref, manifest, resolver = defaultRushBinaryRe
|
|
|
21
21
|
return void 0;
|
|
22
22
|
}
|
|
23
23
|
if (!isRushOwnPlugin(ref)) {
|
|
24
|
-
return
|
|
24
|
+
return normalizeRemoteMcpDefaults(servers);
|
|
25
25
|
}
|
|
26
26
|
const rushServer = servers[RUSH_MCP_SERVER_KEY];
|
|
27
27
|
if (!rushServer) {
|
|
28
|
-
return
|
|
28
|
+
return normalizeRemoteMcpDefaults(servers);
|
|
29
29
|
}
|
|
30
30
|
const currentCommand = rushServer.command;
|
|
31
31
|
if (typeof currentCommand === "string" && isAbsolute(currentCommand)) {
|
|
32
|
-
return
|
|
32
|
+
return normalizeRemoteMcpDefaults(servers);
|
|
33
33
|
}
|
|
34
34
|
const resolved = resolver();
|
|
35
35
|
if (!resolved || !isAbsolute(resolved)) {
|
|
36
|
-
return
|
|
36
|
+
return normalizeRemoteMcpDefaults(servers);
|
|
37
37
|
}
|
|
38
|
-
return {
|
|
38
|
+
return normalizeRemoteMcpDefaults({
|
|
39
39
|
...servers,
|
|
40
40
|
[RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved }
|
|
41
|
-
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function normalizeRemoteMcpDefaults(servers) {
|
|
44
|
+
const out = {};
|
|
45
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
46
|
+
if (typeof server.url === "string" && server.url.length > 0 && typeof server.command !== "string" && typeof server.type !== "string") {
|
|
47
|
+
out[name] = { ...server, type: "http" };
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
out[name] = { ...server };
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
42
53
|
}
|
|
43
54
|
async function readExternalMcpServers(sourceDir, relativeRef) {
|
|
44
55
|
if (typeof relativeRef !== "string" || relativeRef.length === 0) return null;
|
|
@@ -95,4 +106,4 @@ export {
|
|
|
95
106
|
readExternalMcpServers,
|
|
96
107
|
defaultRushBinaryResolver
|
|
97
108
|
};
|
|
98
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-22YQT6XF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/installers/claude-code/mcp.ts"],"sourcesContent":["/**\n * MCP command 规范化(task-6 产物)。\n *\n * 仅对 rush-ai 自己的插件(`rush@rush-marketplace`)做 MCP command 绝对路径规范化;\n * 其他 plugin 透传(见 plan §7.1 / spec §1.5)。\n *\n * 说明:resolver 层(`plugins/resolver.ts` 的 `normalizeRushMcpCommand`)已经做过\n * 同样的规范化。本文件提供一个 Installer 侧的**兜底**:如果 resolver 没解析到绝对路径\n * (例如 rushAiBinaryResolver 返回 undefined,resolver 选择不改),Installer 写\n * plugin.json 前再尝试一次——用 `which rush-ai`(PATH 解析)。**不 fallback 到\n * `process.argv[0]`**(那通常是 `node`,会写错误 command)。\n *\n * 为什么在两个层都做:\n * - Resolver 层:给各家 Installer 一个统一的起点(避免 Claude Code / Codex / Cursor\n * Installer 各自重复实现)\n * - Installer 层:resolver 的兜底策略有意保持宽松(失败就透传),Installer 层可以\n * 更激进(Claude Code 的 plugin.json 要求绝对路径更严)\n *\n * 注意:对 rush plugin 且 resolver + 本兜底都拿不到绝对路径时,我们仍然写入 plugin.json\n * 的原始值——不抛错、不阻塞 install。Claude Code 运行时会自己 fail(下游问题),但\n * Installer 保持 \"能装就装\" 的语义。\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport {\n isAbsolute,\n relative as pathRelative,\n resolve as pathResolve,\n} from 'node:path';\nimport type { McpServerConfig, PluginManifest, PluginRef } from '../types.js';\n\n/** rush 自身 plugin 的 identifier(与 resolver 层保持一致) */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n/**\n * 决定是否应对该 plugin 做 MCP command 规范化。\n *\n * 约束条件(plan §7.1):\n * - `ref.name === 'rush'`\n * - `ref.marketplace === 'rush-marketplace'`\n *\n * 第三方 marketplace 的 rush plugin(同名但不同 marketplace)**不**命中——\n * 只有我们自己发布的插件才保证 `rush-ai` binary 可解析到绝对路径。\n */\nexport function isRushOwnPlugin(ref: PluginRef): boolean {\n return (\n ref.name === RUSH_AI_PLUGIN_NAME &&\n ref.marketplace === RUSH_AI_MARKETPLACE_NAME\n );\n}\n\n/**\n * 规范化后的 MCP servers 对象——返回 Claude Code plugin.json 顶层所需的 `mcpServers`\n * 结构(`Record<string, McpServerConfig>`)。\n *\n * - `manifest.mcpServers` 是 `string`(外部文件引用)→ 返回 `undefined`,调用方需要走\n * {@link readExternalMcpServers} 读源文件,并把结果作为 normalizer 的输入再调一次\n * - 非 rush 插件 → 原样返回\n * - rush 插件:仅对 `rush` server key 做 command 规范化(其他 key 保持)\n *\n * 规范化策略(rush 插件且 command 非绝对路径时):\n * 1. 调用者注入的 `resolver`(通常是 `whichRushAiBinary`)\n * 2. 返回值是绝对路径 → 用它\n * 3. 否则保留原始 command(不抛错,让 Claude Code 运行时报)\n */\nexport function normalizeClaudeMcpServers(\n ref: PluginRef,\n manifest: PluginManifest,\n resolver: () => string | undefined = defaultRushBinaryResolver\n): Record<string, McpServerConfig> | undefined {\n const servers = manifest.mcpServers;\n if (servers === undefined || servers === null) return undefined;\n if (typeof servers === 'string') {\n // 字符串外部文件引用(典型:plugin resolver 隐式注入的 `\"./.mcp.json\"`,\n // 或作者显式声明的相对路径引用)。Installer 主流程负责读盘解析后再调\n // normalizer 一次(见 readExternalMcpServers)。这里返回 undefined,\n // installer 应该把它当作\"未直接内嵌\"。\n return undefined;\n }\n if (!isRushOwnPlugin(ref)) {\n // 第三方 plugin:保留作者配置,并为远程 MCP 补齐 Claude/Cursor 兼容的默认 type。\n return normalizeRemoteMcpDefaults(servers);\n }\n\n // rush own plugin —— 仅规范化 `rush` server key\n const rushServer = servers[RUSH_MCP_SERVER_KEY];\n if (!rushServer) {\n return normalizeRemoteMcpDefaults(servers);\n }\n const currentCommand = rushServer.command;\n if (typeof currentCommand === 'string' && isAbsolute(currentCommand)) {\n // 已经是绝对路径,不动\n return normalizeRemoteMcpDefaults(servers);\n }\n\n const resolved = resolver();\n if (!resolved || !isAbsolute(resolved)) {\n // 兜底失败:保留原值,不阻塞 install\n return normalizeRemoteMcpDefaults(servers);\n }\n\n return normalizeRemoteMcpDefaults({\n ...servers,\n [RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved },\n });\n}\n\nfunction normalizeRemoteMcpDefaults(\n servers: Record<string, McpServerConfig>\n): Record<string, McpServerConfig> {\n const out: Record<string, McpServerConfig> = {};\n for (const [name, server] of Object.entries(servers)) {\n if (\n typeof server.url === 'string' &&\n server.url.length > 0 &&\n typeof server.command !== 'string' &&\n typeof server.type !== 'string'\n ) {\n out[name] = { ...server, type: 'http' };\n continue;\n }\n out[name] = { ...server };\n }\n return out;\n}\n\n/**\n * 当 `manifest.mcpServers` 是字符串外部文件引用时,从源 plugin 目录读取该\n * `.mcp.json` 文件并解析成 `Record<string, McpServerConfig>`。\n *\n * 触发场景:\n * - plugin 作者显式声明 `\"mcpServers\": \"./.mcp.json\"`\n * - resolver 隐式注入(plugin.json 没声明 + 目录有 `.mcp.json`)—— 见\n * `plugins/resolver.ts` 的 `injectImplicitMcpRef`\n *\n * 与 Codex installer 的 `copyAuthorProvidedMcp` 形成对称:那边读完后 copy 到\n * 版本目录 + 解析 mcpKeys;Claude Code 这边只需要解析后塞回 normalizer 流程。\n *\n * 容忍两种 .mcp.json 形态:\n * - `{ \"mcpServers\": { ... } }`(rush-ai 自己写出的,带 wrapper)\n * - `{ \"<server-name>\": { ... } }`(claude-plugins-official 的 external_plugins/*\n * 不带 wrapper)\n *\n * 安全:拒绝 `../` 逃逸 sourceDir 的引用,缺失文件 / 损坏 JSON / 路径非法 →\n * 返回 `null`,调用方按 \"无 MCP\" 继续 install(不阻塞)。\n */\nexport async function readExternalMcpServers(\n sourceDir: string,\n relativeRef: string\n): Promise<Record<string, McpServerConfig> | null> {\n if (typeof relativeRef !== 'string' || relativeRef.length === 0) return null;\n const srcPath = pathResolve(sourceDir, relativeRef);\n // 路径穿越守护:必须落在 sourceDir 下\n const rel = pathRelative(pathResolve(sourceDir), srcPath);\n if (rel === '..' || rel.startsWith('..') || rel.startsWith('/')) return null;\n\n let raw: string;\n try {\n raw = await readFile(srcPath, 'utf8');\n } catch {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n\n // 形态 1:`{ mcpServers: {...} }`\n const obj = parsed as { mcpServers?: unknown } & Record<string, unknown>;\n if (\n obj.mcpServers &&\n typeof obj.mcpServers === 'object' &&\n !Array.isArray(obj.mcpServers)\n ) {\n return obj.mcpServers as Record<string, McpServerConfig>;\n }\n\n // 形态 2:`{ <server-name>: {...} }` —— external_plugins/* 形态\n // 简单识别:所有 value 都是 object(不含 mcpServers 这个 key)\n const flat = obj as Record<string, unknown>;\n if (Object.keys(flat).length === 0) return null;\n const allObjects = Object.values(flat).every(\n (v) => v !== null && typeof v === 'object' && !Array.isArray(v)\n );\n if (!allObjects) return null;\n return flat as Record<string, McpServerConfig>;\n}\n\n/**\n * 默认 rush-ai binary 解析:`which rush-ai`(PATH 解析)。\n *\n * 作为 Installer 层兜底——resolver 层已用 `process.argv[1]` 试过一次;本函数\n * 用更稳的 PATH 查找逻辑。\n *\n * **不 fallback 到 `process.argv[0]`**(那通常是 `node`,会把 rush MCP command\n * 规范化成 node 路径,运行时语义错误)——宁可返回 `undefined`(保留原 command),\n * 也不冒险写入错误绝对路径。\n *\n * 失败返回 `undefined`,调用方保留原值。\n */\nexport function defaultRushBinaryResolver(): string | undefined {\n try {\n const result = execFileSync('which', ['rush-ai'], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n if (result.length > 0 && isAbsolute(result)) {\n return result;\n }\n } catch {\n // `which` 失败 —— rush-ai 未在 PATH 上,放弃兜底\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|