comet-code 0.1.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.
- cli/__init__.py +1 -0
- cli/commands.py +266 -0
- cli/completer.py +18 -0
- cli/render.py +420 -0
- cli/state.py +18 -0
- cli/ui.py +424 -0
- comet_code-0.1.0.dist-info/METADATA +154 -0
- comet_code-0.1.0.dist-info/RECORD +32 -0
- comet_code-0.1.0.dist-info/WHEEL +4 -0
- comet_code-0.1.0.dist-info/entry_points.txt +2 -0
- comet_code-0.1.0.dist-info/licenses/LICENSE +21 -0
- config.py +97 -0
- core/__init__.py +1 -0
- core/graph.py +62 -0
- core/graph_state.py +52 -0
- core/nodes.py +711 -0
- core/orchestrator.py +217 -0
- llm/__init__.py +1 -0
- llm/models.py +46 -0
- llm/openrouter_client.py +32 -0
- llm/prompts.py +179 -0
- main.py +91 -0
- schemas/__init__.py +1 -0
- schemas/attempt.py +65 -0
- schemas/code_chunk.py +30 -0
- schemas/events.py +33 -0
- schemas/mode_policy.py +11 -0
- schemas/plan.py +34 -0
- schemas/session.py +36 -0
- schemas/task.py +66 -0
- schemas/tool.py +24 -0
- tools/__init__.py +450 -0
cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI entrypoints."""
|
cli/commands.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Slash-command handling for the interactive shell.
|
|
2
|
+
|
|
3
|
+
Commands return a `CommandResult` so the shell loop knows whether to keep
|
|
4
|
+
running, exit, or just continue after printing feedback. Anything that
|
|
5
|
+
doesn't start with `/` is treated as a user task and handled by the shell.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
# Completable slash commands (used by the prompt_toolkit autocompleter in ui.py)
|
|
11
|
+
SLASH_COMMANDS = [
|
|
12
|
+
"/help",
|
|
13
|
+
"/mode", "/mode explain", "/mode debug", "/mode refactor", "/mode implement", "/mode plan",
|
|
14
|
+
"/explain", "/debug", "/refactor", "/implement", "/plan",
|
|
15
|
+
"/model",
|
|
16
|
+
"/tools", "/tools toggle", "/tools collapse", "/tools expand",
|
|
17
|
+
"/clear",
|
|
18
|
+
"/key", "/key show", "/key set", "/key clear",
|
|
19
|
+
"/exit", "/quit",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
|
|
25
|
+
from rich.console import Console
|
|
26
|
+
from rich.panel import Panel
|
|
27
|
+
from rich.table import Table
|
|
28
|
+
|
|
29
|
+
from cli.state import ShellState
|
|
30
|
+
from config import clear_key, config_file_path, get_stored_key, is_valid_key, mask_key, save_key
|
|
31
|
+
from core.orchestrator import Orchestrator
|
|
32
|
+
from llm.models import AVAILABLE_MODELS, find_model
|
|
33
|
+
from schemas.task import TaskMode
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CommandResult:
|
|
38
|
+
handled: bool # True if input was a recognized slash command
|
|
39
|
+
should_exit: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _print_modes(console: Console, state: ShellState) -> None:
|
|
43
|
+
table = Table(title="modes", title_style="bold bright_cyan", border_style="cyan")
|
|
44
|
+
table.add_column("name", style="bright_white")
|
|
45
|
+
table.add_column("current", style="bright_magenta")
|
|
46
|
+
for m in TaskMode:
|
|
47
|
+
marker = "●" if m == state.mode else ""
|
|
48
|
+
table.add_row(m.value, marker)
|
|
49
|
+
console.print(table)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _print_models(console: Console, state: ShellState) -> None:
|
|
53
|
+
table = Table(title="models", title_style="bold bright_cyan", border_style="cyan")
|
|
54
|
+
table.add_column("alias", style="bright_white")
|
|
55
|
+
table.add_column("slug", style="dim")
|
|
56
|
+
table.add_column("label", style="white")
|
|
57
|
+
table.add_column("current", style="bright_magenta")
|
|
58
|
+
for m in AVAILABLE_MODELS:
|
|
59
|
+
marker = "●" if m.slug == state.model.slug else ""
|
|
60
|
+
table.add_row(m.short, m.slug, m.label, marker)
|
|
61
|
+
console.print(table)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _print_help(console: Console) -> None:
|
|
65
|
+
table = Table(title="commands", title_style="bold bright_cyan", border_style="cyan")
|
|
66
|
+
table.add_column("command", style="bright_white")
|
|
67
|
+
table.add_column("description", style="white")
|
|
68
|
+
rows = [
|
|
69
|
+
("/help", "show this help"),
|
|
70
|
+
("/mode", "show current mode and list available modes"),
|
|
71
|
+
("/mode <name>", "switch to a mode (explain, debug, refactor, implement, plan)"),
|
|
72
|
+
("/<mode>", "shortcut: e.g. /plan, /debug, /explain, /refactor, /implement"),
|
|
73
|
+
("/model", "show current model and list available models"),
|
|
74
|
+
("/model <name>", "switch model (by alias, slug, or label)"),
|
|
75
|
+
("/tools", "show last run tool-call history"),
|
|
76
|
+
("/tools <toggle|collapse|expand>", "set live tool view mode"),
|
|
77
|
+
("/clear", "clear conversation history"),
|
|
78
|
+
("/key", "show current API key source and masked value"),
|
|
79
|
+
("/key set <key>", "save key to ~/.comet/config.json"),
|
|
80
|
+
("/key clear", "remove stored key from config"),
|
|
81
|
+
("/exit, /quit", "leave the shell"),
|
|
82
|
+
]
|
|
83
|
+
for cmd, desc in rows:
|
|
84
|
+
table.add_row(cmd, desc)
|
|
85
|
+
console.print(table)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _cmd_key(console: Console, args: list[str]) -> None:
|
|
89
|
+
import os
|
|
90
|
+
subcommand = args[0].lower() if args else "show"
|
|
91
|
+
|
|
92
|
+
if subcommand == "set":
|
|
93
|
+
if len(args) < 2:
|
|
94
|
+
console.print("[red]usage:[/red] /key set <api-key>")
|
|
95
|
+
return
|
|
96
|
+
key = args[1].strip()
|
|
97
|
+
if not is_valid_key(key):
|
|
98
|
+
console.print("[red]invalid key:[/red] OpenRouter keys must start with [bold]sk-or-[/bold]")
|
|
99
|
+
return
|
|
100
|
+
save_key(key)
|
|
101
|
+
console.print(
|
|
102
|
+
f"[bright_cyan]key saved →[/bright_cyan] [bold]{mask_key(key)}[/bold] "
|
|
103
|
+
f"[dim]({config_file_path()})[/dim]"
|
|
104
|
+
)
|
|
105
|
+
console.print("[dim]restart Comet (or set OPENROUTER_API_KEY) to use the new key in this session[/dim]")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
if subcommand == "clear":
|
|
109
|
+
clear_key()
|
|
110
|
+
console.print(f"[bright_cyan]key removed[/bright_cyan] [dim]({config_file_path()})[/dim]")
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
# show (default)
|
|
114
|
+
env_key = os.environ.get("OPENROUTER_API_KEY", "").strip()
|
|
115
|
+
stored_key = get_stored_key()
|
|
116
|
+
if env_key:
|
|
117
|
+
console.print(
|
|
118
|
+
f"[bright_cyan]key source →[/bright_cyan] [bold]env var[/bold] (OPENROUTER_API_KEY) "
|
|
119
|
+
f"[dim]{mask_key(env_key)}[/dim]"
|
|
120
|
+
)
|
|
121
|
+
elif stored_key:
|
|
122
|
+
console.print(
|
|
123
|
+
f"[bright_cyan]key source →[/bright_cyan] [bold]config file[/bold] "
|
|
124
|
+
f"[dim]{mask_key(stored_key)} ({config_file_path()})[/dim]"
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
console.print(
|
|
128
|
+
"[yellow]no API key configured[/yellow] "
|
|
129
|
+
"[dim]run /key set <key> or set OPENROUTER_API_KEY[/dim]"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _cmd_mode(console: Console, state: ShellState, args: list[str]) -> None:
|
|
134
|
+
if not args:
|
|
135
|
+
_print_modes(console, state)
|
|
136
|
+
return
|
|
137
|
+
name = args[0].lower()
|
|
138
|
+
try:
|
|
139
|
+
state.mode = TaskMode(name)
|
|
140
|
+
except ValueError:
|
|
141
|
+
console.print(
|
|
142
|
+
f"[red]unknown mode:[/red] {name} "
|
|
143
|
+
f"[dim](try: {', '.join(TaskMode.names())})[/dim]"
|
|
144
|
+
)
|
|
145
|
+
return
|
|
146
|
+
console.print(f"[bright_cyan]mode →[/bright_cyan] [bold]{state.mode.value}[/bold]")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _cmd_model(console: Console, state: ShellState, args: list[str]) -> None:
|
|
150
|
+
if not args:
|
|
151
|
+
_print_models(console, state)
|
|
152
|
+
return
|
|
153
|
+
query = " ".join(args)
|
|
154
|
+
found = find_model(query)
|
|
155
|
+
if found is None:
|
|
156
|
+
console.print(f"[red]unknown model:[/red] {query} [dim](try /model for the list)[/dim]")
|
|
157
|
+
return
|
|
158
|
+
state.model = found
|
|
159
|
+
console.print(
|
|
160
|
+
f"[bright_cyan]model →[/bright_cyan] [bold]{found.label}[/bold] "
|
|
161
|
+
f"[dim]({found.slug})[/dim]"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _print_tool_history(console: Console, state: ShellState) -> None:
|
|
166
|
+
mode_label = "collapsed" if state.tool_view_collapsed else "expanded"
|
|
167
|
+
if not state.last_tool_history:
|
|
168
|
+
console.print(
|
|
169
|
+
f"[bright_cyan]tools view →[/bright_cyan] [bold]{mode_label}[/bold] "
|
|
170
|
+
f"[dim](no tool history yet)[/dim]"
|
|
171
|
+
)
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
lines: list[str] = []
|
|
175
|
+
for idx, item in enumerate(state.last_tool_history, start=1):
|
|
176
|
+
status = item.get("status", "unknown")
|
|
177
|
+
tool_name = item.get("tool_name", "tool")
|
|
178
|
+
args_str = item.get("args_json", "{}")
|
|
179
|
+
lines.append(f"{idx}. {tool_name} {args_str} [{status}]")
|
|
180
|
+
reason = item.get("reason")
|
|
181
|
+
if reason:
|
|
182
|
+
lines.append(f" why: {reason}")
|
|
183
|
+
preview = item.get("preview")
|
|
184
|
+
if preview:
|
|
185
|
+
lines.append(f" {preview}")
|
|
186
|
+
error = item.get("error")
|
|
187
|
+
if error:
|
|
188
|
+
lines.append(f" error: {error}")
|
|
189
|
+
|
|
190
|
+
console.print(
|
|
191
|
+
Panel(
|
|
192
|
+
"\n".join(lines),
|
|
193
|
+
title=f"[bold bright_cyan]tool history ({mode_label})[/bold bright_cyan]",
|
|
194
|
+
border_style="cyan",
|
|
195
|
+
padding=(1, 2),
|
|
196
|
+
expand=True,
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def handle_command(
|
|
202
|
+
text: str,
|
|
203
|
+
console: Console,
|
|
204
|
+
state: ShellState,
|
|
205
|
+
orchestrator: Orchestrator,
|
|
206
|
+
) -> CommandResult:
|
|
207
|
+
"""Dispatch a slash-prefixed line. Non-slash lines return handled=False."""
|
|
208
|
+
if not text.startswith("/"):
|
|
209
|
+
return CommandResult(handled=False)
|
|
210
|
+
|
|
211
|
+
parts = text[1:].split()
|
|
212
|
+
if not parts:
|
|
213
|
+
return CommandResult(handled=True)
|
|
214
|
+
|
|
215
|
+
name, args = parts[0].lower(), parts[1:]
|
|
216
|
+
|
|
217
|
+
if name in {"exit", "quit"}:
|
|
218
|
+
return CommandResult(handled=True, should_exit=True)
|
|
219
|
+
|
|
220
|
+
if name == "help":
|
|
221
|
+
_print_help(console)
|
|
222
|
+
return CommandResult(handled=True)
|
|
223
|
+
|
|
224
|
+
if name == "mode":
|
|
225
|
+
_cmd_mode(console, state, args)
|
|
226
|
+
return CommandResult(handled=True)
|
|
227
|
+
|
|
228
|
+
if name == "model":
|
|
229
|
+
_cmd_model(console, state, args)
|
|
230
|
+
return CommandResult(handled=True)
|
|
231
|
+
|
|
232
|
+
if name == "clear":
|
|
233
|
+
orchestrator.reset_history()
|
|
234
|
+
state.last_tool_history = []
|
|
235
|
+
console.print("[bright_cyan]history cleared[/bright_cyan]")
|
|
236
|
+
return CommandResult(handled=True)
|
|
237
|
+
|
|
238
|
+
if name == "key":
|
|
239
|
+
_cmd_key(console, args)
|
|
240
|
+
return CommandResult(handled=True)
|
|
241
|
+
|
|
242
|
+
if name == "tools":
|
|
243
|
+
if args:
|
|
244
|
+
action = args[0].lower()
|
|
245
|
+
if action == "toggle":
|
|
246
|
+
state.tool_view_collapsed = not state.tool_view_collapsed
|
|
247
|
+
elif action == "collapse":
|
|
248
|
+
state.tool_view_collapsed = True
|
|
249
|
+
elif action == "expand":
|
|
250
|
+
state.tool_view_collapsed = False
|
|
251
|
+
else:
|
|
252
|
+
console.print("[red]unknown /tools option:[/red] " + action)
|
|
253
|
+
return CommandResult(handled=True)
|
|
254
|
+
|
|
255
|
+
_print_tool_history(console, state)
|
|
256
|
+
return CommandResult(handled=True)
|
|
257
|
+
|
|
258
|
+
# Mode shortcuts: /plan, /debug, /explain, /refactor, /implement
|
|
259
|
+
if name in TaskMode.names():
|
|
260
|
+
_cmd_mode(console, state, [name])
|
|
261
|
+
return CommandResult(handled=True)
|
|
262
|
+
|
|
263
|
+
console.print(
|
|
264
|
+
f"[red]unknown command:[/red] /{name} [dim](try /help)[/dim]"
|
|
265
|
+
)
|
|
266
|
+
return CommandResult(handled=True)
|
cli/completer.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Prompt Toolkit completer for Comet slash commands.
|
|
2
|
+
|
|
3
|
+
Provides a simple WordCompleter that suggests all commands defined in
|
|
4
|
+
`cli.commands.SLASH_COMMANDS`. The completer is used by the interactive UI
|
|
5
|
+
so that pressing <Tab> (or any configured completion key) shows available
|
|
6
|
+
commands.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from prompt_toolkit.completion import WordCompleter
|
|
10
|
+
|
|
11
|
+
from .commands import SLASH_COMMANDS
|
|
12
|
+
|
|
13
|
+
# Case‑insensitive, matches whole words (including the leading '/')
|
|
14
|
+
command_completer = WordCompleter(
|
|
15
|
+
SLASH_COMMANDS,
|
|
16
|
+
ignore_case=True,
|
|
17
|
+
sentence=True,
|
|
18
|
+
)
|