devcli-dashboard 0.1.0__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 (30) hide show
  1. devcli_dashboard-0.1.0/LICENSE +21 -0
  2. devcli_dashboard-0.1.0/PKG-INFO +119 -0
  3. devcli_dashboard-0.1.0/README.md +90 -0
  4. devcli_dashboard-0.1.0/devcli/__init__.py +0 -0
  5. devcli_dashboard-0.1.0/devcli/commands/dashboard.py +21 -0
  6. devcli_dashboard-0.1.0/devcli/commands/plugin.py +16 -0
  7. devcli_dashboard-0.1.0/devcli/commands/remote.py +21 -0
  8. devcli_dashboard-0.1.0/devcli/commands/run.py +17 -0
  9. devcli_dashboard-0.1.0/devcli/commands/tui.py +10 -0
  10. devcli_dashboard-0.1.0/devcli/core/config.py +12 -0
  11. devcli_dashboard-0.1.0/devcli/core/logger.py +12 -0
  12. devcli_dashboard-0.1.0/devcli/core/plugin_loader.py +26 -0
  13. devcli_dashboard-0.1.0/devcli/core/plugin_market.py +19 -0
  14. devcli_dashboard-0.1.0/devcli/core/ssh.py +27 -0
  15. devcli_dashboard-0.1.0/devcli/core/state.py +0 -0
  16. devcli_dashboard-0.1.0/devcli/core/tui.py +126 -0
  17. devcli_dashboard-0.1.0/devcli/main.py +25 -0
  18. devcli_dashboard-0.1.0/devcli/plugins/cpu_temp.py +32 -0
  19. devcli_dashboard-0.1.0/devcli/plugins/fun_quotes.py +31 -0
  20. devcli_dashboard-0.1.0/devcli/plugins/git_status.py +42 -0
  21. devcli_dashboard-0.1.0/devcli/plugins/ping_monitor.py +32 -0
  22. devcli_dashboard-0.1.0/devcli/plugins/task_runner.py +59 -0
  23. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/PKG-INFO +119 -0
  24. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/SOURCES.txt +28 -0
  25. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/dependency_links.txt +1 -0
  26. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/entry_points.txt +2 -0
  27. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/requires.txt +10 -0
  28. devcli_dashboard-0.1.0/devcli_dashboard.egg-info/top_level.txt +1 -0
  29. devcli_dashboard-0.1.0/pyproject.toml +43 -0
  30. devcli_dashboard-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DevCLI Project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: devcli-dashboard
3
+ Version: 0.1.0
4
+ Summary: A plugin-driven developer CLI with a live Textual dashboard.
5
+ License: MIT
6
+ Keywords: cli,dashboard,plugins,textual,devops,monitoring
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: User Interfaces
15
+ Classifier: Topic :: System :: Monitoring
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: typer>=0.10
20
+ Requires-Dist: rich>=13.0
21
+ Requires-Dist: psutil>=5.9
22
+ Requires-Dist: python-dotenv>=1.0
23
+ Requires-Dist: textual>=0.38
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: build; extra == "dev"
27
+ Requires-Dist: twine; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # DevCLI
31
+
32
+ DevCLI is a plugin-driven command line toolkit with a live Textual dashboard for developers and sysadmins.
33
+
34
+ It combines system metrics, task status, and dynamic plugins into a single terminal UI, making it easy to extend with custom monitoring, git status, and tooling panels.
35
+
36
+ ## Features
37
+
38
+ - Plugin-based dashboard architecture
39
+ - Live updates for system stats, git status, ping, and quotes
40
+ - Textual-powered terminal UI with left/right layout
41
+ - Easy plugin registration via `register(tui_app)` hooks
42
+ - CLI entrypoint via `devcli`
43
+
44
+ ## Installation
45
+
46
+ Install from PyPI once published:
47
+
48
+ ```bash
49
+ pip install devcli
50
+ ```
51
+
52
+ For local development:
53
+
54
+ ```bash
55
+ python3 -m pip install -e .
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ Run the dashboard from the repository or installed package:
61
+
62
+ ```bash
63
+ python3 -m devcli.main start
64
+ ```
65
+
66
+ or if the package is installed:
67
+
68
+ ```bash
69
+ devcli start
70
+ ```
71
+
72
+ ## Plugin System
73
+
74
+ The dashboard is built around plugins located in `devcli/plugins/`.
75
+
76
+ Each plugin should expose a `register(tui_app)` function that appends a widget to `tui_app.tui_panels`.
77
+
78
+ Example plugin characteristics:
79
+
80
+ - `cpu_temp.py` — CPU temperature and frequency
81
+ - `git_status.py` — current git repo status
82
+ - `ping_monitor.py` — ping latency reporting
83
+ - `fun_quotes.py` — rotating motivational quotes
84
+
85
+ ### Creating a plugin
86
+
87
+ 1. Add a new file under `devcli/plugins/`.
88
+ 2. Define a `Static`-based widget.
89
+ 3. Implement `register(tui_app)` and append the widget to `tui_app.tui_panels`.
90
+
91
+ ## Packaging for PyPI
92
+
93
+ This project is configured with `pyproject.toml` and uses `setuptools` for packaging.
94
+
95
+ ### Build and publish
96
+
97
+ ```bash
98
+ python3 -m pip install build twine
99
+ python3 -m build
100
+ python3 -m twine upload dist/*
101
+ ```
102
+
103
+ ## Development dependencies
104
+
105
+ Install development tooling:
106
+
107
+ ```bash
108
+ python3 -m pip install -e .[dev]
109
+ ```
110
+
111
+ ## Notes
112
+
113
+ - Requires Python 3.11+
114
+ - Depends on `textual` for the TUI
115
+ - `python-dotenv` is included for environment configuration support
116
+
117
+ ## Contribution
118
+
119
+ Contributions are welcome. Add new plugins, improve dashboard styling, or extend the CLI commands.
@@ -0,0 +1,90 @@
1
+ # DevCLI
2
+
3
+ DevCLI is a plugin-driven command line toolkit with a live Textual dashboard for developers and sysadmins.
4
+
5
+ It combines system metrics, task status, and dynamic plugins into a single terminal UI, making it easy to extend with custom monitoring, git status, and tooling panels.
6
+
7
+ ## Features
8
+
9
+ - Plugin-based dashboard architecture
10
+ - Live updates for system stats, git status, ping, and quotes
11
+ - Textual-powered terminal UI with left/right layout
12
+ - Easy plugin registration via `register(tui_app)` hooks
13
+ - CLI entrypoint via `devcli`
14
+
15
+ ## Installation
16
+
17
+ Install from PyPI once published:
18
+
19
+ ```bash
20
+ pip install devcli
21
+ ```
22
+
23
+ For local development:
24
+
25
+ ```bash
26
+ python3 -m pip install -e .
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Run the dashboard from the repository or installed package:
32
+
33
+ ```bash
34
+ python3 -m devcli.main start
35
+ ```
36
+
37
+ or if the package is installed:
38
+
39
+ ```bash
40
+ devcli start
41
+ ```
42
+
43
+ ## Plugin System
44
+
45
+ The dashboard is built around plugins located in `devcli/plugins/`.
46
+
47
+ Each plugin should expose a `register(tui_app)` function that appends a widget to `tui_app.tui_panels`.
48
+
49
+ Example plugin characteristics:
50
+
51
+ - `cpu_temp.py` — CPU temperature and frequency
52
+ - `git_status.py` — current git repo status
53
+ - `ping_monitor.py` — ping latency reporting
54
+ - `fun_quotes.py` — rotating motivational quotes
55
+
56
+ ### Creating a plugin
57
+
58
+ 1. Add a new file under `devcli/plugins/`.
59
+ 2. Define a `Static`-based widget.
60
+ 3. Implement `register(tui_app)` and append the widget to `tui_app.tui_panels`.
61
+
62
+ ## Packaging for PyPI
63
+
64
+ This project is configured with `pyproject.toml` and uses `setuptools` for packaging.
65
+
66
+ ### Build and publish
67
+
68
+ ```bash
69
+ python3 -m pip install build twine
70
+ python3 -m build
71
+ python3 -m twine upload dist/*
72
+ ```
73
+
74
+ ## Development dependencies
75
+
76
+ Install development tooling:
77
+
78
+ ```bash
79
+ python3 -m pip install -e .[dev]
80
+ ```
81
+
82
+ ## Notes
83
+
84
+ - Requires Python 3.11+
85
+ - Depends on `textual` for the TUI
86
+ - `python-dotenv` is included for environment configuration support
87
+
88
+ ## Contribution
89
+
90
+ Contributions are welcome. Add new plugins, improve dashboard styling, or extend the CLI commands.
File without changes
@@ -0,0 +1,21 @@
1
+ import psutil
2
+ from rich.table import Table
3
+ from rich.console import Console
4
+
5
+ console = Console()
6
+
7
+ def dashboard():
8
+ """Show system stats"""
9
+ table = Table(title="💻 System Stats")
10
+ table.add_column("Metric", justify="left")
11
+ table.add_column("Usage", justify="right")
12
+
13
+ cpu = psutil.cpu_percent(interval=1)
14
+ memory = psutil.virtual_memory().percent
15
+ disk = psutil.disk_usage("/").percent
16
+
17
+ table.add_row("CPU", f"{cpu}%")
18
+ table.add_row("Memory", f"{memory}%")
19
+ table.add_row("Disk", f"{disk}%")
20
+
21
+ console.print(table)
@@ -0,0 +1,16 @@
1
+ import typer
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ app = typer.Typer()
6
+ PLUGIN_DIR = Path("devcli/plugins")
7
+
8
+ @app.command()
9
+ def install(repo: str):
10
+ """Install a plugin from GitHub"""
11
+ target = PLUGIN_DIR / repo.split("/")[-1]
12
+ if target.exists():
13
+ print(f"{target.name} already installed")
14
+ return
15
+ subprocess.run(["git", "clone", f"https://github.com/{repo}.git", str(target)])
16
+ print(f"Installed {target.name}")
@@ -0,0 +1,21 @@
1
+ import typer
2
+ from devcli.core.logger import logger
3
+ import paramiko
4
+
5
+ app = typer.Typer()
6
+
7
+ @app.command()
8
+ def exec(host: str, user: str, command: str, password: str = None, key: str = None):
9
+ """Run a remote command over SSH"""
10
+ client = paramiko.SSHClient()
11
+ client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
12
+ if key:
13
+ client.connect(host, username=user, key_filename=key)
14
+ else:
15
+ client.connect(host, username=user, password=password)
16
+ stdin, stdout, stderr = client.exec_command(command)
17
+ print(stdout.read().decode())
18
+ err = stderr.read().decode()
19
+ if err:
20
+ logger.error(err)
21
+ client.close()
@@ -0,0 +1,17 @@
1
+ import typer
2
+ import asyncio
3
+ import random
4
+ from rich.progress import Progress
5
+
6
+ app = typer.Typer()
7
+
8
+ @app.command()
9
+ def run(task: str):
10
+ """Run a fake task with progress animation"""
11
+ async def runner():
12
+ with Progress() as progress:
13
+ job = progress.add_task(f"Running {task}", total=100)
14
+ while not progress.finished:
15
+ progress.update(job, advance=random.randint(1,5))
16
+ await asyncio.sleep(0.05)
17
+ asyncio.run(runner())
@@ -0,0 +1,10 @@
1
+ import typer
2
+ from devcli.core.tui import start as run_tui
3
+
4
+ app = typer.Typer()
5
+
6
+ @app.command()
7
+ def start():
8
+ """Launch the TUI dashboard"""
9
+ run_tui()
10
+
@@ -0,0 +1,12 @@
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+ class Config:
7
+ APP_NAME = "DevCLI"
8
+ DEBUG = os.getenv("DEBUG", "False").lower() == "true"
9
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
10
+ PLUGINS_DIR = os.getenv("PLUGINS_DIR", "devcli/plugins")
11
+
12
+ config = Config()
@@ -0,0 +1,12 @@
1
+ import logging
2
+ from rich.logging import RichHandler
3
+
4
+ def setup_logger():
5
+ logging.basicConfig(
6
+ level="INFO",
7
+ format="%(message)s",
8
+ handlers=[RichHandler(rich_tracebacks=True)],
9
+ )
10
+ return logging.getLogger("devcli")
11
+
12
+ logger = setup_logger()
@@ -0,0 +1,26 @@
1
+ # devcli/core/plugin_loader.py
2
+ import importlib
3
+ import logging
4
+ import os
5
+
6
+ logger = logging.getLogger("devcli")
7
+
8
+ BASE_DIR = os.path.dirname(__file__)
9
+ PLUGIN_FOLDER = os.path.abspath(os.path.join(BASE_DIR, os.pardir, "plugins"))
10
+
11
+ def load_plugins(app=None):
12
+ loaded_plugins = []
13
+ if not os.path.isdir(PLUGIN_FOLDER):
14
+ logger.warning(f"Plugin folder not found: {PLUGIN_FOLDER}")
15
+ return loaded_plugins
16
+ for file in os.listdir(PLUGIN_FOLDER):
17
+ if file.endswith(".py") and file != "__init__.py":
18
+ plugin_name = file[:-3]
19
+ module_path = f"devcli.plugins.{plugin_name}"
20
+ try:
21
+ module = importlib.import_module(module_path)
22
+ logger.info(f"Loaded plugin: {plugin_name}")
23
+ loaded_plugins.append(module) # <-- append module object
24
+ except Exception as e:
25
+ logger.error(f"Failed to load plugin {plugin_name}: {e}")
26
+ return loaded_plugins # <-- return the list
@@ -0,0 +1,19 @@
1
+ import subprocess
2
+ from pathlib import Path
3
+
4
+ PLUGIN_DIR = Path("devcli/plugins")
5
+
6
+ def install_plugin(repo: str):
7
+ name = repo.split("/")[-1]
8
+ target = PLUGIN_DIR / name
9
+
10
+ if target.exists():
11
+ return f"{name} already installed"
12
+
13
+ subprocess.run([
14
+ "git", "clone",
15
+ f"https://github.com/{repo}.git",
16
+ str(target)
17
+ ])
18
+
19
+ return f"Installed {name}"
@@ -0,0 +1,27 @@
1
+ import paramiko
2
+ from devcli.core.logger import logger
3
+
4
+ class SSHClient:
5
+ def __init__(self, host, user, password=None, key_path=None):
6
+ self.host = host
7
+ self.user = user
8
+ self.password = password
9
+ self.key_path = key_path
10
+
11
+ def connect(self):
12
+ self.client = paramiko.SSHClient()
13
+ self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
14
+
15
+ if self.key_path:
16
+ self.client.connect(self.host, username=self.user, key_filename=self.key_path)
17
+ else:
18
+ self.client.connect(self.host, username=self.user, password=self.password)
19
+
20
+ logger.info(f"Connected to {self.host}")
21
+
22
+ def run(self, command: str):
23
+ stdin, stdout, stderr = self.client.exec_command(command)
24
+ return stdout.read().decode(), stderr.read().decode()
25
+
26
+ def close(self):
27
+ self.client.close()
File without changes
@@ -0,0 +1,126 @@
1
+ from textual.app import App, ComposeResult
2
+ from textual.widgets import Static
3
+ from textual.containers import Horizontal, Vertical
4
+ import psutil
5
+ from rich.table import Table
6
+ from rich.text import Text
7
+ from devcli.core.plugin_loader import load_plugins
8
+
9
+ ACTIVE_TASKS = []
10
+
11
+ class SystemStats(Static):
12
+ def on_mount(self):
13
+ self.set_interval(1, self.refresh_stats)
14
+
15
+ def refresh_stats(self):
16
+ cpu = psutil.cpu_percent()
17
+ mem = psutil.virtual_memory().percent
18
+ disk = psutil.disk_usage("/").percent
19
+ table = Table(title="💻 System Stats")
20
+ table.add_column("Metric")
21
+ table.add_column("Usage")
22
+ table.add_row("CPU", f"{cpu}%")
23
+ table.add_row("Memory", f"{mem}%")
24
+ table.add_row("Disk", f"{disk}%")
25
+ self.update(table)
26
+
27
+ class ActiveTasks(Static):
28
+ def on_mount(self):
29
+ self.set_interval(1, self.refresh_tasks)
30
+
31
+ def refresh_tasks(self):
32
+ if not ACTIVE_TASKS:
33
+ self.update("No active tasks")
34
+ return
35
+ table = Table(title="🚀 Active Tasks")
36
+ table.add_column("Task")
37
+ table.add_column("Progress")
38
+ for task in ACTIVE_TASKS:
39
+ table.add_row(task["name"], f"{task['progress']}%")
40
+ self.update(table)
41
+
42
+ class DashboardHeader(Static):
43
+ def on_mount(self):
44
+ self.update(Text("⚡ DevCLI Dashboard • Plugin overview", style="bold white"))
45
+
46
+ class DashboardFooter(Static):
47
+ def on_mount(self):
48
+ self.update(Text("Press ^+q to quit | Run `devcli start` from the repo root", style="dim cyan"))
49
+
50
+ class DevCLIApp(App):
51
+ CSS = """
52
+ Screen {
53
+ background: #0b1220;
54
+ }
55
+
56
+ Static.dashboard-header,
57
+ Static.dashboard-footer {
58
+ border: round cornflowerblue;
59
+ background: #111923;
60
+ color: white;
61
+ padding: 1 2;
62
+ }
63
+
64
+ Horizontal {
65
+ padding: 1 2;
66
+ }
67
+
68
+ Vertical {
69
+ background: transparent;
70
+ padding: 0 1;
71
+ }
72
+
73
+ Static {
74
+ border: round cornflowerblue;
75
+ background: #0f172a;
76
+ padding: 1 2;
77
+ min-height: 6;
78
+ }
79
+
80
+ #left {
81
+ width: 40%;
82
+ min-width: 32;
83
+ }
84
+
85
+ #right {
86
+ width: 60%;
87
+ }
88
+ """
89
+
90
+ tui_panels = []
91
+
92
+ async def on_mount(self):
93
+ # Create layout containers
94
+ self.left = Vertical(id="left")
95
+ self.right = Vertical(id="right")
96
+ await self.mount(DashboardHeader(classes="dashboard-header"))
97
+ await self.mount(Horizontal(self.left, self.right))
98
+
99
+ # Mount core panels in left column
100
+ await self.left.mount(SystemStats(), ActiveTasks())
101
+
102
+ # If no plugins were registered externally, load them now
103
+ if not self.tui_panels:
104
+ plugins = load_plugins(self)
105
+ for plugin in plugins:
106
+ if hasattr(plugin, "register"):
107
+ registered = plugin.register(self)
108
+ if registered is not None:
109
+ if isinstance(registered, list):
110
+ self.tui_panels.extend(registered)
111
+ else:
112
+ self.tui_panels.append(registered)
113
+
114
+ # Mount plugin panels to left or right column based on 'column' attribute
115
+ for panel in self.tui_panels:
116
+ if panel is not None:
117
+ target_column = getattr(panel, 'column', 'right')
118
+ if target_column == 'left':
119
+ await self.left.mount(panel)
120
+ else:
121
+ await self.right.mount(panel)
122
+
123
+ await self.mount(DashboardFooter(classes="dashboard-footer"))
124
+
125
+ def start():
126
+ DevCLIApp().run()
@@ -0,0 +1,25 @@
1
+ # devcli/main.py
2
+ import typer
3
+ from devcli.commands import dashboard, run, remote, plugin, tui
4
+ from devcli.core import tui as core_tui
5
+ from devcli.core.plugin_loader import load_plugins
6
+
7
+ app = typer.Typer(help="⚡ DevCLI - Production CLI")
8
+
9
+ # Flat command registration
10
+ app.command()(dashboard.dashboard)
11
+ app.command()(run.run)
12
+ app.command()(remote.exec)
13
+ app.command()(plugin.install)
14
+ app.command()(tui.start)
15
+
16
+ # Load plugins
17
+ plugins = load_plugins(app)
18
+
19
+ core_tui.DevCLIApp.tui_panels = []
20
+ for plugin_module in plugins:
21
+ if hasattr(plugin_module, "register"):
22
+ plugin_module.register(core_tui.DevCLIApp)
23
+
24
+ if __name__ == "__main__":
25
+ app()
@@ -0,0 +1,32 @@
1
+ # devcli/plugins/cpu_temp.py
2
+ from textual.widgets import Static
3
+ import psutil
4
+ from rich.table import Table
5
+
6
+ class CPUTempPanel(Static):
7
+ """Shows CPU temperature and frequency (if available)."""
8
+
9
+ def on_mount(self):
10
+ self.set_interval(2, self.refresh_stats)
11
+
12
+ def refresh_stats(self):
13
+ table = Table(title="🌡 CPU Temperature")
14
+ table.add_column("Metric", style="bold cyan")
15
+ table.add_column("Value", justify="right")
16
+ try:
17
+ freq = psutil.cpu_freq().current
18
+ temps = psutil.sensors_temperatures().get("coretemp", [])
19
+ temp_str = ", ".join(f"{t.current:.1f}°C" for t in temps) if temps else "N/A"
20
+ table.add_row("Temp", temp_str)
21
+ table.add_row("Freq", f"{freq:.0f} MHz")
22
+ except Exception:
23
+ table.add_row("Temp", "N/A")
24
+ table.add_row("Freq", "N/A")
25
+ self.update(table)
26
+
27
+
28
+ def register(tui_app):
29
+ panel = CPUTempPanel()
30
+ if not hasattr(tui_app, "tui_panels"):
31
+ tui_app.tui_panels = []
32
+ tui_app.tui_panels.append(panel)
@@ -0,0 +1,31 @@
1
+ # devcli/plugins/fun_quotes.py
2
+ from textual.widgets import Static
3
+ import random
4
+ from rich.panel import Panel
5
+ from rich.text import Text
6
+
7
+ QUOTES = [
8
+ "💡 Code is like humor. When you have to explain it, it’s bad.",
9
+ "🚀 Talk is cheap. Show me the code.",
10
+ "🐍 Pythonic code is readable code.",
11
+ "⚡ Optimism is an understatement for programmers.",
12
+ "🧠 Debugging is like being the detective in a crime movie where you are also the murderer."
13
+ ]
14
+
15
+ class FunQuotesPanel(Static):
16
+ """Displays a random motivational/funny programming quote."""
17
+
18
+ column = "left"
19
+
20
+ def on_mount(self):
21
+ self.set_interval(5, self.refresh_quote)
22
+
23
+ def refresh_quote(self):
24
+ quote = random.choice(QUOTES)
25
+ self.update(Panel(Text(quote, style="bold magenta"), title="💬 Fun Quote", border_style="magenta"))
26
+
27
+ def register(tui_app):
28
+ panel = FunQuotesPanel()
29
+ if not hasattr(tui_app, "tui_panels"):
30
+ tui_app.tui_panels = []
31
+ tui_app.tui_panels.append(panel)
@@ -0,0 +1,42 @@
1
+ # devcli/plugins/git_status.py
2
+ from textual.widgets import Static
3
+ import subprocess
4
+ import os
5
+ from rich.table import Table
6
+
7
+ class GitStatusPanel(Static):
8
+ """Textual panel showing the current git status of a repo."""
9
+
10
+ def __init__(self, repo_path=None):
11
+ super().__init__()
12
+ self.repo_path = repo_path or os.getcwd()
13
+
14
+ def on_mount(self):
15
+ self.set_interval(3, self.refresh_status)
16
+
17
+ def refresh_status(self):
18
+ table = Table(title=f"🐙 Git Status ({os.path.basename(self.repo_path)})")
19
+ table.add_column("Path", no_wrap=True)
20
+ table.add_column("Status")
21
+ try:
22
+ result = subprocess.run(
23
+ ["git", "-C", self.repo_path, "status", "--short"],
24
+ capture_output=True,
25
+ text=True
26
+ )
27
+ changes = result.stdout.strip().splitlines() or ["No changes"]
28
+ for change in changes:
29
+ if change == "No changes":
30
+ table.add_row("—", change)
31
+ else:
32
+ table.add_row(change[:2], change[3:])
33
+ except Exception:
34
+ table.add_row("—", "Not a git repo")
35
+ self.update(table)
36
+
37
+
38
+ def register(tui_app):
39
+ panel = GitStatusPanel()
40
+ if not hasattr(tui_app, "tui_panels"):
41
+ tui_app.tui_panels = []
42
+ tui_app.tui_panels.append(panel)
@@ -0,0 +1,32 @@
1
+ # devcli/plugins/ping_monitor.py
2
+ from textual.widgets import Static
3
+ import subprocess
4
+ from rich.panel import Panel
5
+
6
+ class PingMonitorPanel(Static):
7
+ """Continuously pings google.com and shows average latency."""
8
+
9
+ def on_mount(self):
10
+ self.set_interval(5, self.refresh_ping)
11
+
12
+ def refresh_ping(self):
13
+ try:
14
+ result = subprocess.run(
15
+ ["ping", "-c", "1", "google.com"],
16
+ capture_output=True,
17
+ text=True
18
+ )
19
+ lines = result.stdout.splitlines()
20
+ if lines:
21
+ latency_line = lines[-1]
22
+ self.update(Panel(latency_line, title="📡 Ping Monitor", border_style="bright_blue"))
23
+ else:
24
+ self.update(Panel("No response", title="📡 Ping Monitor", border_style="red"))
25
+ except Exception:
26
+ self.update(Panel("Error", title="📡 Ping Monitor", border_style="red"))
27
+
28
+ def register(tui_app):
29
+ panel = PingMonitorPanel()
30
+ if not hasattr(tui_app, "tui_panels"):
31
+ tui_app.tui_panels = []
32
+ tui_app.tui_panels.append(panel)
@@ -0,0 +1,59 @@
1
+ # devcli/plugins/task_runner.py
2
+ from textual.widgets import Static
3
+ import asyncio
4
+ from rich.table import Table
5
+ from rich.text import Text
6
+
7
+ # Shared task state (import ACTIVE_TASKS from core)
8
+ from devcli.core.tui import ACTIVE_TASKS
9
+
10
+
11
+ class TaskRunnerPanel(Static):
12
+ """Displays active task runner with simulated background tasks."""
13
+
14
+ def on_mount(self):
15
+ self.task_count = 0
16
+ self.set_interval(0.5, self.check_tasks)
17
+ # Start a demo task on mount
18
+ self.start_demo_task()
19
+
20
+ def start_demo_task(self):
21
+ """Start a simulated background task."""
22
+ self.task_count += 1
23
+ task_id = self.task_count
24
+ ACTIVE_TASKS.append({"name": f"Task {task_id}", "progress": 0, "id": task_id})
25
+
26
+ def check_tasks(self):
27
+ """Update progress and remove completed tasks."""
28
+ table = Table(title="🔄 Task Runner")
29
+ table.add_column("Task", style="bold cyan")
30
+ table.add_column("Progress", justify="right")
31
+
32
+ if not ACTIVE_TASKS:
33
+ table.add_row("—", "Idle")
34
+ self.update(table)
35
+ return
36
+
37
+ # Update progress for each task
38
+ for task in ACTIVE_TASKS:
39
+ task["progress"] += 5
40
+ bar_length = 20
41
+ filled = int((task["progress"] / 100) * bar_length)
42
+ bar = "█" * filled + "░" * (bar_length - filled)
43
+ table.add_row(task["name"], f"{bar} {task['progress']}%")
44
+
45
+ # Remove completed tasks
46
+ ACTIVE_TASKS[:] = [t for t in ACTIVE_TASKS if t["progress"] < 100]
47
+
48
+ self.update(table)
49
+
50
+ def on_click(self):
51
+ """Allow users to start a new task by clicking."""
52
+ self.start_demo_task()
53
+
54
+
55
+ def register(tui_app):
56
+ panel = TaskRunnerPanel()
57
+ if not hasattr(tui_app, "tui_panels"):
58
+ tui_app.tui_panels = []
59
+ tui_app.tui_panels.append(panel)
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: devcli-dashboard
3
+ Version: 0.1.0
4
+ Summary: A plugin-driven developer CLI with a live Textual dashboard.
5
+ License: MIT
6
+ Keywords: cli,dashboard,plugins,textual,devops,monitoring
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: User Interfaces
15
+ Classifier: Topic :: System :: Monitoring
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: typer>=0.10
20
+ Requires-Dist: rich>=13.0
21
+ Requires-Dist: psutil>=5.9
22
+ Requires-Dist: python-dotenv>=1.0
23
+ Requires-Dist: textual>=0.38
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: build; extra == "dev"
27
+ Requires-Dist: twine; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # DevCLI
31
+
32
+ DevCLI is a plugin-driven command line toolkit with a live Textual dashboard for developers and sysadmins.
33
+
34
+ It combines system metrics, task status, and dynamic plugins into a single terminal UI, making it easy to extend with custom monitoring, git status, and tooling panels.
35
+
36
+ ## Features
37
+
38
+ - Plugin-based dashboard architecture
39
+ - Live updates for system stats, git status, ping, and quotes
40
+ - Textual-powered terminal UI with left/right layout
41
+ - Easy plugin registration via `register(tui_app)` hooks
42
+ - CLI entrypoint via `devcli`
43
+
44
+ ## Installation
45
+
46
+ Install from PyPI once published:
47
+
48
+ ```bash
49
+ pip install devcli
50
+ ```
51
+
52
+ For local development:
53
+
54
+ ```bash
55
+ python3 -m pip install -e .
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ Run the dashboard from the repository or installed package:
61
+
62
+ ```bash
63
+ python3 -m devcli.main start
64
+ ```
65
+
66
+ or if the package is installed:
67
+
68
+ ```bash
69
+ devcli start
70
+ ```
71
+
72
+ ## Plugin System
73
+
74
+ The dashboard is built around plugins located in `devcli/plugins/`.
75
+
76
+ Each plugin should expose a `register(tui_app)` function that appends a widget to `tui_app.tui_panels`.
77
+
78
+ Example plugin characteristics:
79
+
80
+ - `cpu_temp.py` — CPU temperature and frequency
81
+ - `git_status.py` — current git repo status
82
+ - `ping_monitor.py` — ping latency reporting
83
+ - `fun_quotes.py` — rotating motivational quotes
84
+
85
+ ### Creating a plugin
86
+
87
+ 1. Add a new file under `devcli/plugins/`.
88
+ 2. Define a `Static`-based widget.
89
+ 3. Implement `register(tui_app)` and append the widget to `tui_app.tui_panels`.
90
+
91
+ ## Packaging for PyPI
92
+
93
+ This project is configured with `pyproject.toml` and uses `setuptools` for packaging.
94
+
95
+ ### Build and publish
96
+
97
+ ```bash
98
+ python3 -m pip install build twine
99
+ python3 -m build
100
+ python3 -m twine upload dist/*
101
+ ```
102
+
103
+ ## Development dependencies
104
+
105
+ Install development tooling:
106
+
107
+ ```bash
108
+ python3 -m pip install -e .[dev]
109
+ ```
110
+
111
+ ## Notes
112
+
113
+ - Requires Python 3.11+
114
+ - Depends on `textual` for the TUI
115
+ - `python-dotenv` is included for environment configuration support
116
+
117
+ ## Contribution
118
+
119
+ Contributions are welcome. Add new plugins, improve dashboard styling, or extend the CLI commands.
@@ -0,0 +1,28 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ devcli/__init__.py
5
+ devcli/main.py
6
+ devcli/commands/dashboard.py
7
+ devcli/commands/plugin.py
8
+ devcli/commands/remote.py
9
+ devcli/commands/run.py
10
+ devcli/commands/tui.py
11
+ devcli/core/config.py
12
+ devcli/core/logger.py
13
+ devcli/core/plugin_loader.py
14
+ devcli/core/plugin_market.py
15
+ devcli/core/ssh.py
16
+ devcli/core/state.py
17
+ devcli/core/tui.py
18
+ devcli/plugins/cpu_temp.py
19
+ devcli/plugins/fun_quotes.py
20
+ devcli/plugins/git_status.py
21
+ devcli/plugins/ping_monitor.py
22
+ devcli/plugins/task_runner.py
23
+ devcli_dashboard.egg-info/PKG-INFO
24
+ devcli_dashboard.egg-info/SOURCES.txt
25
+ devcli_dashboard.egg-info/dependency_links.txt
26
+ devcli_dashboard.egg-info/entry_points.txt
27
+ devcli_dashboard.egg-info/requires.txt
28
+ devcli_dashboard.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ devcli = devcli.main:app
@@ -0,0 +1,10 @@
1
+ typer>=0.10
2
+ rich>=13.0
3
+ psutil>=5.9
4
+ python-dotenv>=1.0
5
+ textual>=0.38
6
+
7
+ [dev]
8
+ pytest>=8.0
9
+ build
10
+ twine
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=65.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "devcli-dashboard"
7
+ version = "0.1.0"
8
+ description = "A plugin-driven developer CLI with a live Textual dashboard."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ keywords = ["cli", "dashboard", "plugins", "textual", "devops", "monitoring"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Software Development :: User Interfaces",
22
+ "Topic :: System :: Monitoring",
23
+ ]
24
+ dependencies = [
25
+ "typer>=0.10",
26
+ "rich>=13.0",
27
+ "psutil>=5.9",
28
+ "python-dotenv>=1.0",
29
+ "textual>=0.38"
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = ["pytest>=8.0", "build", "twine"]
34
+
35
+ [project.scripts]
36
+ devcli = "devcli.main:app"
37
+
38
+ [tool.setuptools]
39
+ license-files = ["LICENSE"]
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["."]
43
+ include = ["devcli*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+