yncli 1.0.2__tar.gz → 1.0.4__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.
- {yncli-1.0.2 → yncli-1.0.4}/PKG-INFO +1 -1
- {yncli-1.0.2 → yncli-1.0.4}/pyproject.toml +1 -1
- {yncli-1.0.2 → yncli-1.0.4}/setup.py +1 -1
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tui.py +15 -1
- yncli-1.0.4/yncli/update_checker.py +83 -0
- yncli-1.0.4/yncli/version.py +1 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/PKG-INFO +1 -1
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/SOURCES.txt +2 -0
- {yncli-1.0.2 → yncli-1.0.4}/README.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/setup.cfg +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/__init__.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/agent.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/architect.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/code_review.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/debug_oracle.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/debugger.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/polyglot.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/refactor_master.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/security_auditor.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/system_architect.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/test_runner.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/built_in_skills/ultrabrain.md +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/clean_text.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/client.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/config.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/language_detector.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/main.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/skills_manager.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/system_prompt.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tools/__init__.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tools/file_tools.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tools/polyglot_tools.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tools/search_tools.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/tools/system_tools.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/workspace_memory.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli/workspace_scanner.py +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/dependency_links.txt +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/entry_points.txt +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/requires.txt +0 -0
- {yncli-1.0.2 → yncli-1.0.4}/yncli.egg-info/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8") if
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="yncli",
|
|
9
|
-
version="1.0.
|
|
9
|
+
version="1.0.4",
|
|
10
10
|
description="Autonomous Polyglot AI Coding Agent & TUI",
|
|
11
11
|
long_description=long_description,
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
@@ -16,6 +16,8 @@ from rich.columns import Columns
|
|
|
16
16
|
from yncli.agent import Agent
|
|
17
17
|
from yncli.config import save_config, CONFIG_DIR
|
|
18
18
|
from yncli.clean_text import clean_text_for_terminal
|
|
19
|
+
from yncli.version import __version__
|
|
20
|
+
from yncli.update_checker import get_cached_update, trigger_background_update_check
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class TerminalUI:
|
|
@@ -67,12 +69,24 @@ class TerminalUI:
|
|
|
67
69
|
return self.session
|
|
68
70
|
|
|
69
71
|
def print_banner(self) -> None:
|
|
72
|
+
trigger_background_update_check()
|
|
70
73
|
cwd = Path(self.agent.workspace_dir).resolve()
|
|
71
74
|
folder_name = cwd.name or str(cwd)
|
|
72
75
|
cwd_str = str(cwd).replace(str(Path.home()), "~")
|
|
73
76
|
mode_title = self.agent.mode.capitalize()
|
|
74
77
|
mode_color = "#38bdf8" if self.agent.mode == "plan" else ("#10b981" if self.agent.mode == "build" else "#f59e0b")
|
|
75
78
|
|
|
79
|
+
# Check for available update
|
|
80
|
+
update_info = get_cached_update()
|
|
81
|
+
if update_info:
|
|
82
|
+
update_panel = Panel(
|
|
83
|
+
f"[bold yellow]🔔 Update Tersedia:[/] [dim]v{update_info['current']}[/] → [bold green]v{update_info['latest']}[/]\n"
|
|
84
|
+
f"[dim]Perbarui sekarang:[reset] [cyan]pip install -U yncli[/cyan] [dim]atau[/dim] [cyan]npm i -g @yanzyuyu/yncli[/cyan]",
|
|
85
|
+
border_style="yellow",
|
|
86
|
+
padding=(0, 2)
|
|
87
|
+
)
|
|
88
|
+
self.console.print(update_panel)
|
|
89
|
+
|
|
76
90
|
# Top pill badge
|
|
77
91
|
self.console.print()
|
|
78
92
|
pill = Text()
|
|
@@ -93,7 +107,7 @@ class TerminalUI:
|
|
|
93
107
|
|
|
94
108
|
# Info metadata
|
|
95
109
|
info = Text()
|
|
96
|
-
info.append("yncli
|
|
110
|
+
info.append(f"yncli {__version__}\n", style="bold #60a5fa")
|
|
97
111
|
info.append("Autonomous Polyglot AI Coding Agent\n", style="dim white")
|
|
98
112
|
info.append(f"{self.agent.model} ", style="white")
|
|
99
113
|
info.append(f"({mode_title} Mode)\n", style=mode_color)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
|
+
|
|
7
|
+
from yncli.version import __version__
|
|
8
|
+
from yncli.config import CONFIG_DIR
|
|
9
|
+
|
|
10
|
+
CACHE_FILE = CONFIG_DIR / "update_cache.json"
|
|
11
|
+
CHECK_INTERVAL_SECONDS = 12 * 3600 # 12 hours
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_version_tuple(v: str):
|
|
15
|
+
"""Parse version string into tuple of ints for accurate semver comparison."""
|
|
16
|
+
try:
|
|
17
|
+
clean = v.strip().lstrip("v").split("-")[0]
|
|
18
|
+
return tuple(int(x) for x in clean.split(".") if x.isdigit())
|
|
19
|
+
except Exception:
|
|
20
|
+
return (0, 0, 0)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _fetch_latest_version_pypi() -> Optional[str]:
|
|
24
|
+
try:
|
|
25
|
+
import requests
|
|
26
|
+
resp = requests.get("https://pypi.org/pypi/yncli/json", timeout=2.0)
|
|
27
|
+
if resp.status_code == 200:
|
|
28
|
+
data = resp.json()
|
|
29
|
+
return data.get("info", {}).get("version")
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_cached_update() -> Optional[Dict[str, Any]]:
|
|
36
|
+
"""Returns cached update info if available and latest > current."""
|
|
37
|
+
try:
|
|
38
|
+
if CACHE_FILE.exists():
|
|
39
|
+
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
40
|
+
latest_str = data.get("latest_version")
|
|
41
|
+
if latest_str:
|
|
42
|
+
curr_tuple = parse_version_tuple(__version__)
|
|
43
|
+
latest_tuple = parse_version_tuple(latest_str)
|
|
44
|
+
if latest_tuple > curr_tuple:
|
|
45
|
+
return {
|
|
46
|
+
"current": __version__,
|
|
47
|
+
"latest": latest_str
|
|
48
|
+
}
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def trigger_background_update_check() -> None:
|
|
55
|
+
"""Spawns a detached background thread to check PyPI/NPM without blocking startup."""
|
|
56
|
+
def _worker():
|
|
57
|
+
try:
|
|
58
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
last_checked = 0
|
|
60
|
+
if CACHE_FILE.exists():
|
|
61
|
+
try:
|
|
62
|
+
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
63
|
+
last_checked = data.get("last_checked", 0)
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
# Only check network if 12h passed
|
|
68
|
+
now = time.time()
|
|
69
|
+
if (now - last_checked) < CHECK_INTERVAL_SECONDS:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
latest = _fetch_latest_version_pypi()
|
|
73
|
+
if latest:
|
|
74
|
+
cache_data = {
|
|
75
|
+
"last_checked": now,
|
|
76
|
+
"latest_version": latest
|
|
77
|
+
}
|
|
78
|
+
CACHE_FILE.write_text(json.dumps(cache_data, indent=2), encoding="utf-8")
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
t = threading.Thread(target=_worker, daemon=True)
|
|
83
|
+
t.start()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|