@guanmu/ccprofile 0.1.13 → 0.1.15

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 +128 -155
  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) {
@@ -239,10 +253,28 @@ async function addPlugin(profileName, plugin) {
239
253
  p.log.warn("All available plugins already added.");
240
254
  return;
241
255
  }
256
+ let filtered = available;
257
+ if (available.length > 10) {
258
+ const query = (await p.text({
259
+ message: `Search plugins (leave empty to list all):`,
260
+ }));
261
+ if (p.isCancel(query))
262
+ return;
263
+ const q = query.trim().toLowerCase();
264
+ if (q) {
265
+ filtered = available.filter((pl) => pl.name.toLowerCase().includes(q) ||
266
+ pl.description.toLowerCase().includes(q) ||
267
+ (pl.category && pl.category.toLowerCase().includes(q)));
268
+ if (filtered.length === 0) {
269
+ p.log.warn(`No plugins matching "${query.trim()}".`);
270
+ return;
271
+ }
272
+ }
273
+ }
242
274
  const selected = await p.select({
243
275
  message: `Add plugin to "${profileName}":`,
244
276
  options: [
245
- ...available.map((pl) => ({
277
+ ...filtered.map((pl) => ({
246
278
  value: pl.name,
247
279
  label: pl.name,
248
280
  hint: pl.description.slice(0, 60),
@@ -323,17 +355,17 @@ async function listPlugins(profileName) {
323
355
  p.log.success(pl);
324
356
  }
325
357
  }
326
- async function searchPlugins(keyword) {
358
+ async function searchPlugins(keyword, currentProfile) {
327
359
  if (!keyword) {
328
360
  if (!canPrompt()) {
329
361
  missingArg("Search keyword is required.", "ccx search <keyword>");
330
- return;
362
+ return undefined;
331
363
  }
332
364
  keyword = (await p.text({
333
365
  message: "Search plugins:",
334
366
  }));
335
367
  if (p.isCancel(keyword))
336
- return;
368
+ return undefined;
337
369
  }
338
370
  const allPlugins = getAllPlugins();
339
371
  const lower = keyword.toLowerCase();
@@ -342,7 +374,24 @@ async function searchPlugins(keyword) {
342
374
  (pl.category || "").toLowerCase().includes(lower));
343
375
  if (results.length === 0) {
344
376
  p.log.warn(`No plugins matching "${keyword}".`);
345
- return;
377
+ return undefined;
378
+ }
379
+ if (currentProfile) {
380
+ 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
+ ],
391
+ });
392
+ if (p.isCancel(selected) || selected === "__back__")
393
+ return undefined;
394
+ return selected;
346
395
  }
347
396
  const grouped = new Map();
348
397
  for (const pl of results) {
@@ -361,6 +410,7 @@ async function searchPlugins(keyword) {
361
410
  }
362
411
  console.log();
363
412
  }
413
+ return undefined;
364
414
  }
365
415
  async function executeProfile(profileName) {
366
416
  const normalizedProfileName = normalizeProfileName(profileName);
@@ -391,119 +441,10 @@ async function executeProfile(profileName) {
391
441
  s.stop(`${installed} installed${failed > 0 ? `, ${failed} failed` : ""}`);
392
442
  p.log.success("Done.");
393
443
  }
394
- async function installWizard() {
395
- while (true) {
396
- const action = await p.select({
397
- message: "Install",
398
- options: [
399
- { value: "install", label: "Install profile plugins", hint: "Run a profile" },
400
- { value: "back", label: "Back" },
401
- { value: "exit", label: "Exit" },
402
- ],
403
- });
404
- if (p.isCancel(action) || action === "exit")
405
- return "exit";
406
- if (action === "back")
407
- return "back";
408
- const name = await selectProfile("Select profile to install:");
409
- if (name)
410
- await executeProfile(name);
411
- }
412
- }
413
- async function profilesWizard() {
414
- while (true) {
415
- const action = await p.select({
416
- message: "Profiles",
417
- options: [
418
- { value: "create", label: "Create profile", hint: "Create a new empty profile" },
419
- { value: "list", label: "List profiles", hint: "Show all profiles" },
420
- { value: "delete", label: "Delete profile", hint: "Remove a profile" },
421
- { value: "back", label: "Back" },
422
- { value: "exit", label: "Exit" },
423
- ],
424
- });
425
- if (p.isCancel(action) || action === "exit")
426
- return "exit";
427
- if (action === "back")
428
- return "back";
429
- switch (action) {
430
- case "create":
431
- await addProfile();
432
- break;
433
- case "list":
434
- await listProfiles();
435
- break;
436
- case "delete":
437
- await removeProfile();
438
- break;
439
- }
440
- }
441
- }
442
- async function pluginsWizard() {
443
- while (true) {
444
- const action = await p.select({
445
- message: "Plugins",
446
- options: [
447
- { value: "add", label: "Add plugin to profile", hint: "Choose a profile, then a plugin" },
448
- { value: "remove", label: "Remove plugin from profile", hint: "Choose a profile, then a plugin" },
449
- { value: "list", label: "List profile plugins", hint: "Show plugins in a profile" },
450
- { value: "back", label: "Back" },
451
- { value: "exit", label: "Exit" },
452
- ],
453
- });
454
- if (p.isCancel(action) || action === "exit")
455
- return "exit";
456
- if (action === "back")
457
- return "back";
458
- const name = await selectProfile("Select profile:");
459
- if (!name)
460
- continue;
461
- switch (action) {
462
- case "add":
463
- await addPlugin(name);
464
- break;
465
- case "remove":
466
- await removePlugin(name);
467
- break;
468
- case "list":
469
- await listPlugins(name);
470
- break;
471
- }
472
- }
473
- }
474
- async function marketplaceWizard() {
475
- while (true) {
476
- const action = await p.select({
477
- message: "Marketplace",
478
- options: [
479
- { value: "search", label: "Search plugins", hint: "Search installed marketplaces" },
480
- { value: "back", label: "Back" },
481
- { value: "exit", label: "Exit" },
482
- ],
483
- });
484
- if (p.isCancel(action) || action === "exit")
485
- return "exit";
486
- if (action === "back")
487
- return "back";
488
- await searchPlugins();
489
- }
490
- }
491
- const CCX_LOGO = [
492
- " ██████╗██████╗",
493
- " ██╔════╝██╔══██╗",
494
- " ██║ ██████╔╝",
495
- " ██║ ██╔══██╗",
496
- " ╚██████╗██║ ██║",
497
- " ╚═════╝╚═╝ ╚═╝",
498
- ];
499
444
  function printBanner() {
500
445
  const require = createRequire(import.meta.url);
501
446
  const pkg = require("../package.json");
502
- console.log();
503
- CCX_LOGO.forEach((line) => console.log(pc.bold(pc.magenta(line))));
504
- console.log();
505
- console.log(pc.dim(" ") + pc.italic(pc.white("Agent Profile Manager")) + pc.dim(" ") + pc.gray(`v${pkg.version}`));
506
- console.log();
447
+ p.note(pc.bold("ccx") + pc.dim(" — Agent Profile Manager ") + pc.gray(`v${pkg.version}`));
507
448
  }
508
449
  async function interactiveMode() {
509
450
  if (!canPrompt()) {
@@ -512,40 +453,72 @@ async function interactiveMode() {
512
453
  return;
513
454
  }
514
455
  printBanner();
515
- let shouldExit = false;
516
- while (!shouldExit) {
517
- const area = await p.select({
518
- message: "Choose area:",
519
- options: [
520
- { value: "install", label: "Install", hint: "Run a profile" },
521
- { value: "profiles", label: "Profiles", hint: "Create, list, or delete profiles" },
522
- { value: "plugins", label: "Plugins", hint: "Manage plugins inside profiles" },
523
- { value: "marketplace", label: "Marketplace", hint: "Search available plugins" },
524
- { value: "help", label: "Help", hint: "Show command usage" },
525
- { value: "exit", label: "Exit" },
526
- ],
527
- });
528
- if (p.isCancel(area) || area === "exit")
456
+ let currentProfile;
457
+ // Profile selection loop
458
+ while (true) {
459
+ currentProfile = await selectProfileOrNew();
460
+ if (!currentProfile)
529
461
  break;
530
- let result = "back";
531
- switch (area) {
532
- case "install":
533
- result = await installWizard();
534
- break;
535
- case "profiles":
536
- result = await profilesWizard();
537
- break;
538
- case "plugins":
539
- result = await pluginsWizard();
540
- break;
541
- case "marketplace":
542
- result = await marketplaceWizard();
543
- break;
544
- case "help":
545
- printHelp();
462
+ // Action loop — all operations on current profile
463
+ let stayInProfile = true;
464
+ while (stayInProfile && currentProfile) {
465
+ const data = readProfile(currentProfile);
466
+ const action = await p.select({
467
+ message: pc.bold(pc.cyan(`[${currentProfile}]`)) + ` ${data.plugins.length} plugin(s)`,
468
+ options: [
469
+ { value: "install", label: "Install", hint: "Apply to current project" },
470
+ { value: "add", label: "Add plugin", hint: "Search and add a plugin" },
471
+ { value: "remove", label: "Remove plugin", hint: "Remove a plugin" },
472
+ { value: "list", label: "List plugins", hint: "Show all plugins" },
473
+ { value: "search", label: "Search marketplace", hint: "Find new plugins" },
474
+ { value: "switch", label: "Switch profile", hint: "Choose a different profile" },
475
+ { value: "delete", label: "Delete profile", hint: "Remove this profile" },
476
+ { value: "exit", label: "Exit" },
477
+ ],
478
+ });
479
+ if (p.isCancel(action) || action === "exit") {
480
+ currentProfile = undefined;
481
+ stayInProfile = false;
546
482
  break;
483
+ }
484
+ switch (action) {
485
+ case "install":
486
+ await executeProfile(currentProfile);
487
+ break;
488
+ case "add":
489
+ await addPlugin(currentProfile);
490
+ break;
491
+ case "remove":
492
+ await removePlugin(currentProfile);
493
+ break;
494
+ case "list":
495
+ await listPlugins(currentProfile);
496
+ break;
497
+ case "search": {
498
+ const found = await searchPlugins(undefined, currentProfile);
499
+ if (found) {
500
+ 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
+ }
509
+ }
510
+ break;
511
+ }
512
+ case "switch":
513
+ stayInProfile = false;
514
+ break;
515
+ case "delete":
516
+ await removeProfile(currentProfile);
517
+ currentProfile = undefined;
518
+ stayInProfile = false;
519
+ break;
520
+ }
547
521
  }
548
- shouldExit = result === "exit";
549
522
  }
550
523
  p.outro("Done.");
551
524
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanmu/ccprofile",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Agent Profile Manager for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {