claude-dev-cli 0.16.2__py3-none-any.whl → 0.18.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.

Potentially problematic release.


This version of claude-dev-cli might be problematic. Click here for more details.

@@ -9,7 +9,7 @@ Features:
9
9
  - Interactive and single-shot modes
10
10
  """
11
11
 
12
- __version__ = "0.16.2"
12
+ __version__ = "0.18.0"
13
13
  __author__ = "Julio"
14
14
  __license__ = "MIT"
15
15
 
claude_dev_cli/cli.py CHANGED
@@ -2873,5 +2873,429 @@ def workflow_validate(ctx: click.Context, workflow_file: str) -> None:
2873
2873
  sys.exit(1)
2874
2874
 
2875
2875
 
2876
+ # ============================================================================
2877
+ # PROJECT MANAGEMENT COMMANDS (v0.17.0)
2878
+ # ============================================================================
2879
+
2880
+ @main.group()
2881
+ def ticket() -> None:
2882
+ """Execute tickets from external systems (repo-tickets, Jira, etc.)."""
2883
+ pass
2884
+
2885
+
2886
+ @ticket.command('execute')
2887
+ @click.argument('ticket_id')
2888
+ @click.option('--backend', type=click.Choice(['repo-tickets', 'markdown']), default='markdown',
2889
+ help='Ticket backend to use')
2890
+ @click.option('--notify', is_flag=True, help='Send notifications')
2891
+ @click.option('--commit', is_flag=True, help='Auto-commit changes')
2892
+ @click.option('--no-context', is_flag=True, help='Skip codebase context gathering')
2893
+ @click.option('-a', '--api', help='API config for AI generation')
2894
+ @click.option('-m', '--model', help='Model to use')
2895
+ @click.pass_context
2896
+ def ticket_execute(
2897
+ ctx: click.Context,
2898
+ ticket_id: str,
2899
+ backend: str,
2900
+ notify: bool,
2901
+ commit: bool,
2902
+ no_context: bool,
2903
+ api: Optional[str],
2904
+ model: Optional[str]
2905
+ ) -> None:
2906
+ """Execute a ticket: fetch → analyze → generate code → tests → commit.
2907
+
2908
+ By default, gathers codebase context (dependencies, frameworks, similar code)
2909
+ before generating code. Use --no-context to skip this step for faster execution.
2910
+
2911
+ Examples:
2912
+ cdc ticket execute TASK-123
2913
+ cdc ticket execute JIRA-456 --backend repo-tickets --commit --notify
2914
+ cdc ticket execute TASK-789 --no-context # Skip context gathering
2915
+ """
2916
+ console = ctx.obj['console']
2917
+
2918
+ try:
2919
+ from claude_dev_cli.project import TicketExecutor
2920
+ from claude_dev_cli.tickets import MarkdownBackend, RepoTicketsBackend
2921
+ from claude_dev_cli.logging import MarkdownLogger
2922
+ from claude_dev_cli.notifications import NtfyNotifier
2923
+ from claude_dev_cli.vcs import GitManager
2924
+ from claude_dev_cli.core import ClaudeClient
2925
+
2926
+ # Initialize backend
2927
+ if backend == 'repo-tickets':
2928
+ ticket_backend = RepoTicketsBackend()
2929
+ else:
2930
+ ticket_backend = MarkdownBackend()
2931
+
2932
+ if not ticket_backend.connect():
2933
+ console.print(f"[red]Failed to connect to {backend} backend[/red]")
2934
+ sys.exit(1)
2935
+
2936
+ # Initialize components
2937
+ logger = MarkdownLogger()
2938
+ logger.init(f"Ticket Execution - {ticket_id}")
2939
+
2940
+ notifier = NtfyNotifier(topic="cdc-tickets") if notify else None
2941
+ vcs = GitManager() if commit else None
2942
+
2943
+ ai_client = ClaudeClient(api_config_name=api) if api else None
2944
+
2945
+ # Create executor
2946
+ executor = TicketExecutor(
2947
+ ticket_backend=ticket_backend,
2948
+ ai_client=ai_client,
2949
+ logger=logger,
2950
+ notifier=notifier,
2951
+ vcs=vcs,
2952
+ auto_commit=commit,
2953
+ gather_context=not no_context # Gather context unless --no-context flag is set
2954
+ )
2955
+
2956
+ console.print(f"[cyan]Executing ticket:[/cyan] {ticket_id}")
2957
+ if not no_context:
2958
+ console.print("[dim]Context gathering: ENABLED[/dim]")
2959
+ else:
2960
+ console.print("[dim]Context gathering: DISABLED[/dim]")
2961
+ console.print()
2962
+
2963
+ with console.status(f"[bold blue]Processing {ticket_id}..."):
2964
+ success = executor.execute_ticket(ticket_id)
2965
+
2966
+ if success:
2967
+ console.print(f"\n[green]✅ Ticket {ticket_id} completed successfully![/green]")
2968
+ console.print(f"\n[dim]📊 Check .cdc-logs/progress.md for details[/dim]")
2969
+ else:
2970
+ console.print(f"\n[red]❌ Ticket {ticket_id} execution failed[/red]")
2971
+ sys.exit(1)
2972
+
2973
+ except Exception as e:
2974
+ console.print(f"[red]Error: {e}[/red]")
2975
+ sys.exit(1)
2976
+
2977
+
2978
+ @main.group()
2979
+ def tickets() -> None:
2980
+ """Manage tickets (create, list, update)."""
2981
+ pass
2982
+
2983
+
2984
+ @tickets.command('create')
2985
+ @click.argument('title')
2986
+ @click.option('--description', '-d', help='Ticket description')
2987
+ @click.option('--priority', type=click.Choice(['critical', 'high', 'medium', 'low']),
2988
+ default='medium', help='Priority level')
2989
+ @click.option('--type', 'ticket_type', type=click.Choice(['feature', 'bug', 'refactor', 'test', 'doc']),
2990
+ default='feature', help='Ticket type')
2991
+ @click.option('--backend', type=click.Choice(['repo-tickets', 'markdown']), default='markdown',
2992
+ help='Ticket backend')
2993
+ @click.pass_context
2994
+ def tickets_create(
2995
+ ctx: click.Context,
2996
+ title: str,
2997
+ description: Optional[str],
2998
+ priority: str,
2999
+ ticket_type: str,
3000
+ backend: str
3001
+ ) -> None:
3002
+ """Create a new ticket.
3003
+
3004
+ Examples:
3005
+ cdc tickets create "Add user auth" --priority high
3006
+ cdc tickets create "Fix login bug" --type bug --backend repo-tickets
3007
+ """
3008
+ console = ctx.obj['console']
3009
+
3010
+ try:
3011
+ from claude_dev_cli.tickets import MarkdownBackend, RepoTicketsBackend
3012
+
3013
+ if backend == 'repo-tickets':
3014
+ ticket_backend = RepoTicketsBackend()
3015
+ else:
3016
+ ticket_backend = MarkdownBackend()
3017
+
3018
+ ticket_backend.connect()
3019
+
3020
+ ticket = ticket_backend.create_task(
3021
+ story_id=None,
3022
+ title=title,
3023
+ description=description or "",
3024
+ priority=priority,
3025
+ ticket_type=ticket_type
3026
+ )
3027
+
3028
+ console.print(f"[green]✅ Created ticket:[/green] {ticket.id}")
3029
+ console.print(f" Title: {ticket.title}")
3030
+ console.print(f" Priority: {ticket.priority}")
3031
+ console.print(f" Type: {ticket.ticket_type}")
3032
+
3033
+ except Exception as e:
3034
+ console.print(f"[red]Error: {e}[/red]")
3035
+ sys.exit(1)
3036
+
3037
+
3038
+ @tickets.command('list')
3039
+ @click.option('--status', help='Filter by status')
3040
+ @click.option('--backend', type=click.Choice(['repo-tickets', 'markdown']), default='markdown')
3041
+ @click.pass_context
3042
+ def tickets_list(ctx: click.Context, status: Optional[str], backend: str) -> None:
3043
+ """List tickets."""
3044
+ console = ctx.obj['console']
3045
+
3046
+ try:
3047
+ from claude_dev_cli.tickets import MarkdownBackend, RepoTicketsBackend
3048
+ from rich.table import Table
3049
+
3050
+ if backend == 'repo-tickets':
3051
+ ticket_backend = RepoTicketsBackend()
3052
+ else:
3053
+ ticket_backend = MarkdownBackend()
3054
+
3055
+ ticket_backend.connect()
3056
+ tickets = ticket_backend.list_tickets(status=status)
3057
+
3058
+ if not tickets:
3059
+ console.print("[yellow]No tickets found[/yellow]")
3060
+ return
3061
+
3062
+ table = Table(show_header=True, header_style="bold magenta")
3063
+ table.add_column("ID", style="cyan")
3064
+ table.add_column("Title")
3065
+ table.add_column("Status", style="yellow")
3066
+ table.add_column("Priority", style="red")
3067
+ table.add_column("Type", style="blue")
3068
+
3069
+ for ticket in tickets:
3070
+ table.add_row(
3071
+ ticket.id,
3072
+ ticket.title[:50],
3073
+ ticket.status,
3074
+ ticket.priority,
3075
+ ticket.ticket_type
3076
+ )
3077
+
3078
+ console.print(table)
3079
+ console.print(f"\n[dim]Total: {len(tickets)} ticket(s)[/dim]")
3080
+
3081
+ except Exception as e:
3082
+ console.print(f"[red]Error: {e}[/red]")
3083
+ sys.exit(1)
3084
+
3085
+
3086
+ @main.group()
3087
+ def bug() -> None:
3088
+ """Report and triage bugs."""
3089
+ pass
3090
+
3091
+
3092
+ @bug.command('report')
3093
+ @click.option('--title', '-t', required=True, help='Bug title')
3094
+ @click.option('--description', '-d', required=True, help='Bug description')
3095
+ @click.option('--expected', '-e', required=True, help='Expected behavior')
3096
+ @click.option('--actual', '-a', required=True, help='Actual behavior')
3097
+ @click.option('--steps', '-s', multiple=True, help='Steps to reproduce')
3098
+ @click.option('--environment', help='Environment (production/staging/dev)')
3099
+ @click.option('--severity', type=click.Choice(['critical', 'high', 'medium', 'low', 'trivial']),
3100
+ help='Override auto-triage severity')
3101
+ @click.option('--no-triage', is_flag=True, help='Skip auto-triage')
3102
+ @click.option('--backend', type=click.Choice(['repo-tickets', 'markdown']), default='markdown')
3103
+ @click.pass_context
3104
+ def bug_report(
3105
+ ctx: click.Context,
3106
+ title: str,
3107
+ description: str,
3108
+ expected: str,
3109
+ actual: str,
3110
+ steps: tuple,
3111
+ environment: Optional[str],
3112
+ severity: Optional[str],
3113
+ no_triage: bool,
3114
+ backend: str
3115
+ ) -> None:
3116
+ """Report a bug with automatic triage.
3117
+
3118
+ Examples:
3119
+ cdc bug report \\
3120
+ --title "App crashes on startup" \\
3121
+ --description "Application fails to launch" \\
3122
+ --expected "App should start normally" \\
3123
+ --actual "App crashes immediately" \\
3124
+ --steps "Open app" --steps "Wait 2 seconds" \\
3125
+ --environment production
3126
+ """
3127
+ console = ctx.obj['console']
3128
+
3129
+ try:
3130
+ from claude_dev_cli.project import BugTriageSystem, BugReport, BugSeverity
3131
+ from claude_dev_cli.tickets import MarkdownBackend, RepoTicketsBackend
3132
+ from claude_dev_cli.notifications import NtfyNotifier
3133
+
3134
+ if backend == 'repo-tickets':
3135
+ ticket_backend = RepoTicketsBackend()
3136
+ else:
3137
+ ticket_backend = MarkdownBackend()
3138
+
3139
+ ticket_backend.connect()
3140
+
3141
+ # Create bug report
3142
+ bug = BugReport(
3143
+ id=None,
3144
+ title=title,
3145
+ description=description,
3146
+ steps_to_reproduce=list(steps) if steps else [],
3147
+ expected_behavior=expected,
3148
+ actual_behavior=actual,
3149
+ environment=environment,
3150
+ severity=BugSeverity(severity) if severity else None
3151
+ )
3152
+
3153
+ # Initialize triage system
3154
+ triage = BugTriageSystem(
3155
+ ticket_backend=ticket_backend,
3156
+ notifier=NtfyNotifier(topic="cdc-bugs")
3157
+ )
3158
+
3159
+ console.print(f"[cyan]Submitting bug report:[/cyan] {title}\n")
3160
+
3161
+ with console.status("[bold blue]Triaging bug...") if not no_triage else console:
3162
+ ticket = triage.submit_bug(bug, auto_triage=not no_triage)
3163
+
3164
+ console.print(f"\n[green]✅ Bug reported:[/green] {ticket.id}")
3165
+ console.print(f" Title: {bug.title}")
3166
+
3167
+ if bug.severity:
3168
+ severity_color = {
3169
+ "critical": "red",
3170
+ "high": "yellow",
3171
+ "medium": "blue",
3172
+ "low": "cyan",
3173
+ "trivial": "dim"
3174
+ }.get(bug.severity.value, "white")
3175
+ console.print(f" Severity: [{severity_color}]{bug.severity.value.upper()}[/{severity_color}]")
3176
+
3177
+ if bug.category:
3178
+ console.print(f" Category: {bug.category.value}")
3179
+
3180
+ if bug.priority:
3181
+ console.print(f" Priority: {bug.priority}")
3182
+
3183
+ except Exception as e:
3184
+ console.print(f"[red]Error: {e}[/red]")
3185
+ sys.exit(1)
3186
+
3187
+
3188
+ @main.group()
3189
+ def log() -> None:
3190
+ """Manage progress logs."""
3191
+ pass
3192
+
3193
+
3194
+ @log.command('init')
3195
+ @click.argument('project_name')
3196
+ @click.pass_context
3197
+ def log_init(ctx: click.Context, project_name: str) -> None:
3198
+ """Initialize progress logging."""
3199
+ console = ctx.obj['console']
3200
+
3201
+ try:
3202
+ from claude_dev_cli.logging import MarkdownLogger
3203
+
3204
+ logger = MarkdownLogger()
3205
+ logger.init(project_name)
3206
+
3207
+ console.print(f"[green]✅ Initialized logging for:[/green] {project_name}")
3208
+ console.print(f"[dim]Log file: .cdc-logs/progress.md[/dim]")
3209
+
3210
+ except Exception as e:
3211
+ console.print(f"[red]Error: {e}[/red]")
3212
+ sys.exit(1)
3213
+
3214
+
3215
+ @log.command('entry')
3216
+ @click.argument('message')
3217
+ @click.option('--ticket', help='Associated ticket ID')
3218
+ @click.option('--level', type=click.Choice(['info', 'success', 'error', 'warning']),
3219
+ default='info', help='Log level')
3220
+ @click.pass_context
3221
+ def log_entry(
3222
+ ctx: click.Context,
3223
+ message: str,
3224
+ ticket: Optional[str],
3225
+ level: str
3226
+ ) -> None:
3227
+ """Add a log entry."""
3228
+ console = ctx.obj['console']
3229
+
3230
+ try:
3231
+ from claude_dev_cli.logging import MarkdownLogger
3232
+
3233
+ logger = MarkdownLogger()
3234
+ logger.log(message, ticket_id=ticket, level=level)
3235
+
3236
+ console.print(f"[green]✅ Logged:[/green] {message}")
3237
+
3238
+ except Exception as e:
3239
+ console.print(f"[red]Error: {e}[/red]")
3240
+ sys.exit(1)
3241
+
3242
+
3243
+ @log.command('report')
3244
+ @click.pass_context
3245
+ def log_report(ctx: click.Context) -> None:
3246
+ """Generate progress report."""
3247
+ console = ctx.obj['console']
3248
+
3249
+ try:
3250
+ from claude_dev_cli.logging import MarkdownLogger
3251
+
3252
+ logger = MarkdownLogger()
3253
+ report = logger.get_report()
3254
+
3255
+ md = Markdown(report)
3256
+ console.print(md)
3257
+
3258
+ except Exception as e:
3259
+ console.print(f"[red]Error: {e}[/red]")
3260
+ sys.exit(1)
3261
+
3262
+
3263
+ @main.group()
3264
+ def notify() -> None:
3265
+ """Test notifications."""
3266
+ pass
3267
+
3268
+
3269
+ @notify.command('test')
3270
+ @click.option('--topic', default='cdc-test', help='Ntfy topic')
3271
+ @click.pass_context
3272
+ def notify_test(ctx: click.Context, topic: str) -> None:
3273
+ """Send a test notification."""
3274
+ console = ctx.obj['console']
3275
+
3276
+ try:
3277
+ from claude_dev_cli.notifications import NtfyNotifier, NotificationPriority
3278
+
3279
+ notifier = NtfyNotifier(topic=topic)
3280
+
3281
+ success = notifier.send(
3282
+ title="Test Notification",
3283
+ message="✅ claude-dev-cli notifications are working!",
3284
+ priority=NotificationPriority.NORMAL,
3285
+ tags=["white_check_mark", "test"]
3286
+ )
3287
+
3288
+ if success:
3289
+ console.print(f"[green]✅ Notification sent![/green]")
3290
+ console.print(f"\n[dim]Check: https://ntfy.sh/{topic}[/dim]")
3291
+ else:
3292
+ console.print("[red]❌ Failed to send notification[/red]")
3293
+ sys.exit(1)
3294
+
3295
+ except Exception as e:
3296
+ console.print(f"[red]Error: {e}[/red]")
3297
+ sys.exit(1)
3298
+
3299
+
2876
3300
  if __name__ == '__main__':
2877
3301
  main(obj={})
@@ -0,0 +1,6 @@
1
+ """Progress logging system for claude-dev-cli project management."""
2
+
3
+ from claude_dev_cli.logging.logger import ProgressLogger
4
+ from claude_dev_cli.logging.markdown_logger import MarkdownLogger
5
+
6
+ __all__ = ["ProgressLogger", "MarkdownLogger"]
@@ -0,0 +1,84 @@
1
+ """Abstract progress logger interface."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Optional, Dict, Any
5
+ from datetime import datetime
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass
10
+ class LogEntry:
11
+ """A log entry for progress tracking."""
12
+ timestamp: datetime
13
+ message: str
14
+ ticket_id: Optional[str] = None
15
+ level: str = "info" # info, success, error, warning
16
+ metadata: Dict[str, Any] = None
17
+
18
+ def __post_init__(self):
19
+ """Initialize metadata."""
20
+ if self.metadata is None:
21
+ self.metadata = {}
22
+
23
+
24
+ class ProgressLogger(ABC):
25
+ """Abstract base class for progress logging."""
26
+
27
+ @abstractmethod
28
+ def init(self, project_name: str) -> bool:
29
+ """Initialize logging for a project.
30
+
31
+ Args:
32
+ project_name: Name of the project
33
+
34
+ Returns:
35
+ True if successful
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ def log(
41
+ self,
42
+ message: str,
43
+ ticket_id: Optional[str] = None,
44
+ level: str = "info",
45
+ **metadata
46
+ ) -> bool:
47
+ """Log a progress entry.
48
+
49
+ Args:
50
+ message: Log message
51
+ ticket_id: Related ticket ID
52
+ level: Log level (info, success, error, warning)
53
+ **metadata: Additional metadata
54
+
55
+ Returns:
56
+ True if successful
57
+ """
58
+ pass
59
+
60
+ @abstractmethod
61
+ def link_artifact(self, ticket_id: str, artifact_path: str) -> bool:
62
+ """Link an artifact (file, code, etc.) to a ticket.
63
+
64
+ Args:
65
+ ticket_id: Ticket identifier
66
+ artifact_path: Path to artifact
67
+
68
+ Returns:
69
+ True if successful
70
+ """
71
+ pass
72
+
73
+ @abstractmethod
74
+ def get_report(self) -> str:
75
+ """Generate a progress report.
76
+
77
+ Returns:
78
+ Formatted progress report
79
+ """
80
+ pass
81
+
82
+ def get_logger_name(self) -> str:
83
+ """Get logger backend name."""
84
+ return self.__class__.__name__.replace('Logger', '').lower()
@@ -0,0 +1,131 @@
1
+ """Markdown-based progress logger."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+ from datetime import datetime
6
+
7
+ from claude_dev_cli.logging.logger import ProgressLogger, LogEntry
8
+
9
+
10
+ class MarkdownLogger(ProgressLogger):
11
+ """Markdown file-based progress logger.
12
+
13
+ Creates a progress.md file to track project execution.
14
+ """
15
+
16
+ def __init__(self, log_dir: Optional[Path] = None):
17
+ """Initialize markdown logger.
18
+
19
+ Args:
20
+ log_dir: Directory for log files (default: .cdc-logs)
21
+ """
22
+ self.log_dir = log_dir or Path.cwd() / ".cdc-logs"
23
+ self.log_file = self.log_dir / "progress.md"
24
+ self.entries: List[LogEntry] = []
25
+
26
+ def init(self, project_name: str) -> bool:
27
+ """Initialize logging directory and file."""
28
+ try:
29
+ self.log_dir.mkdir(exist_ok=True)
30
+
31
+ # Create initial progress.md with header
32
+ with open(self.log_file, 'w') as f:
33
+ f.write(f"# {project_name} - Progress Log\n\n")
34
+ f.write(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
35
+ f.write("---\n\n")
36
+
37
+ return True
38
+ except OSError:
39
+ return False
40
+
41
+ def log(
42
+ self,
43
+ message: str,
44
+ ticket_id: Optional[str] = None,
45
+ level: str = "info",
46
+ **metadata
47
+ ) -> bool:
48
+ """Log an entry to markdown file."""
49
+ try:
50
+ entry = LogEntry(
51
+ timestamp=datetime.now(),
52
+ message=message,
53
+ ticket_id=ticket_id,
54
+ level=level,
55
+ metadata=metadata
56
+ )
57
+ self.entries.append(entry)
58
+
59
+ # Append to file
60
+ with open(self.log_file, 'a') as f:
61
+ # Format entry
62
+ icon = self._get_level_icon(level)
63
+ timestamp_str = entry.timestamp.strftime('%Y-%m-%d %H:%M:%S')
64
+
65
+ f.write(f"## {icon} {timestamp_str}\n\n")
66
+
67
+ if ticket_id:
68
+ f.write(f"**Ticket:** {ticket_id}\n\n")
69
+
70
+ f.write(f"{message}\n\n")
71
+
72
+ # Add metadata if present
73
+ if metadata:
74
+ f.write("**Details:**\n")
75
+ for key, value in metadata.items():
76
+ f.write(f"- {key}: {value}\n")
77
+ f.write("\n")
78
+
79
+ f.write("---\n\n")
80
+
81
+ return True
82
+ except OSError:
83
+ return False
84
+
85
+ def link_artifact(self, ticket_id: str, artifact_path: str) -> bool:
86
+ """Link an artifact to a ticket in the log."""
87
+ return self.log(
88
+ f"📎 Generated artifact: `{artifact_path}`",
89
+ ticket_id=ticket_id,
90
+ level="info",
91
+ artifact=artifact_path
92
+ )
93
+
94
+ def get_report(self) -> str:
95
+ """Generate a summary report."""
96
+ if not self.log_file.exists():
97
+ return "No progress log found."
98
+
99
+ with open(self.log_file, 'r') as f:
100
+ content = f.read()
101
+
102
+ # Add summary at top
103
+ total_entries = len(self.entries)
104
+ success_count = sum(1 for e in self.entries if e.level == "success")
105
+ error_count = sum(1 for e in self.entries if e.level == "error")
106
+
107
+ summary = f"""# Progress Summary
108
+
109
+ **Total Entries:** {total_entries}
110
+ **Successes:** ✅ {success_count}
111
+ **Errors:** ❌ {error_count}
112
+
113
+ ---
114
+
115
+ {content}
116
+ """
117
+ return summary
118
+
119
+ def _get_level_icon(self, level: str) -> str:
120
+ """Get emoji icon for log level."""
121
+ icons = {
122
+ "info": "ℹ️",
123
+ "success": "✅",
124
+ "error": "❌",
125
+ "warning": "⚠️"
126
+ }
127
+ return icons.get(level, "📝")
128
+
129
+ def get_logger_name(self) -> str:
130
+ """Return logger name."""
131
+ return "markdown"
@@ -0,0 +1,6 @@
1
+ """Notification system for claude-dev-cli project management."""
2
+
3
+ from claude_dev_cli.notifications.notifier import Notifier, NotificationPriority
4
+ from claude_dev_cli.notifications.ntfy import NtfyNotifier
5
+
6
+ __all__ = ["Notifier", "NotificationPriority", "NtfyNotifier"]