88import typer
99
1010from code_assistant_manager .menu .base import Colors
11- from code_assistant_manager .prompts import PROMPT_FILE_PATHS , Prompt , PromptManager
11+ from code_assistant_manager .prompts import (
12+ PROMPT_FILE_PATHS ,
13+ Prompt ,
14+ PromptManager ,
15+ get_prompt_file_path ,
16+ )
1217
1318logger = logging .getLogger (__name__ )
1419
1924
2025# Valid app types
2126VALID_APP_TYPES = ["claude" , "codex" , "gemini" ]
27+ VALID_LEVELS = ["user" , "project" ]
2228
2329
2430def _get_prompt_manager () -> PromptManager :
@@ -264,8 +270,20 @@ def sync_prompts(
264270 "-a" ,
265271 help = "App type to sync to (claude, codex, gemini). Required if prompt_id is specified." ,
266272 ),
273+ level : str = typer .Option (
274+ "user" ,
275+ "--level" ,
276+ "-l" ,
277+ help = "Sync level: 'user' (~/.claude/) or 'project' (current directory)" ,
278+ ),
267279):
268280 """Sync prompts to app files. Can sync a specific prompt or all active prompts."""
281+ if level not in VALID_LEVELS :
282+ typer .echo (
283+ f"{ Colors .RED } ✗ Invalid level: { level } . Valid: { ', ' .join (VALID_LEVELS )} { Colors .RESET } "
284+ )
285+ raise typer .Exit (1 )
286+
269287 manager = _get_prompt_manager ()
270288
271289 if prompt_id :
@@ -287,21 +305,37 @@ def sync_prompts(
287305 typer .echo (f"{ Colors .RED } ✗ Prompt not found: { prompt_id } { Colors .RESET } " )
288306 raise typer .Exit (1 )
289307
308+ file_path = get_prompt_file_path (app_type , level )
309+ if not file_path :
310+ typer .echo (f"{ Colors .RED } ✗ Unknown app type: { app_type } { Colors .RESET } " )
311+ raise typer .Exit (1 )
312+
290313 try :
291- manager ._sync_prompt_to_file (prompt .content , app_type )
292- typer .echo (f"{ Colors .GREEN } ✓ Synced '{ prompt_id } ' to { app_type } { Colors .RESET } " )
293- file_path = PROMPT_FILE_PATHS .get (app_type )
314+ # Ensure parent directory exists
315+ file_path .parent .mkdir (parents = True , exist_ok = True )
316+ file_path .write_text (prompt .content , encoding = "utf-8" )
317+ typer .echo (
318+ f"{ Colors .GREEN } ✓ Synced '{ prompt_id } ' to { app_type } ({ level } ){ Colors .RESET } "
319+ )
294320 typer .echo (f" { Colors .CYAN } File:{ Colors .RESET } { file_path } " )
295321 except Exception as e :
296322 typer .echo (f"{ Colors .RED } ✗ Error: { e } { Colors .RESET } " )
297323 raise typer .Exit (1 )
298324 else :
299325 # Sync all active prompts
326+ if level == "project" :
327+ typer .echo (
328+ f"{ Colors .RED } ✗ --level project requires specifying a prompt ID{ Colors .RESET } "
329+ )
330+ raise typer .Exit (1 )
331+
300332 results = manager .sync_all ()
301333
302334 for app , synced_prompt_id in results .items ():
303335 if synced_prompt_id :
304- typer .echo (f"{ Colors .GREEN } ✓ { app } : synced ({ synced_prompt_id } ){ Colors .RESET } " )
336+ typer .echo (
337+ f"{ Colors .GREEN } ✓ { app } : synced ({ synced_prompt_id } ){ Colors .RESET } "
338+ )
305339 else :
306340 typer .echo (f"{ Colors .YELLOW } ○ { app } : no active prompt{ Colors .RESET } " )
307341
@@ -409,6 +443,12 @@ def unsync_prompt(
409443 "-a" ,
410444 help = "App type to unsync (claude, codex, gemini)" ,
411445 ),
446+ level : str = typer .Option (
447+ "user" ,
448+ "--level" ,
449+ "-l" ,
450+ help = "Unsync level: 'user' (~/.claude/) or 'project' (current directory)" ,
451+ ),
412452 force : bool = typer .Option (False , "--force" , "-f" , help = "Skip confirmation" ),
413453):
414454 """Clear/unsync the prompt file for an app (removes CLAUDE.md, AGENTS.md, or GEMINI.md content)."""
@@ -418,13 +458,21 @@ def unsync_prompt(
418458 )
419459 raise typer .Exit (1 )
420460
421- file_path = PROMPT_FILE_PATHS .get (app_type )
461+ if level not in VALID_LEVELS :
462+ typer .echo (
463+ f"{ Colors .RED } ✗ Invalid level: { level } . Valid: { ', ' .join (VALID_LEVELS )} { Colors .RESET } "
464+ )
465+ raise typer .Exit (1 )
466+
467+ file_path = get_prompt_file_path (app_type , level )
422468 if not file_path :
423469 typer .echo (f"{ Colors .RED } ✗ Unknown app type: { app_type } { Colors .RESET } " )
424470 raise typer .Exit (1 )
425471
426472 if not file_path .exists ():
427- typer .echo (f"{ Colors .YELLOW } Prompt file does not exist: { file_path } { Colors .RESET } " )
473+ typer .echo (
474+ f"{ Colors .YELLOW } Prompt file does not exist: { file_path } { Colors .RESET } "
475+ )
428476 return
429477
430478 if not force :
@@ -434,53 +482,74 @@ def unsync_prompt(
434482 file_path .write_text ("" , encoding = "utf-8" )
435483 typer .echo (f"{ Colors .GREEN } ✓ Cleared prompt file: { file_path } { Colors .RESET } " )
436484
437- # Deactivate any active prompt for this app
438- manager = _get_prompt_manager ()
439- prompts = manager .get_all ()
440- for prompt in prompts .values ():
441- if prompt .enabled and prompt .app_type == app_type :
442- prompt .enabled = False
443- manager .update (prompt )
444- typer .echo (f" { Colors .CYAN } Deactivated:{ Colors .RESET } { prompt .id } " )
485+ # Deactivate any active prompt for this app (only for user level)
486+ if level == "user" :
487+ manager = _get_prompt_manager ()
488+ prompts = manager .get_all ()
489+ for prompt in prompts .values ():
490+ if prompt .enabled and prompt .app_type == app_type :
491+ prompt .enabled = False
492+ manager .update (prompt )
493+ typer .echo (f" { Colors .CYAN } Deactivated:{ Colors .RESET } { prompt .id } " )
445494 except Exception as e :
446495 typer .echo (f"{ Colors .RED } ✗ Error: { e } { Colors .RESET } " )
447496 raise typer .Exit (1 )
448497
449498
450499@prompt_app .command ("status" )
451- def show_prompt_status ():
500+ def show_prompt_status (
501+ level : str = typer .Option (
502+ "all" ,
503+ "--level" ,
504+ "-l" ,
505+ help = "Show status for: 'user', 'project', or 'all' (default)" ,
506+ ),
507+ ):
452508 """Show prompt status for all apps."""
453509 manager = _get_prompt_manager ()
454510
455- typer . echo ( f" \n { Colors . BOLD } Prompt Status: { Colors . RESET } \n " )
511+ levels_to_show = [ "user" , "project" ] if level == "all" else [ level ]
456512
457- for app_type in VALID_APP_TYPES :
458- file_path = PROMPT_FILE_PATHS .get (app_type )
459- active_prompt = manager .get_active_prompt (app_type )
460-
461- typer .echo (f"{ Colors .BOLD } { app_type .capitalize ()} :{ Colors .RESET } " )
462- typer .echo (f" { Colors .CYAN } File:{ Colors .RESET } { file_path } " )
463-
464- if file_path and file_path .exists ():
465- content = file_path .read_text (encoding = "utf-8" )
466- if content .strip ():
467- lines = content .strip ().split ("\n " )
468- preview = lines [0 ][:50 ] + "..." if len (lines [0 ]) > 50 else lines [0 ]
469- typer .echo (f" { Colors .GREEN } Content:{ Colors .RESET } { preview } " )
470- typer .echo (f" { Colors .CYAN } Lines:{ Colors .RESET } { len (lines )} " )
471- else :
472- typer .echo (f" { Colors .YELLOW } Content:{ Colors .RESET } (empty)" )
473- else :
474- typer .echo (f" { Colors .YELLOW } Content:{ Colors .RESET } (file not found)" )
513+ if level != "all" and level not in VALID_LEVELS :
514+ typer .echo (
515+ f"{ Colors .RED } ✗ Invalid level: { level } . Valid: user, project, all{ Colors .RESET } "
516+ )
517+ raise typer .Exit (1 )
475518
476- if active_prompt :
477- typer .echo (
478- f" { Colors .GREEN } Active Prompt:{ Colors .RESET } { active_prompt .name } ({ active_prompt .id } )"
479- )
480- else :
481- typer .echo (f" { Colors .YELLOW } Active Prompt:{ Colors .RESET } None" )
519+ for lvl in levels_to_show :
520+ typer .echo (
521+ f"\n { Colors .BOLD } Prompt Status ({ lvl .capitalize ()} Level):{ Colors .RESET } \n "
522+ )
482523
483- typer .echo ()
524+ for app_type in VALID_APP_TYPES :
525+ file_path = get_prompt_file_path (app_type , lvl )
526+
527+ typer .echo (f"{ Colors .BOLD } { app_type .capitalize ()} :{ Colors .RESET } " )
528+ typer .echo (f" { Colors .CYAN } File:{ Colors .RESET } { file_path } " )
529+
530+ if file_path and file_path .exists ():
531+ content = file_path .read_text (encoding = "utf-8" )
532+ if content .strip ():
533+ lines = content .strip ().split ("\n " )
534+ preview = lines [0 ][:50 ] + "..." if len (lines [0 ]) > 50 else lines [0 ]
535+ typer .echo (f" { Colors .GREEN } Content:{ Colors .RESET } { preview } " )
536+ typer .echo (f" { Colors .CYAN } Lines:{ Colors .RESET } { len (lines )} " )
537+ else :
538+ typer .echo (f" { Colors .YELLOW } Content:{ Colors .RESET } (empty)" )
539+ else :
540+ typer .echo (f" { Colors .YELLOW } Content:{ Colors .RESET } (file not found)" )
541+
542+ # Only show active prompt for user level
543+ if lvl == "user" :
544+ active_prompt = manager .get_active_prompt (app_type )
545+ if active_prompt :
546+ typer .echo (
547+ f" { Colors .GREEN } Active Prompt:{ Colors .RESET } { active_prompt .name } ({ active_prompt .id } )"
548+ )
549+ else :
550+ typer .echo (f" { Colors .YELLOW } Active Prompt:{ Colors .RESET } None" )
551+
552+ typer .echo ()
484553
485554
486555# Add list shorthand
0 commit comments