Skip to content

Commit 8ffb946

Browse files
committed
fix: filter generated docs from change detection, merge metadata, async I/O
- Filter output_dir and .codewiki/ from untracked files in git change detection so generated docs don't falsely trigger incremental updates - Merge into existing metadata.json on close_session to preserve stats from other workflows (e.g. CLI generate_docs) - Remove unused format_user_prompt import in prompt_server.py - Replace mutable default list argument with None in module_tree.py - Route blocking file I/O (read_text/write_text/exists/mkdir) through asyncio.to_thread() in doc_writer.py to avoid blocking MCP event loop
1 parent 90f1ec9 commit 8ffb946

5 files changed

Lines changed: 48 additions & 21 deletions

File tree

‎codewiki/mcp/server.py‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ def _text(content: str) -> TextContent:
546546
def _write_generation_metadata(session: SessionState) -> None:
547547
"""Write ``metadata.json`` to the session's output directory.
548548
549-
Records the current git commit and timestamp so that
550-
:func:`_detect_changes` can diff against this baseline on the next
551-
``analyze_repo`` call, enabling incremental updates.
549+
Merges the current git commit and timestamp into any existing metadata
550+
so that stats from other workflows (e.g. CLI ``generate_docs``) are
551+
preserved.
552552
"""
553553
try:
554554
output_dir = Path(session.output_dir)
@@ -563,14 +563,23 @@ def _write_generation_metadata(session: SessionState) -> None:
563563
pass
564564

565565
from datetime import datetime
566-
metadata = {
567-
"generation_info": {
568-
"commit_id": commit_id,
569-
"timestamp": datetime.now().isoformat(),
570-
},
566+
567+
# Read existing metadata (if any) and merge
568+
metadata_path = output_dir / "metadata.json"
569+
existing: dict = {}
570+
if metadata_path.exists():
571+
try:
572+
existing = json.loads(metadata_path.read_text(encoding="utf-8"))
573+
except (json.JSONDecodeError, OSError):
574+
pass
575+
576+
existing["generation_info"] = {
577+
**existing.get("generation_info", {}),
578+
"commit_id": commit_id,
579+
"timestamp": datetime.now().isoformat(),
571580
}
572-
(output_dir / "metadata.json").write_text(
573-
json.dumps(metadata, indent=2, ensure_ascii=False),
581+
metadata_path.write_text(
582+
json.dumps(existing, indent=2, ensure_ascii=False),
574583
encoding="utf-8",
575584
)
576585
except Exception as e:

‎codewiki/mcp/tools/analysis.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def _detect_changes(
5252
return None
5353

5454
# Try git-based detection first
55-
changes = _detect_via_git(repo_path, metadata)
55+
changes = _detect_via_git(repo_path, metadata, output_dir)
5656

5757
# Fallback to mtime-based detection
5858
if changes is None:
@@ -90,6 +90,7 @@ def _detect_changes(
9090
def _detect_via_git(
9191
repo_path: Path,
9292
metadata: Dict[str, Any],
93+
output_dir: Path | None = None,
9394
) -> Optional[Dict[str, Any]]:
9495
"""Detect changes via git. Returns None if not in a git repo or if no
9596
previous commit is recorded (so the caller can fall through to mtime).
@@ -146,6 +147,16 @@ def _detect_via_git(
146147
pass
147148

148149
# 2) Uncommitted changes: staged (index vs HEAD) + unstaged (working tree vs index) + untracked
150+
# Compute output_dir relative prefix so we can skip generated docs
151+
output_dir_rel = ""
152+
if output_dir is not None:
153+
try:
154+
output_dir_rel = Path(output_dir).resolve().relative_to(repo_root).as_posix()
155+
if output_dir_rel == ".":
156+
output_dir_rel = ""
157+
except (ValueError, TypeError):
158+
pass
159+
149160
try:
150161
for d in list(repo.index.diff("HEAD")) + list(repo.index.diff(None)):
151162
p = d.a_path
@@ -163,6 +174,11 @@ def _detect_via_git(
163174
p = p[len(subpath) + 1:]
164175
else:
165176
continue
177+
# Skip generated docs and session workspace files
178+
if p.startswith(".codewiki/"):
179+
continue
180+
if output_dir_rel and (p == output_dir_rel or p.startswith(output_dir_rel + "/")):
181+
continue
166182
if p not in changed:
167183
changed.append(p)
168184
except Exception:

‎codewiki/mcp/tools/doc_writer.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from __future__ import annotations
88

9+
import asyncio
910
import json
1011
import logging
1112
from pathlib import Path
@@ -86,14 +87,14 @@ async def handle_write_doc_file(
8687

8788
content = arguments["content"]
8889

89-
_ensure_parent_dirs(doc_path)
90+
await asyncio.to_thread(_ensure_parent_dirs, doc_path)
9091

91-
if doc_path.exists():
92+
if await asyncio.to_thread(doc_path.exists):
9293
return json.dumps({
9394
"error": f"File already exists: {filename}. Use edit_doc_file to modify it."
9495
})
9596

96-
doc_path.write_text(content, encoding="utf-8")
97+
await asyncio.to_thread(doc_path.write_text, content, "utf-8")
9798

9899
# Mermaid validation
99100
mermaid_result = await _validate_mermaid(str(doc_path), filename)
@@ -136,7 +137,7 @@ async def handle_edit_doc_file(
136137
old_content = path_history.pop()
137138
history[str(doc_path)] = path_history
138139
session.registry["file_history"] = history
139-
doc_path.write_text(old_content, encoding="utf-8")
140+
await asyncio.to_thread(doc_path.write_text, old_content, "utf-8")
140141

141142
# Validate Mermaid after undo
142143
mermaid_result = await _validate_mermaid(str(doc_path), filename)
@@ -146,11 +147,11 @@ async def handle_edit_doc_file(
146147
"mermaid_validation": mermaid_result,
147148
}, ensure_ascii=False)
148149

149-
if not doc_path.exists():
150+
if not await asyncio.to_thread(doc_path.exists):
150151
return json.dumps({"error": f"File not found: {filename}. Use write_doc_file to create it."})
151152

152153
# Save current content to history before editing
153-
current_content = doc_path.read_text(encoding="utf-8")
154+
current_content = await asyncio.to_thread(doc_path.read_text, "utf-8")
154155
_save_history(session, doc_path, current_content)
155156

156157
if command == "str_replace":
@@ -166,7 +167,7 @@ async def handle_edit_doc_file(
166167
return json.dumps({"error": f"old_str appears {occurrences} times in {filename}. Make it unique."})
167168

168169
new_content = current_content.replace(old_str, new_str, 1)
169-
doc_path.write_text(new_content, encoding="utf-8")
170+
await asyncio.to_thread(doc_path.write_text, new_content, "utf-8")
170171

171172
# Snippet around the edit
172173
replacement_line = current_content.split(old_str)[0].count("\n")
@@ -186,7 +187,7 @@ async def handle_edit_doc_file(
186187
new_str_lines = new_str.split("\n")
187188
lines = lines[:insert_line] + new_str_lines + lines[insert_line:]
188189
new_content = "\n".join(lines)
189-
doc_path.write_text(new_content, encoding="utf-8")
190+
await asyncio.to_thread(doc_path.write_text, new_content, "utf-8")
190191

191192
start = max(0, insert_line - 4)
192193
end = min(len(lines), start + len(new_str_lines) + 8)

‎codewiki/mcp/tools/module_tree.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
logger = logging.getLogger(__name__)
2020

2121

22-
def _get_processing_order(module_tree: Dict[str, Any], parent_path: List[str] = []) -> List[Dict[str, Any]]:
22+
def _get_processing_order(module_tree: Dict[str, Any], parent_path: List[str] | None = None) -> List[Dict[str, Any]]:
2323
"""Compute leaf-first processing order from a module tree.
2424
2525
Returns a list of dicts with module path, name, leaf status, and
2626
component/children info.
2727
"""
28+
if parent_path is None:
29+
parent_path = []
2830
order: List[Dict[str, Any]] = []
2931

3032
def _collect(tree: Dict[str, Any], path: List[str]) -> None:

‎codewiki/mcp/tools/prompt_server.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
format_system_prompt,
2020
format_leaf_system_prompt,
2121
format_cluster_prompt,
22-
format_user_prompt,
2322
)
2423

2524

0 commit comments

Comments
 (0)