|
1 | 1 | """Path-scoped IO tools for the skill-create agent. |
2 | 2 |
|
3 | 3 | The skill-create agent runs with these capabilities: |
4 | | - * READ wiki structure — ``list_wiki_dir`` |
5 | | - * READ wiki markdown — ``read_wiki_file_for_skill`` |
| 4 | + * READ wiki structure — ``list_wiki_dir`` (delegates to |
| 5 | + ``openkb.agent.tools.list_wiki_files``) |
| 6 | + * READ wiki markdown — ``read_wiki_file_for_skill`` (delegates to |
| 7 | + ``openkb.agent.tools.read_wiki_file``) |
6 | 8 | * READ PageIndex source pages — ``get_skill_page_content`` (delegates to |
7 | 9 | ``openkb.agent.tools.get_wiki_page_content``) |
8 | 10 | * READ wiki images — ``read_skill_image`` (delegates to |
9 | 11 | ``openkb.agent.tools.read_wiki_image``) |
10 | 12 | * WRITE under skill root — ``write_skill_file`` |
11 | 13 |
|
12 | | -The first four wrap the canonical wiki tools in ``openkb/agent/tools.py`` |
13 | | -so the skill agent traverses the wiki the same way the query agent does — |
14 | | -no separate retrieval semantics, no second implementation to drift. |
| 14 | +The first four are thin wrappers around the canonical wiki tools in |
| 15 | +``openkb/agent/tools.py`` so the skill agent traverses the wiki the same |
| 16 | +way the query agent does — no separate retrieval semantics, no second |
| 17 | +implementation to drift. |
15 | 18 |
|
16 | 19 | These helpers enforce write boundaries at the Python level — every write |
17 | 20 | resolves its target path, then verifies it stays inside the skill root. |
|
23 | 26 |
|
24 | 27 | from openkb.agent.tools import ( |
25 | 28 | get_wiki_page_content as _get_wiki_page_content, |
| 29 | + list_wiki_files as _list_wiki_files, |
| 30 | + read_wiki_file as _read_wiki_file, |
26 | 31 | read_wiki_image as _read_wiki_image, |
27 | 32 | ) |
28 | 33 |
|
29 | 34 |
|
30 | 35 | def list_wiki_dir(directory: str, wiki_root: str) -> str: |
31 | 36 | """List ``.md`` files in a wiki subdirectory. |
32 | 37 |
|
| 38 | + Thin wrapper around :func:`openkb.agent.tools.list_wiki_files`. |
| 39 | +
|
33 | 40 | Args: |
34 | 41 | directory: Path relative to *wiki_root* (e.g. ``"concepts"``). |
35 | 42 | wiki_root: Absolute path to ``<kb>/wiki``. |
36 | 43 | """ |
37 | | - root = Path(wiki_root).resolve() |
38 | | - target = (root / directory).resolve() |
39 | | - if not target.is_relative_to(root): |
40 | | - return "Access denied: path escapes wiki root." |
41 | | - if not target.exists() or not target.is_dir(): |
42 | | - return "No files found." |
43 | | - names = sorted(p.name for p in target.iterdir() if p.suffix == ".md") |
44 | | - return "\n".join(names) if names else "No files found." |
| 44 | + return _list_wiki_files(directory, wiki_root) |
45 | 45 |
|
46 | 46 |
|
47 | 47 | def read_wiki_file_for_skill(path: str, wiki_root: str) -> str: |
48 | 48 | """Read a Markdown file from the wiki. |
49 | 49 |
|
| 50 | + Thin wrapper around :func:`openkb.agent.tools.read_wiki_file`. |
| 51 | +
|
50 | 52 | Args: |
51 | 53 | path: File path relative to *wiki_root* (e.g. ``"concepts/attention.md"``). |
52 | 54 | wiki_root: Absolute path to ``<kb>/wiki``. |
53 | 55 | """ |
54 | | - root = Path(wiki_root).resolve() |
55 | | - full = (root / path).resolve() |
56 | | - if not full.is_relative_to(root): |
57 | | - return "Access denied: path escapes wiki root." |
58 | | - if not full.exists(): |
59 | | - return f"File not found: {path}" |
60 | | - return full.read_text(encoding="utf-8") |
| 56 | + return _read_wiki_file(path, wiki_root) |
61 | 57 |
|
62 | 58 |
|
63 | 59 | def get_skill_page_content(doc_name: str, pages: str, wiki_root: str) -> str: |
|
0 commit comments