Skip to content

Commit 90f1ec9

Browse files
committed
fix: monorepo path support, unstaged change detection, dead code cleanup
- Strip git-root-to-repo_path prefix from diff paths for monorepo subdirs - Add index.diff(None) alongside index.diff('HEAD') to capture unstaged edits - Return None from _detect_via_git when prev_commit is null, enabling mtime fallback - Remove dead code and unused imports in prompt_server.py (4 raw prompt constants, logging, Optional, session resolution stub) - Remove unused 'time' import in analysis.py
1 parent 58314c2 commit 90f1ec9

2 files changed

Lines changed: 48 additions & 34 deletions

File tree

‎codewiki/mcp/tools/analysis.py‎

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import json
1313
import logging
1414
import os
15-
import time
1615
from pathlib import Path
1716
from typing import Any, Dict, List, Optional, Tuple
1817

@@ -92,10 +91,11 @@ def _detect_via_git(
9291
repo_path: Path,
9392
metadata: Dict[str, Any],
9493
) -> Optional[Dict[str, Any]]:
95-
"""Detect changes via git. Returns None if not in a git repo.
94+
"""Detect changes via git. Returns None if not in a git repo or if no
95+
previous commit is recorded (so the caller can fall through to mtime).
9696
97-
Checks both committed changes (diff against stored commit_id) and
98-
uncommitted changes (``git status``).
97+
Checks committed changes (diff against stored commit_id), staged changes
98+
(``index.diff('HEAD')``), and unstaged/untracked changes.
9999
"""
100100
try:
101101
import git
@@ -104,37 +104,67 @@ def _detect_via_git(
104104
return None
105105

106106
prev_commit = metadata.get("generation_info", {}).get("commit_id")
107+
if not prev_commit:
108+
return None # No baseline to compare; let mtime fallback handle it
109+
107110
try:
108111
current_commit = repo.head.commit.hexsha
109112
except Exception:
110113
return None
111114

115+
# Compute subpath prefix for monorepo support.
116+
# Git diff returns paths relative to the git root, but component IDs
117+
# use paths relative to repo_path. Strip the prefix so they align.
118+
git_root = Path(repo.working_dir).resolve()
119+
repo_root = repo_path.resolve()
120+
try:
121+
subpath = repo_root.relative_to(git_root).as_posix()
122+
except ValueError:
123+
subpath = ""
124+
if subpath == ".":
125+
subpath = ""
126+
112127
changed: list[str] = []
113128
method = "git"
114129

115130
# 1) Committed changes since last generation
116-
if prev_commit and prev_commit != current_commit:
131+
if prev_commit != current_commit:
117132
try:
118133
diff_index = repo.commit(prev_commit).diff(current_commit)
119134
seen: set[str] = set()
120135
for diff in diff_index:
121-
if diff.a_path and diff.a_path not in seen:
122-
changed.append(diff.a_path)
123-
seen.add(diff.a_path)
124-
if diff.b_path and diff.b_path not in seen:
125-
changed.append(diff.b_path)
126-
seen.add(diff.b_path)
136+
for p in (diff.a_path, diff.b_path):
137+
if p and p not in seen:
138+
if subpath:
139+
if p.startswith(subpath + "/"):
140+
p = p[len(subpath) + 1:]
141+
else:
142+
continue # outside target subdirectory
143+
changed.append(p)
144+
seen.add(p)
127145
except Exception:
128146
pass
129147

130-
# 2) Uncommitted changes (user may have edited but not committed)
148+
# 2) Uncommitted changes: staged (index vs HEAD) + unstaged (working tree vs index) + untracked
131149
try:
150+
for d in list(repo.index.diff("HEAD")) + list(repo.index.diff(None)):
151+
p = d.a_path
152+
if p and p not in changed:
153+
if subpath:
154+
if p.startswith(subpath + "/"):
155+
p = p[len(subpath) + 1:]
156+
else:
157+
continue
158+
changed.append(p)
132159
for item in repo.untracked_files:
133-
if item not in changed:
134-
changed.append(item)
135-
for file_path in [d.a_path for d in repo.index.diff("HEAD")]:
136-
if file_path and file_path not in changed:
137-
changed.append(file_path)
160+
p = item
161+
if subpath:
162+
if p.startswith(subpath + "/"):
163+
p = p[len(subpath) + 1:]
164+
else:
165+
continue
166+
if p not in changed:
167+
changed.append(p)
138168
except Exception:
139169
pass
140170

‎codewiki/mcp/tools/prompt_server.py‎

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,10 @@
99
from __future__ import annotations
1010

1111
import json
12-
import logging
13-
from typing import Any, Dict, Optional
12+
from typing import Any, Dict
1413

1514
from codewiki.mcp.session import SessionStore
1615
from codewiki.src.be.prompt_template import (
17-
CLUSTER_REPO_PROMPT,
18-
CLUSTER_MODULE_PROMPT,
19-
SYSTEM_PROMPT,
20-
LEAF_SYSTEM_PROMPT,
2116
USER_PROMPT,
2217
REPO_OVERVIEW_PROMPT,
2318
MODULE_OVERVIEW_PROMPT,
@@ -27,8 +22,6 @@
2722
format_user_prompt,
2823
)
2924

30-
logger = logging.getLogger(__name__)
31-
3225

3326
# Prompt catalog: maps prompt_type to (raw_template, usage_hint, variables_doc)
3427
_PROMPT_CATALOG: Dict[str, Dict[str, str]] = {
@@ -135,18 +128,9 @@ def _resolve_prompt(prompt_type: str, variables: Dict[str, Any]) -> str:
135128
return format_leaf_system_prompt(module_name, custom_instructions)
136129

137130
elif prompt_type == "user":
138-
# If full variables are provided, use the full formatter
139-
session_id = variables.get("session_id")
140131
module_name = variables.get("module_name", "MODULE_NAME")
141-
core_component_ids = variables.get("core_component_ids", [])
142132
module_tree = variables.get("module_tree", {})
143133

144-
if session_id and core_component_ids:
145-
# Try to resolve from session
146-
from codewiki.mcp.session import SessionStore
147-
# We can't easily access the store here, so fall back to template
148-
pass
149-
150134
# Return the template with placeholders filled as possible
151135
return USER_PROMPT.format(
152136
module_name=module_name,

0 commit comments

Comments
 (0)