claude-team-mcp 0.4.0__py3-none-any.whl

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 (42) hide show
  1. claude_team_mcp/__init__.py +24 -0
  2. claude_team_mcp/__main__.py +8 -0
  3. claude_team_mcp/cli_backends/__init__.py +44 -0
  4. claude_team_mcp/cli_backends/base.py +132 -0
  5. claude_team_mcp/cli_backends/claude.py +110 -0
  6. claude_team_mcp/cli_backends/codex.py +110 -0
  7. claude_team_mcp/colors.py +108 -0
  8. claude_team_mcp/formatting.py +120 -0
  9. claude_team_mcp/idle_detection.py +488 -0
  10. claude_team_mcp/iterm_utils.py +1119 -0
  11. claude_team_mcp/names.py +427 -0
  12. claude_team_mcp/profile.py +364 -0
  13. claude_team_mcp/registry.py +426 -0
  14. claude_team_mcp/schemas/__init__.py +5 -0
  15. claude_team_mcp/schemas/codex.py +267 -0
  16. claude_team_mcp/server.py +390 -0
  17. claude_team_mcp/session_state.py +1058 -0
  18. claude_team_mcp/subprocess_cache.py +119 -0
  19. claude_team_mcp/tools/__init__.py +52 -0
  20. claude_team_mcp/tools/adopt_worker.py +122 -0
  21. claude_team_mcp/tools/annotate_worker.py +57 -0
  22. claude_team_mcp/tools/bd_help.py +42 -0
  23. claude_team_mcp/tools/check_idle_workers.py +98 -0
  24. claude_team_mcp/tools/close_workers.py +194 -0
  25. claude_team_mcp/tools/discover_workers.py +129 -0
  26. claude_team_mcp/tools/examine_worker.py +56 -0
  27. claude_team_mcp/tools/list_workers.py +76 -0
  28. claude_team_mcp/tools/list_worktrees.py +106 -0
  29. claude_team_mcp/tools/message_workers.py +311 -0
  30. claude_team_mcp/tools/read_worker_logs.py +158 -0
  31. claude_team_mcp/tools/spawn_workers.py +634 -0
  32. claude_team_mcp/tools/wait_idle_workers.py +148 -0
  33. claude_team_mcp/utils/__init__.py +17 -0
  34. claude_team_mcp/utils/constants.py +87 -0
  35. claude_team_mcp/utils/errors.py +87 -0
  36. claude_team_mcp/utils/worktree_detection.py +79 -0
  37. claude_team_mcp/worker_prompt.py +350 -0
  38. claude_team_mcp/worktree.py +532 -0
  39. claude_team_mcp-0.4.0.dist-info/METADATA +414 -0
  40. claude_team_mcp-0.4.0.dist-info/RECORD +42 -0
  41. claude_team_mcp-0.4.0.dist-info/WHEEL +4 -0
  42. claude_team_mcp-0.4.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,427 @@
1
+ """Iconic names module for assigning memorable session names."""
2
+
3
+ import random
4
+
5
+ # =============================================================================
6
+ # NAME SETS ORGANIZED BY SIZE
7
+ # =============================================================================
8
+
9
+ # Solo icons - artists known by one name
10
+ SOLOS: dict[str, list[str]] = {
11
+ "cher": ["Cher"],
12
+ "madonna": ["Madonna"],
13
+ "prince": ["Prince"],
14
+ "beyonce": ["Beyoncé"],
15
+ "bono": ["Bono"],
16
+ "sting": ["Sting"],
17
+ "liberace": ["Liberace"],
18
+ "elvis": ["Elvis"],
19
+ "adele": ["Adele"],
20
+ "rihanna": ["Rihanna"],
21
+ "shakira": ["Shakira"],
22
+ "banksy": ["Banksy"],
23
+ "voltaire": ["Voltaire"],
24
+ "aristotle": ["Aristotle"],
25
+ "plato": ["Plato"],
26
+ "socrates": ["Socrates"],
27
+ "rembrandt": ["Rembrandt"],
28
+ "michelangelo": ["Michelangelo"],
29
+ "napoleon": ["Napoleon"],
30
+ "cleopatra": ["Cleopatra"],
31
+ "gandhi": ["Gandhi"],
32
+ "pele": ["Pelé"],
33
+ "ronaldinho": ["Ronaldinho"],
34
+ "zendaya": ["Zendaya"],
35
+ "lizzo": ["Lizzo"],
36
+ "bjork": ["Björk"],
37
+ "moby": ["Moby"],
38
+ "seal": ["Seal"],
39
+ "enya": ["Enya"],
40
+ "yanni": ["Yanni"],
41
+ # International artists
42
+ "sade": ["Sade"],
43
+ "stromae": ["Stromae"],
44
+ "lorde": ["Lorde"],
45
+ # Historical/cultural icons
46
+ "confucius": ["Confucius"],
47
+ "nefertiti": ["Nefertiti"],
48
+ "hypatia": ["Hypatia"],
49
+ "frida": ["Frida"],
50
+ "oprah": ["Oprah"],
51
+ "basquiat": ["Basquiat"],
52
+ "hokusai": ["Hokusai"],
53
+ # Fiction
54
+ "hiro": ["Hiro Protagonist"],
55
+ "wishbone": ["Wishbone"],
56
+ # Philosophy
57
+ "sartre": ["Sartre"],
58
+ "negarestani": ["Negarestani"],
59
+ }
60
+
61
+ # Dynamic duos - iconic pairs
62
+ DUOS: dict[str, list[str]] = {
63
+ "abbott_costello": ["Abbott", "Costello"],
64
+ "simon_garfunkel": ["Simon", "Garfunkel"],
65
+ "laurel_hardy": ["Laurel", "Hardy"],
66
+ "bonnie_clyde": ["Bonnie", "Clyde"],
67
+ "thelma_louise": ["Thelma", "Louise"],
68
+ "tom_jerry": ["Tom", "Jerry"],
69
+ "batman_robin": ["Batman", "Robin"],
70
+ "bert_ernie": ["Bert", "Ernie"],
71
+ "cheech_chong": ["Cheech", "Chong"],
72
+ "penn_teller": ["Penn", "Teller"],
73
+ "chip_dale": ["Chip", "Dale"],
74
+ "ren_stimpy": ["Ren", "Stimpy"],
75
+ "pinky_brain": ["Pinky", "Brain"],
76
+ "rick_morty": ["Rick", "Morty"],
77
+ "beavis_butthead": ["Beavis", "Butthead"],
78
+ "starsky_hutch": ["Starsky", "Hutch"],
79
+ "cagney_lacey": ["Cagney", "Lacey"],
80
+ "tango_cash": ["Tango", "Cash"],
81
+ "rocky_bullwinkle": ["Rocky", "Bullwinkle"],
82
+ "calvin_hobbes": ["Calvin", "Hobbes"],
83
+ "mario_luigi": ["Mario", "Luigi"],
84
+ "sonic_tails": ["Sonic", "Tails"],
85
+ "sherlock_watson": ["Sherlock", "Watson"],
86
+ "frodo_sam": ["Frodo", "Sam"],
87
+ "han_chewie": ["Han", "Chewie"],
88
+ "r2_3po": ["R2D2", "C-3PO"],
89
+ "bill_ted": ["Bill", "Ted"],
90
+ "wayne_garth": ["Wayne", "Garth"],
91
+ "blues_brothers": ["Jake", "Elwood"],
92
+ "key_peele": ["Key", "Peele"],
93
+ "hall_oates": ["Hall", "Oates"],
94
+ "outkast": ["André", "Big Boi"],
95
+ "daft_punk": ["Thomas", "Guy-Man"],
96
+ "flight_concords": ["Bret", "Jemaine"],
97
+ "odd_couple": ["Oscar", "Felix"],
98
+ "peanut_butter_jelly": ["PB", "J"],
99
+ "salt_pepa": ["Salt", "Pepa"],
100
+ "milli_vanilli": ["Fab", "Rob"],
101
+ "wham": ["George", "Andrew"],
102
+ # Literature & mythology
103
+ "don_sancho": ["Don Quixote", "Sancho"],
104
+ "romeo_juliet": ["Romeo", "Juliet"],
105
+ # Film
106
+ "jules_vincent": ["Jules", "Vincent"],
107
+ # Gaming
108
+ "ratchet_clank": ["Ratchet", "Clank"],
109
+ "jak_daxter": ["Jak", "Daxter"],
110
+ "goku_vegeta": ["Goku", "Vegeta"],
111
+ # Science & innovation
112
+ "wright_bros": ["Orville", "Wilbur"],
113
+ "jobs_woz": ["Steve", "Woz"],
114
+ # Philosophy
115
+ "phil_science": ["Peirce", "Kuhn"],
116
+ "pragmatists": ["James", "Rorty"],
117
+ "d_and_g": ["Deleuze", "Guattari"],
118
+ # TV
119
+ "tia_tamera": ["Tia", "Tamera"],
120
+ "statler_waldorf": ["Statler", "Waldorf"],
121
+ "laverne_shirley": ["Laverne", "Shirley"],
122
+ # Star Trek
123
+ "kirk_khan": ["Kirk", "Khan"],
124
+ "picard_q": ["Picard", "Q"],
125
+ }
126
+
127
+ # Terrific trios - famous threesomes
128
+ TRIOS: dict[str, list[str]] = {
129
+ "three_stooges": ["Larry", "Moe", "Curly"],
130
+ "destinys_child": ["Beyoncé", "Kelly", "Michelle"],
131
+ "nirvana": ["Kurt", "Krist", "Dave"],
132
+ "the_police": ["Sting", "Andy", "Stewart"],
133
+ "bee_gees": ["Barry", "Robin", "Maurice"],
134
+ "rush": ["Geddy", "Alex", "Neil"],
135
+ "cream": ["Eric", "Jack", "Ginger"],
136
+ "zz_top": ["Billy", "Dusty", "Frank"],
137
+ "green_day": ["Billie", "Mike", "Tré"],
138
+ "blink_182": ["Mark", "Tom", "Travis"],
139
+ "tlc": ["T-Boz", "Left Eye", "Chilli"],
140
+ "supremes": ["Diana", "Mary", "Florence"],
141
+ "charlie_angels": ["Kelly", "Jill", "Sabrina"],
142
+ "powerpuff_girls": ["Blossom", "Bubbles", "Buttercup"],
143
+ "oai_research": ["Ilya", "Dario", "Sam"],
144
+ "southern_reach": ["Biologist", "Control", "Saul"],
145
+ "homeward_bound": ["Shadow", "Chance", "Sassy"],
146
+ "john_dies": ["David", "John", "Amy"],
147
+ "totoro": ["Satsuki", "Mei", "Totoro"],
148
+ "trio_mandili": ["Tatuli", "Tako", "Mariam"],
149
+ # Philosophy
150
+ "vienna_circle": ["Carnap", "Schlick", "Neurath"],
151
+ "ccru": ["Land", "Plant", "Fisher"],
152
+ # Film
153
+ "stalker": ["Stalker", "Writer", "Professor"],
154
+ # Horror
155
+ "pontypool": ["Grant", "Sydney", "Laurel-Ann"],
156
+ # TV/Animation
157
+ "ed_edd_eddy": ["Ed", "Edd", "Eddy"],
158
+ "chipettes": ["Brittany", "Jeanette", "Eleanor"],
159
+ "chipmunks": ["Alvin", "Simon", "Theodore"],
160
+ "animaniacs": ["Yakko", "Wakko", "Dot"],
161
+ "three_musketeers": ["Athos", "Porthos", "Aramis"],
162
+ "three_tenors": ["Luciano", "Plácido", "José"],
163
+ "three_amigos": ["Lucky", "Dusty", "Ned"],
164
+ "hanson": ["Isaac", "Taylor", "Zac"],
165
+ "jonas_brothers": ["Kevin", "Joe", "Nick"],
166
+ "dixie_chicks": ["Natalie", "Martie", "Emily"],
167
+ "trinity": ["Neo", "Trinity", "Morpheus"],
168
+ "good_bad_ugly": ["Blondie", "Angel Eyes", "Tuco"],
169
+ "ghostbusters_og": ["Peter", "Ray", "Egon"],
170
+ "hp_trio": ["Harry", "Ron", "Hermione"],
171
+ "lotr_hunters": ["Aragorn", "Legolas", "Gimli"],
172
+ "fairly_odd": ["Timmy", "Cosmo", "Wanda"],
173
+ # Mythology & folklore
174
+ "three_fates": ["Clotho", "Lachesis", "Atropos"],
175
+ "norns": ["Urd", "Verdandi", "Skuld"],
176
+ "trimurti": ["Brahma", "Vishnu", "Shiva"],
177
+ # Literature
178
+ "bronte_sisters": ["Charlotte", "Emily", "Anne"],
179
+ # Gaming & anime
180
+ "triforce": ["Link", "Zelda", "Ganon"],
181
+ "team_rocket": ["Jessie", "James", "Meowth"],
182
+ "sannin": ["Jiraiya", "Tsunade", "Orochimaru"],
183
+ # TV
184
+ "top_gear": ["Jeremy", "Richard", "James"],
185
+ "trailer_park": ["Julian", "Ricky", "Bubbles"],
186
+ "totally_spies": ["Sam", "Clover", "Alex"],
187
+ }
188
+
189
+ # Fabulous fours - quartets
190
+ QUARTETS: dict[str, list[str]] = {
191
+ "beatles": ["John", "Paul", "George", "Ringo"],
192
+ "tmnt": ["Leonardo", "Donatello", "Raphael", "Michelangelo"],
193
+ "fantastic_four": ["Reed", "Sue", "Johnny", "Ben"],
194
+ "a_team": ["Hannibal", "Face", "Murdock", "B.A."],
195
+ "marx_brothers": ["Groucho", "Harpo", "Chico", "Zeppo"],
196
+ "ghostbusters": ["Peter", "Ray", "Egon", "Winston"],
197
+ "hogwarts_founders": ["Godric", "Helga", "Rowena", "Salazar"],
198
+ "horsemen": ["Conquest", "War", "Famine", "Death"],
199
+ "golden_girls": ["Dorothy", "Rose", "Blanche", "Sophia"],
200
+ "sex_city": ["Carrie", "Samantha", "Charlotte", "Miranda"],
201
+ "queen": ["Freddie", "Brian", "Roger", "John"],
202
+ "led_zeppelin": ["Robert", "Jimmy", "John Paul", "Bonzo"],
203
+ "metallica": ["James", "Lars", "Kirk", "Robert"],
204
+ "u2": ["Bono", "Edge", "Adam", "Larry"],
205
+ "red_hot_chili_peppers": ["Anthony", "Flea", "Chad", "John"],
206
+ "coldplay": ["Chris", "Jonny", "Guy", "Will"],
207
+ "radiohead_core": ["Thom", "Jonny", "Colin", "Ed"],
208
+ "south_park": ["Stan", "Kyle", "Cartman", "Kenny"],
209
+ "seinfeld": ["Jerry", "George", "Elaine", "Kramer"],
210
+ "wizard_oz": ["Dorothy", "Scarecrow", "Tinman", "Lion"],
211
+ "big_bang": ["Sheldon", "Leonard", "Howard", "Raj"],
212
+ "who": ["Roger", "Pete", "John", "Keith"],
213
+ "kiss": ["Gene", "Paul", "Ace", "Peter"],
214
+ "motley_crue": ["Vince", "Nikki", "Mick", "Tommy"],
215
+ "van_halen": ["David", "Eddie", "Alex", "Michael"],
216
+ "doors": ["Jim", "Ray", "Robby", "John"],
217
+ "monkees": ["Davy", "Micky", "Michael", "Peter"],
218
+ "crosby_stills": ["David", "Stephen", "Graham", "Neil"],
219
+ "it_crowd": ["Roy", "Moss", "Jen", "Richmond"],
220
+ "mst3k": ["Cambot", "Tom", "Gypsy", "Crow"],
221
+ # Philosophy
222
+ "frankfurt_school": ["Adorno", "Horkheimer", "Marcuse", "Benjamin"],
223
+ # Mythology
224
+ "cardinal_virtues": ["Prudence", "Justice", "Temperance", "Fortitude"],
225
+ "four_winds": ["Boreas", "Notus", "Eurus", "Zephyrus"],
226
+ # Anime
227
+ "team_avatar": ["Aang", "Katara", "Sokka", "Toph"],
228
+ "cowboy_bebop": ["Spike", "Jet", "Faye", "Ed"],
229
+ # TV comedy
230
+ "its_sunny": ["Dennis", "Mac", "Charlie", "Dee"],
231
+ "impractical": ["Joe", "Murr", "Sal", "Q"],
232
+ "schitts_creek": ["Johnny", "Moira", "David", "Alexis"],
233
+ # Music
234
+ "abba": ["Agnetha", "Björn", "Benny", "Frida"],
235
+ "black_sabbath": ["Ozzy", "Tony", "Geezer", "Bill"],
236
+ # Historical
237
+ "mount_rushmore": ["Washington", "Jefferson", "Roosevelt", "Lincoln"],
238
+ }
239
+
240
+ # Famous fives - quintets
241
+ QUINTETS: dict[str, list[str]] = {
242
+ "spice_girls": ["Scary", "Sporty", "Baby", "Ginger", "Posh"],
243
+ "breakfast_club": ["Brain", "Athlete", "Basket Case", "Princess", "Criminal"],
244
+ "jackson_5": ["Jackie", "Tito", "Jermaine", "Marlon", "Michael"],
245
+ "nsync": ["Justin", "JC", "Chris", "Joey", "Lance"],
246
+ "backstreet_boys": ["AJ", "Howie", "Nick", "Kevin", "Brian"],
247
+ "one_direction": ["Harry", "Niall", "Liam", "Louis", "Zayn"],
248
+ "new_kids": ["Donnie", "Jordan", "Joey", "Jonathan", "Danny"],
249
+ "new_edition": ["Bobby", "Ralph", "Ricky", "Michael", "Ronnie"],
250
+ "temptations": ["Eddie", "David", "Otis", "Paul", "Melvin"],
251
+ "aerosmith": ["Steven", "Joe", "Tom", "Joey", "Brad"],
252
+ "rolling_stones": ["Mick", "Keith", "Charlie", "Ron", "Bill"],
253
+ "maroon_5_core": ["Adam", "Jesse", "Mickey", "James", "Matt"],
254
+ "ac_dc": ["Angus", "Malcolm", "Brian", "Cliff", "Phil"],
255
+ "foo_fighters_core": ["Dave", "Nate", "Taylor", "Pat", "Chris"],
256
+ "grateful_dead_core": ["Jerry", "Bob", "Phil", "Bill", "Mickey"],
257
+ "power_rangers_og": ["Jason", "Trini", "Zack", "Billy", "Kimberly"],
258
+ "voltron_force": ["Keith", "Lance", "Pidge", "Hunk", "Allura"],
259
+ "planeteers": ["Kwame", "Wheeler", "Linka", "Gi", "Ma-Ti"],
260
+ "scooby_gang": ["Fred", "Daphne", "Velma", "Shaggy", "Scooby"],
261
+ "queer_eye_og": ["Ted", "Kyan", "Thom", "Carson", "Jai"],
262
+ "rat_pack": ["Frank", "Dean", "Sammy", "Peter", "Joey"],
263
+ "yamadas": ["Takashi", "Matsuko", "Shige", "Noboru", "Nonoko"],
264
+ }
265
+
266
+ # Larger ensembles for flexibility
267
+ LARGER: dict[str, list[str]] = {
268
+ "monty_python": ["Chapman", "Cleese", "Gilliam", "Idle", "Jones", "Palin"],
269
+ "friends": ["Rachel", "Monica", "Phoebe", "Ross", "Joey", "Chandler"],
270
+ "ocean_eleven": [
271
+ "Danny", "Rusty", "Linus", "Basher", "Yen",
272
+ "Virgil", "Turk", "Frank", "Reuben", "Livingston", "Saul",
273
+ ],
274
+ "fellowship": [
275
+ "Frodo", "Sam", "Merry", "Pippin", "Gandalf",
276
+ "Aragorn", "Legolas", "Gimli", "Boromir",
277
+ ],
278
+ "avengers_og": [
279
+ "Tony", "Steve", "Thor", "Bruce", "Natasha", "Clint",
280
+ ],
281
+ "justice_league_core": [
282
+ "Clark", "Bruce", "Diana", "Barry", "Arthur", "Victor", "Hal",
283
+ ],
284
+ "x_men_og": [
285
+ "Charles", "Scott", "Jean", "Hank", "Bobby", "Warren",
286
+ ],
287
+ "brady_bunch": [
288
+ "Mike", "Carol", "Greg", "Marcia", "Peter",
289
+ "Jan", "Bobby", "Cindy",
290
+ ],
291
+ "simpsons": ["Homer", "Marge", "Bart", "Lisa", "Maggie"],
292
+ "parks_rec": ["Leslie", "Ron", "Tom", "April", "Andy", "Ben", "Ann"],
293
+ "office_core": ["Michael", "Jim", "Pam", "Dwight", "Angela", "Kevin", "Oscar"],
294
+ "stranger_things_kids": ["Mike", "Eleven", "Dustin", "Lucas", "Will", "Max"],
295
+ "goonies": ["Mikey", "Mouth", "Chunk", "Data", "Brand", "Andy", "Stef"],
296
+ "lost_boys": ["Michael", "Star", "Sam", "Edgar", "Alan", "Laddie"],
297
+ "sandlot": ["Scotty", "Benny", "Ham", "Squints", "Yeah-Yeah", "Kenny", "Bertram", "Timmy", "Tommy"],
298
+ "mighty_ducks": ["Charlie", "Adam", "Fulton", "Goldberg", "Jesse", "Guy", "Connie", "Averman"],
299
+ }
300
+
301
+ # Combined lookup for all sizes
302
+ SETS_BY_SIZE: dict[int, dict[str, list[str]]] = {
303
+ 1: SOLOS,
304
+ 2: DUOS,
305
+ 3: TRIOS,
306
+ 4: QUARTETS,
307
+ 5: QUINTETS,
308
+ }
309
+
310
+ # Legacy combined dict for backward compatibility
311
+ NAME_SETS: dict[str, list[str]] = {}
312
+ for size_dict in [SOLOS, DUOS, TRIOS, QUARTETS, QUINTETS, LARGER]:
313
+ NAME_SETS.update(size_dict)
314
+
315
+
316
+ def get_name_set(name: str) -> list[str]:
317
+ """Get names from a specific set.
318
+
319
+ Args:
320
+ name: The name of the set (e.g., "beatles", "tmnt")
321
+
322
+ Returns:
323
+ List of names in the set
324
+
325
+ Raises:
326
+ KeyError: If the set name doesn't exist
327
+ """
328
+ return NAME_SETS[name]
329
+
330
+
331
+ def pick_names_for_count(count: int) -> tuple[str, list[str]]:
332
+ """Pick a name set that matches the requested count.
333
+
334
+ For counts 1-5, picks a random set of exactly that size.
335
+ For counts > 5, combines sets to reach the target count.
336
+
337
+ Args:
338
+ count: Number of names needed
339
+
340
+ Returns:
341
+ Tuple of (set_description, list of names)
342
+ """
343
+ if count <= 0:
344
+ return ("empty", [])
345
+
346
+ if count <= 5:
347
+ # Pick a random set of exactly this size
348
+ size_sets = SETS_BY_SIZE[count]
349
+ set_name = random.choice(list(size_sets.keys()))
350
+ return (set_name, list(size_sets[set_name]))
351
+
352
+ # For count > 5, combine sets
353
+ names: list[str] = []
354
+ set_names: list[str] = []
355
+ remaining = count
356
+
357
+ while remaining > 0:
358
+ if remaining >= 5:
359
+ # Add a quintet
360
+ set_name = random.choice(list(QUINTETS.keys()))
361
+ names.extend(QUINTETS[set_name])
362
+ set_names.append(set_name)
363
+ remaining -= 5
364
+ elif remaining in SETS_BY_SIZE:
365
+ # Add a set of exact size
366
+ size_sets = SETS_BY_SIZE[remaining]
367
+ set_name = random.choice(list(size_sets.keys()))
368
+ names.extend(size_sets[set_name])
369
+ set_names.append(set_name)
370
+ remaining = 0
371
+ else:
372
+ # Should not happen for counts 1-5, but just in case
373
+ # Use a larger set and take what we need
374
+ if remaining > 5:
375
+ larger_name = random.choice(list(LARGER.keys()))
376
+ larger_set = LARGER[larger_name]
377
+ take = min(remaining, len(larger_set))
378
+ names.extend(larger_set[:take])
379
+ set_names.append(larger_name)
380
+ remaining -= take
381
+ else:
382
+ break
383
+
384
+ combined_name = " + ".join(set_names)
385
+ return (combined_name, names)
386
+
387
+
388
+ def pick_names(count: int, name_set: str | None = None) -> list[str]:
389
+ """Pick N names from a set, using size-matched selection when no set specified.
390
+
391
+ Args:
392
+ count: Number of names to pick
393
+ name_set: Optional set name. If None, picks a size-matched set.
394
+
395
+ Returns:
396
+ List of names (may combine sets if count > set size)
397
+
398
+ Raises:
399
+ KeyError: If the specified name_set doesn't exist
400
+ """
401
+ if name_set is None:
402
+ # Use size-matched selection
403
+ _, names = pick_names_for_count(count)
404
+ return names
405
+
406
+ # Use specified set
407
+ names = get_name_set(name_set)
408
+
409
+ result = []
410
+ for i in range(count):
411
+ result.append(names[i % len(names)])
412
+ return result
413
+
414
+
415
+ def list_sets_by_size() -> dict[int, list[str]]:
416
+ """List available name sets grouped by size.
417
+
418
+ Returns:
419
+ Dict mapping size to list of set names
420
+ """
421
+ result: dict[int, list[str]] = {}
422
+ for size, sets in SETS_BY_SIZE.items():
423
+ result[size] = list(sets.keys())
424
+ result[6] = [k for k, v in LARGER.items() if len(v) == 6]
425
+ result[7] = [k for k, v in LARGER.items() if len(v) == 7]
426
+ result[8] = [k for k, v in LARGER.items() if len(v) >= 8]
427
+ return result