yncli 1.0.9__tar.gz → 1.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 (42) hide show
  1. {yncli-1.0.9 → yncli-1.1.0}/PKG-INFO +1 -1
  2. {yncli-1.0.9 → yncli-1.1.0}/pyproject.toml +1 -1
  3. {yncli-1.0.9 → yncli-1.1.0}/setup.py +1 -1
  4. yncli-1.1.0/yncli/mention_resolver.py +88 -0
  5. {yncli-1.0.9 → yncli-1.1.0}/yncli/tui.py +37 -1
  6. yncli-1.1.0/yncli/version.py +1 -0
  7. {yncli-1.0.9 → yncli-1.1.0}/yncli.egg-info/PKG-INFO +1 -1
  8. {yncli-1.0.9 → yncli-1.1.0}/yncli.egg-info/SOURCES.txt +1 -0
  9. yncli-1.0.9/yncli/version.py +0 -1
  10. {yncli-1.0.9 → yncli-1.1.0}/README.md +0 -0
  11. {yncli-1.0.9 → yncli-1.1.0}/setup.cfg +0 -0
  12. {yncli-1.0.9 → yncli-1.1.0}/yncli/__init__.py +0 -0
  13. {yncli-1.0.9 → yncli-1.1.0}/yncli/agent.py +0 -0
  14. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/architect.md +0 -0
  15. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/code_review.md +0 -0
  16. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/debug_oracle.md +0 -0
  17. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/debugger.md +0 -0
  18. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/polyglot.md +0 -0
  19. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/refactor_master.md +0 -0
  20. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/security_auditor.md +0 -0
  21. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/system_architect.md +0 -0
  22. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/test_runner.md +0 -0
  23. {yncli-1.0.9 → yncli-1.1.0}/yncli/built_in_skills/ultrabrain.md +0 -0
  24. {yncli-1.0.9 → yncli-1.1.0}/yncli/clean_text.py +0 -0
  25. {yncli-1.0.9 → yncli-1.1.0}/yncli/client.py +0 -0
  26. {yncli-1.0.9 → yncli-1.1.0}/yncli/config.py +0 -0
  27. {yncli-1.0.9 → yncli-1.1.0}/yncli/language_detector.py +0 -0
  28. {yncli-1.0.9 → yncli-1.1.0}/yncli/main.py +0 -0
  29. {yncli-1.0.9 → yncli-1.1.0}/yncli/skills_manager.py +0 -0
  30. {yncli-1.0.9 → yncli-1.1.0}/yncli/system_prompt.py +0 -0
  31. {yncli-1.0.9 → yncli-1.1.0}/yncli/tools/__init__.py +0 -0
  32. {yncli-1.0.9 → yncli-1.1.0}/yncli/tools/file_tools.py +0 -0
  33. {yncli-1.0.9 → yncli-1.1.0}/yncli/tools/polyglot_tools.py +0 -0
  34. {yncli-1.0.9 → yncli-1.1.0}/yncli/tools/search_tools.py +0 -0
  35. {yncli-1.0.9 → yncli-1.1.0}/yncli/tools/system_tools.py +0 -0
  36. {yncli-1.0.9 → yncli-1.1.0}/yncli/update_checker.py +0 -0
  37. {yncli-1.0.9 → yncli-1.1.0}/yncli/workspace_memory.py +0 -0
  38. {yncli-1.0.9 → yncli-1.1.0}/yncli/workspace_scanner.py +0 -0
  39. {yncli-1.0.9 → yncli-1.1.0}/yncli.egg-info/dependency_links.txt +0 -0
  40. {yncli-1.0.9 → yncli-1.1.0}/yncli.egg-info/entry_points.txt +0 -0
  41. {yncli-1.0.9 → yncli-1.1.0}/yncli.egg-info/requires.txt +0 -0
  42. {yncli-1.0.9 → yncli-1.1.0}/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.9
3
+ Version: 1.1.0
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.9"
7
+ version = "1.1.0"
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.9",
9
+ version="1.1.0",
10
10
  description="Autonomous Polyglot AI Coding Agent & TUI",
11
11
  long_description=long_description,
12
12
  long_description_content_type="text/markdown",
@@ -0,0 +1,88 @@
1
+ import re
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Tuple, List, Dict
5
+
6
+
7
+ def resolve_file_mentions(prompt: str, workspace_dir: str = ".") -> Tuple[str, List[Dict[str, str]]]:
8
+ """
9
+ Scans the prompt for @file or @folder mentions (e.g. @app/Http/Controllers/UserController.php or @index.html).
10
+ Loads the contents of referenced files and appends them as explicit contextual attachments.
11
+
12
+ Returns:
13
+ (cleaned_prompt, list_of_attached_files)
14
+ """
15
+ cwd = Path(workspace_dir).resolve()
16
+
17
+ # Matches patterns like @filename.ext or @"path with spaces/file.ext" or @path/to/file
18
+ mention_pattern = re.compile(r'@(?:"([^"]+)"|([a-zA-Z0-9_\-\.\/\\]+))')
19
+
20
+ matches = mention_pattern.findall(prompt)
21
+ if not matches:
22
+ return prompt, []
23
+
24
+ attached_files = []
25
+ seen_paths = set()
26
+
27
+ for m_quoted, m_unquoted in matches:
28
+ rel_path = (m_quoted or m_unquoted).strip()
29
+ if not rel_path:
30
+ continue
31
+
32
+ clean_path = rel_path.replace("\\", "/")
33
+ if clean_path in seen_paths:
34
+ continue
35
+ seen_paths.add(clean_path)
36
+
37
+ target_file = cwd / rel_path
38
+ if target_file.exists() and target_file.is_file():
39
+ try:
40
+ # Read content up to 100KB
41
+ size = target_file.stat().st_size
42
+ if size < 150000:
43
+ with open(target_file, "r", encoding="utf-8", errors="replace") as fp:
44
+ content = fp.read()
45
+ attached_files.append({
46
+ "path": clean_path,
47
+ "content": content,
48
+ "lines": len(content.splitlines()),
49
+ "size_bytes": size
50
+ })
51
+ except Exception:
52
+ pass
53
+ elif not target_file.exists():
54
+ # Try searching in subdirectories
55
+ for root, _, files in os.walk(cwd):
56
+ for f in files:
57
+ if f.lower() == Path(rel_path).name.lower():
58
+ found = Path(root) / f
59
+ try:
60
+ rel_found = found.relative_to(cwd).as_posix()
61
+ if rel_found not in seen_paths and found.stat().st_size < 150000:
62
+ seen_paths.add(rel_found)
63
+ with open(found, "r", encoding="utf-8", errors="replace") as fp:
64
+ content = fp.read()
65
+ attached_files.append({
66
+ "path": rel_found,
67
+ "content": content,
68
+ "lines": len(content.splitlines()),
69
+ "size_bytes": found.stat().st_size
70
+ })
71
+ except Exception:
72
+ pass
73
+ break
74
+
75
+ if not attached_files:
76
+ return prompt, []
77
+
78
+ # Build enriched context block
79
+ enrichment_blocks = ["\n\n### [USER ATTACHED @FILES CONTEXT]:"]
80
+ for item in attached_files:
81
+ enrichment_blocks.append(
82
+ f"\n--- ATTACHED FILE: @{item['path']} ({item['lines']} lines) ---\n"
83
+ f"{item['content']}\n"
84
+ f"--- END OF @{item['path']} ---"
85
+ )
86
+
87
+ enriched_prompt = prompt + "\n" + "\n".join(enrichment_blocks)
88
+ return enriched_prompt, attached_files
@@ -50,6 +50,7 @@ class TerminalUI:
50
50
  from prompt_toolkit.history import FileHistory
51
51
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
52
52
  from prompt_toolkit.styles import Style
53
+ from prompt_toolkit.completion import Completer, Completion
53
54
 
54
55
  CONFIG_DIR.mkdir(parents=True, exist_ok=True)
55
56
  history_file = CONFIG_DIR / "history.txt"
@@ -57,11 +58,37 @@ class TerminalUI:
57
58
  prompt_style = Style.from_dict({
58
59
  "prompt": "#38bdf8",
59
60
  "arrow": "#a855f7",
61
+ "completion-menu.completion": "bg:#1e1e2e #cdd6f4",
62
+ "completion-menu.completion.current": "bg:#313244 #89b4fa bold",
63
+ "completion-menu.meta": "bg:#181825 #a6adc8",
60
64
  })
61
65
 
66
+ class MentionCompleter(Completer):
67
+ def __init__(self, workspace_memory):
68
+ self.workspace_memory = workspace_memory
69
+
70
+ def get_completions(self, document, complete_event):
71
+ text = document.text_before_cursor
72
+ # Check if user just typed '@' or is typing a path after '@'
73
+ if "@" in text:
74
+ last_at = text.rfind("@")
75
+ word = text[last_at + 1:]
76
+ # Offer completion for files in workspace tree
77
+ for fpath in self.workspace_memory.file_tree:
78
+ if fpath.lower().startswith(word.lower()) or word.lower() in fpath.lower():
79
+ yield Completion(
80
+ fpath,
81
+ start_position=-len(word),
82
+ display=f"@{fpath}",
83
+ display_meta="file"
84
+ )
85
+
86
+ completer = MentionCompleter(self.agent.workspace_memory)
87
+
62
88
  self.session = PromptSession(
63
89
  history=FileHistory(str(history_file)),
64
90
  auto_suggest=AutoSuggestFromHistory(),
91
+ completer=completer,
65
92
  style=prompt_style
66
93
  )
67
94
  except Exception:
@@ -459,6 +486,15 @@ class TerminalUI:
459
486
  is_thinking = False
460
487
  step_history: List[str] = []
461
488
 
489
+ from yncli.mention_resolver import resolve_file_mentions
490
+ resolved_prompt, attached_files = resolve_file_mentions(user_prompt, workspace_dir=self.agent.workspace_dir)
491
+
492
+ if attached_files:
493
+ pills = []
494
+ for af in attached_files:
495
+ pills.append(f"[bold cyan]@{af['path']}[/bold cyan] [dim]({af['lines']} baris)[/dim]")
496
+ self.console.print(f"[dim]📎 Menyertakan file:[/dim] {', '.join(pills)}")
497
+
462
498
  def _update_live_display(current_status: str) -> None:
463
499
  # Maintain max 3 recent steps
464
500
  display_lines = list(step_history[-2:]) + [f"[bold cyan]●[/bold cyan] {current_status}"]
@@ -525,7 +561,7 @@ class TerminalUI:
525
561
  _update_live_display(f"[bold #38bdf8][ - ][/bold #38bdf8] Menganalisis instruksi ({self.agent.model})...")
526
562
  try:
527
563
  res = self.agent.run_turn(
528
- user_prompt=user_prompt,
564
+ user_prompt=resolved_prompt,
529
565
  on_thinking=on_thinking,
530
566
  on_content=on_content,
531
567
  on_tool_start=on_tool_start,
@@ -0,0 +1 @@
1
+ __version__ = "1.1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yncli
3
- Version: 1.0.9
3
+ Version: 1.1.0
4
4
  Summary: Autonomous Polyglot AI Coding Agent & TUI
5
5
  Author: Yanzyuyu
6
6
  Requires-Python: >=3.8
@@ -8,6 +8,7 @@ yncli/client.py
8
8
  yncli/config.py
9
9
  yncli/language_detector.py
10
10
  yncli/main.py
11
+ yncli/mention_resolver.py
11
12
  yncli/skills_manager.py
12
13
  yncli/system_prompt.py
13
14
  yncli/tui.py
@@ -1 +0,0 @@
1
- __version__ = "1.0.9"
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