@clinebot/shared 0.0.11 → 0.0.13

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.
@@ -1,5 +1,11 @@
1
- import { existsSync, mkdirSync } from "node:fs";
2
- import { dirname, join } from "node:path";
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readdirSync,
5
+ readFileSync,
6
+ statSync,
7
+ } from "node:fs";
8
+ import { dirname, join, resolve } from "node:path";
3
9
 
4
10
  export const AGENT_CONFIG_DIRECTORY_NAME = "agents";
5
11
  export const HOOKS_CONFIG_DIRECTORY_NAME = "hooks";
@@ -86,6 +92,10 @@ export function resolveDocumentsWorkflowsDirectoryPath(): string {
86
92
  return join(resolveDocumentsClineDirectoryPath(), "Workflows");
87
93
  }
88
94
 
95
+ export function resolveDocumentsPluginsDirectoryPath(): string {
96
+ return join(HOME_DIR, "Documents", "Plugins");
97
+ }
98
+
89
99
  export function resolveClineDataDir(): string {
90
100
  const explicitDir = process.env.CLINE_DATA_DIR?.trim();
91
101
  if (explicitDir) {
@@ -212,10 +222,164 @@ export function resolvePluginConfigSearchPaths(
212
222
  ? join(workspacePath, ".clinerules", PLUGINS_DIRECTORY_NAME)
213
223
  : "",
214
224
  join(resolveClineDir(), PLUGINS_DIRECTORY_NAME),
215
- join(HOME_DIR, ".agents", PLUGINS_DIRECTORY_NAME),
225
+ resolveDocumentsPluginsDirectoryPath(),
216
226
  ]);
217
227
  }
218
228
 
229
+ const PLUGIN_MODULE_EXTENSIONS = new Set([
230
+ ".js",
231
+ ".mjs",
232
+ ".cjs",
233
+ ".ts",
234
+ ".mts",
235
+ ".cts",
236
+ ]);
237
+ const PLUGIN_PACKAGE_JSON_FILE_NAME = "package.json";
238
+ const PLUGIN_DIRECTORY_INDEX_CANDIDATES = [
239
+ "index.ts",
240
+ "index.mts",
241
+ "index.cts",
242
+ "index.js",
243
+ "index.mjs",
244
+ "index.cjs",
245
+ ];
246
+
247
+ interface PluginPackageManifest {
248
+ plugins?: unknown;
249
+ extensions?: unknown;
250
+ }
251
+
252
+ export function isPluginModulePath(path: string): boolean {
253
+ const dot = path.lastIndexOf(".");
254
+ if (dot === -1) {
255
+ return false;
256
+ }
257
+ return PLUGIN_MODULE_EXTENSIONS.has(path.slice(dot));
258
+ }
259
+
260
+ function readPluginPackageManifest(
261
+ packageJsonPath: string,
262
+ ): PluginPackageManifest | null {
263
+ try {
264
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
265
+ cline?: PluginPackageManifest;
266
+ };
267
+ if (!packageJson.cline || typeof packageJson.cline !== "object") {
268
+ return null;
269
+ }
270
+ return packageJson.cline;
271
+ } catch {
272
+ return null;
273
+ }
274
+ }
275
+
276
+ function getManifestPluginEntries(
277
+ manifest: PluginPackageManifest | null,
278
+ ): string[] {
279
+ const entries = manifest?.plugins ?? manifest?.extensions;
280
+ if (!Array.isArray(entries)) {
281
+ return [];
282
+ }
283
+ return entries.filter((entry): entry is string => typeof entry === "string");
284
+ }
285
+
286
+ export function resolvePluginModuleEntries(
287
+ directoryPath: string,
288
+ ): string[] | null {
289
+ const root = resolve(directoryPath);
290
+ if (!existsSync(root) || !statSync(root).isDirectory()) {
291
+ return null;
292
+ }
293
+
294
+ const packageJsonPath = join(root, PLUGIN_PACKAGE_JSON_FILE_NAME);
295
+ if (existsSync(packageJsonPath)) {
296
+ const manifest = readPluginPackageManifest(packageJsonPath);
297
+ const entries = getManifestPluginEntries(manifest)
298
+ .map((entry) => resolve(root, entry))
299
+ .filter(
300
+ (entryPath) =>
301
+ existsSync(entryPath) &&
302
+ statSync(entryPath).isFile() &&
303
+ isPluginModulePath(entryPath),
304
+ );
305
+ if (entries.length > 0) {
306
+ return entries;
307
+ }
308
+ }
309
+
310
+ for (const candidate of PLUGIN_DIRECTORY_INDEX_CANDIDATES) {
311
+ const entryPath = join(root, candidate);
312
+ if (existsSync(entryPath) && statSync(entryPath).isFile()) {
313
+ return [entryPath];
314
+ }
315
+ }
316
+
317
+ return null;
318
+ }
319
+
320
+ export function discoverPluginModulePaths(directoryPath: string): string[] {
321
+ const root = resolve(directoryPath);
322
+ if (!existsSync(root)) {
323
+ return [];
324
+ }
325
+ const discovered: string[] = [];
326
+ const stack = [root];
327
+ while (stack.length > 0) {
328
+ const current = stack.pop();
329
+ if (!current) {
330
+ continue;
331
+ }
332
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
333
+ const candidate = join(current, entry.name);
334
+ if (entry.isDirectory()) {
335
+ stack.push(candidate);
336
+ continue;
337
+ }
338
+ if (entry.name.startsWith(".")) {
339
+ continue;
340
+ }
341
+ if (entry.isFile() && isPluginModulePath(candidate)) {
342
+ discovered.push(candidate);
343
+ }
344
+ }
345
+ }
346
+ return discovered.sort((a, b) => a.localeCompare(b));
347
+ }
348
+
349
+ export function resolveConfiguredPluginModulePaths(
350
+ pluginPaths: ReadonlyArray<string>,
351
+ cwd: string,
352
+ ): string[] {
353
+ const resolvedPaths: string[] = [];
354
+ for (const pluginPath of pluginPaths) {
355
+ const trimmed = pluginPath.trim();
356
+ if (!trimmed) {
357
+ continue;
358
+ }
359
+ const absolutePath = resolve(cwd, trimmed);
360
+ if (!existsSync(absolutePath)) {
361
+ throw new Error(`Plugin path does not exist: ${absolutePath}`);
362
+ }
363
+ const stats = statSync(absolutePath);
364
+ if (stats.isDirectory()) {
365
+ const entries = resolvePluginModuleEntries(absolutePath);
366
+ if (entries) {
367
+ resolvedPaths.push(...entries);
368
+ continue;
369
+ }
370
+ resolvedPaths.push(...discoverPluginModulePaths(absolutePath));
371
+ continue;
372
+ }
373
+ if (!isPluginModulePath(absolutePath)) {
374
+ throw new Error(
375
+ `Plugin file must use a supported extension (${[...PLUGIN_MODULE_EXTENSIONS].join(", ")}): ${absolutePath}`,
376
+ );
377
+ }
378
+ resolvedPaths.push(absolutePath);
379
+ }
380
+ return resolvedPaths;
381
+ }
382
+
219
383
  export function ensureParentDir(filePath: string): void {
220
384
  const parent = dirname(filePath);
221
385
  if (!existsSync(parent)) {