claude-sdk-tutor 0.1.3__tar.gz → 0.1.5__tar.gz

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.
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-sdk-tutor
3
- Version: 0.1.3
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
@@ -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 Input()
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="bright_blue"))
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
@@ -1,11 +1,12 @@
1
1
  [project]
2
2
  name = "claude-sdk-tutor"
3
- version = "0.1.3"
3
+ version = "0.1.5"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
8
  "claude-agent-sdk>=0.1.26",
9
+ "platformdirs>=4.0.0",
9
10
  "textual>=7.5.0",
10
11
  "textual-dev>=1.8.0",
11
12
  "watchfiles>=1.1.1",
@@ -0,0 +1,4 @@
1
+ from claude.history import CommandHistory
2
+ from claude.widgets import HistoryInput
3
+
4
+ __all__ = ["CommandHistory", "HistoryInput"]
@@ -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
@@ -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)
@@ -206,10 +206,11 @@ wheels = [
206
206
 
207
207
  [[package]]
208
208
  name = "claude-sdk-tutor"
209
- version = "0.1.3"
209
+ version = "0.1.4"
210
210
  source = { editable = "." }
211
211
  dependencies = [
212
212
  { name = "claude-agent-sdk" },
213
+ { name = "platformdirs" },
213
214
  { name = "textual" },
214
215
  { name = "textual-dev" },
215
216
  { name = "watchfiles" },
@@ -218,6 +219,7 @@ dependencies = [
218
219
  [package.metadata]
219
220
  requires-dist = [
220
221
  { name = "claude-agent-sdk", specifier = ">=0.1.26" },
222
+ { name = "platformdirs", specifier = ">=4.0.0" },
221
223
  { name = "textual", specifier = ">=7.5.0" },
222
224
  { name = "textual-dev", specifier = ">=1.8.0" },
223
225
  { name = "watchfiles", specifier = ">=1.1.1" },
File without changes