kader 0.1.4__tar.gz → 0.1.6__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.
Files changed (79) hide show
  1. {kader-0.1.4 → kader-0.1.6}/PKG-INFO +1 -1
  2. {kader-0.1.4 → kader-0.1.6}/cli/app.py +58 -55
  3. kader-0.1.6/cli/app.tcss +289 -0
  4. {kader-0.1.4 → kader-0.1.6}/cli/utils.py +13 -13
  5. {kader-0.1.4 → kader-0.1.6}/cli/widgets/confirmation.py +5 -5
  6. {kader-0.1.4 → kader-0.1.6}/cli/widgets/conversation.py +1 -1
  7. {kader-0.1.4 → kader-0.1.6}/cli/widgets/loading.py +1 -1
  8. {kader-0.1.4 → kader-0.1.6}/pyproject.toml +1 -1
  9. {kader-0.1.4 → kader-0.1.6}/uv.lock +1 -1
  10. kader-0.1.4/cli/app.tcss +0 -665
  11. {kader-0.1.4 → kader-0.1.6}/.github/workflows/ci.yml +0 -0
  12. {kader-0.1.4 → kader-0.1.6}/.github/workflows/release.yml +0 -0
  13. {kader-0.1.4 → kader-0.1.6}/.gitignore +0 -0
  14. {kader-0.1.4 → kader-0.1.6}/.python-version +0 -0
  15. {kader-0.1.4 → kader-0.1.6}/.qwen/QWEN.md +0 -0
  16. {kader-0.1.4 → kader-0.1.6}/.qwen/agents/technical-writer.md +0 -0
  17. {kader-0.1.4 → kader-0.1.6}/.qwen/agents/test-automation-specialist.md +0 -0
  18. {kader-0.1.4 → kader-0.1.6}/README.md +0 -0
  19. {kader-0.1.4 → kader-0.1.6}/cli/README.md +0 -0
  20. {kader-0.1.4 → kader-0.1.6}/cli/__init__.py +0 -0
  21. {kader-0.1.4 → kader-0.1.6}/cli/__main__.py +0 -0
  22. {kader-0.1.4 → kader-0.1.6}/cli/widgets/__init__.py +0 -0
  23. {kader-0.1.4 → kader-0.1.6}/examples/.gitignore +0 -0
  24. {kader-0.1.4 → kader-0.1.6}/examples/README.md +0 -0
  25. {kader-0.1.4 → kader-0.1.6}/examples/memory_example.py +0 -0
  26. {kader-0.1.4 → kader-0.1.6}/examples/ollama_example.py +0 -0
  27. {kader-0.1.4 → kader-0.1.6}/examples/planning_agent_example.py +0 -0
  28. {kader-0.1.4 → kader-0.1.6}/examples/python_developer/main.py +0 -0
  29. {kader-0.1.4 → kader-0.1.6}/examples/python_developer/template.yaml +0 -0
  30. {kader-0.1.4 → kader-0.1.6}/examples/react_agent_example.py +0 -0
  31. {kader-0.1.4 → kader-0.1.6}/examples/simple_agent.py +0 -0
  32. {kader-0.1.4 → kader-0.1.6}/examples/todo_agent/main.py +0 -0
  33. {kader-0.1.4 → kader-0.1.6}/examples/tools_example.py +0 -0
  34. {kader-0.1.4 → kader-0.1.6}/kader/__init__.py +0 -0
  35. {kader-0.1.4 → kader-0.1.6}/kader/agent/__init__.py +0 -0
  36. {kader-0.1.4 → kader-0.1.6}/kader/agent/agents.py +0 -0
  37. {kader-0.1.4 → kader-0.1.6}/kader/agent/base.py +0 -0
  38. {kader-0.1.4 → kader-0.1.6}/kader/agent/logger.py +0 -0
  39. {kader-0.1.4 → kader-0.1.6}/kader/config.py +0 -0
  40. {kader-0.1.4 → kader-0.1.6}/kader/memory/__init__.py +0 -0
  41. {kader-0.1.4 → kader-0.1.6}/kader/memory/conversation.py +0 -0
  42. {kader-0.1.4 → kader-0.1.6}/kader/memory/session.py +0 -0
  43. {kader-0.1.4 → kader-0.1.6}/kader/memory/state.py +0 -0
  44. {kader-0.1.4 → kader-0.1.6}/kader/memory/types.py +0 -0
  45. {kader-0.1.4 → kader-0.1.6}/kader/prompts/__init__.py +0 -0
  46. {kader-0.1.4 → kader-0.1.6}/kader/prompts/agent_prompts.py +0 -0
  47. {kader-0.1.4 → kader-0.1.6}/kader/prompts/base.py +0 -0
  48. {kader-0.1.4 → kader-0.1.6}/kader/prompts/templates/planning_agent.j2 +0 -0
  49. {kader-0.1.4 → kader-0.1.6}/kader/prompts/templates/react_agent.j2 +0 -0
  50. {kader-0.1.4 → kader-0.1.6}/kader/providers/__init__.py +0 -0
  51. {kader-0.1.4 → kader-0.1.6}/kader/providers/base.py +0 -0
  52. {kader-0.1.4 → kader-0.1.6}/kader/providers/mock.py +0 -0
  53. {kader-0.1.4 → kader-0.1.6}/kader/providers/ollama.py +0 -0
  54. {kader-0.1.4 → kader-0.1.6}/kader/tools/README.md +0 -0
  55. {kader-0.1.4 → kader-0.1.6}/kader/tools/__init__.py +0 -0
  56. {kader-0.1.4 → kader-0.1.6}/kader/tools/base.py +0 -0
  57. {kader-0.1.4 → kader-0.1.6}/kader/tools/exec_commands.py +0 -0
  58. {kader-0.1.4 → kader-0.1.6}/kader/tools/filesys.py +0 -0
  59. {kader-0.1.4 → kader-0.1.6}/kader/tools/filesystem.py +0 -0
  60. {kader-0.1.4 → kader-0.1.6}/kader/tools/protocol.py +0 -0
  61. {kader-0.1.4 → kader-0.1.6}/kader/tools/rag.py +0 -0
  62. {kader-0.1.4 → kader-0.1.6}/kader/tools/todo.py +0 -0
  63. {kader-0.1.4 → kader-0.1.6}/kader/tools/utils.py +0 -0
  64. {kader-0.1.4 → kader-0.1.6}/kader/tools/web.py +0 -0
  65. {kader-0.1.4 → kader-0.1.6}/tests/conftest.py +0 -0
  66. {kader-0.1.4 → kader-0.1.6}/tests/providers/test_mock.py +0 -0
  67. {kader-0.1.4 → kader-0.1.6}/tests/providers/test_ollama.py +0 -0
  68. {kader-0.1.4 → kader-0.1.6}/tests/providers/test_providers_base.py +0 -0
  69. {kader-0.1.4 → kader-0.1.6}/tests/test_agent_logger.py +0 -0
  70. {kader-0.1.4 → kader-0.1.6}/tests/test_agent_logger_integration.py +0 -0
  71. {kader-0.1.4 → kader-0.1.6}/tests/test_base_agent.py +0 -0
  72. {kader-0.1.4 → kader-0.1.6}/tests/test_file_memory.py +0 -0
  73. {kader-0.1.4 → kader-0.1.6}/tests/test_todo_tool.py +0 -0
  74. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_exec_commands.py +0 -0
  75. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_filesys_tools.py +0 -0
  76. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_filesystem_tools.py +0 -0
  77. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_rag.py +0 -0
  78. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_tools_base.py +0 -0
  79. {kader-0.1.4 → kader-0.1.6}/tests/tools/test_web.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kader
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: kader coding agent
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: faiss-cpu>=1.9.0
@@ -10,12 +10,12 @@ from textual.app import App, ComposeResult
10
10
  from textual.binding import Binding
11
11
  from textual.containers import Container, Horizontal, Vertical
12
12
  from textual.widgets import (
13
- DirectoryTree,
14
13
  Footer,
15
14
  Header,
16
15
  Input,
17
16
  Markdown,
18
17
  Static,
18
+ Tree,
19
19
  )
20
20
 
21
21
  from kader.agent.agents import ReActAgent
@@ -29,7 +29,6 @@ from kader.tools import get_default_registry
29
29
  from .utils import (
30
30
  DEFAULT_MODEL,
31
31
  HELP_TEXT,
32
- THEME_NAMES,
33
32
  )
34
33
  from .widgets import ConversationView, InlineSelector, LoadingSpinner, ModelSelector
35
34
 
@@ -51,7 +50,6 @@ Type a message below to start chatting, or use one of the commands:
51
50
 
52
51
  - `/help` - Show available commands
53
52
  - `/models` - View available LLM models
54
- - `/theme` - Change the color theme
55
53
  - `/clear` - Clear the conversation
56
54
  - `/save` - Save current session
57
55
  - `/load` - Load a saved session
@@ -66,17 +64,23 @@ MIN_WIDTH = 89
66
64
  MIN_HEIGHT = 29
67
65
 
68
66
 
67
+ class ASCIITree(Tree):
68
+ """A Tree widget that uses no icons."""
69
+
70
+ ICON_NODE = ""
71
+ ICON_NODE_EXPANDED = ""
72
+
73
+
69
74
  class KaderApp(App):
70
75
  """Main Kader CLI application."""
71
76
 
72
77
  TITLE = "Kader CLI"
73
- SUB_TITLE = "Modern Vibe Coding Assistant"
78
+ SUB_TITLE = f"v{get_version('kader')}"
74
79
  CSS_PATH = "app.tcss"
75
80
 
76
81
  BINDINGS = [
77
82
  Binding("ctrl+q", "quit", "Quit"),
78
83
  Binding("ctrl+l", "clear", "Clear"),
79
- Binding("ctrl+t", "cycle_theme", "Theme"),
80
84
  Binding("ctrl+s", "save_session", "Save"),
81
85
  Binding("ctrl+r", "refresh_tree", "Refresh"),
82
86
  Binding("tab", "focus_next", "Next", show=False),
@@ -85,7 +89,6 @@ class KaderApp(App):
85
89
 
86
90
  def __init__(self) -> None:
87
91
  super().__init__()
88
- self._current_theme_index = 0
89
92
  self._is_processing = False
90
93
  self._current_model = DEFAULT_MODEL
91
94
  self._current_session_id: str | None = None
@@ -180,7 +183,7 @@ class KaderApp(App):
180
183
  if event.confirmed:
181
184
  if tool_message:
182
185
  conversation.add_message(tool_message, "assistant")
183
- conversation.add_message(" Executing tool...", "assistant")
186
+ conversation.add_message("(+) Executing tool...", "assistant")
184
187
  # Restart spinner
185
188
  try:
186
189
  spinner = self.query_one(LoadingSpinner)
@@ -188,7 +191,7 @@ class KaderApp(App):
188
191
  except Exception:
189
192
  pass
190
193
  else:
191
- conversation.add_message(" Tool execution skipped.", "assistant")
194
+ conversation.add_message("(-) Tool execution skipped.", "assistant")
192
195
 
193
196
  # Re-enable input
194
197
  prompt_input = self.query_one("#prompt-input", Input)
@@ -210,7 +213,8 @@ class KaderApp(App):
210
213
  models = OllamaProvider.get_supported_models()
211
214
  if not models:
212
215
  conversation.add_message(
213
- "## Models 🤖\n\n*No models found. Is Ollama running?*", "assistant"
216
+ "## Models (^^)\n\n*No models found. Is Ollama running?*",
217
+ "assistant",
214
218
  )
215
219
  return
216
220
 
@@ -228,7 +232,7 @@ class KaderApp(App):
228
232
 
229
233
  except Exception as e:
230
234
  conversation.add_message(
231
- f"## Models 🤖\n\n*Error fetching models: {e}*", "assistant"
235
+ f"## Models (^^)\n\n*Error fetching models: {e}*", "assistant"
232
236
  )
233
237
 
234
238
  def on_model_selector_model_selected(
@@ -248,7 +252,7 @@ class KaderApp(App):
248
252
  self._agent = self._create_agent(self._current_model)
249
253
 
250
254
  conversation.add_message(
251
- f" Model changed from `{old_model}` to `{self._current_model}`",
255
+ f"(+) Model changed from `{old_model}` to `{self._current_model}`",
252
256
  "assistant",
253
257
  )
254
258
 
@@ -285,8 +289,8 @@ class KaderApp(App):
285
289
  with Horizontal(id="main-container"):
286
290
  # Sidebar with directory tree
287
291
  with Vertical(id="sidebar"):
288
- yield Static("📁 Files", id="sidebar-title")
289
- yield DirectoryTree(Path.cwd(), id="directory-tree")
292
+ yield Static("Files", id="sidebar-title")
293
+ yield ASCIITree(str(Path.cwd().name), id="directory-tree")
290
294
 
291
295
  # Main content area
292
296
  with Vertical(id="content-area"):
@@ -316,9 +320,32 @@ class KaderApp(App):
316
320
  # Check initial size
317
321
  self._check_terminal_size()
318
322
 
323
+ # Start background update check
319
324
  # Start background update check
320
325
  threading.Thread(target=self._check_for_updates, daemon=True).start()
321
326
 
327
+ # Initial tree population
328
+ self._refresh_directory_tree()
329
+
330
+ def _populate_tree(self, node, path: Path) -> None:
331
+ """Recursively populate the tree with ASCII symbols."""
332
+ try:
333
+ # Sort: directories first, then files
334
+ items = sorted(
335
+ path.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())
336
+ )
337
+ for child in items:
338
+ if child.name.startswith((".", "__pycache__")):
339
+ continue
340
+
341
+ if child.is_dir():
342
+ new_node = node.add(f"[+] {child.name}", expand=False)
343
+ self._populate_tree(new_node, child)
344
+ else:
345
+ node.add(f"{child.name}")
346
+ except Exception:
347
+ pass
348
+
322
349
  def _check_for_updates(self) -> None:
323
350
  """Check for package updates in background thread."""
324
351
  try:
@@ -369,7 +396,7 @@ class KaderApp(App):
369
396
  except Exception:
370
397
  if too_small:
371
398
  # Show warning overlay
372
- warning_text = f"""⚠️ Terminal Too Small
399
+ warning_text = f"""<!> Terminal Too Small
373
400
 
374
401
  Current: {width}x{height}
375
402
  Minimum: {MIN_WIDTH}x{MIN_HEIGHT}
@@ -402,12 +429,6 @@ Please resize your terminal."""
402
429
  conversation.add_message(HELP_TEXT, "assistant")
403
430
  elif cmd == "/models":
404
431
  await self._show_model_selector(conversation)
405
- elif cmd == "/theme":
406
- self._cycle_theme()
407
- theme_name = THEME_NAMES[self._current_theme_index]
408
- conversation.add_message(
409
- f"🎨 Theme changed to **{theme_name}**!", "assistant"
410
- )
411
432
  elif cmd == "/clear":
412
433
  conversation.clear_messages()
413
434
  self._agent.memory.clear()
@@ -482,7 +503,7 @@ Please resize your terminal."""
482
503
 
483
504
  except Exception as e:
484
505
  spinner.stop()
485
- error_msg = f" **Error:** {str(e)}\n\nMake sure Ollama is running and the model `{self._current_model}` is available."
506
+ error_msg = f"(-) **Error:** {str(e)}\n\nMake sure Ollama is running and the model `{self._current_model}` is available."
486
507
  conversation.add_message(error_msg, "assistant")
487
508
  self.notify(f"Error: {e}", severity="error")
488
509
 
@@ -491,21 +512,6 @@ Please resize your terminal."""
491
512
  # Auto-refresh directory tree in case agent created/modified files
492
513
  self._refresh_directory_tree()
493
514
 
494
- def _cycle_theme(self) -> None:
495
- """Cycle through available themes."""
496
- # Remove current theme class if it's not dark
497
- current_theme = THEME_NAMES[self._current_theme_index]
498
- if current_theme != "dark":
499
- self.remove_class(f"theme-{current_theme}")
500
-
501
- # Move to next theme
502
- self._current_theme_index = (self._current_theme_index + 1) % len(THEME_NAMES)
503
- new_theme = THEME_NAMES[self._current_theme_index]
504
-
505
- # Apply new theme class (dark is default, no class needed)
506
- if new_theme != "dark":
507
- self.add_class(f"theme-{new_theme}")
508
-
509
515
  def action_clear(self) -> None:
510
516
  """Clear the conversation (Ctrl+L)."""
511
517
  conversation = self.query_one("#conversation-view", ConversationView)
@@ -513,12 +519,6 @@ Please resize your terminal."""
513
519
  self._agent.memory.clear()
514
520
  self.notify("Conversation cleared!", severity="information")
515
521
 
516
- def action_cycle_theme(self) -> None:
517
- """Cycle theme (Ctrl+T)."""
518
- self._cycle_theme()
519
- theme_name = THEME_NAMES[self._current_theme_index]
520
- self.notify(f"Theme: {theme_name}", severity="information")
521
-
522
522
  def action_save_session(self) -> None:
523
523
  """Save session (Ctrl+S)."""
524
524
  conversation = self.query_one("#conversation-view", ConversationView)
@@ -530,10 +530,13 @@ Please resize your terminal."""
530
530
  self.notify("Directory tree refreshed!", severity="information")
531
531
 
532
532
  def _refresh_directory_tree(self) -> None:
533
- """Refresh the directory tree to show new/modified files."""
533
+ """Refresh the directory tree with ASCII symbols."""
534
534
  try:
535
- tree = self.query_one("#directory-tree", DirectoryTree)
536
- tree.reload()
535
+ tree = self.query_one("#directory-tree", ASCIITree)
536
+ tree.clear()
537
+ tree.root.label = str(Path.cwd().name)
538
+ self._populate_tree(tree.root, Path.cwd())
539
+ tree.root.expand()
537
540
  except Exception:
538
541
  pass # Silently ignore if tree not found
539
542
 
@@ -550,12 +553,12 @@ Please resize your terminal."""
550
553
  self._session_manager.save_conversation(self._current_session_id, messages)
551
554
 
552
555
  conversation.add_message(
553
- f" Session saved!\n\n**Session ID:** `{self._current_session_id}`",
556
+ f"(+) Session saved!\n\n**Session ID:** `{self._current_session_id}`",
554
557
  "assistant",
555
558
  )
556
559
  self.notify("Session saved!", severity="information")
557
560
  except Exception as e:
558
- conversation.add_message(f" Error saving session: {e}", "assistant")
561
+ conversation.add_message(f"(-) Error saving session: {e}", "assistant")
559
562
  self.notify(f"Error: {e}", severity="error")
560
563
 
561
564
  def _handle_load_session(
@@ -567,7 +570,7 @@ Please resize your terminal."""
567
570
  session = self._session_manager.get_session(session_id)
568
571
  if not session:
569
572
  conversation.add_message(
570
- f" Session `{session_id}` not found.\n\nUse `/sessions` to see available sessions.",
573
+ f"(-) Session `{session_id}` not found.\n\nUse `/sessions` to see available sessions.",
571
574
  "assistant",
572
575
  )
573
576
  return
@@ -589,12 +592,12 @@ Please resize your terminal."""
589
592
 
590
593
  self._current_session_id = session_id
591
594
  conversation.add_message(
592
- f" Session `{session_id}` loaded with {len(messages)} messages.",
595
+ f"(+) Session `{session_id}` loaded with {len(messages)} messages.",
593
596
  "assistant",
594
597
  )
595
598
  self.notify("Session loaded!", severity="information")
596
599
  except Exception as e:
597
- conversation.add_message(f" Error loading session: {e}", "assistant")
600
+ conversation.add_message(f"(-) Error loading session: {e}", "assistant")
598
601
  self.notify(f"Error: {e}", severity="error")
599
602
 
600
603
  def _handle_list_sessions(self, conversation: ConversationView) -> None:
@@ -604,13 +607,13 @@ Please resize your terminal."""
604
607
 
605
608
  if not sessions:
606
609
  conversation.add_message(
607
- "📭 No saved sessions found.\n\nUse `/save` to save the current session.",
610
+ "[ ] No saved sessions found.\n\nUse `/save` to save the current session.",
608
611
  "assistant",
609
612
  )
610
613
  return
611
614
 
612
615
  lines = [
613
- "## Saved Sessions 📂\n",
616
+ "## Saved Sessions [=]\n",
614
617
  "| Session ID | Created | Updated |",
615
618
  "|------------|---------|---------|",
616
619
  ]
@@ -635,7 +638,7 @@ Please resize your terminal."""
635
638
  model = self._agent.provider.model
636
639
 
637
640
  lines = [
638
- "## Usage Costs 💰\n",
641
+ "## Usage Costs ($)\n",
639
642
  f"**Model:** `{model}`\n",
640
643
  "### Cost Breakdown",
641
644
  "| Type | Amount |",
@@ -654,12 +657,12 @@ Please resize your terminal."""
654
657
 
655
658
  if cost.total_cost == 0.0:
656
659
  lines.append(
657
- "\n> 💡 *Note: Ollama runs locally, so there are no API costs.*"
660
+ "\n> (!) *Note: Ollama runs locally, so there are no API costs.*"
658
661
  )
659
662
 
660
663
  conversation.add_message("\n".join(lines), "assistant")
661
664
  except Exception as e:
662
- conversation.add_message(f" Error getting costs: {e}", "assistant")
665
+ conversation.add_message(f"(-) Error getting costs: {e}", "assistant")
663
666
  self.notify(f"Error: {e}", severity="error")
664
667
 
665
668
 
@@ -0,0 +1,289 @@
1
+ /* Kader CLI - Modern Terminal UI Stylesheet */
2
+
3
+ /* ===== Color Themes ===== */
4
+
5
+ /* Default Dark Theme */
6
+ $primary: #7c3aed;
7
+ $secondary: #06b6d4;
8
+ $success: #10b981;
9
+ $warning: #f59e0b;
10
+ $error: #ef4444;
11
+ $surface: #1e1e2e;
12
+ $background: #11111b;
13
+ $text: #cdd6f4;
14
+ $text-muted: #6c7086;
15
+
16
+ /* ===== Root App ===== */
17
+
18
+ Screen {
19
+ background: $background;
20
+ min-width: 89;
21
+ min-height: 29;
22
+ }
23
+
24
+ /* ===== Size Warning Overlay ===== */
25
+
26
+ #size-warning {
27
+ dock: top;
28
+ width: 100%;
29
+ height: 100%;
30
+ background: $background 95%;
31
+ color: $warning;
32
+ text-align: center;
33
+ content-align: center middle;
34
+ text-style: bold;
35
+ padding: 2;
36
+ layer: overlay;
37
+ }
38
+
39
+ /* ===== Header ===== */
40
+
41
+ Header {
42
+ background: $primary;
43
+ color: $text;
44
+ text-style: bold;
45
+ height: 1;
46
+ dock: top;
47
+ }
48
+
49
+ /* ===== Footer ===== */
50
+
51
+ Footer {
52
+ background: $surface;
53
+ color: $text-muted;
54
+ height: 1;
55
+ dock: bottom;
56
+ }
57
+
58
+ FooterKey {
59
+ background: transparent;
60
+ color: $text-muted;
61
+ }
62
+
63
+ FooterKey > .footer-key--key {
64
+ background: $primary;
65
+ color: $text;
66
+ }
67
+
68
+ /* ===== Main Layout ===== */
69
+
70
+ #main-container {
71
+ layout: horizontal;
72
+ height: 1fr;
73
+ }
74
+
75
+ /* ===== Sidebar (Directory Tree) ===== */
76
+
77
+ #sidebar {
78
+ width: 22;
79
+ min-width: 18;
80
+ max-width: 35;
81
+ background: $surface;
82
+ border-right: thick $primary;
83
+ padding: 0;
84
+ }
85
+
86
+ #sidebar-title {
87
+ background: $primary 20%;
88
+ color: $text;
89
+ text-style: bold;
90
+ padding: 1;
91
+ text-align: center;
92
+ height: 3;
93
+ }
94
+
95
+ DirectoryTree {
96
+ background: transparent;
97
+ padding: 0 1;
98
+ scrollbar-size: 1 1;
99
+ }
100
+
101
+ DirectoryTree > .directory-tree--folder {
102
+ color: $secondary;
103
+ }
104
+
105
+ DirectoryTree > .directory-tree--file {
106
+ color: $text;
107
+ }
108
+
109
+ DirectoryTree > .directory-tree--extension {
110
+ color: $text-muted;
111
+ }
112
+
113
+ DirectoryTree:focus > .directory-tree--cursor {
114
+ background: $primary 40%;
115
+ }
116
+
117
+ /* ===== Content Area ===== */
118
+
119
+ #content-area {
120
+ width: 1fr;
121
+ layout: vertical;
122
+ }
123
+
124
+ /* ===== Conversation ===== */
125
+
126
+ #conversation {
127
+ height: 1fr;
128
+ background: $background;
129
+ }
130
+
131
+ ConversationView {
132
+ scrollbar-size: 1 1;
133
+ }
134
+
135
+ /* ===== Welcome Message ===== */
136
+
137
+ #welcome {
138
+ padding: 2 4;
139
+ text-align: center;
140
+ }
141
+
142
+ #welcome Markdown {
143
+ text-align: center;
144
+ }
145
+
146
+ /* ===== Input Area ===== */
147
+
148
+ #input-container {
149
+ height: auto;
150
+ min-height: 4;
151
+ max-height: 7;
152
+ background: $background;
153
+ padding: 1;
154
+ margin-bottom: 1;
155
+ }
156
+
157
+ #prompt-input {
158
+ background: transparent;
159
+ border: round $primary;
160
+ padding: 0 1;
161
+ height: 3;
162
+ }
163
+
164
+ #prompt-input:focus {
165
+ border: round $secondary;
166
+ }
167
+
168
+ Input.-valid {
169
+ border: round $success;
170
+ }
171
+
172
+ Input > .input--placeholder {
173
+ color: $text-muted;
174
+ text-style: italic;
175
+ }
176
+
177
+ /* ===== Loading Spinner ===== */
178
+
179
+ LoadingSpinner {
180
+ background: $surface;
181
+ margin: 0 2;
182
+ padding: 1;
183
+ border-left: thick $warning;
184
+ }
185
+
186
+ /* ===== Command Hints in Footer ===== */
187
+
188
+ #command-hints {
189
+ dock: bottom;
190
+ height: 1;
191
+ background: $surface;
192
+ padding: 0 1;
193
+ text-align: center;
194
+ }
195
+
196
+ .command-hint {
197
+ margin: 0 1;
198
+ color: $text-muted;
199
+ }
200
+
201
+ .command-key {
202
+ color: $primary;
203
+ text-style: bold;
204
+ }
205
+
206
+ /* ===== Markdown Styling ===== */
207
+
208
+ Markdown {
209
+ margin: 0;
210
+ padding: 0;
211
+ }
212
+
213
+ MarkdownH1 {
214
+ color: $primary;
215
+ text-style: bold underline;
216
+ margin: 1 0;
217
+ }
218
+
219
+ MarkdownH2 {
220
+ color: $secondary;
221
+ text-style: bold;
222
+ margin: 1 0;
223
+ }
224
+
225
+ MarkdownH3 {
226
+ color: $success;
227
+ text-style: bold;
228
+ margin: 1 0;
229
+ }
230
+
231
+ MarkdownFence {
232
+ background: $surface;
233
+ border: round #6c7086;
234
+ margin: 1 0;
235
+ padding: 1;
236
+ }
237
+
238
+ MarkdownBlockQuote {
239
+ background: $primary 10%;
240
+ border-left: thick $primary;
241
+ padding: 0 1;
242
+ margin: 1 0;
243
+ }
244
+
245
+ MarkdownBulletList {
246
+ margin: 0;
247
+ padding-left: 2;
248
+ }
249
+
250
+ MarkdownTable {
251
+ margin: 1 0;
252
+ }
253
+
254
+ MarkdownTH {
255
+ background: $primary 20%;
256
+ text-style: bold;
257
+ }
258
+
259
+ MarkdownTD {
260
+ padding: 0 1;
261
+ }
262
+
263
+ /* ===== Scrollbars ===== */
264
+
265
+ Scrollbar {
266
+ background: $surface;
267
+ }
268
+
269
+ ScrollbarSlider {
270
+ color: $primary;
271
+ }
272
+
273
+ ScrollbarSlider:hover {
274
+ color: $secondary;
275
+ }
276
+
277
+ /* ===== Scrollbars ===== */
278
+
279
+ Scrollbar {
280
+ background: $surface;
281
+ }
282
+
283
+ ScrollbarSlider {
284
+ color: $primary;
285
+ }
286
+
287
+ ScrollbarSlider:hover {
288
+ color: $secondary;
289
+ }
@@ -2,18 +2,14 @@
2
2
 
3
3
  from kader.providers import OllamaProvider
4
4
 
5
- # Theme names for cycling
6
- THEME_NAMES = ["dark", "ocean", "forest", "sunset"]
7
-
8
5
  # Default model
9
6
  DEFAULT_MODEL = "qwen3-coder:480b-cloud"
10
7
 
11
- HELP_TEXT = """## Kader CLI Commands 📖
8
+ HELP_TEXT = """## Kader CLI Commands
12
9
 
13
10
  | Command | Description |
14
11
  |---------|-------------|
15
12
  | `/models` | Show available LLM models |
16
- | `/theme` | Cycle through color themes |
17
13
  | `/help` | Show this help message |
18
14
  | `/clear` | Clear the conversation |
19
15
  | `/save` | Save current session |
@@ -23,17 +19,16 @@ HELP_TEXT = """## Kader CLI Commands 📖
23
19
  | `/refresh` | Refresh file tree |
24
20
  | `/exit` | Exit the CLI |
25
21
 
26
- ### Keyboard Shortcuts ⌨️
22
+ ### Keyboard Shortcuts
27
23
 
28
24
  | Shortcut | Action |
29
25
  |----------|--------|
30
26
  | `Ctrl+L` | Clear conversation |
31
- | `Ctrl+T` | Cycle theme |
32
27
  | `Ctrl+S` | Save session |
33
28
  | `Ctrl+R` | Refresh file tree |
34
29
  | `Ctrl+Q` | Quit |
35
30
 
36
- ### Input Editing 📝
31
+ ### Input Editing
37
32
 
38
33
  | Shortcut | Action |
39
34
  |----------|--------|
@@ -44,7 +39,8 @@ HELP_TEXT = """## Kader CLI Commands 📖
44
39
 
45
40
  ### Tips:
46
41
  - Type any question to chat with the AI
47
- - Use **Tab** to navigate between panels"""
42
+ - Use **Tab** to navigate between panels
43
+ """
48
44
 
49
45
 
50
46
  def get_models_text() -> str:
@@ -52,12 +48,16 @@ def get_models_text() -> str:
52
48
  try:
53
49
  models = OllamaProvider.get_supported_models()
54
50
  if not models:
55
- return "## Available Models 🤖\n\n*No models found. Is Ollama running?*"
51
+ return "## Available Models (^^)\n\n*No models found. Is Ollama running?*"
56
52
 
57
- lines = ["## Available Models 🤖\n", "| Model | Status |", "|-------|--------|"]
53
+ lines = [
54
+ "## Available Models (^^)\n",
55
+ "| Model | Status |",
56
+ "|-------|--------|",
57
+ ]
58
58
  for model in models:
59
- lines.append(f"| {model} | Available |")
59
+ lines.append(f"| {model} | (+) Available |")
60
60
  lines.append(f"\n*Currently using: **{DEFAULT_MODEL}***")
61
61
  return "\n".join(lines)
62
62
  except Exception as e:
63
- return f"## Available Models 🤖\n\n*Error fetching models: {e}*"
63
+ return f"## Available Models (^^)\n\n*Error fetching models: {e}*"