@guanmu/ccprofile 0.1.15 → 0.1.16

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.
Files changed (2) hide show
  1. package/dist/index.js +60 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -271,29 +271,27 @@ async function addPlugin(profileName, plugin) {
271
271
  }
272
272
  }
273
273
  }
274
- const selected = await p.select({
275
- message: `Add plugin to "${profileName}":`,
274
+ const selected = await p.multiselect({
275
+ message: `Select plugins to add to "${profileName}":`,
276
276
  options: [
277
277
  ...filtered.map((pl) => ({
278
278
  value: pl.name,
279
279
  label: pl.name,
280
- hint: pl.description.slice(0, 60),
280
+ hint: pl.description.slice(0, 50),
281
281
  })),
282
- { value: "__url__", label: "Enter URL manually...", hint: "Input a GitHub URL or plugin name" },
283
282
  ],
283
+ required: false,
284
284
  });
285
285
  if (p.isCancel(selected))
286
286
  return;
287
- if (selected === "__url__") {
288
- plugin = (await p.text({
289
- message: "Plugin URL or name:",
290
- }));
291
- if (p.isCancel(plugin))
292
- return;
287
+ for (const name of selected) {
288
+ data.plugins.push(name);
293
289
  }
294
- else {
295
- plugin = selected;
290
+ writeProfile(normalizedProfileName, data);
291
+ if (selected.length > 0) {
292
+ p.log.success(`Added ${selected.length} plugin(s) to profile "${normalizedProfileName}".`);
296
293
  }
294
+ return;
297
295
  }
298
296
  plugin = normalizePluginName(plugin);
299
297
  if (!plugin)
@@ -355,54 +353,55 @@ async function listPlugins(profileName) {
355
353
  p.log.success(pl);
356
354
  }
357
355
  }
358
- async function searchPlugins(keyword, currentProfile) {
359
- if (!keyword) {
360
- if (!canPrompt()) {
361
- missingArg("Search keyword is required.", "ccx search <keyword>");
362
- return undefined;
363
- }
364
- keyword = (await p.text({
365
- message: "Search plugins:",
366
- }));
367
- if (p.isCancel(keyword))
368
- return undefined;
369
- }
356
+ async function browsePlugins(currentProfile) {
370
357
  const allPlugins = getAllPlugins();
371
- const lower = keyword.toLowerCase();
372
- const results = allPlugins.filter((pl) => pl.name.toLowerCase().includes(lower) ||
373
- pl.description.toLowerCase().includes(lower) ||
374
- (pl.category || "").toLowerCase().includes(lower));
375
- if (results.length === 0) {
376
- p.log.warn(`No plugins matching "${keyword}".`);
377
- return undefined;
358
+ if (allPlugins.length === 0) {
359
+ p.log.warn("No plugins available in marketplaces.");
360
+ return [];
361
+ }
362
+ let plugins = allPlugins;
363
+ if (allPlugins.length > 15) {
364
+ const query = (await p.text({
365
+ message: "Search plugins (leave empty to list all):",
366
+ }));
367
+ if (p.isCancel(query))
368
+ return [];
369
+ const q = query.trim().toLowerCase();
370
+ if (q) {
371
+ plugins = allPlugins.filter((pl) => pl.name.toLowerCase().includes(q) ||
372
+ pl.description.toLowerCase().includes(q) ||
373
+ (pl.category || "").toLowerCase().includes(q));
374
+ if (plugins.length === 0) {
375
+ p.log.warn(`No plugins matching "${query.trim()}".`);
376
+ return [];
377
+ }
378
+ }
378
379
  }
379
380
  if (currentProfile) {
380
381
  const data = readProfile(currentProfile);
381
- const selected = await p.select({
382
- message: `Search results for "${keyword}":`,
383
- options: [
384
- ...results.map((pl) => ({
385
- value: pl.name,
386
- label: pl.name,
387
- hint: pl.description.slice(0, 60) + (data.plugins.includes(pl.name) ? " ✓" : ""),
388
- })),
389
- { value: "__back__", label: "Back", hint: "Return to menu" },
390
- ],
382
+ const selected = await p.multiselect({
383
+ message: "Select plugins to add:",
384
+ options: plugins.map((pl) => ({
385
+ value: pl.name,
386
+ label: pl.name,
387
+ hint: pl.description.slice(0, 50) + (data.plugins.includes(pl.name) ? " (installed)" : ""),
388
+ })),
389
+ required: false,
391
390
  });
392
- if (p.isCancel(selected) || selected === "__back__")
393
- return undefined;
394
- return selected;
391
+ if (p.isCancel(selected))
392
+ return [];
393
+ return selected.filter((name) => !data.plugins.includes(name));
395
394
  }
396
395
  const grouped = new Map();
397
- for (const pl of results) {
396
+ for (const pl of plugins) {
398
397
  const list = grouped.get(pl.marketplace) || [];
399
398
  list.push(pl);
400
399
  grouped.set(pl.marketplace, list);
401
400
  }
402
- console.log(`\n Found ${results.length} plugin(s) for "${keyword}":\n`);
403
- for (const [marketplace, plugins] of grouped) {
401
+ console.log(`\n ${plugins.length} plugin(s) available:\n`);
402
+ for (const [marketplace, mPlugins] of grouped) {
404
403
  console.log(` [${marketplace}]`);
405
- for (const pl of plugins) {
404
+ for (const pl of mPlugins) {
406
405
  const desc = pl.description.length > 70
407
406
  ? pl.description.slice(0, 67) + "..."
408
407
  : pl.description;
@@ -410,7 +409,7 @@ async function searchPlugins(keyword, currentProfile) {
410
409
  }
411
410
  console.log();
412
411
  }
413
- return undefined;
412
+ return [];
414
413
  }
415
414
  async function executeProfile(profileName) {
416
415
  const normalizedProfileName = normalizeProfileName(profileName);
@@ -463,13 +462,16 @@ async function interactiveMode() {
463
462
  let stayInProfile = true;
464
463
  while (stayInProfile && currentProfile) {
465
464
  const data = readProfile(currentProfile);
465
+ const pluginList = data.plugins.length > 0
466
+ ? data.plugins.map((pl) => ` ${pc.dim("•")} ${pl}`).join("\n")
467
+ : pc.dim(" (empty)");
468
+ p.note(`${pc.bold(pc.cyan(currentProfile))}\n${pluginList}`);
466
469
  const action = await p.select({
467
- message: pc.bold(pc.cyan(`[${currentProfile}]`)) + ` ${data.plugins.length} plugin(s)`,
470
+ message: "Choose action:",
468
471
  options: [
469
472
  { value: "install", label: "Install", hint: "Apply to current project" },
470
- { value: "add", label: "Add plugin", hint: "Search and add a plugin" },
473
+ { value: "add", label: "Add plugin", hint: "Search and add plugins" },
471
474
  { value: "remove", label: "Remove plugin", hint: "Remove a plugin" },
472
- { value: "list", label: "List plugins", hint: "Show all plugins" },
473
475
  { value: "search", label: "Search marketplace", hint: "Find new plugins" },
474
476
  { value: "switch", label: "Switch profile", hint: "Choose a different profile" },
475
477
  { value: "delete", label: "Delete profile", hint: "Remove this profile" },
@@ -491,21 +493,13 @@ async function interactiveMode() {
491
493
  case "remove":
492
494
  await removePlugin(currentProfile);
493
495
  break;
494
- case "list":
495
- await listPlugins(currentProfile);
496
- break;
497
496
  case "search": {
498
- const found = await searchPlugins(undefined, currentProfile);
499
- if (found) {
497
+ const found = await browsePlugins(currentProfile);
498
+ if (found.length > 0) {
500
499
  const d = readProfile(currentProfile);
501
- if (d.plugins.includes(found)) {
502
- p.log.warn(`"${found}" already in profile "${currentProfile}".`);
503
- }
504
- else {
505
- d.plugins.push(found);
506
- writeProfile(currentProfile, d);
507
- p.log.success(`Added "${found}" to profile "${currentProfile}".`);
508
- }
500
+ d.plugins.push(...found);
501
+ writeProfile(currentProfile, d);
502
+ p.log.success(`Added ${found.length} plugin(s) to profile "${currentProfile}".`);
509
503
  }
510
504
  break;
511
505
  }
@@ -609,7 +603,7 @@ async function main(args) {
609
603
  }
610
604
  break;
611
605
  case "search":
612
- await searchPlugins(args[1]);
606
+ await browsePlugins();
613
607
  break;
614
608
  default: {
615
609
  const profileName = cmd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanmu/ccprofile",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Agent Profile Manager for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {