claude-sdk-tutor 0.1.3__py3-none-any.whl → 0.1.5__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.
- app.py +8 -3
- claude/__init__.py +4 -0
- claude/history.py +73 -0
- claude/widgets.py +27 -0
- {claude_sdk_tutor-0.1.3.dist-info → claude_sdk_tutor-0.1.5.dist-info}/METADATA +2 -1
- claude_sdk_tutor-0.1.5.dist-info/RECORD +10 -0
- claude_sdk_tutor-0.1.3.dist-info/RECORD +0 -8
- {claude_sdk_tutor-0.1.3.dist-info → claude_sdk_tutor-0.1.5.dist-info}/WHEEL +0 -0
- {claude_sdk_tutor-0.1.3.dist-info → claude_sdk_tutor-0.1.5.dist-info}/entry_points.txt +0 -0
- {claude_sdk_tutor-0.1.3.dist-info → claude_sdk_tutor-0.1.5.dist-info}/licenses/LICENSE +0 -0
app.py
CHANGED
|
@@ -12,6 +12,8 @@ from claude.claude_agent import (
|
|
|
12
12
|
create_claude_client,
|
|
13
13
|
stream_helpful_claude,
|
|
14
14
|
)
|
|
15
|
+
from claude.history import CommandHistory
|
|
16
|
+
from claude.widgets import HistoryInput
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class MyApp(App):
|
|
@@ -22,6 +24,7 @@ class MyApp(App):
|
|
|
22
24
|
self.client = create_claude_client(
|
|
23
25
|
tutor_mode=self.tutor_mode, web_search=self.web_search_enabled
|
|
24
26
|
)
|
|
27
|
+
self.history = CommandHistory()
|
|
25
28
|
|
|
26
29
|
CSS = """
|
|
27
30
|
#main {
|
|
@@ -59,7 +62,7 @@ class MyApp(App):
|
|
|
59
62
|
yield Static("Welcome to claude SDK tutor!", id="header")
|
|
60
63
|
yield RichLog(markup=True, highlight=True)
|
|
61
64
|
yield LoadingIndicator(id="spinner")
|
|
62
|
-
yield
|
|
65
|
+
yield HistoryInput(history=self.history)
|
|
63
66
|
yield Footer()
|
|
64
67
|
|
|
65
68
|
async def on_mount(self) -> None:
|
|
@@ -68,7 +71,7 @@ class MyApp(App):
|
|
|
68
71
|
|
|
69
72
|
def write_user_message(self, message: str) -> None:
|
|
70
73
|
log = self.query_one(RichLog)
|
|
71
|
-
log.write(Panel(RichMarkdown(message), title="You", border_style="
|
|
74
|
+
log.write(Panel(RichMarkdown(message), title="You", border_style="dodger_blue1"))
|
|
72
75
|
|
|
73
76
|
def write_system_message(self, message: str) -> None:
|
|
74
77
|
log = self.query_one(RichLog)
|
|
@@ -85,8 +88,10 @@ class MyApp(App):
|
|
|
85
88
|
log.write(Panel(RichMarkdown(message), title="Slash", border_style="green"))
|
|
86
89
|
|
|
87
90
|
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
88
|
-
self.query_one(Input).value = ""
|
|
89
91
|
command = event.value.strip()
|
|
92
|
+
self.query_one(HistoryInput).value = ""
|
|
93
|
+
if command:
|
|
94
|
+
self.history.add(command)
|
|
90
95
|
if command == "/clear":
|
|
91
96
|
self.run_worker(self.clear_conversation())
|
|
92
97
|
return
|
claude/__init__.py
CHANGED
claude/history.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from platformdirs import user_data_dir
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CommandHistory:
|
|
7
|
+
"""Manages command history with persistence to disk."""
|
|
8
|
+
|
|
9
|
+
MAX_ENTRIES = 1000
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.history: list[str] = []
|
|
13
|
+
self.index: int = -1
|
|
14
|
+
self.temp_input: str = ""
|
|
15
|
+
self._history_file = Path(user_data_dir("claude-sdk-tutor")) / "command_history.txt"
|
|
16
|
+
self._load()
|
|
17
|
+
|
|
18
|
+
def _load(self) -> None:
|
|
19
|
+
"""Load history from disk."""
|
|
20
|
+
if self._history_file.exists():
|
|
21
|
+
try:
|
|
22
|
+
lines = self._history_file.read_text().splitlines()
|
|
23
|
+
self.history = lines[-self.MAX_ENTRIES :]
|
|
24
|
+
except OSError:
|
|
25
|
+
self.history = []
|
|
26
|
+
|
|
27
|
+
def _save(self) -> None:
|
|
28
|
+
"""Save history to disk."""
|
|
29
|
+
try:
|
|
30
|
+
self._history_file.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
self._history_file.write_text("\n".join(self.history[-self.MAX_ENTRIES :]))
|
|
32
|
+
except OSError:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def add(self, command: str) -> None:
|
|
36
|
+
"""Add a command to history, skipping consecutive duplicates."""
|
|
37
|
+
command = command.strip()
|
|
38
|
+
if not command:
|
|
39
|
+
return
|
|
40
|
+
if not self.history or self.history[-1] != command:
|
|
41
|
+
self.history.append(command)
|
|
42
|
+
self._save()
|
|
43
|
+
self.reset_navigation()
|
|
44
|
+
|
|
45
|
+
def reset_navigation(self) -> None:
|
|
46
|
+
"""Reset navigation state."""
|
|
47
|
+
self.index = -1
|
|
48
|
+
self.temp_input = ""
|
|
49
|
+
|
|
50
|
+
def navigate_up(self, current_input: str) -> str:
|
|
51
|
+
"""Navigate to previous command in history."""
|
|
52
|
+
if not self.history:
|
|
53
|
+
return current_input
|
|
54
|
+
|
|
55
|
+
if self.index == -1:
|
|
56
|
+
self.temp_input = current_input
|
|
57
|
+
self.index = len(self.history) - 1
|
|
58
|
+
elif self.index > 0:
|
|
59
|
+
self.index -= 1
|
|
60
|
+
|
|
61
|
+
return self.history[self.index]
|
|
62
|
+
|
|
63
|
+
def navigate_down(self, current_input: str) -> str:
|
|
64
|
+
"""Navigate to next command in history, or restore original input."""
|
|
65
|
+
if self.index == -1:
|
|
66
|
+
return current_input
|
|
67
|
+
|
|
68
|
+
if self.index < len(self.history) - 1:
|
|
69
|
+
self.index += 1
|
|
70
|
+
return self.history[self.index]
|
|
71
|
+
else:
|
|
72
|
+
self.index = -1
|
|
73
|
+
return self.temp_input
|
claude/widgets.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from textual.binding import Binding
|
|
2
|
+
from textual.widgets import Input
|
|
3
|
+
|
|
4
|
+
from claude.history import CommandHistory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HistoryInput(Input):
|
|
8
|
+
"""Input widget with command history navigation."""
|
|
9
|
+
|
|
10
|
+
BINDINGS = [
|
|
11
|
+
Binding("up", "history_previous", "Previous command", show=False),
|
|
12
|
+
Binding("down", "history_next", "Next command", show=False),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
def __init__(self, history: CommandHistory, **kwargs):
|
|
16
|
+
super().__init__(**kwargs)
|
|
17
|
+
self.history = history
|
|
18
|
+
|
|
19
|
+
def action_history_previous(self) -> None:
|
|
20
|
+
"""Navigate to previous command in history."""
|
|
21
|
+
self.value = self.history.navigate_up(self.value)
|
|
22
|
+
self.cursor_position = len(self.value)
|
|
23
|
+
|
|
24
|
+
def action_history_next(self) -> None:
|
|
25
|
+
"""Navigate to next command in history."""
|
|
26
|
+
self.value = self.history.navigate_down(self.value)
|
|
27
|
+
self.cursor_position = len(self.value)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-sdk-tutor
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.13
|
|
7
7
|
Requires-Dist: claude-agent-sdk>=0.1.26
|
|
8
|
+
Requires-Dist: platformdirs>=4.0.0
|
|
8
9
|
Requires-Dist: textual-dev>=1.8.0
|
|
9
10
|
Requires-Dist: textual>=7.5.0
|
|
10
11
|
Requires-Dist: watchfiles>=1.1.1
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
app.py,sha256=rZvIJ18RaJSR1PLe6lN-4GrAthzB_lYOi5o549N71DA,5883
|
|
2
|
+
claude/__init__.py,sha256=rGXaYQtfDG3XywfxU_vHUR5afA_ArxLxzprR04pSnZM,128
|
|
3
|
+
claude/claude_agent.py,sha256=-xs3CNr5p2AosKy6UJvf6nTQLdJd8GG7qtS4jd6K_ts,1304
|
|
4
|
+
claude/history.py,sha256=-JpVhha552jZkyxuaLbbL2GluQXvFzGmhS6mtB63940,2273
|
|
5
|
+
claude/widgets.py,sha256=yYhoxhiEhYK1PjTKNB3YYbCIvEl1SrjhhGFknGNcADM,902
|
|
6
|
+
claude_sdk_tutor-0.1.5.dist-info/METADATA,sha256=bbocPf-vW0ZI00XJaSCBmaCozQ5j0_NCaJ2UzTT7Cw8,2480
|
|
7
|
+
claude_sdk_tutor-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
claude_sdk_tutor-0.1.5.dist-info/entry_points.txt,sha256=vI78kiiqb59KzHEa8UsnkvCbmCs0IMLXOuO2qiho4U4,46
|
|
9
|
+
claude_sdk_tutor-0.1.5.dist-info/licenses/LICENSE,sha256=KzxybQVVAEGBifrjNj5OGwQ_rsbzCIGPm0xrTL6-VZs,1067
|
|
10
|
+
claude_sdk_tutor-0.1.5.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
app.py,sha256=y7zwZmWRZwKtccIuNhmFpiCpuVARuEWcEECn0zKkI38,5668
|
|
2
|
-
claude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
claude/claude_agent.py,sha256=-xs3CNr5p2AosKy6UJvf6nTQLdJd8GG7qtS4jd6K_ts,1304
|
|
4
|
-
claude_sdk_tutor-0.1.3.dist-info/METADATA,sha256=1zMn56c_TVb92jxvF--qB_c6Cq1sB7l_JhJRc43oRZA,2445
|
|
5
|
-
claude_sdk_tutor-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
claude_sdk_tutor-0.1.3.dist-info/entry_points.txt,sha256=vI78kiiqb59KzHEa8UsnkvCbmCs0IMLXOuO2qiho4U4,46
|
|
7
|
-
claude_sdk_tutor-0.1.3.dist-info/licenses/LICENSE,sha256=KzxybQVVAEGBifrjNj5OGwQ_rsbzCIGPm0xrTL6-VZs,1067
|
|
8
|
-
claude_sdk_tutor-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|