mcp-ticketer 0.3.0__py3-none-any.whl → 2.2.9__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.
- mcp_ticketer/__init__.py +10 -10
- mcp_ticketer/__version__.py +3 -3
- mcp_ticketer/_version_scm.py +1 -0
- mcp_ticketer/adapters/__init__.py +2 -0
- mcp_ticketer/adapters/aitrackdown.py +930 -52
- mcp_ticketer/adapters/asana/__init__.py +15 -0
- mcp_ticketer/adapters/asana/adapter.py +1537 -0
- mcp_ticketer/adapters/asana/client.py +292 -0
- mcp_ticketer/adapters/asana/mappers.py +348 -0
- mcp_ticketer/adapters/asana/types.py +146 -0
- mcp_ticketer/adapters/github/__init__.py +26 -0
- mcp_ticketer/adapters/github/adapter.py +3229 -0
- mcp_ticketer/adapters/github/client.py +335 -0
- mcp_ticketer/adapters/github/mappers.py +797 -0
- mcp_ticketer/adapters/github/queries.py +692 -0
- mcp_ticketer/adapters/github/types.py +460 -0
- mcp_ticketer/adapters/hybrid.py +58 -16
- mcp_ticketer/adapters/jira/__init__.py +35 -0
- mcp_ticketer/adapters/jira/adapter.py +1351 -0
- mcp_ticketer/adapters/jira/client.py +271 -0
- mcp_ticketer/adapters/jira/mappers.py +246 -0
- mcp_ticketer/adapters/jira/queries.py +216 -0
- mcp_ticketer/adapters/jira/types.py +304 -0
- mcp_ticketer/adapters/linear/__init__.py +1 -1
- mcp_ticketer/adapters/linear/adapter.py +3810 -462
- mcp_ticketer/adapters/linear/client.py +312 -69
- mcp_ticketer/adapters/linear/mappers.py +305 -85
- mcp_ticketer/adapters/linear/queries.py +317 -17
- mcp_ticketer/adapters/linear/types.py +187 -64
- mcp_ticketer/adapters/linear.py +2 -2
- mcp_ticketer/analysis/__init__.py +56 -0
- mcp_ticketer/analysis/dependency_graph.py +255 -0
- mcp_ticketer/analysis/health_assessment.py +304 -0
- mcp_ticketer/analysis/orphaned.py +218 -0
- mcp_ticketer/analysis/project_status.py +594 -0
- mcp_ticketer/analysis/similarity.py +224 -0
- mcp_ticketer/analysis/staleness.py +266 -0
- mcp_ticketer/automation/__init__.py +11 -0
- mcp_ticketer/automation/project_updates.py +378 -0
- mcp_ticketer/cache/memory.py +9 -8
- mcp_ticketer/cli/adapter_diagnostics.py +91 -54
- mcp_ticketer/cli/auggie_configure.py +116 -15
- mcp_ticketer/cli/codex_configure.py +274 -82
- mcp_ticketer/cli/configure.py +1323 -151
- mcp_ticketer/cli/cursor_configure.py +314 -0
- mcp_ticketer/cli/diagnostics.py +209 -114
- mcp_ticketer/cli/discover.py +297 -26
- mcp_ticketer/cli/gemini_configure.py +119 -26
- mcp_ticketer/cli/init_command.py +880 -0
- mcp_ticketer/cli/install_mcp_server.py +418 -0
- mcp_ticketer/cli/instruction_commands.py +435 -0
- mcp_ticketer/cli/linear_commands.py +256 -130
- mcp_ticketer/cli/main.py +140 -1544
- mcp_ticketer/cli/mcp_configure.py +1013 -100
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/migrate_config.py +12 -8
- mcp_ticketer/cli/platform_commands.py +123 -0
- mcp_ticketer/cli/platform_detection.py +477 -0
- mcp_ticketer/cli/platform_installer.py +545 -0
- mcp_ticketer/cli/project_update_commands.py +350 -0
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/cli/queue_commands.py +15 -15
- mcp_ticketer/cli/setup_command.py +794 -0
- mcp_ticketer/cli/simple_health.py +84 -59
- mcp_ticketer/cli/ticket_commands.py +1375 -0
- mcp_ticketer/cli/update_checker.py +313 -0
- mcp_ticketer/cli/utils.py +195 -72
- mcp_ticketer/core/__init__.py +64 -1
- mcp_ticketer/core/adapter.py +618 -18
- mcp_ticketer/core/config.py +77 -68
- mcp_ticketer/core/env_discovery.py +75 -16
- mcp_ticketer/core/env_loader.py +121 -97
- mcp_ticketer/core/exceptions.py +32 -24
- mcp_ticketer/core/http_client.py +26 -26
- mcp_ticketer/core/instructions.py +405 -0
- mcp_ticketer/core/label_manager.py +732 -0
- mcp_ticketer/core/mappers.py +42 -30
- mcp_ticketer/core/milestone_manager.py +252 -0
- mcp_ticketer/core/models.py +566 -19
- mcp_ticketer/core/onepassword_secrets.py +379 -0
- mcp_ticketer/core/priority_matcher.py +463 -0
- mcp_ticketer/core/project_config.py +189 -49
- mcp_ticketer/core/project_utils.py +281 -0
- mcp_ticketer/core/project_validator.py +376 -0
- mcp_ticketer/core/registry.py +3 -3
- mcp_ticketer/core/session_state.py +176 -0
- mcp_ticketer/core/state_matcher.py +592 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- mcp_ticketer/defaults/ticket_instructions.md +644 -0
- mcp_ticketer/mcp/__init__.py +29 -1
- mcp_ticketer/mcp/__main__.py +60 -0
- mcp_ticketer/mcp/server/__init__.py +25 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/server/constants.py +58 -0
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/dto.py +195 -0
- mcp_ticketer/mcp/server/main.py +1343 -0
- mcp_ticketer/mcp/server/response_builder.py +206 -0
- mcp_ticketer/mcp/server/routing.py +723 -0
- mcp_ticketer/mcp/server/server_sdk.py +151 -0
- mcp_ticketer/mcp/server/tools/__init__.py +69 -0
- mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +224 -0
- mcp_ticketer/mcp/server/tools/bulk_tools.py +330 -0
- mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
- mcp_ticketer/mcp/server/tools/config_tools.py +1564 -0
- mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/instruction_tools.py +295 -0
- mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +150 -0
- mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
- mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +318 -0
- mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1413 -0
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +364 -0
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/health_monitor.py +168 -136
- mcp_ticketer/queue/manager.py +78 -63
- mcp_ticketer/queue/queue.py +108 -21
- mcp_ticketer/queue/run_worker.py +2 -2
- mcp_ticketer/queue/ticket_registry.py +213 -155
- mcp_ticketer/queue/worker.py +96 -58
- mcp_ticketer/utils/__init__.py +5 -0
- mcp_ticketer/utils/token_utils.py +246 -0
- mcp_ticketer-2.2.9.dist-info/METADATA +1396 -0
- mcp_ticketer-2.2.9.dist-info/RECORD +158 -0
- mcp_ticketer-2.2.9.dist-info/top_level.txt +2 -0
- py_mcp_installer/examples/phase3_demo.py +178 -0
- py_mcp_installer/scripts/manage_version.py +54 -0
- py_mcp_installer/setup.py +6 -0
- py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
- py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
- py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
- py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
- py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
- py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
- py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
- py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
- py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
- py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
- py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
- py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
- py_mcp_installer/src/py_mcp_installer/types.py +222 -0
- py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
- py_mcp_installer/tests/__init__.py +0 -0
- py_mcp_installer/tests/platforms/__init__.py +0 -0
- py_mcp_installer/tests/test_platform_detector.py +17 -0
- mcp_ticketer/adapters/github.py +0 -1354
- mcp_ticketer/adapters/jira.py +0 -1011
- mcp_ticketer/mcp/server.py +0 -2030
- mcp_ticketer-0.3.0.dist-info/METADATA +0 -414
- mcp_ticketer-0.3.0.dist-info/RECORD +0 -59
- mcp_ticketer-0.3.0.dist-info/top_level.txt +0 -1
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""Ticket instructions management commands.
|
|
2
|
+
|
|
3
|
+
This module implements CLI commands for managing ticket writing instructions,
|
|
4
|
+
allowing users to customize and view the guidelines that help create
|
|
5
|
+
well-structured, consistent tickets.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.markdown import Markdown
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
|
|
16
|
+
from ..core.instructions import (
|
|
17
|
+
InstructionsError,
|
|
18
|
+
InstructionsValidationError,
|
|
19
|
+
TicketInstructionsManager,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
app = typer.Typer(
|
|
23
|
+
name="instructions",
|
|
24
|
+
help="Manage ticket writing instructions for your project",
|
|
25
|
+
)
|
|
26
|
+
console = Console()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.command()
|
|
30
|
+
def show(
|
|
31
|
+
default: bool = typer.Option(
|
|
32
|
+
False,
|
|
33
|
+
"--default",
|
|
34
|
+
help="Show default instructions instead of custom",
|
|
35
|
+
),
|
|
36
|
+
raw: bool = typer.Option(
|
|
37
|
+
False,
|
|
38
|
+
"--raw",
|
|
39
|
+
help="Output raw markdown without formatting",
|
|
40
|
+
),
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Display current ticket writing instructions.
|
|
43
|
+
|
|
44
|
+
By default, shows custom instructions if they exist, otherwise shows defaults.
|
|
45
|
+
Use --default to always show the default instructions.
|
|
46
|
+
Use --raw to output raw markdown without Rich formatting (useful for piping).
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
--------
|
|
50
|
+
# Show current instructions (custom or default)
|
|
51
|
+
mcp-ticketer instructions show
|
|
52
|
+
|
|
53
|
+
# Always show default instructions
|
|
54
|
+
mcp-ticketer instructions show --default
|
|
55
|
+
|
|
56
|
+
# Output raw markdown for piping
|
|
57
|
+
mcp-ticketer instructions show --raw > team_guide.md
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
manager = TicketInstructionsManager()
|
|
62
|
+
|
|
63
|
+
if default:
|
|
64
|
+
instructions = manager.get_default_instructions()
|
|
65
|
+
source = "default"
|
|
66
|
+
else:
|
|
67
|
+
instructions = manager.get_instructions()
|
|
68
|
+
source = "custom" if manager.has_custom_instructions() else "default"
|
|
69
|
+
|
|
70
|
+
if raw:
|
|
71
|
+
# Raw output for piping
|
|
72
|
+
console.print(instructions)
|
|
73
|
+
else:
|
|
74
|
+
# Rich formatted output
|
|
75
|
+
if source == "custom":
|
|
76
|
+
title = f"[green]Custom Instructions[/green] ({manager.get_instructions_path()})"
|
|
77
|
+
else:
|
|
78
|
+
title = "[blue]Default Instructions[/blue]"
|
|
79
|
+
|
|
80
|
+
panel = Panel(
|
|
81
|
+
Markdown(instructions),
|
|
82
|
+
title=title,
|
|
83
|
+
border_style="cyan",
|
|
84
|
+
)
|
|
85
|
+
console.print(panel)
|
|
86
|
+
|
|
87
|
+
except InstructionsError as e:
|
|
88
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
89
|
+
raise typer.Exit(1) from None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@app.command()
|
|
93
|
+
def add(
|
|
94
|
+
file_path: str | None = typer.Argument(
|
|
95
|
+
None,
|
|
96
|
+
help="Path to markdown file with custom instructions",
|
|
97
|
+
),
|
|
98
|
+
stdin: bool = typer.Option(
|
|
99
|
+
False,
|
|
100
|
+
"--stdin",
|
|
101
|
+
help="Read instructions from stdin instead of file",
|
|
102
|
+
),
|
|
103
|
+
force: bool = typer.Option(
|
|
104
|
+
False,
|
|
105
|
+
"--force",
|
|
106
|
+
"-f",
|
|
107
|
+
help="Overwrite existing custom instructions without confirmation",
|
|
108
|
+
),
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Add custom ticket writing instructions for your project.
|
|
111
|
+
|
|
112
|
+
You can provide instructions from a file or via stdin. If custom instructions
|
|
113
|
+
already exist, you'll be prompted for confirmation unless --force is used.
|
|
114
|
+
|
|
115
|
+
Examples:
|
|
116
|
+
--------
|
|
117
|
+
# Add from file
|
|
118
|
+
mcp-ticketer instructions add team_guidelines.md
|
|
119
|
+
|
|
120
|
+
# Add from stdin
|
|
121
|
+
cat guidelines.md | mcp-ticketer instructions add --stdin
|
|
122
|
+
|
|
123
|
+
# Force overwrite existing
|
|
124
|
+
mcp-ticketer instructions add new_guide.md --force
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
manager = TicketInstructionsManager()
|
|
129
|
+
|
|
130
|
+
# Check for existing custom instructions
|
|
131
|
+
if manager.has_custom_instructions() and not force:
|
|
132
|
+
path = manager.get_instructions_path()
|
|
133
|
+
console.print(
|
|
134
|
+
f"[yellow]Warning:[/yellow] Custom instructions already exist at {path}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
confirm = typer.confirm("Do you want to overwrite them?")
|
|
138
|
+
if not confirm:
|
|
139
|
+
console.print("[yellow]Operation cancelled[/yellow]")
|
|
140
|
+
raise typer.Exit(0) from None
|
|
141
|
+
|
|
142
|
+
# Get content from stdin or file
|
|
143
|
+
if stdin:
|
|
144
|
+
console.print("[dim]Reading from stdin... (Press Ctrl+D when done)[/dim]")
|
|
145
|
+
content = sys.stdin.read()
|
|
146
|
+
if not content.strip():
|
|
147
|
+
console.print("[red]Error:[/red] No content provided on stdin")
|
|
148
|
+
raise typer.Exit(1) from None
|
|
149
|
+
elif file_path:
|
|
150
|
+
source_path = Path(file_path)
|
|
151
|
+
if not source_path.exists():
|
|
152
|
+
console.print(f"[red]Error:[/red] File not found: {file_path}")
|
|
153
|
+
raise typer.Exit(1) from None
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
content = source_path.read_text(encoding="utf-8")
|
|
157
|
+
except Exception as e:
|
|
158
|
+
console.print(f"[red]Error:[/red] Failed to read file: {e}")
|
|
159
|
+
raise typer.Exit(1) from None
|
|
160
|
+
else:
|
|
161
|
+
console.print("[red]Error:[/red] Either provide a file path or use --stdin")
|
|
162
|
+
console.print("Example: mcp-ticketer instructions add guidelines.md")
|
|
163
|
+
raise typer.Exit(1) from None
|
|
164
|
+
|
|
165
|
+
# Set instructions
|
|
166
|
+
manager.set_instructions(content)
|
|
167
|
+
|
|
168
|
+
path = manager.get_instructions_path()
|
|
169
|
+
console.print(f"[green]✓[/green] Custom instructions saved to: {path}")
|
|
170
|
+
console.print("[dim]Use 'mcp-ticketer instructions show' to view them[/dim]")
|
|
171
|
+
|
|
172
|
+
except InstructionsValidationError as e:
|
|
173
|
+
console.print(f"[red]Validation Error:[/red] {e}")
|
|
174
|
+
raise typer.Exit(1) from None
|
|
175
|
+
except InstructionsError as e:
|
|
176
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
177
|
+
raise typer.Exit(1) from None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@app.command()
|
|
181
|
+
def update(
|
|
182
|
+
file_path: str | None = typer.Argument(
|
|
183
|
+
None,
|
|
184
|
+
help="Path to markdown file with updated instructions",
|
|
185
|
+
),
|
|
186
|
+
stdin: bool = typer.Option(
|
|
187
|
+
False,
|
|
188
|
+
"--stdin",
|
|
189
|
+
help="Read instructions from stdin instead of file",
|
|
190
|
+
),
|
|
191
|
+
) -> None:
|
|
192
|
+
"""Update existing custom instructions (alias for 'add --force').
|
|
193
|
+
|
|
194
|
+
This is a convenience command that overwrites existing custom instructions
|
|
195
|
+
without prompting for confirmation.
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
--------
|
|
199
|
+
# Update from file
|
|
200
|
+
mcp-ticketer instructions update new_guidelines.md
|
|
201
|
+
|
|
202
|
+
# Update from stdin
|
|
203
|
+
cat updated.md | mcp-ticketer instructions update --stdin
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
manager = TicketInstructionsManager()
|
|
208
|
+
|
|
209
|
+
if not manager.has_custom_instructions():
|
|
210
|
+
console.print("[yellow]Warning:[/yellow] No custom instructions exist yet")
|
|
211
|
+
console.print("Use 'mcp-ticketer instructions add' to create them first")
|
|
212
|
+
raise typer.Exit(1) from None
|
|
213
|
+
|
|
214
|
+
# Get content from stdin or file
|
|
215
|
+
if stdin:
|
|
216
|
+
console.print("[dim]Reading from stdin... (Press Ctrl+D when done)[/dim]")
|
|
217
|
+
content = sys.stdin.read()
|
|
218
|
+
if not content.strip():
|
|
219
|
+
console.print("[red]Error:[/red] No content provided on stdin")
|
|
220
|
+
raise typer.Exit(1) from None
|
|
221
|
+
elif file_path:
|
|
222
|
+
source_path = Path(file_path)
|
|
223
|
+
if not source_path.exists():
|
|
224
|
+
console.print(f"[red]Error:[/red] File not found: {file_path}")
|
|
225
|
+
raise typer.Exit(1) from None
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
content = source_path.read_text(encoding="utf-8")
|
|
229
|
+
except Exception as e:
|
|
230
|
+
console.print(f"[red]Error:[/red] Failed to read file: {e}")
|
|
231
|
+
raise typer.Exit(1) from None
|
|
232
|
+
else:
|
|
233
|
+
console.print("[red]Error:[/red] Either provide a file path or use --stdin")
|
|
234
|
+
console.print("Example: mcp-ticketer instructions update guidelines.md")
|
|
235
|
+
raise typer.Exit(1) from None
|
|
236
|
+
|
|
237
|
+
# Update instructions (force overwrite)
|
|
238
|
+
manager.set_instructions(content)
|
|
239
|
+
|
|
240
|
+
path = manager.get_instructions_path()
|
|
241
|
+
console.print(f"[green]✓[/green] Custom instructions updated: {path}")
|
|
242
|
+
console.print("[dim]Use 'mcp-ticketer instructions show' to view them[/dim]")
|
|
243
|
+
|
|
244
|
+
except InstructionsValidationError as e:
|
|
245
|
+
console.print(f"[red]Validation Error:[/red] {e}")
|
|
246
|
+
raise typer.Exit(1) from None
|
|
247
|
+
except InstructionsError as e:
|
|
248
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
249
|
+
raise typer.Exit(1) from None
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@app.command()
|
|
253
|
+
def delete(
|
|
254
|
+
yes: bool = typer.Option(
|
|
255
|
+
False,
|
|
256
|
+
"--yes",
|
|
257
|
+
"-y",
|
|
258
|
+
help="Skip confirmation prompt",
|
|
259
|
+
),
|
|
260
|
+
) -> None:
|
|
261
|
+
"""Delete custom instructions and revert to defaults.
|
|
262
|
+
|
|
263
|
+
This removes your project-specific instructions file. After deletion,
|
|
264
|
+
the default instructions will be used.
|
|
265
|
+
|
|
266
|
+
Examples:
|
|
267
|
+
--------
|
|
268
|
+
# Delete with confirmation prompt
|
|
269
|
+
mcp-ticketer instructions delete
|
|
270
|
+
|
|
271
|
+
# Skip confirmation
|
|
272
|
+
mcp-ticketer instructions delete --yes
|
|
273
|
+
|
|
274
|
+
"""
|
|
275
|
+
try:
|
|
276
|
+
manager = TicketInstructionsManager()
|
|
277
|
+
|
|
278
|
+
if not manager.has_custom_instructions():
|
|
279
|
+
console.print("[yellow]No custom instructions to delete[/yellow]")
|
|
280
|
+
console.print("[dim]Already using default instructions[/dim]")
|
|
281
|
+
raise typer.Exit(0) from None
|
|
282
|
+
|
|
283
|
+
path = manager.get_instructions_path()
|
|
284
|
+
|
|
285
|
+
if not yes:
|
|
286
|
+
console.print(f"[yellow]Warning:[/yellow] This will delete: {path}")
|
|
287
|
+
console.print("After deletion, default instructions will be used.")
|
|
288
|
+
|
|
289
|
+
confirm = typer.confirm("Are you sure?")
|
|
290
|
+
if not confirm:
|
|
291
|
+
console.print("[yellow]Operation cancelled[/yellow]")
|
|
292
|
+
raise typer.Exit(0) from None
|
|
293
|
+
|
|
294
|
+
# Delete instructions
|
|
295
|
+
manager.delete_instructions()
|
|
296
|
+
|
|
297
|
+
console.print("[green]✓[/green] Custom instructions deleted")
|
|
298
|
+
console.print("[dim]Now using default instructions[/dim]")
|
|
299
|
+
|
|
300
|
+
except InstructionsError as e:
|
|
301
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
302
|
+
raise typer.Exit(1) from None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@app.command()
|
|
306
|
+
def path() -> None:
|
|
307
|
+
"""Show path to custom instructions file.
|
|
308
|
+
|
|
309
|
+
Displays the path where custom instructions are (or would be) stored
|
|
310
|
+
for this project, along with status information.
|
|
311
|
+
|
|
312
|
+
Examples:
|
|
313
|
+
--------
|
|
314
|
+
# Show instructions file path
|
|
315
|
+
mcp-ticketer instructions path
|
|
316
|
+
|
|
317
|
+
# Use in scripts
|
|
318
|
+
INST_PATH=$(mcp-ticketer instructions path --quiet)
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
try:
|
|
322
|
+
manager = TicketInstructionsManager()
|
|
323
|
+
inst_path = manager.get_instructions_path()
|
|
324
|
+
exists = manager.has_custom_instructions()
|
|
325
|
+
|
|
326
|
+
console.print(f"[cyan]Instructions file:[/cyan] {inst_path}")
|
|
327
|
+
|
|
328
|
+
if exists:
|
|
329
|
+
console.print("[green]Status:[/green] Custom instructions exist")
|
|
330
|
+
|
|
331
|
+
# Show file size
|
|
332
|
+
try:
|
|
333
|
+
size = inst_path.stat().st_size
|
|
334
|
+
console.print(f"[dim]Size: {size} bytes[/dim]")
|
|
335
|
+
except Exception:
|
|
336
|
+
pass
|
|
337
|
+
else:
|
|
338
|
+
console.print(
|
|
339
|
+
"[yellow]Status:[/yellow] No custom instructions (using defaults)"
|
|
340
|
+
)
|
|
341
|
+
console.print(
|
|
342
|
+
"[dim]Create with: mcp-ticketer instructions add <file>[/dim]"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
except InstructionsError as e:
|
|
346
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
347
|
+
raise typer.Exit(1) from None
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@app.command()
|
|
351
|
+
def edit() -> None:
|
|
352
|
+
"""Open instructions in default editor.
|
|
353
|
+
|
|
354
|
+
Opens the custom instructions file in your system's default text editor.
|
|
355
|
+
If custom instructions don't exist yet, creates them with default content
|
|
356
|
+
first.
|
|
357
|
+
|
|
358
|
+
The editor is determined by the EDITOR environment variable, or falls back
|
|
359
|
+
to sensible defaults (vim on Unix, notepad on Windows).
|
|
360
|
+
|
|
361
|
+
Examples:
|
|
362
|
+
--------
|
|
363
|
+
# Edit instructions
|
|
364
|
+
mcp-ticketer instructions edit
|
|
365
|
+
|
|
366
|
+
# Use specific editor
|
|
367
|
+
EDITOR=nano mcp-ticketer instructions edit
|
|
368
|
+
|
|
369
|
+
"""
|
|
370
|
+
import os
|
|
371
|
+
import platform
|
|
372
|
+
import subprocess
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
manager = TicketInstructionsManager()
|
|
376
|
+
|
|
377
|
+
# If no custom instructions exist, create them with defaults
|
|
378
|
+
if not manager.has_custom_instructions():
|
|
379
|
+
console.print("[yellow]No custom instructions yet[/yellow]")
|
|
380
|
+
console.print("[dim]Creating from defaults...[/dim]")
|
|
381
|
+
|
|
382
|
+
# Copy defaults to custom location
|
|
383
|
+
default_content = manager.get_default_instructions()
|
|
384
|
+
manager.set_instructions(default_content)
|
|
385
|
+
|
|
386
|
+
console.print(
|
|
387
|
+
f"[green]✓[/green] Created custom instructions at: {manager.get_instructions_path()}"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
inst_path = manager.get_instructions_path()
|
|
391
|
+
|
|
392
|
+
# Determine editor
|
|
393
|
+
editor = os.environ.get("EDITOR")
|
|
394
|
+
|
|
395
|
+
if not editor:
|
|
396
|
+
# Platform-specific defaults
|
|
397
|
+
system = platform.system()
|
|
398
|
+
if system == "Windows":
|
|
399
|
+
editor = "notepad"
|
|
400
|
+
else:
|
|
401
|
+
# Unix-like: try common editors
|
|
402
|
+
for candidate in ["vim", "vi", "nano", "emacs"]:
|
|
403
|
+
try:
|
|
404
|
+
result = subprocess.run(
|
|
405
|
+
["which", candidate],
|
|
406
|
+
capture_output=True,
|
|
407
|
+
text=True,
|
|
408
|
+
timeout=1,
|
|
409
|
+
)
|
|
410
|
+
if result.returncode == 0:
|
|
411
|
+
editor = candidate
|
|
412
|
+
break
|
|
413
|
+
except Exception:
|
|
414
|
+
continue
|
|
415
|
+
|
|
416
|
+
if not editor:
|
|
417
|
+
editor = "vi" # Ultimate fallback
|
|
418
|
+
|
|
419
|
+
console.print(f"[dim]Opening with {editor}...[/dim]")
|
|
420
|
+
|
|
421
|
+
# Open editor
|
|
422
|
+
try:
|
|
423
|
+
subprocess.run([editor, str(inst_path)], check=True)
|
|
424
|
+
console.print(f"[green]✓[/green] Finished editing: {inst_path}")
|
|
425
|
+
except subprocess.CalledProcessError as e:
|
|
426
|
+
console.print(f"[red]Error:[/red] Editor exited with code {e.returncode}")
|
|
427
|
+
raise typer.Exit(1) from None
|
|
428
|
+
except FileNotFoundError:
|
|
429
|
+
console.print(f"[red]Error:[/red] Editor not found: {editor}")
|
|
430
|
+
console.print("Set EDITOR environment variable to your preferred editor")
|
|
431
|
+
raise typer.Exit(1) from None
|
|
432
|
+
|
|
433
|
+
except InstructionsError as e:
|
|
434
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
435
|
+
raise typer.Exit(1) from None
|