@guanmu/ccprofile 0.1.14 → 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 +133 -172
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -141,19 +141,33 @@ function missingArg(message, usage) {
141
141
  console.error(`Usage: ${usage}`);
142
142
  process.exitCode = 1;
143
143
  }
144
- async function selectProfile(message) {
144
+ async function selectProfileOrNew() {
145
145
  const names = getProfileNames();
146
146
  if (names.length === 0) {
147
- p.log.warn("No profiles found.");
148
- return undefined;
149
- }
150
- const name = await p.select({
151
- message,
152
- options: names.map((n) => ({ value: n, label: n })),
147
+ const create = await p.confirm({
148
+ message: "No profiles found. Create one?",
149
+ initialValue: true,
150
+ });
151
+ if (p.isCancel(create) || !create)
152
+ return undefined;
153
+ await addProfile();
154
+ return getProfileNames()[0];
155
+ }
156
+ const selected = await p.select({
157
+ message: "Select profile:",
158
+ options: [
159
+ ...names.map((n) => ({ value: n, label: n })),
160
+ { value: "__create__", label: "Create new profile...", hint: "Add a new profile" },
161
+ ],
153
162
  });
154
- if (p.isCancel(name))
163
+ if (p.isCancel(selected))
155
164
  return undefined;
156
- return name;
165
+ if (selected === "__create__") {
166
+ await addProfile();
167
+ const updated = getProfileNames();
168
+ return updated[updated.length - 1];
169
+ }
170
+ return selected;
157
171
  }
158
172
  // ── Commands ──────────────────────────────────────────────
159
173
  async function addProfile(name) {
@@ -257,29 +271,27 @@ async function addPlugin(profileName, plugin) {
257
271
  }
258
272
  }
259
273
  }
260
- const selected = await p.select({
261
- message: `Add plugin to "${profileName}":`,
274
+ const selected = await p.multiselect({
275
+ message: `Select plugins to add to "${profileName}":`,
262
276
  options: [
263
277
  ...filtered.map((pl) => ({
264
278
  value: pl.name,
265
279
  label: pl.name,
266
- hint: pl.description.slice(0, 60),
280
+ hint: pl.description.slice(0, 50),
267
281
  })),
268
- { value: "__url__", label: "Enter URL manually...", hint: "Input a GitHub URL or plugin name" },
269
282
  ],
283
+ required: false,
270
284
  });
271
285
  if (p.isCancel(selected))
272
286
  return;
273
- if (selected === "__url__") {
274
- plugin = (await p.text({
275
- message: "Plugin URL or name:",
276
- }));
277
- if (p.isCancel(plugin))
278
- return;
287
+ for (const name of selected) {
288
+ data.plugins.push(name);
279
289
  }
280
- else {
281
- plugin = selected;
290
+ writeProfile(normalizedProfileName, data);
291
+ if (selected.length > 0) {
292
+ p.log.success(`Added ${selected.length} plugin(s) to profile "${normalizedProfileName}".`);
282
293
  }
294
+ return;
283
295
  }
284
296
  plugin = normalizePluginName(plugin);
285
297
  if (!plugin)
@@ -341,37 +353,55 @@ async function listPlugins(profileName) {
341
353
  p.log.success(pl);
342
354
  }
343
355
  }
344
- async function searchPlugins(keyword) {
345
- if (!keyword) {
346
- if (!canPrompt()) {
347
- missingArg("Search keyword is required.", "ccx search <keyword>");
348
- return;
349
- }
350
- keyword = (await p.text({
351
- message: "Search plugins:",
356
+ async function browsePlugins(currentProfile) {
357
+ const allPlugins = getAllPlugins();
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):",
352
366
  }));
353
- if (p.isCancel(keyword))
354
- return;
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
+ }
355
379
  }
356
- const allPlugins = getAllPlugins();
357
- const lower = keyword.toLowerCase();
358
- const results = allPlugins.filter((pl) => pl.name.toLowerCase().includes(lower) ||
359
- pl.description.toLowerCase().includes(lower) ||
360
- (pl.category || "").toLowerCase().includes(lower));
361
- if (results.length === 0) {
362
- p.log.warn(`No plugins matching "${keyword}".`);
363
- return;
380
+ if (currentProfile) {
381
+ const data = readProfile(currentProfile);
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,
390
+ });
391
+ if (p.isCancel(selected))
392
+ return [];
393
+ return selected.filter((name) => !data.plugins.includes(name));
364
394
  }
365
395
  const grouped = new Map();
366
- for (const pl of results) {
396
+ for (const pl of plugins) {
367
397
  const list = grouped.get(pl.marketplace) || [];
368
398
  list.push(pl);
369
399
  grouped.set(pl.marketplace, list);
370
400
  }
371
- console.log(`\n Found ${results.length} plugin(s) for "${keyword}":\n`);
372
- for (const [marketplace, plugins] of grouped) {
401
+ console.log(`\n ${plugins.length} plugin(s) available:\n`);
402
+ for (const [marketplace, mPlugins] of grouped) {
373
403
  console.log(` [${marketplace}]`);
374
- for (const pl of plugins) {
404
+ for (const pl of mPlugins) {
375
405
  const desc = pl.description.length > 70
376
406
  ? pl.description.slice(0, 67) + "..."
377
407
  : pl.description;
@@ -379,6 +409,7 @@ async function searchPlugins(keyword) {
379
409
  }
380
410
  console.log();
381
411
  }
412
+ return [];
382
413
  }
383
414
  async function executeProfile(profileName) {
384
415
  const normalizedProfileName = normalizeProfileName(profileName);
@@ -409,103 +440,6 @@ async function executeProfile(profileName) {
409
440
  s.stop(`${installed} installed${failed > 0 ? `, ${failed} failed` : ""}`);
410
441
  p.log.success("Done.");
411
442
  }
412
- async function installWizard() {
413
- while (true) {
414
- const action = await p.select({
415
- message: "Install",
416
- options: [
417
- { value: "install", label: "Install profile plugins", hint: "Run a profile" },
418
- { value: "back", label: "Back" },
419
- { value: "exit", label: "Exit" },
420
- ],
421
- });
422
- if (p.isCancel(action) || action === "exit")
423
- return "exit";
424
- if (action === "back")
425
- return "back";
426
- const name = await selectProfile("Select profile to install:");
427
- if (name)
428
- await executeProfile(name);
429
- }
430
- }
431
- async function profilesWizard() {
432
- while (true) {
433
- const action = await p.select({
434
- message: "Profiles",
435
- options: [
436
- { value: "create", label: "Create profile", hint: "Create a new empty profile" },
437
- { value: "list", label: "List profiles", hint: "Show all profiles" },
438
- { value: "delete", label: "Delete profile", hint: "Remove a profile" },
439
- { value: "back", label: "Back" },
440
- { value: "exit", label: "Exit" },
441
- ],
442
- });
443
- if (p.isCancel(action) || action === "exit")
444
- return "exit";
445
- if (action === "back")
446
- return "back";
447
- switch (action) {
448
- case "create":
449
- await addProfile();
450
- break;
451
- case "list":
452
- await listProfiles();
453
- break;
454
- case "delete":
455
- await removeProfile();
456
- break;
457
- }
458
- }
459
- }
460
- async function pluginsWizard() {
461
- while (true) {
462
- const action = await p.select({
463
- message: "Plugins",
464
- options: [
465
- { value: "add", label: "Add plugin to profile", hint: "Choose a profile, then a plugin" },
466
- { value: "remove", label: "Remove plugin from profile", hint: "Choose a profile, then a plugin" },
467
- { value: "list", label: "List profile plugins", hint: "Show plugins in a profile" },
468
- { value: "back", label: "Back" },
469
- { value: "exit", label: "Exit" },
470
- ],
471
- });
472
- if (p.isCancel(action) || action === "exit")
473
- return "exit";
474
- if (action === "back")
475
- return "back";
476
- const name = await selectProfile("Select profile:");
477
- if (!name)
478
- continue;
479
- switch (action) {
480
- case "add":
481
- await addPlugin(name);
482
- break;
483
- case "remove":
484
- await removePlugin(name);
485
- break;
486
- case "list":
487
- await listPlugins(name);
488
- break;
489
- }
490
- }
491
- }
492
- async function marketplaceWizard() {
493
- while (true) {
494
- const action = await p.select({
495
- message: "Marketplace",
496
- options: [
497
- { value: "search", label: "Search plugins", hint: "Search installed marketplaces" },
498
- { value: "back", label: "Back" },
499
- { value: "exit", label: "Exit" },
500
- ],
501
- });
502
- if (p.isCancel(action) || action === "exit")
503
- return "exit";
504
- if (action === "back")
505
- return "back";
506
- await searchPlugins();
507
- }
508
- }
509
443
  function printBanner() {
510
444
  const require = createRequire(import.meta.url);
511
445
  const pkg = require("../package.json");
@@ -518,40 +452,67 @@ async function interactiveMode() {
518
452
  return;
519
453
  }
520
454
  printBanner();
521
- let shouldExit = false;
522
- while (!shouldExit) {
523
- const area = await p.select({
524
- message: "Choose area:",
525
- options: [
526
- { value: "install", label: "Install", hint: "Run a profile" },
527
- { value: "profiles", label: "Profiles", hint: "Create, list, or delete profiles" },
528
- { value: "plugins", label: "Plugins", hint: "Manage plugins inside profiles" },
529
- { value: "marketplace", label: "Marketplace", hint: "Search available plugins" },
530
- { value: "help", label: "Help", hint: "Show command usage" },
531
- { value: "exit", label: "Exit" },
532
- ],
533
- });
534
- if (p.isCancel(area) || area === "exit")
455
+ let currentProfile;
456
+ // Profile selection loop
457
+ while (true) {
458
+ currentProfile = await selectProfileOrNew();
459
+ if (!currentProfile)
535
460
  break;
536
- let result = "back";
537
- switch (area) {
538
- case "install":
539
- result = await installWizard();
540
- break;
541
- case "profiles":
542
- result = await profilesWizard();
543
- break;
544
- case "plugins":
545
- result = await pluginsWizard();
546
- break;
547
- case "marketplace":
548
- result = await marketplaceWizard();
549
- break;
550
- case "help":
551
- printHelp();
461
+ // Action loop — all operations on current profile
462
+ let stayInProfile = true;
463
+ while (stayInProfile && currentProfile) {
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}`);
469
+ const action = await p.select({
470
+ message: "Choose action:",
471
+ options: [
472
+ { value: "install", label: "Install", hint: "Apply to current project" },
473
+ { value: "add", label: "Add plugin", hint: "Search and add plugins" },
474
+ { value: "remove", label: "Remove plugin", hint: "Remove a plugin" },
475
+ { value: "search", label: "Search marketplace", hint: "Find new plugins" },
476
+ { value: "switch", label: "Switch profile", hint: "Choose a different profile" },
477
+ { value: "delete", label: "Delete profile", hint: "Remove this profile" },
478
+ { value: "exit", label: "Exit" },
479
+ ],
480
+ });
481
+ if (p.isCancel(action) || action === "exit") {
482
+ currentProfile = undefined;
483
+ stayInProfile = false;
552
484
  break;
485
+ }
486
+ switch (action) {
487
+ case "install":
488
+ await executeProfile(currentProfile);
489
+ break;
490
+ case "add":
491
+ await addPlugin(currentProfile);
492
+ break;
493
+ case "remove":
494
+ await removePlugin(currentProfile);
495
+ break;
496
+ case "search": {
497
+ const found = await browsePlugins(currentProfile);
498
+ if (found.length > 0) {
499
+ const d = readProfile(currentProfile);
500
+ d.plugins.push(...found);
501
+ writeProfile(currentProfile, d);
502
+ p.log.success(`Added ${found.length} plugin(s) to profile "${currentProfile}".`);
503
+ }
504
+ break;
505
+ }
506
+ case "switch":
507
+ stayInProfile = false;
508
+ break;
509
+ case "delete":
510
+ await removeProfile(currentProfile);
511
+ currentProfile = undefined;
512
+ stayInProfile = false;
513
+ break;
514
+ }
553
515
  }
554
- shouldExit = result === "exit";
555
516
  }
556
517
  p.outro("Done.");
557
518
  }
@@ -642,7 +603,7 @@ async function main(args) {
642
603
  }
643
604
  break;
644
605
  case "search":
645
- await searchPlugins(args[1]);
606
+ await browsePlugins();
646
607
  break;
647
608
  default: {
648
609
  const profileName = cmd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanmu/ccprofile",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Agent Profile Manager for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {