agent-wiki-cli 0.3.28__py3-none-any.whl
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.
- agent_wiki_cli-0.3.28.dist-info/METADATA +425 -0
- agent_wiki_cli-0.3.28.dist-info/RECORD +47 -0
- agent_wiki_cli-0.3.28.dist-info/WHEEL +5 -0
- agent_wiki_cli-0.3.28.dist-info/entry_points.txt +2 -0
- agent_wiki_cli-0.3.28.dist-info/licenses/LICENSE +21 -0
- agent_wiki_cli-0.3.28.dist-info/top_level.txt +1 -0
- llm_wiki_cli/__init__.py +7 -0
- llm_wiki_cli/cli.py +231 -0
- llm_wiki_cli/commands/__init__.py +1 -0
- llm_wiki_cli/commands/bootstrap_cmd.py +1072 -0
- llm_wiki_cli/commands/bump_cmd.py +55 -0
- llm_wiki_cli/commands/context_cmd.py +427 -0
- llm_wiki_cli/commands/extract_cmd.py +745 -0
- llm_wiki_cli/commands/generate_prompt_cmd.py +89 -0
- llm_wiki_cli/commands/hook_cmd.py +161 -0
- llm_wiki_cli/commands/init_cmd.py +92 -0
- llm_wiki_cli/commands/lint_cmd.py +294 -0
- llm_wiki_cli/commands/migrate_cmd.py +892 -0
- llm_wiki_cli/commands/release_cmd.py +163 -0
- llm_wiki_cli/commands/status_cmd.py +70 -0
- llm_wiki_cli/commands/sync_cmd.py +521 -0
- llm_wiki_cli/commands/trigger_cmd.py +205 -0
- llm_wiki_cli/commands/uninstall_cmd.py +221 -0
- llm_wiki_cli/commands/upgrade_cmd.py +196 -0
- llm_wiki_cli/config.py +318 -0
- llm_wiki_cli/extractors/__init__.py +46 -0
- llm_wiki_cli/extractors/common.py +90 -0
- llm_wiki_cli/extractors/go_extractor.py +143 -0
- llm_wiki_cli/extractors/go_scripts/go.mod +3 -0
- llm_wiki_cli/extractors/go_scripts/main.go +668 -0
- llm_wiki_cli/extractors/python_extractor.py +346 -0
- llm_wiki_cli/extractors/rust_extractor.py +143 -0
- llm_wiki_cli/extractors/rust_scripts/Cargo.lock +110 -0
- llm_wiki_cli/extractors/rust_scripts/Cargo.toml +11 -0
- llm_wiki_cli/extractors/rust_scripts/src/main.rs +803 -0
- llm_wiki_cli/extractors/ts_extractor.py +206 -0
- llm_wiki_cli/extractors/ts_scripts/extract.js +485 -0
- llm_wiki_cli/extractors/ts_scripts/package.json +10 -0
- llm_wiki_cli/services/__init__.py +0 -0
- llm_wiki_cli/services/circuit_breaker.py +79 -0
- llm_wiki_cli/services/io.py +47 -0
- llm_wiki_cli/services/lockfile.py +60 -0
- llm_wiki_cli/services/packages.py +173 -0
- llm_wiki_cli/services/paths.py +31 -0
- llm_wiki_cli/services/schema.py +214 -0
- llm_wiki_cli/services/secure_file.py +22 -0
- llm_wiki_cli/services/versioning.py +193 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
try: # Python 3.11+
|
|
7
|
+
import tomllib
|
|
8
|
+
except ModuleNotFoundError: # pragma: no cover - exercised on Python 3.9/3.10
|
|
9
|
+
try:
|
|
10
|
+
import tomli as tomllib
|
|
11
|
+
except ModuleNotFoundError: # pragma: no cover - dependency missing in ad-hoc envs
|
|
12
|
+
tomllib = None
|
|
13
|
+
|
|
14
|
+
# Supported version file patterns in priority order
|
|
15
|
+
VERSION_PATTERNS = [
|
|
16
|
+
("pyproject.toml", re.compile(r"")),
|
|
17
|
+
("setup.cfg", re.compile(r'^(version\s*=\s*)(\d+\.\d+\.\d+)', re.MULTILINE)),
|
|
18
|
+
("package.json", re.compile(r'("version"\s*:\s*")(\d+\.\d+\.\d+)(")', re.MULTILINE)),
|
|
19
|
+
("VERSION", re.compile(r'^(\d+\.\d+\.\d+)$', re.MULTILINE)),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
VERSION_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)$')
|
|
23
|
+
_TABLE_RE = re.compile(r"(?m)^\s*\[([^\]]+)\]\s*(?:#.*)?$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def find_version_file(root: str = ".") -> Path | None:
|
|
27
|
+
"""Auto-detect the version file in a project root."""
|
|
28
|
+
root_path = Path(root)
|
|
29
|
+
for filename, _ in VERSION_PATTERNS:
|
|
30
|
+
candidate = root_path / filename
|
|
31
|
+
if candidate.exists():
|
|
32
|
+
return candidate
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def read_version(path: Path) -> str | None:
|
|
37
|
+
"""Parse X.Y.Z version from a detected file."""
|
|
38
|
+
content = path.read_text(encoding="utf-8")
|
|
39
|
+
if path.name == "pyproject.toml":
|
|
40
|
+
return _read_pyproject_version(content)
|
|
41
|
+
for filename, pattern in VERSION_PATTERNS:
|
|
42
|
+
if path.name == filename:
|
|
43
|
+
match = pattern.search(content)
|
|
44
|
+
if match:
|
|
45
|
+
# VERSION file has no prefix/suffix groups
|
|
46
|
+
groups = match.groups()
|
|
47
|
+
for g in groups:
|
|
48
|
+
if VERSION_RE.match(g):
|
|
49
|
+
return g
|
|
50
|
+
break
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def write_version(path: Path, new_version: str) -> None:
|
|
55
|
+
"""Update the version string in-place, preserving file format."""
|
|
56
|
+
content = path.read_text(encoding="utf-8")
|
|
57
|
+
if path.name == "pyproject.toml":
|
|
58
|
+
updated = _write_pyproject_version(content, new_version, path)
|
|
59
|
+
path.write_bytes(updated.encode("utf-8"))
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
original = content
|
|
63
|
+
for filename, pattern in VERSION_PATTERNS:
|
|
64
|
+
if path.name == filename:
|
|
65
|
+
if filename == "VERSION":
|
|
66
|
+
content = pattern.sub(new_version, content)
|
|
67
|
+
else:
|
|
68
|
+
def _replacer(m):
|
|
69
|
+
groups = list(m.groups())
|
|
70
|
+
for i, g in enumerate(groups):
|
|
71
|
+
if VERSION_RE.match(g):
|
|
72
|
+
groups[i] = new_version
|
|
73
|
+
break
|
|
74
|
+
return "".join(groups)
|
|
75
|
+
content = pattern.sub(_replacer, content, count=1)
|
|
76
|
+
break
|
|
77
|
+
if content == original:
|
|
78
|
+
raise ValueError(f"Version pattern not found in {path}")
|
|
79
|
+
path.write_bytes(content.encode("utf-8"))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _read_pyproject_version(text: str) -> str | None:
|
|
83
|
+
if tomllib is not None:
|
|
84
|
+
try:
|
|
85
|
+
data = tomllib.loads(text)
|
|
86
|
+
except Exception:
|
|
87
|
+
data = {}
|
|
88
|
+
project = data.get("project", {}) if isinstance(data, dict) else {}
|
|
89
|
+
if isinstance(project, dict):
|
|
90
|
+
if "version" in project.get("dynamic", []):
|
|
91
|
+
return None
|
|
92
|
+
version = project.get("version")
|
|
93
|
+
if isinstance(version, str) and VERSION_RE.match(version):
|
|
94
|
+
return version
|
|
95
|
+
poetry = data.get("tool", {}).get("poetry", {}) if isinstance(data, dict) else {}
|
|
96
|
+
if isinstance(poetry, dict):
|
|
97
|
+
version = poetry.get("version")
|
|
98
|
+
if isinstance(version, str) and VERSION_RE.match(version):
|
|
99
|
+
return version
|
|
100
|
+
|
|
101
|
+
project = _table_body(text, "project")
|
|
102
|
+
if project is not None:
|
|
103
|
+
if _project_version_is_dynamic(project):
|
|
104
|
+
return None
|
|
105
|
+
version = _static_version_from_body(project)
|
|
106
|
+
if version:
|
|
107
|
+
return version
|
|
108
|
+
|
|
109
|
+
poetry = _table_body(text, "tool.poetry")
|
|
110
|
+
if poetry is not None:
|
|
111
|
+
return _static_version_from_body(poetry)
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _write_pyproject_version(text: str, new_version: str, path: Path) -> str:
|
|
116
|
+
project_bounds = _table_bounds(text, "project")
|
|
117
|
+
if project_bounds is not None:
|
|
118
|
+
start, end = project_bounds
|
|
119
|
+
body = text[start:end]
|
|
120
|
+
if _project_version_is_dynamic(body):
|
|
121
|
+
raise ValueError(f"{path}: [project].version is dynamic; cannot bump a static version")
|
|
122
|
+
updated_body, changed = _replace_static_version_line(body, new_version)
|
|
123
|
+
if changed:
|
|
124
|
+
return text[:start] + updated_body + text[end:]
|
|
125
|
+
|
|
126
|
+
poetry_bounds = _table_bounds(text, "tool.poetry")
|
|
127
|
+
if poetry_bounds is not None:
|
|
128
|
+
start, end = poetry_bounds
|
|
129
|
+
body = text[start:end]
|
|
130
|
+
updated_body, changed = _replace_static_version_line(body, new_version)
|
|
131
|
+
if changed:
|
|
132
|
+
return text[:start] + updated_body + text[end:]
|
|
133
|
+
|
|
134
|
+
raise ValueError(f"Version pattern not found in {path}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _table_bounds(text: str, table_name: str) -> tuple[int, int] | None:
|
|
138
|
+
for match in _TABLE_RE.finditer(text):
|
|
139
|
+
if match.group(1).strip() != table_name:
|
|
140
|
+
continue
|
|
141
|
+
start = match.end()
|
|
142
|
+
next_match = _TABLE_RE.search(text, start)
|
|
143
|
+
end = next_match.start() if next_match else len(text)
|
|
144
|
+
return start, end
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _table_body(text: str, table_name: str) -> str | None:
|
|
149
|
+
bounds = _table_bounds(text, table_name)
|
|
150
|
+
if bounds is None:
|
|
151
|
+
return None
|
|
152
|
+
start, end = bounds
|
|
153
|
+
return text[start:end]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _static_version_from_body(body: str) -> str | None:
|
|
157
|
+
match = re.search(r'(?m)^\s*version\s*=\s*"([^"]+)"', body)
|
|
158
|
+
if match and VERSION_RE.match(match.group(1)):
|
|
159
|
+
return match.group(1)
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _replace_static_version_line(body: str, new_version: str) -> tuple[str, bool]:
|
|
164
|
+
pattern = re.compile(r'(?m)^(\s*version\s*=\s*")([^"]+)(".*)$')
|
|
165
|
+
|
|
166
|
+
def repl(match: re.Match[str]) -> str:
|
|
167
|
+
return f"{match.group(1)}{new_version}{match.group(3)}"
|
|
168
|
+
|
|
169
|
+
updated, count = pattern.subn(repl, body, count=1)
|
|
170
|
+
return updated, count > 0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _project_version_is_dynamic(body: str) -> bool:
|
|
174
|
+
match = re.search(r'(?ms)^\s*dynamic\s*=\s*\[(.*?)\]', body)
|
|
175
|
+
return bool(match and re.search(r'["\']version["\']', match.group(1)))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def bump_patch(version: str) -> str:
|
|
179
|
+
"""0.1.5 -> 0.1.6"""
|
|
180
|
+
m = VERSION_RE.match(version)
|
|
181
|
+
if not m:
|
|
182
|
+
raise ValueError(f"Invalid version format: {version}")
|
|
183
|
+
major, minor, patch = int(m.group(1)), int(m.group(2)), int(m.group(3))
|
|
184
|
+
return f"{major}.{minor}.{patch + 1}"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def bump_minor(version: str) -> str:
|
|
188
|
+
"""0.1.6 -> 0.2.0"""
|
|
189
|
+
m = VERSION_RE.match(version)
|
|
190
|
+
if not m:
|
|
191
|
+
raise ValueError(f"Invalid version format: {version}")
|
|
192
|
+
major, minor, _ = int(m.group(1)), int(m.group(2)), int(m.group(3))
|
|
193
|
+
return f"{major}.{minor + 1}.0"
|