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.
Files changed (47) hide show
  1. agent_wiki_cli-0.3.28.dist-info/METADATA +425 -0
  2. agent_wiki_cli-0.3.28.dist-info/RECORD +47 -0
  3. agent_wiki_cli-0.3.28.dist-info/WHEEL +5 -0
  4. agent_wiki_cli-0.3.28.dist-info/entry_points.txt +2 -0
  5. agent_wiki_cli-0.3.28.dist-info/licenses/LICENSE +21 -0
  6. agent_wiki_cli-0.3.28.dist-info/top_level.txt +1 -0
  7. llm_wiki_cli/__init__.py +7 -0
  8. llm_wiki_cli/cli.py +231 -0
  9. llm_wiki_cli/commands/__init__.py +1 -0
  10. llm_wiki_cli/commands/bootstrap_cmd.py +1072 -0
  11. llm_wiki_cli/commands/bump_cmd.py +55 -0
  12. llm_wiki_cli/commands/context_cmd.py +427 -0
  13. llm_wiki_cli/commands/extract_cmd.py +745 -0
  14. llm_wiki_cli/commands/generate_prompt_cmd.py +89 -0
  15. llm_wiki_cli/commands/hook_cmd.py +161 -0
  16. llm_wiki_cli/commands/init_cmd.py +92 -0
  17. llm_wiki_cli/commands/lint_cmd.py +294 -0
  18. llm_wiki_cli/commands/migrate_cmd.py +892 -0
  19. llm_wiki_cli/commands/release_cmd.py +163 -0
  20. llm_wiki_cli/commands/status_cmd.py +70 -0
  21. llm_wiki_cli/commands/sync_cmd.py +521 -0
  22. llm_wiki_cli/commands/trigger_cmd.py +205 -0
  23. llm_wiki_cli/commands/uninstall_cmd.py +221 -0
  24. llm_wiki_cli/commands/upgrade_cmd.py +196 -0
  25. llm_wiki_cli/config.py +318 -0
  26. llm_wiki_cli/extractors/__init__.py +46 -0
  27. llm_wiki_cli/extractors/common.py +90 -0
  28. llm_wiki_cli/extractors/go_extractor.py +143 -0
  29. llm_wiki_cli/extractors/go_scripts/go.mod +3 -0
  30. llm_wiki_cli/extractors/go_scripts/main.go +668 -0
  31. llm_wiki_cli/extractors/python_extractor.py +346 -0
  32. llm_wiki_cli/extractors/rust_extractor.py +143 -0
  33. llm_wiki_cli/extractors/rust_scripts/Cargo.lock +110 -0
  34. llm_wiki_cli/extractors/rust_scripts/Cargo.toml +11 -0
  35. llm_wiki_cli/extractors/rust_scripts/src/main.rs +803 -0
  36. llm_wiki_cli/extractors/ts_extractor.py +206 -0
  37. llm_wiki_cli/extractors/ts_scripts/extract.js +485 -0
  38. llm_wiki_cli/extractors/ts_scripts/package.json +10 -0
  39. llm_wiki_cli/services/__init__.py +0 -0
  40. llm_wiki_cli/services/circuit_breaker.py +79 -0
  41. llm_wiki_cli/services/io.py +47 -0
  42. llm_wiki_cli/services/lockfile.py +60 -0
  43. llm_wiki_cli/services/packages.py +173 -0
  44. llm_wiki_cli/services/paths.py +31 -0
  45. llm_wiki_cli/services/schema.py +214 -0
  46. llm_wiki_cli/services/secure_file.py +22 -0
  47. llm_wiki_cli/services/versioning.py +193 -0
@@ -0,0 +1,346 @@
1
+ """Python AST extractor for agent-wiki-cli."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from ..config import build_gitignore_matcher
10
+ from .common import discover_source_files
11
+
12
+
13
+ # ── AST helper utilities ──────────────────────────────────────────────
14
+
15
+
16
+ def _annotation_to_str(node) -> str:
17
+ """Convert an AST annotation node to a readable string."""
18
+ if node is None:
19
+ return ""
20
+ if isinstance(node, ast.Constant):
21
+ return repr(node.value)
22
+ if isinstance(node, ast.Name):
23
+ return node.id
24
+ if isinstance(node, ast.Attribute):
25
+ return f"{_annotation_to_str(node.value)}.{node.attr}"
26
+ if isinstance(node, ast.Subscript):
27
+ return f"{_annotation_to_str(node.value)}[{_annotation_to_str(node.slice)}]"
28
+ if isinstance(node, ast.Tuple):
29
+ return ", ".join(_annotation_to_str(e) for e in node.elts)
30
+ if isinstance(node, ast.List):
31
+ return "[" + ", ".join(_annotation_to_str(e) for e in node.elts) + "]"
32
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
33
+ return f"{_annotation_to_str(node.left)} | {_annotation_to_str(node.right)}"
34
+ return ast.dump(node)
35
+
36
+
37
+ def _default_to_str(node) -> str:
38
+ """Convert a default-value AST node to a readable string."""
39
+ if node is None:
40
+ return ""
41
+ if isinstance(node, ast.Constant):
42
+ return repr(node.value)
43
+ if isinstance(node, ast.Name):
44
+ return node.id
45
+ if isinstance(node, ast.List):
46
+ return "[" + ", ".join(_default_to_str(e) for e in node.elts) + "]"
47
+ if isinstance(node, ast.Dict):
48
+ return "{...}"
49
+ if isinstance(node, ast.Call):
50
+ func = _annotation_to_str(node.func)
51
+ return f"{func}(...)"
52
+ return "..."
53
+
54
+
55
+ def _extract_decorators(node) -> list[str]:
56
+ """Extract decorator names from a node."""
57
+ decorators = []
58
+ for dec in node.decorator_list:
59
+ if isinstance(dec, ast.Name):
60
+ decorators.append(dec.id)
61
+ elif isinstance(dec, ast.Attribute):
62
+ decorators.append(_annotation_to_str(dec))
63
+ elif isinstance(dec, ast.Call):
64
+ func_str = _annotation_to_str(dec.func)
65
+ args_parts = []
66
+ for a in dec.args:
67
+ args_parts.append(_annotation_to_str(a))
68
+ for kw in dec.keywords:
69
+ args_parts.append(f"{kw.arg}={_annotation_to_str(kw.value)}")
70
+ decorators.append(f"{func_str}({', '.join(args_parts)})")
71
+ return decorators
72
+
73
+
74
+ def _extract_function_info(node) -> dict:
75
+ """Extract full function/method info from a FunctionDef or AsyncFunctionDef."""
76
+ info = {
77
+ "name": node.name,
78
+ "line": node.lineno,
79
+ "docstring": ast.get_docstring(node) or "",
80
+ "decorators": _extract_decorators(node),
81
+ "is_async": isinstance(node, ast.AsyncFunctionDef),
82
+ }
83
+
84
+ # Parameters (skip 'self'/'cls' for methods)
85
+ params = []
86
+ args_node = node.args
87
+
88
+ # Pair defaults with args (defaults align to the end of the args list)
89
+ num_args = len(args_node.args)
90
+ num_defaults = len(args_node.defaults)
91
+ default_offset = num_args - num_defaults
92
+
93
+ for i, arg in enumerate(args_node.args):
94
+ if arg.arg in ("self", "cls"):
95
+ continue
96
+ param = {
97
+ "name": arg.arg,
98
+ "type": _annotation_to_str(arg.annotation),
99
+ }
100
+ default_idx = i - default_offset
101
+ if default_idx >= 0:
102
+ param["default"] = _default_to_str(args_node.defaults[default_idx])
103
+ params.append(param)
104
+
105
+ info["params"] = params
106
+ info["return_type"] = _annotation_to_str(node.returns)
107
+
108
+ return info
109
+
110
+
111
+ def _extract_class_attributes(node) -> list[dict]:
112
+ """Extract annotated attributes from a class body (Pydantic fields, dataclass fields, etc.)."""
113
+ attrs = []
114
+ for child in node.body:
115
+ if isinstance(child, ast.AnnAssign) and isinstance(child.target, ast.Name):
116
+ attr = {
117
+ "name": child.target.id,
118
+ "type": _annotation_to_str(child.annotation),
119
+ "default": _default_to_str(child.value) if child.value else "",
120
+ }
121
+ attrs.append(attr)
122
+ return attrs
123
+
124
+
125
+ # ── AST visitor ───────────────────────────────────────────────────────
126
+
127
+
128
+ class ComponentVisitor(ast.NodeVisitor):
129
+ def __init__(self, deep: bool = False):
130
+ self.classes = []
131
+ self.functions = [] # top-level functions only
132
+ self.imports = []
133
+ self.constants = [] # UPPER_CASE module-level assignments
134
+ self.has_all = False # whether __all__ is defined
135
+ self._class_depth = 0
136
+ self._function_depth = 0
137
+ self._deep = deep
138
+
139
+ def visit_Import(self, node):
140
+ for alias in node.names:
141
+ self.imports.append({
142
+ "module": alias.name,
143
+ "name": alias.asname or alias.name,
144
+ "type": "import",
145
+ })
146
+ self.generic_visit(node)
147
+
148
+ def visit_ImportFrom(self, node):
149
+ module = "." * node.level + (node.module or "")
150
+ for alias in node.names:
151
+ self.imports.append({
152
+ "module": module,
153
+ "name": alias.name,
154
+ "alias": alias.asname,
155
+ "type": "from",
156
+ })
157
+ self.generic_visit(node)
158
+
159
+ def visit_ClassDef(self, node):
160
+ if self._class_depth > 0 or self._function_depth > 0:
161
+ return
162
+
163
+ bases = [_annotation_to_str(b) for b in node.bases]
164
+ docstring = ast.get_docstring(node) or ""
165
+ decorators = _extract_decorators(node)
166
+ attributes = _extract_class_attributes(node)
167
+
168
+ # Extract methods (including private for completeness)
169
+ methods = []
170
+ for child in node.body:
171
+ if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
172
+ methods.append(_extract_function_info(child))
173
+
174
+ self.classes.append({
175
+ "name": node.name,
176
+ "bases": bases,
177
+ "line": node.lineno,
178
+ "docstring": docstring,
179
+ "decorators": decorators,
180
+ "attributes": attributes,
181
+ "methods": methods,
182
+ })
183
+ # Don't generic_visit — we already walked class body for methods/attrs
184
+
185
+ def visit_FunctionDef(self, node):
186
+ # Only capture top-level functions (not methods inside classes)
187
+ if self._class_depth == 0 and self._function_depth == 0:
188
+ if not node.name.startswith("_"):
189
+ self.functions.append(_extract_function_info(node))
190
+ elif self._deep:
191
+ info = _extract_function_info(node)
192
+ info["private"] = True
193
+ self.functions.append(info)
194
+ self._function_depth += 1
195
+ try:
196
+ self.generic_visit(node)
197
+ finally:
198
+ self._function_depth -= 1
199
+
200
+ def visit_AsyncFunctionDef(self, node):
201
+ if self._class_depth == 0 and self._function_depth == 0:
202
+ if not node.name.startswith("_"):
203
+ self.functions.append(_extract_function_info(node))
204
+ elif self._deep:
205
+ info = _extract_function_info(node)
206
+ info["private"] = True
207
+ self.functions.append(info)
208
+ self._function_depth += 1
209
+ try:
210
+ self.generic_visit(node)
211
+ finally:
212
+ self._function_depth -= 1
213
+
214
+ def visit_Assign(self, node):
215
+ """Detect module-level UPPER_CASE constants and ``__all__``."""
216
+ if self._class_depth == 0 and self._function_depth == 0:
217
+ for target in node.targets:
218
+ if isinstance(target, ast.Name):
219
+ if target.id == "__all__":
220
+ self.has_all = True
221
+ elif target.id == target.id.upper() and target.id.replace("_", "").isalnum() and not target.id[0].isdigit():
222
+ self.constants.append({
223
+ "name": target.id,
224
+ "line": node.lineno,
225
+ })
226
+ self.generic_visit(node)
227
+
228
+
229
+ # ── Core scan logic ──────────────────────────────────────────────────
230
+
231
+
232
+ def _scan_python_files(
233
+ src_dir: str,
234
+ deep: bool = False,
235
+ only_files: list[str] | None = None,
236
+ include_empty: bool = False,
237
+ ) -> dict:
238
+ """Scan Python files under *src_dir* and return a raw inventory dict.
239
+
240
+ The returned dict maps *relative* filepath strings (relative to
241
+ *src_dir*) to file entry dicts. The ``"language"`` key is
242
+ intentionally absent here — callers (e.g. :class:`PythonExtractor`)
243
+ are responsible for stamping it.
244
+ """
245
+ src_path = Path(src_dir).resolve()
246
+ inventory = {}
247
+ matcher = build_gitignore_matcher(src_path)
248
+ py_files = [
249
+ src_path / rel
250
+ for rel in discover_source_files(
251
+ str(src_path), (".py",), only_files=only_files, language="python", matcher=matcher,
252
+ )
253
+ ]
254
+
255
+ for py_file in py_files:
256
+ rel = py_file.relative_to(src_path)
257
+ try:
258
+ data = py_file.read_bytes()
259
+ try:
260
+ source = data.decode("utf-8")
261
+ except UnicodeDecodeError:
262
+ source = data.decode("cp1252")
263
+ tree = ast.parse(source, filename=str(py_file))
264
+ except UnicodeDecodeError:
265
+ print(f"llm-wiki Python extractor: skipped undecodable file {rel.as_posix()}", file=sys.stderr)
266
+ continue
267
+ except OSError as exc:
268
+ print(f"llm-wiki Python extractor: failed to read {rel.as_posix()}: {exc}", file=sys.stderr)
269
+ continue
270
+ except SyntaxError:
271
+ continue
272
+
273
+ visitor = ComponentVisitor(deep=deep)
274
+ visitor.visit(tree)
275
+
276
+ # Include the file if it has classes, public functions, constants,
277
+ # __all__, or (in deep mode) private functions.
278
+ has_content = (
279
+ visitor.classes
280
+ or visitor.functions
281
+ or visitor.constants
282
+ or visitor.has_all
283
+ or include_empty
284
+ )
285
+ if has_content:
286
+ file_entry = {
287
+ "classes": visitor.classes,
288
+ "functions": visitor.functions,
289
+ }
290
+
291
+ if visitor.constants:
292
+ file_entry["constants"] = visitor.constants
293
+ if visitor.has_all:
294
+ file_entry["has_all"] = True
295
+
296
+ if deep:
297
+ file_entry["imports"] = visitor.imports
298
+ file_entry["module_docstring"] = ast.get_docstring(tree) or ""
299
+ else:
300
+ # Slim format: strip rich fields for backward compat
301
+ file_entry["classes"] = [
302
+ {"name": c["name"], "bases": c["bases"], "line": c["line"]}
303
+ for c in visitor.classes
304
+ ]
305
+ fns = []
306
+ for f in visitor.functions:
307
+ fn = {"name": f["name"], "line": f["line"]}
308
+ if f.get("is_async"):
309
+ fn["async"] = True
310
+ if f.get("private"):
311
+ fn["private"] = True
312
+ fns.append(fn)
313
+ file_entry["functions"] = fns
314
+
315
+ inventory[rel.as_posix()] = file_entry
316
+
317
+ return inventory
318
+
319
+
320
+ # ── Public extractor class ────────────────────────────────────────────
321
+
322
+
323
+ class PythonExtractor:
324
+ """Extractor for Python source files using the built-in :mod:`ast` module.
325
+
326
+ Implements :class:`~llm_wiki_cli.extractors.ExtractorProtocol`.
327
+ """
328
+
329
+ def extract(
330
+ self,
331
+ src_dir: str,
332
+ only_files: list[str] | None = None,
333
+ deep: bool = False,
334
+ include_empty: bool = False,
335
+ ) -> dict:
336
+ """Scan *src_dir* for Python files and return an inventory dict.
337
+
338
+ Each file entry includes ``"language": "python"``.
339
+ """
340
+ inventory = _scan_python_files(
341
+ src_dir, deep=deep, only_files=only_files,
342
+ include_empty=include_empty,
343
+ )
344
+ for entry in inventory.values():
345
+ entry["language"] = "python"
346
+ return inventory
@@ -0,0 +1,143 @@
1
+ """Rust AST extractor for agent-wiki-cli.
2
+
3
+ Implements :class:`~llm_wiki_cli.extractors.ExtractorProtocol` by delegating
4
+ to a bundled Rust binary (``rust_scripts/src/main.rs``) that uses the ``syn``
5
+ crate for Rust AST parsing.
6
+
7
+ Requirements
8
+ ------------
9
+ * Rust toolchain (``cargo``) on PATH.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import shutil
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ from .common import discover_source_files, filter_bundled_inventory
21
+
22
+ _RUST_SCRIPTS_DIR = Path(__file__).parent / "rust_scripts"
23
+
24
+
25
+ class RustExtractor:
26
+ """Extractor for Rust source files using a ``cargo run`` subprocess.
27
+
28
+ Implements :class:`~llm_wiki_cli.extractors.ExtractorProtocol`.
29
+
30
+ Each returned file entry includes ``"language": "rust"``.
31
+ """
32
+
33
+ last_error: str | None = None
34
+
35
+ def extract(
36
+ self,
37
+ src_dir: str,
38
+ only_files: list[str] | None = None,
39
+ deep: bool = False,
40
+ ) -> dict:
41
+ """Scan *src_dir* for Rust files and return an inventory dict.
42
+
43
+ Parameters
44
+ ----------
45
+ src_dir:
46
+ Root directory to scan.
47
+ only_files:
48
+ Optional list of paths (relative to *src_dir*) to restrict
49
+ extraction to. When ``None``, all ``.rs`` files found under
50
+ *src_dir* are scanned (excluding ``target/``, ``vendor/``, etc.).
51
+ deep:
52
+ When ``True``, include enriched data (doc comments, struct fields,
53
+ method details, imports). When ``False``, return a slim format.
54
+
55
+ Returns
56
+ -------
57
+ dict
58
+ ``{filepath: file_entry}`` where each ``file_entry`` contains at
59
+ minimum ``"classes"``, ``"functions"``, and ``"language"``.
60
+ """
61
+ self.last_error = None
62
+ source_files = discover_source_files(
63
+ src_dir, (".rs",), only_files=only_files, language="rust",
64
+ )
65
+ if not source_files:
66
+ return {}
67
+
68
+ if not shutil.which("cargo"):
69
+ self.last_error = "cargo not found. Install Rust (https://rustup.rs/) to enable Rust extraction."
70
+ print(f"llm-wiki Rust extractor: {self.last_error}", file=sys.stderr)
71
+ return {}
72
+
73
+ cmd = [
74
+ "cargo", "run", "--quiet", "--",
75
+ "--src-dir", str(Path(src_dir).resolve()),
76
+ ]
77
+ cmd += ["--only-files", ",".join(source_files)]
78
+ if deep:
79
+ cmd.append("--deep")
80
+
81
+ try:
82
+ result = subprocess.run(
83
+ cmd,
84
+ capture_output=True,
85
+ text=True,
86
+ check=True,
87
+ timeout=180,
88
+ cwd=str(_RUST_SCRIPTS_DIR),
89
+ )
90
+ except subprocess.CalledProcessError as exc:
91
+ self.last_error = "extraction failed"
92
+ print(
93
+ f"llm-wiki Rust extractor: extraction failed.\n{exc.stderr}",
94
+ file=sys.stderr,
95
+ )
96
+ return {}
97
+ except subprocess.TimeoutExpired:
98
+ self.last_error = "extraction timed out after 180 s"
99
+ print(
100
+ "llm-wiki Rust extractor: extraction timed out after 180 s.",
101
+ file=sys.stderr,
102
+ )
103
+ return {}
104
+ except FileNotFoundError:
105
+ self.last_error = "cargo executable not found"
106
+ print(
107
+ "llm-wiki Rust extractor: cargo executable not found.",
108
+ file=sys.stderr,
109
+ )
110
+ return {}
111
+
112
+ # Forward any warnings the Rust script wrote to stderr.
113
+ if result.stderr.strip():
114
+ sys.stderr.write(result.stderr)
115
+
116
+ if not result.stdout.strip():
117
+ return {}
118
+
119
+ try:
120
+ inventory: dict = json.loads(result.stdout)
121
+ except json.JSONDecodeError as exc:
122
+ self.last_error = "malformed JSON output"
123
+ print(
124
+ f"llm-wiki Rust extractor: malformed JSON output — {exc}",
125
+ file=sys.stderr,
126
+ )
127
+ return {}
128
+
129
+ for entry in inventory.values():
130
+ entry["language"] = "rust"
131
+
132
+ inventory = filter_bundled_inventory(inventory, _RUST_SCRIPTS_DIR)
133
+
134
+ src_root = Path(src_dir).resolve()
135
+ normalized_inventory: dict = {}
136
+ for fp, data in inventory.items():
137
+ try:
138
+ rel = Path(fp).resolve().relative_to(src_root).as_posix()
139
+ except ValueError:
140
+ rel = fp.replace("\\", "/")
141
+ normalized_inventory[rel] = data
142
+
143
+ return normalized_inventory
@@ -0,0 +1,110 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "itoa"
7
+ version = "1.0.18"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
10
+
11
+ [[package]]
12
+ name = "llm-wiki-rust-extractor"
13
+ version = "0.1.0"
14
+ dependencies = [
15
+ "proc-macro2",
16
+ "quote",
17
+ "serde",
18
+ "serde_json",
19
+ "syn",
20
+ ]
21
+
22
+ [[package]]
23
+ name = "memchr"
24
+ version = "2.8.0"
25
+ source = "registry+https://github.com/rust-lang/crates.io-index"
26
+ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
27
+
28
+ [[package]]
29
+ name = "proc-macro2"
30
+ version = "1.0.106"
31
+ source = "registry+https://github.com/rust-lang/crates.io-index"
32
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
33
+ dependencies = [
34
+ "unicode-ident",
35
+ ]
36
+
37
+ [[package]]
38
+ name = "quote"
39
+ version = "1.0.45"
40
+ source = "registry+https://github.com/rust-lang/crates.io-index"
41
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
42
+ dependencies = [
43
+ "proc-macro2",
44
+ ]
45
+
46
+ [[package]]
47
+ name = "serde"
48
+ version = "1.0.228"
49
+ source = "registry+https://github.com/rust-lang/crates.io-index"
50
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
51
+ dependencies = [
52
+ "serde_core",
53
+ "serde_derive",
54
+ ]
55
+
56
+ [[package]]
57
+ name = "serde_core"
58
+ version = "1.0.228"
59
+ source = "registry+https://github.com/rust-lang/crates.io-index"
60
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
61
+ dependencies = [
62
+ "serde_derive",
63
+ ]
64
+
65
+ [[package]]
66
+ name = "serde_derive"
67
+ version = "1.0.228"
68
+ source = "registry+https://github.com/rust-lang/crates.io-index"
69
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
70
+ dependencies = [
71
+ "proc-macro2",
72
+ "quote",
73
+ "syn",
74
+ ]
75
+
76
+ [[package]]
77
+ name = "serde_json"
78
+ version = "1.0.149"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
81
+ dependencies = [
82
+ "itoa",
83
+ "memchr",
84
+ "serde",
85
+ "serde_core",
86
+ "zmij",
87
+ ]
88
+
89
+ [[package]]
90
+ name = "syn"
91
+ version = "2.0.117"
92
+ source = "registry+https://github.com/rust-lang/crates.io-index"
93
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
94
+ dependencies = [
95
+ "proc-macro2",
96
+ "quote",
97
+ "unicode-ident",
98
+ ]
99
+
100
+ [[package]]
101
+ name = "unicode-ident"
102
+ version = "1.0.24"
103
+ source = "registry+https://github.com/rust-lang/crates.io-index"
104
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
105
+
106
+ [[package]]
107
+ name = "zmij"
108
+ version = "1.0.21"
109
+ source = "registry+https://github.com/rust-lang/crates.io-index"
110
+ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
@@ -0,0 +1,11 @@
1
+ [package]
2
+ name = "llm-wiki-rust-extractor"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ syn = { version = "2", features = ["full", "extra-traits"] }
8
+ quote = "1"
9
+ proc-macro2 = { version = "1", features = ["span-locations"] }
10
+ serde = { version = "1", features = ["derive"] }
11
+ serde_json = "1"