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.
Files changed (42) hide show
  1. {yncli-1.0.8 → yncli-1.0.9}/PKG-INFO +1 -1
  2. {yncli-1.0.8 → yncli-1.0.9}/pyproject.toml +1 -1
  3. {yncli-1.0.8 → yncli-1.0.9}/setup.py +1 -1
  4. {yncli-1.0.8 → yncli-1.0.9}/yncli/main.py +11 -0
  5. {yncli-1.0.8 → yncli-1.0.9}/yncli/tui.py +5 -0
  6. yncli-1.0.9/yncli/update_checker.py +120 -0
  7. yncli-1.0.9/yncli/version.py +1 -0
  8. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/PKG-INFO +1 -1
  9. yncli-1.0.8/yncli/update_checker.py +0 -86
  10. yncli-1.0.8/yncli/version.py +0 -1
  11. {yncli-1.0.8 → yncli-1.0.9}/README.md +0 -0
  12. {yncli-1.0.8 → yncli-1.0.9}/setup.cfg +0 -0
  13. {yncli-1.0.8 → yncli-1.0.9}/yncli/__init__.py +0 -0
  14. {yncli-1.0.8 → yncli-1.0.9}/yncli/agent.py +0 -0
  15. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/architect.md +0 -0
  16. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/code_review.md +0 -0
  17. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/debug_oracle.md +0 -0
  18. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/debugger.md +0 -0
  19. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/polyglot.md +0 -0
  20. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/refactor_master.md +0 -0
  21. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/security_auditor.md +0 -0
  22. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/system_architect.md +0 -0
  23. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/test_runner.md +0 -0
  24. {yncli-1.0.8 → yncli-1.0.9}/yncli/built_in_skills/ultrabrain.md +0 -0
  25. {yncli-1.0.8 → yncli-1.0.9}/yncli/clean_text.py +0 -0
  26. {yncli-1.0.8 → yncli-1.0.9}/yncli/client.py +0 -0
  27. {yncli-1.0.8 → yncli-1.0.9}/yncli/config.py +0 -0
  28. {yncli-1.0.8 → yncli-1.0.9}/yncli/language_detector.py +0 -0
  29. {yncli-1.0.8 → yncli-1.0.9}/yncli/skills_manager.py +0 -0
  30. {yncli-1.0.8 → yncli-1.0.9}/yncli/system_prompt.py +0 -0
  31. {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/__init__.py +0 -0
  32. {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/file_tools.py +0 -0
  33. {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/polyglot_tools.py +0 -0
  34. {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/search_tools.py +0 -0
  35. {yncli-1.0.8 → yncli-1.0.9}/yncli/tools/system_tools.py +0 -0
  36. {yncli-1.0.8 → yncli-1.0.9}/yncli/workspace_memory.py +0 -0
  37. {yncli-1.0.8 → yncli-1.0.9}/yncli/workspace_scanner.py +0 -0
  38. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/SOURCES.txt +0 -0
  39. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/dependency_links.txt +0 -0
  40. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/entry_points.txt +0 -0
  41. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/requires.txt +0 -0
  42. {yncli-1.0.8 → yncli-1.0.9}/yncli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yncli
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: Autonomous Polyglot AI Coding Agent & TUI
5
5
  Author: Yanzyuyu
6
6
  Requires-Python: >=3.8
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "yncli"
7
- version = "1.0.8"
7
+ version = "1.0.9"
8
8
  description = "Autonomous Polyglot AI Coding Agent & TUI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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.8",
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yncli
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: Autonomous Polyglot AI Coding Agent & TUI
5
5
  Author: Yanzyuyu
6
6
  Requires-Python: >=3.8
@@ -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
@@ -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