@ruan-cat/utils 4.12.0 → 4.13.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.
@@ -39,14 +39,101 @@ declare function hasChangelogMd(): boolean;
39
39
  */
40
40
  declare function copyChangelogMd(/** 目标文件夹 */ target: string): void;
41
41
 
42
- /** 检查当前运行的根目录 是否存在 .claude/agents 文件夹 */
43
- declare function hasClaudeAgents(): boolean;
44
42
  /**
45
- * .claude/agents 文件夹复制到指定要求的位置内
43
+ * 检查指定根目录是否存在 .claude/agents 文件夹
44
+ * @param options - 配置选项
45
+ * @param options.rootDir - 可选的根目录路径,支持相对路径(如 `../../../` 表示向上三级目录)。
46
+ * 如果不传入,将自动向上查找包含 pnpm-workspace.yaml 的 monorepo 根目录。
47
+ * 相对路径会基于当前工作目录 (process.cwd()) 解析为绝对路径。
48
+ * @returns 是否存在 .claude/agents 文件夹
49
+ * @example
50
+ * // 自动检测 monorepo 根目录
51
+ * hasClaudeAgents()
52
+ *
53
+ * // 手动指定相对路径(从当前工作目录向上三级)
54
+ * hasClaudeAgents({ rootDir: '../../../' })
55
+ *
56
+ * // 手动指定绝对路径
57
+ * hasClaudeAgents({ rootDir: '/path/to/monorepo' })
58
+ */
59
+ declare function hasClaudeAgents(options?: {
60
+ rootDir?: string;
61
+ }): boolean;
62
+ /**
63
+ * 将 .claude/agents 文件夹复制到指定位置的配置选项
64
+ */
65
+ interface CopyClaudeAgentsOptions {
66
+ /**
67
+ * 目标文件夹路径(必须是相对路径,相对于当前工作目录)
68
+ * @description
69
+ * **重要**:此参数仅接受相对路径,不接受绝对路径(禁止以 `/` 或盘符如 `C:\` 开头)。
70
+ * 使用绝对路径会抛出错误,这是为了防止意外覆盖系统目录。
71
+ *
72
+ * 该地址是写相对路径的 不能写绝对路径,容易导致意外。
73
+ * vitepress 命令运行在 apps/admin 目录内,该地址是相对于该运行目录的。
74
+ * 比如期望将 `.claude/agents` 复制到 `apps/admin/src/docs/prompts/agents` 文件夹。
75
+ * 则写 `src/docs/prompts/agents` 即可。
76
+ *
77
+ * @throws {Error} 当传入绝对路径时抛出错误
78
+ * @example
79
+ * // ✅ 正确:相对路径
80
+ * "src/docs/prompts/agents"
81
+ * "dist/agents"
82
+ * "./public/claude"
83
+ *
84
+ * @example
85
+ * // ❌ 错误:绝对路径(会抛出错误)
86
+ * "/var/www/agents" // Unix 绝对路径
87
+ * "C:\\Users\\agents" // Windows 绝对路径
88
+ */
89
+ target: string;
90
+ /**
91
+ * 可选的根目录路径,支持相对路径(如 `../../../` 表示向上三级目录)
92
+ * @description
93
+ * - 如果不传入,将自动向上查找包含 pnpm-workspace.yaml 的 monorepo 根目录
94
+ * - 相对路径会基于当前工作目录 (process.cwd()) 解析为绝对路径
95
+ * - 绝对路径将直接使用
96
+ * @example
97
+ * // 相对路径:向上三级目录
98
+ * '../../../'
99
+ *
100
+ * // 绝对路径
101
+ * '/absolute/path/to/monorepo'
102
+ */
103
+ rootDir?: string;
104
+ }
105
+ /**
106
+ * 将 .claude/agents 文件夹复制到指定位置
107
+ * @param options - 配置选项
108
+ * @throws {Error} 当 `options.target` 为绝对路径时抛出错误
46
109
  * @description
47
- * 该函数相当于实现 `cpx .claude/agents <target>` 命令
110
+ * 该函数相当于实现 `cpx .claude/agents <target>` 命令。
111
+ * 从根目录的 .claude/agents 复制到目标位置。
112
+ *
113
+ * **安全限制**:`target` 参数必须是相对路径,禁止使用绝对路径,以防止意外覆盖系统目录。
114
+ *
115
+ * @example
116
+ * // ✅ 自动检测 monorepo 根目录,复制到当前目录的 dist 文件夹
117
+ * copyClaudeAgents({ target: 'dist' })
118
+ *
119
+ * // ✅ 手动指定根目录为向上三级,复制到 build 文件夹
120
+ * copyClaudeAgents({
121
+ * target: 'build',
122
+ * rootDir: '../../../'
123
+ * })
124
+ *
125
+ * // ✅ 使用绝对路径指定根目录(rootDir 允许绝对路径)
126
+ * copyClaudeAgents({
127
+ * target: 'dist', // target 必须是相对路径
128
+ * rootDir: '/absolute/path/to/monorepo' // rootDir 可以是绝对路径
129
+ * })
130
+ *
131
+ * @example
132
+ * // ❌ 错误用法:target 使用绝对路径会抛出错误
133
+ * copyClaudeAgents({ target: '/var/www/agents' }) // 抛出 Error
134
+ * copyClaudeAgents({ target: 'C:\\Windows\\agents' }) // 抛出 Error
48
135
  */
49
- declare function copyClaudeAgents(/** 目标文件夹 */ target: string): void;
136
+ declare function copyClaudeAgents(options: CopyClaudeAgentsOptions): void;
50
137
 
51
138
  /** 检查当前运行的根目录 是否存在文件名大写的 `README.md` 文件 */
52
139
  declare function hasCapitalReadmeMd(): boolean;
@@ -81,4 +168,4 @@ interface AddChangelog2docOptions<T = Record<string, any>> {
81
168
  /** 将变更日志添加到指定的文档目录内 并提供参数 */
82
169
  declare function addChangelog2doc<T>(options: AddChangelog2docOptions<T>): void;
83
170
 
84
- export { type AddChangelog2docOptions, type PackageInfo, type WriteYaml2mdParams, addChangelog2doc, clean, copyChangelogMd, copyClaudeAgents, copyReadmeMd, defaultCleanTargets, getRuanCatPkgInfo, hasCapitalReadmeMd, hasChangelogMd, hasClaudeAgents, hasLowerCaseReadmeMd, hasReadmeMd, writeYaml2md };
171
+ export { type AddChangelog2docOptions, type CopyClaudeAgentsOptions, type PackageInfo, type WriteYaml2mdParams, addChangelog2doc, clean, copyChangelogMd, copyClaudeAgents, copyReadmeMd, defaultCleanTargets, getRuanCatPkgInfo, hasCapitalReadmeMd, hasChangelogMd, hasClaudeAgents, hasLowerCaseReadmeMd, hasReadmeMd, writeYaml2md };
@@ -141,20 +141,58 @@ function copyChangelogMd(target) {
141
141
  import fs2 from "fs";
142
142
  import path2 from "path";
143
143
  import consola4 from "consola";
144
- function hasClaudeAgents() {
145
- const res = fs2.existsSync(path2.resolve(process.cwd(), ".claude/agents"));
146
- if (!res) {
147
- consola4.log("\u5F53\u524D\u9879\u76EE\u6839\u76EE\u5F55\u4E3A\uFF1A", process.cwd());
148
- consola4.warn("\u5F53\u524D\u9879\u76EE\u6839\u76EE\u5F55\u4E0D\u5B58\u5728 .claude/agents \u6587\u4EF6\u5939");
144
+ function findMonorepoRoot() {
145
+ let currentDir = process.cwd();
146
+ const root = path2.parse(currentDir).root;
147
+ while (currentDir !== root) {
148
+ const workspaceFile2 = path2.join(currentDir, "pnpm-workspace.yaml");
149
+ if (fs2.existsSync(workspaceFile2)) {
150
+ return currentDir;
151
+ }
152
+ currentDir = path2.dirname(currentDir);
149
153
  }
150
- return res;
154
+ const workspaceFile = path2.join(root, "pnpm-workspace.yaml");
155
+ if (fs2.existsSync(workspaceFile)) {
156
+ return root;
157
+ }
158
+ return null;
151
159
  }
152
- function copyClaudeAgents(target) {
153
- if (!hasClaudeAgents()) {
160
+ function resolveRootDir(rootDir) {
161
+ if (rootDir) {
162
+ return path2.resolve(process.cwd(), rootDir);
163
+ }
164
+ const monorepoRoot = findMonorepoRoot();
165
+ if (monorepoRoot) {
166
+ return monorepoRoot;
167
+ }
168
+ return process.cwd();
169
+ }
170
+ function hasClaudeAgents(options) {
171
+ const root = resolveRootDir(options?.rootDir);
172
+ const claudeAgentsPath = path2.join(root, ".claude/agents");
173
+ const exists = fs2.existsSync(claudeAgentsPath);
174
+ if (!exists) {
175
+ consola4.log("\u68C0\u6D4B\u7684\u6839\u76EE\u5F55\u4E3A\uFF1A", root);
176
+ consola4.warn("\u8BE5\u6839\u76EE\u5F55\u4E0D\u5B58\u5728 .claude/agents \u6587\u4EF6\u5939");
177
+ }
178
+ return exists;
179
+ }
180
+ function copyClaudeAgents(options) {
181
+ if (path2.isAbsolute(options.target)) {
182
+ const errorMessage = [
183
+ `target \u53C2\u6570\u4E0D\u5141\u8BB8\u4F7F\u7528\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u8FD9\u53EF\u80FD\u5BFC\u81F4\u610F\u5916\u7684\u6587\u4EF6\u8986\u76D6\u3002`,
184
+ `\u5F53\u524D\u4F20\u5165\u7684\u8DEF\u5F84: "${options.target}"`,
185
+ `\u8BF7\u4F7F\u7528\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982: "src/docs/prompts/agents"`
186
+ ].join("\n");
187
+ consola4.error(errorMessage);
188
+ throw new Error(errorMessage);
189
+ }
190
+ if (!hasClaudeAgents({ rootDir: options.rootDir })) {
154
191
  return;
155
192
  }
156
- const source = path2.resolve(process.cwd(), ".claude/agents");
157
- const destination = path2.resolve(process.cwd(), target);
193
+ const root = resolveRootDir(options.rootDir);
194
+ const source = path2.join(root, ".claude/agents");
195
+ const destination = path2.resolve(process.cwd(), options.target);
158
196
  fs2.mkdirSync(path2.dirname(destination), { recursive: true });
159
197
  fs2.cpSync(source, destination, { recursive: true });
160
198
  consola4.success(`\u5DF2\u6210\u529F\u590D\u5236 .claude/agents \u5230 ${destination}`);