Skip to content

Commit f1574e7

Browse files
committed
fix(ui): resolve Quick Start button initialization, ruleset handling, and search improvements
1 parent 73b2a19 commit f1574e7

File tree

11 files changed

+456
-91
lines changed

11 files changed

+456
-91
lines changed

.mcp.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
FROM python:3.13-slim
22

3+
# Install git (required for gitingest)
4+
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
5+
36
WORKDIR /app
47

58
COPY requirements.txt .

app/routes/actions.py

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,24 @@ async def get_all_actions():
1818
)
1919

2020
@router.get("/agents", operation_id="get_agents_endpoint")
21-
async def get_agents():
22-
"""Get all available agents with tags only"""
21+
async def get_agents(
22+
after: Optional[str] = Query(None, description="Cursor for pagination (use slug of last item from previous page)"),
23+
limit: int = Query(30, ge=1, le=30, description="Maximum number of results (max 30)")
24+
):
25+
"""Get available agents with tags only, limited to 30 items"""
2326
agents = actions_loader.get_agents()
27+
28+
# Find starting position if 'after' is provided
29+
start_idx = 0
30+
if after:
31+
for idx, agent in enumerate(agents):
32+
if agent.slug == after:
33+
start_idx = idx + 1
34+
break
35+
36+
# Apply limit
37+
paginated_agents = agents[start_idx:start_idx + limit]
38+
2439
return [
2540
{
2641
"name": agent.name,
@@ -29,13 +44,28 @@ async def get_agents():
2944
"tags": agent.tags,
3045
"filename": agent.filename
3146
}
32-
for agent in agents
47+
for agent in paginated_agents
3348
]
3449

3550
@router.get("/rules", operation_id="get_rules_endpoint")
36-
async def get_rules():
37-
"""Get all available rules with tags only"""
51+
async def get_rules(
52+
after: Optional[str] = Query(None, description="Cursor for pagination (use slug of last item from previous page)"),
53+
limit: int = Query(30, ge=1, le=30, description="Maximum number of results (max 30)")
54+
):
55+
"""Get available rules with tags only, limited to 30 items"""
3856
rules = actions_loader.get_rules()
57+
58+
# Find starting position if 'after' is provided
59+
start_idx = 0
60+
if after:
61+
for idx, rule in enumerate(rules):
62+
if rule.slug == after:
63+
start_idx = idx + 1
64+
break
65+
66+
# Apply limit
67+
paginated_rules = rules[start_idx:start_idx + limit]
68+
3969
return [
4070
{
4171
"name": rule.name,
@@ -44,19 +74,34 @@ async def get_rules():
4474
"tags": rule.tags,
4575
"filename": rule.filename
4676
}
47-
for rule in rules
77+
for rule in paginated_rules
4878
]
4979

5080
@router.get("/mcps", operation_id="get_mcps_endpoint")
51-
async def get_mcps():
52-
"""Get all available MCPs with tags only"""
81+
async def get_mcps(
82+
after: Optional[str] = Query(None, description="Cursor for pagination (use name of last item from previous page)"),
83+
limit: int = Query(30, ge=1, le=30, description="Maximum number of results (max 30)")
84+
):
85+
"""Get available MCPs with tags only, limited to 30 items"""
5386
mcps = actions_loader.get_mcps()
87+
88+
# Find starting position if 'after' is provided
89+
start_idx = 0
90+
if after:
91+
for idx, mcp in enumerate(mcps):
92+
if mcp.name == after:
93+
start_idx = idx + 1
94+
break
95+
96+
# Apply limit
97+
paginated_mcps = mcps[start_idx:start_idx + limit]
98+
5499
return [
55100
{
56101
"name": mcp.name,
57102
"tags": mcp.tags if hasattr(mcp, 'tags') else []
58103
}
59-
for mcp in mcps
104+
for mcp in paginated_mcps
60105
]
61106

62107

@@ -102,37 +147,37 @@ async def get_merged_actions_block():
102147

103148
@router.get("/search/agents", tags=["mcp"], operation_id="search_agents_endpoint")
104149
async def search_agents(
105-
query: str = Query(..., description="Search query"),
150+
query: str = Query(..., description="Search query. Supports wildcards: * (any characters) and ? (single character)"),
106151
limit: int = Query(10, ge=1, le=100, description="Maximum number of results")
107152
):
108-
"""Search for agents by name, display_name, or content"""
153+
"""Search for agents by name, display_name, or content. Supports wildcard patterns with * and ?"""
109154
results = search_service.search_agents(query, limit)
110155
return {"results": results}
111156

112157
@router.get("/search/rules", tags=["mcp"], operation_id="search_rules_endpoint")
113158
async def search_rules(
114-
query: str = Query(..., description="Search query"),
159+
query: str = Query(..., description="Search query. Supports wildcards: * (any characters) and ? (single character)"),
115160
limit: int = Query(10, ge=1, le=100, description="Maximum number of results")
116161
):
117-
"""Search for rules by name, display_name, content, tags, or author"""
162+
"""Search for rules by name, display_name, content, tags, or author. Supports wildcard patterns with * and ?"""
118163
results = search_service.search_rules(query, limit)
119164
return {"results": results}
120165

121166
@router.get("/search/mcps", tags=["mcp"], operation_id="search_mcps_endpoint")
122167
async def search_mcps(
123-
query: str = Query(..., description="Search query"),
168+
query: str = Query(..., description="Search query. Supports wildcards: * (any characters) and ? (single character)"),
124169
limit: int = Query(10, ge=1, le=100, description="Maximum number of results")
125170
):
126-
"""Search for MCPs by name or config content"""
171+
"""Search for MCPs by name or config content. Supports wildcard patterns with * and ?"""
127172
results = search_service.search_mcps(query, limit)
128173
return {"results": results}
129174

130175
@router.get("/search", tags=["mcp"], operation_id="search_all_endpoint")
131176
async def search_all(
132-
query: str = Query(..., description="Search query"),
177+
query: str = Query(..., description="Search query. Supports wildcards: * (any characters) and ? (single character)"),
133178
limit: int = Query(10, ge=1, le=100, description="Maximum number of results per category")
134179
):
135-
"""Search across all types (agents, rules, MCPs)"""
180+
"""Search across all types (agents, rules, MCPs). Supports wildcard patterns with * and ?"""
136181
return search_service.search_all(query, limit)
137182

138183
@router.get("/rules/{rule_ids}", tags=["mcp"], operation_id="get_multiple_rules_content")
@@ -201,7 +246,7 @@ async def get_multiple_agents_content(agent_ids: str):
201246

202247
@router.get("/mcps/{mcp_ids}", tags=["mcp"], operation_id="get_multiple_mcps_config")
203248
async def get_multiple_mcps_config(mcp_ids: str):
204-
"""Get config for multiple MCPs by comma-separated names"""
249+
"""Get config for multiple MCPs by comma-separated names. These MCPs will need to be installed on the client side (e.g. in `.mcp.json`)"""
205250
ids = [id.strip() for id in mcp_ids.split(',') if id.strip()]
206251

207252
if not ids:

app/routes/recommend.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,17 @@ async def recommend_tools(request: RecommendRequest):
5353
status_code=400,
5454
detail="Either repo_url or context must be provided"
5555
)
56-
56+
print(f"Getting context for {request.repo_url}")
5757
# Step 1: Get context (ingest if needed)
5858
if request.context:
59+
print(f"Using provided context")
5960
context = request.context
6061
else:
6162
# Ingest the repository
63+
print(f"Ingesting repository {request.repo_url}")
6264
context = await use_gitingest(request.repo_url)
63-
6465
context_size = len(context)
66+
print(f"Context size: {context_size}")
6567

6668
# Step 2: Build catalog
6769
catalog = build_tools_catalog()

app/services/search_service.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,35 @@
22
from fuzzywuzzy import fuzz
33
from app.models.actions import Agent, Rule, MCP
44
from app.services.actions_loader import actions_loader
5+
import re
6+
import fnmatch
57

68
class SearchService:
79
def __init__(self):
810
self.actions_loader = actions_loader
911

12+
def _is_wildcard_query(self, query: str) -> bool:
13+
"""Check if query contains wildcard characters"""
14+
return '*' in query or '?' in query
15+
16+
def _wildcard_match(self, pattern: str, text: str) -> bool:
17+
"""Check if text matches wildcard pattern"""
18+
return fnmatch.fnmatch(text.lower(), pattern.lower())
19+
1020
def _calculate_relevance(self, query: str, text: str) -> int:
11-
"""Calculate relevance score for fuzzy matching"""
21+
"""Calculate relevance score for fuzzy matching with wildcard support"""
1222
if not text:
1323
return 0
1424
query_lower = query.lower()
1525
text_lower = text.lower()
1626

27+
# Handle wildcard queries
28+
if self._is_wildcard_query(query):
29+
if self._wildcard_match(query, text):
30+
return 95 # High score for wildcard matches
31+
else:
32+
return 0
33+
1734
# Exact match gets highest score
1835
if query_lower == text_lower:
1936
return 100

app/templates/base.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@200;300;400;500;600;700;800&display=swap" rel="stylesheet">
2525
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css">
2626
{% include 'components/styles.html' %}
27+
<!-- Load workspace manager early for quick actions -->
28+
<script src="/static/js/workspace_manager.js"></script>
2729
</head>
2830
<body class="bg-gradient-to-br from-pink-50 to-cyan-50 min-h-screen flex flex-col" style="font-family: 'Plus Jakarta Sans', sans-serif;">
2931
{% include 'components/navbar.html' %}

app/templates/components/ActionButton.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@
114114
}
115115

116116
/* Disabled State */
117-
.action-button--disabled {
117+
.action-button--disabled,
118+
.action-button:disabled {
118119
opacity: 0.6;
119120
cursor: not-allowed;
120121
pointer-events: none;

0 commit comments

Comments
 (0)