yncli 1.0.8__tar.gz → 1.0.9__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.8 → yncli-1.0.9}/PKG-INFO +1 -1
- {yncli-1.0.8 → yncli-1.0.9}/pyproject.toml +1 -1
- {yncli-1.0.8 → yncli-1.0.9}/setup.py +1 -1
- {yncli-1.0.8 → yncli-1.0.9}/yncli/main.py +11 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tui.py +5 -0
- yncli-1.0.9/yncli/update_checker.py +120 -0
- yncli-1.0.9/yncli/version.py +1 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/PKG-INFO +1 -1
- yncli-1.0.8/yncli/update_checker.py +0 -86
- yncli-1.0.8/yncli/version.py +0 -1
- {yncli-1.0.8 → yncli-1.0.9}/README.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/setup.cfg +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/__init__.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/agent.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/architect.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/code_review.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/debug_oracle.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/debugger.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/polyglot.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/refactor_master.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/security_auditor.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/system_architect.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/test_runner.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/ultrabrain.md +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/clean_text.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/client.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/config.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/language_detector.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/skills_manager.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/system_prompt.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/__init__.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/file_tools.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/polyglot_tools.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/search_tools.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/system_tools.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/workspace_memory.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli/workspace_scanner.py +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/SOURCES.txt +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/dependency_links.txt +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/entry_points.txt +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/requires.txt +0 -0
- {yncli-1.0.8 → yncli-1.0.9}/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.9",
|
|
10
10
|
description="Autonomous Polyglot AI Coding Agent & TUI",
|
|
11
11
|
long_description=long_description,
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
@@ -74,8 +74,19 @@ def main():
|
|
|
74
74
|
action="store_true",
|
|
75
75
|
help="List available models and exit"
|
|
76
76
|
)
|
|
77
|
+
parser.add_argument(
|
|
78
|
+
"--update", "-u",
|
|
79
|
+
action="store_true",
|
|
80
|
+
help="Check and automatically update yncli to the latest version"
|
|
81
|
+
)
|
|
77
82
|
|
|
78
83
|
args = parser.parse_args()
|
|
84
|
+
|
|
85
|
+
if args.update:
|
|
86
|
+
from yncli.update_checker import perform_self_update
|
|
87
|
+
perform_self_update()
|
|
88
|
+
sys.exit(0)
|
|
89
|
+
|
|
79
90
|
config = load_config()
|
|
80
91
|
|
|
81
92
|
base_url = config.get("base_url")
|
|
@@ -353,6 +353,11 @@ class TerminalUI:
|
|
|
353
353
|
self.console.print(table)
|
|
354
354
|
return True
|
|
355
355
|
|
|
356
|
+
elif cmd in ("/update", "/upgrade"):
|
|
357
|
+
from yncli.update_checker import perform_self_update
|
|
358
|
+
perform_self_update()
|
|
359
|
+
return True
|
|
360
|
+
|
|
356
361
|
elif cmd == "/save":
|
|
357
362
|
fname = args[0] if args else f"chat_export_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
|
|
358
363
|
lines = [f"# yncli export - {datetime.datetime.now().isoformat()}\n\n"]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
import sys
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional, Dict, Any
|
|
7
|
+
|
|
8
|
+
from yncli.version import __version__
|
|
9
|
+
from yncli.config import CONFIG_DIR
|
|
10
|
+
|
|
11
|
+
CACHE_FILE = CONFIG_DIR / "update_cache.json"
|
|
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_live(timeout_sec: float = 1.2) -> Optional[str]:
|
|
24
|
+
"""Queries PyPI directly to get the absolute latest published version."""
|
|
25
|
+
try:
|
|
26
|
+
import requests
|
|
27
|
+
resp = requests.get("https://pypi.org/pypi/yncli/json", timeout=timeout_sec)
|
|
28
|
+
if resp.status_code == 200:
|
|
29
|
+
data = resp.json()
|
|
30
|
+
latest = data.get("info", {}).get("version")
|
|
31
|
+
if latest:
|
|
32
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
CACHE_FILE.write_text(json.dumps({
|
|
34
|
+
"last_checked": time.time(),
|
|
35
|
+
"latest_version": latest
|
|
36
|
+
}, indent=2), encoding="utf-8")
|
|
37
|
+
return latest
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_cached_update() -> Optional[Dict[str, Any]]:
|
|
44
|
+
"""Returns update info from cache if available."""
|
|
45
|
+
try:
|
|
46
|
+
if CACHE_FILE.exists():
|
|
47
|
+
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
48
|
+
latest_str = data.get("latest_version")
|
|
49
|
+
if latest_str:
|
|
50
|
+
curr_tuple = parse_version_tuple(__version__)
|
|
51
|
+
latest_tuple = parse_version_tuple(latest_str)
|
|
52
|
+
if latest_tuple > curr_tuple:
|
|
53
|
+
return {
|
|
54
|
+
"current": __version__,
|
|
55
|
+
"latest": latest_str
|
|
56
|
+
}
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def check_for_updates_fast(timeout_sec: float = 1.2) -> Optional[Dict[str, Any]]:
|
|
63
|
+
"""
|
|
64
|
+
Checks for updates immediately on every startup with a fast 1.2s timeout.
|
|
65
|
+
Falls back to cached update if offline or timeout.
|
|
66
|
+
"""
|
|
67
|
+
# 1. Try fast live fetch
|
|
68
|
+
latest_live = fetch_latest_version_live(timeout_sec=timeout_sec)
|
|
69
|
+
if latest_live:
|
|
70
|
+
curr_tuple = parse_version_tuple(__version__)
|
|
71
|
+
latest_tuple = parse_version_tuple(latest_live)
|
|
72
|
+
if latest_tuple > curr_tuple:
|
|
73
|
+
return {
|
|
74
|
+
"current": __version__,
|
|
75
|
+
"latest": latest_live
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# 2. Fallback to cache if network is slow/offline
|
|
79
|
+
return get_cached_update()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def perform_self_update() -> bool:
|
|
83
|
+
"""
|
|
84
|
+
Direct self-updater: Runs pip install --upgrade --no-cache-dir yncli
|
|
85
|
+
and optionally npm install -g @yanzyuyu/yncli
|
|
86
|
+
"""
|
|
87
|
+
print(f"\n[YNCLI] Memeriksa versi terbaru dari PyPI...")
|
|
88
|
+
latest = fetch_latest_version_live(timeout_sec=5.0)
|
|
89
|
+
curr_tuple = parse_version_tuple(__version__)
|
|
90
|
+
latest_tuple = parse_version_tuple(latest) if latest else curr_tuple
|
|
91
|
+
|
|
92
|
+
print(f"[YNCLI] Versi saat ini: v{__version__}")
|
|
93
|
+
if latest:
|
|
94
|
+
print(f"[YNCLI] Versi terbaru: v{latest}")
|
|
95
|
+
|
|
96
|
+
if latest and latest_tuple <= curr_tuple:
|
|
97
|
+
print("\n[OK] YNCLI Anda sudah menggunakan versi paling baru!")
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
print("\n[YNCLI] Mengunduh dan menginstal pembaruan...")
|
|
101
|
+
|
|
102
|
+
# 1. Update Python module via pip
|
|
103
|
+
pip_cmd = [
|
|
104
|
+
sys.executable, "-m", "pip", "install",
|
|
105
|
+
"--upgrade", "--no-cache-dir",
|
|
106
|
+
"--disable-pip-version-check",
|
|
107
|
+
f"yncli=={latest}" if latest else "yncli"
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
res = subprocess.run(pip_cmd)
|
|
111
|
+
if res.returncode == 0:
|
|
112
|
+
print("\n[SUCCESS] YNCLI berhasil diperbarui ke versi terbaru!")
|
|
113
|
+
print("Silakan jalankan 'yncli' kembali untuk menikmati fitur baru.\n")
|
|
114
|
+
# Clear update cache
|
|
115
|
+
CACHE_FILE.unlink(missing_ok=True)
|
|
116
|
+
return True
|
|
117
|
+
else:
|
|
118
|
+
print("\n[ERROR] Gagal memperbarui via pip. Coba jalankan manual:")
|
|
119
|
+
print(f"pip install --upgrade --no-cache-dir yncli\n")
|
|
120
|
+
return False
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.9"
|
|
@@ -1,86 +0,0 @@
|
|
|
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 = 2 * 3600 # 2 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(timeout_sec: float = 1.0) -> Optional[str]:
|
|
24
|
-
try:
|
|
25
|
-
import requests
|
|
26
|
-
resp = requests.get("https://pypi.org/pypi/yncli/json", timeout=timeout_sec)
|
|
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 check_for_updates_fast(timeout_sec: float = 1.0) -> Optional[Dict[str, Any]]:
|
|
55
|
-
"""Checks cache first, and if not cached or expired, queries PyPI quickly."""
|
|
56
|
-
cached = get_cached_update()
|
|
57
|
-
if cached:
|
|
58
|
-
return cached
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
now = time.time()
|
|
62
|
-
last_checked = 0
|
|
63
|
-
if CACHE_FILE.exists():
|
|
64
|
-
try:
|
|
65
|
-
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
66
|
-
last_checked = data.get("last_checked", 0)
|
|
67
|
-
except Exception:
|
|
68
|
-
pass
|
|
69
|
-
|
|
70
|
-
if (now - last_checked) > CHECK_INTERVAL_SECONDS:
|
|
71
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
72
|
-
latest = _fetch_latest_version_pypi(timeout_sec=timeout_sec)
|
|
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
|
-
curr_tuple = parse_version_tuple(__version__)
|
|
80
|
-
latest_tuple = parse_version_tuple(latest)
|
|
81
|
-
if latest_tuple > curr_tuple:
|
|
82
|
-
return {"current": __version__, "latest": latest}
|
|
83
|
-
except Exception:
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
return None
|
yncli-1.0.8/yncli/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.0.8"
|
|
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
|