Skip to content

Commit ceefe1c

Browse files
committed
Apply exclusion rules consistently across all UI and export paths (closes #14)
- api/projects.py: filter excluded sessions from project session list - api/sessions.py: return 404 for excluded sessions fetched individually - api/search.py: skip excluded sessions during full-text search - api/export_api.py (export_session): return 404 for excluded single-session exports Previously exclusion rules were only enforced in the bulk export; excluded sessions were still browseable, viewable, searchable, and individually downloadable in the UI. Made-with: Cursor
1 parent ebf7540 commit ceefe1c

4 files changed

Lines changed: 51 additions & 0 deletions

File tree

api/export_api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,18 @@ def export_session(project_name, session_id):
165165

166166
fmt = request.args.get("format", "md")
167167
session = parse_session(filepath)
168+
rules = current_app.config.get("EXCLUSION_RULES") or []
169+
if rules:
170+
meta = session["metadata"]
171+
text_parts = [msg.get("text") or "" for msg in session.get("messages", []) if msg.get("text")]
172+
searchable = build_searchable_text(
173+
project_name=project_name,
174+
session_title=session["title"],
175+
model_names=list(meta.get("models_used") or []),
176+
content_snippet="\n\n".join(text_parts),
177+
)
178+
if is_excluded_by_rules(rules, searchable):
179+
return jsonify({"error": "Session not found"}), 404
168180
stats = compute_stats(session)
169181
title_slug = _slugify(session["title"]) or "session"
170182

api/projects.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from flask import Blueprint, current_app, jsonify
66

77
from utils.session_path import get_claude_projects_dir, list_projects, list_sessions, safe_join
8+
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
89

910
projects_bp = Blueprint("projects", __name__)
1011

@@ -50,6 +51,7 @@ def get_project_sessions(project_name):
5051
sessions = list_sessions(project_dir)
5152
# Add summary preview for each session
5253
from utils.jsonl_parser import parse_session
54+
rules = current_app.config.get("EXCLUSION_RULES") or []
5355
result = []
5456
for s in sessions:
5557
try:
@@ -58,6 +60,16 @@ def get_project_sessions(project_name):
5860
# Skip untitled sessions (no real conversation)
5961
if parsed["title"] == "Untitled Session":
6062
continue
63+
if rules:
64+
text_parts = [msg.get("text") or "" for msg in parsed.get("messages", []) if msg.get("text")]
65+
searchable = build_searchable_text(
66+
project_name=project_name,
67+
session_title=parsed["title"],
68+
model_names=list(meta.get("models_used") or []),
69+
content_snippet="\n\n".join(text_parts),
70+
)
71+
if is_excluded_by_rules(rules, searchable):
72+
continue
6173
result.append({
6274
**s,
6375
"title": parsed["title"],

api/search.py

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

77
from utils.session_path import get_claude_projects_dir, list_projects, list_sessions
88
from utils.jsonl_parser import parse_session
9+
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
910

1011
search_bp = Blueprint("search", __name__)
1112

@@ -20,6 +21,7 @@ def search():
2021
base = current_app.config.get("CLAUDE_PROJECTS_DIR") or get_claude_projects_dir()
2122
projects = list_projects(base)
2223

24+
rules = current_app.config.get("EXCLUSION_RULES") or []
2325
results = []
2426
for project in projects:
2527
sessions = list_sessions(project["path"])
@@ -31,6 +33,18 @@ def search():
3133
except Exception:
3234
continue
3335

36+
if rules:
37+
meta = session["metadata"]
38+
text_parts = [msg.get("text") or "" for msg in session.get("messages", []) if msg.get("text")]
39+
searchable = build_searchable_text(
40+
project_name=project["name"],
41+
session_title=session["title"],
42+
model_names=list(meta.get("models_used") or []),
43+
content_snippet="\n\n".join(text_parts),
44+
)
45+
if is_excluded_by_rules(rules, searchable):
46+
continue
47+
3448
for msg in session["messages"]:
3549
text = msg.get("text", "") or msg.get("content", "")
3650
if query in text.lower():

api/sessions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from utils.session_path import get_claude_projects_dir, safe_join
99
from utils.jsonl_parser import parse_session
1010
from utils.session_stats import compute_stats
11+
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
1112

1213
sessions_bp = Blueprint("sessions", __name__)
1314

@@ -25,6 +26,18 @@ def get_session(project_name, session_id):
2526

2627
try:
2728
session = parse_session(filepath)
29+
rules = current_app.config.get("EXCLUSION_RULES") or []
30+
if rules:
31+
meta = session["metadata"]
32+
text_parts = [msg.get("text") or "" for msg in session.get("messages", []) if msg.get("text")]
33+
searchable = build_searchable_text(
34+
project_name=project_name,
35+
session_title=session["title"],
36+
model_names=list(meta.get("models_used") or []),
37+
content_snippet="\n\n".join(text_parts),
38+
)
39+
if is_excluded_by_rules(rules, searchable):
40+
return jsonify({"error": "Session not found"}), 404
2841
return jsonify(session)
2942
except Exception as e:
3043
tb = traceback.format_exc()

0 commit comments

Comments
 (0)