Conversation
|
@claude review this, propose change of skills routes from FastAPI to TanStack server funcs |
|
Claude finished @DIodide's task in 2m 17s —— View job PR Review + TanStack Migration Proposal
Code ReviewOverall this is a solid integration. The pattern of storing Bugs / Correctness
Architecture
Minor
Proposed Migration: Skills Routes → TanStack Server FunctionsWhy migrate?
What stays in FastAPI: The Step 1 — Create import { createServerFn } from "@tanstack/react-start";
const HF_BASE = "https://datasets-server.huggingface.co";
const HF_COMMON = "dataset=tickleliu/all-skills-from-skills-sh&config=default&split=train";
function stripDetail(entry: Record<string, unknown>) {
const r = (entry.row ?? entry) as Record<string, unknown>;
return {
name: (r.name as string) ?? "",
skill_name: (r.skill_name as string) ?? "",
description: (r.description as string) ?? "",
code: (r.code as string) ?? "",
};
}
export const listSkillsFn = createServerFn({ method: "GET" })
.validator((data: { offset: number; limit: number }) => data)
.handler(async ({ data }) => {
const { offset, limit } = data;
const resp = await fetch(
`${HF_BASE}/rows?${HF_COMMON}&offset=${offset}&length=${limit}`,
);
if (!resp.ok) throw new Error("Failed to fetch skills from catalog");
const json = await resp.json();
return {
rows: (json.rows as Record<string, unknown>[]).map(stripDetail),
total: (json.num_rows_total as number) ?? 0,
offset,
limit,
};
});
export const searchSkillsFn = createServerFn({ method: "GET" })
.validator((data: { q: string; offset: number; limit: number }) => data)
.handler(async ({ data }) => {
const { q, offset, limit } = data;
const encoded = encodeURIComponent(q);
const resp = await fetch(
`${HF_BASE}/search?${HF_COMMON}&query=${encoded}&offset=${offset}&length=${limit}`,
);
if (!resp.ok) throw new Error("Failed to search skills catalog");
const json = await resp.json();
return {
rows: (json.rows as Record<string, unknown>[]).map(stripDetail),
total: (json.num_rows_total as number) ?? 0,
offset,
limit,
};
});Step 2 — Update Replace the direct // Before (skills-browser.tsx:57-63)
queryFn: async () => {
const endpoint = debouncedSearch
? `${API_URL}/api/skills/search?q=${encodeURIComponent(debouncedSearch)}&offset=${offset}&limit=${PAGE_SIZE}`
: `${API_URL}/api/skills?offset=${offset}&limit=${PAGE_SIZE}`;
const res = await fetch(endpoint);
if (!res.ok) throw new Error("Failed to fetch skills");
return res.json();
},
// After
queryFn: () =>
debouncedSearch
? searchSkillsFn({ data: { q: debouncedSearch, offset, limit: PAGE_SIZE } })
: listSkillsFn({ data: { offset, limit: PAGE_SIZE } }),Then remove the Step 3 — Remove the FastAPI skills route # packages/fastapi/app/main.py
-from app.routes import chat, health, mcp_health, mcp_oauth, skills
+from app.routes import chat, health, mcp_health, mcp_oauth
-app.include_router(skills.router, prefix="/api/skills", tags=["skills"])Delete Trade-off to consider: TanStack server functions run on the Node.js SSR server, so catalog fetches will originate from your server IP rather than the browser. If HuggingFace has per-IP rate limits this concentrates requests. At 50k skills with pagination this is unlikely to be an issue in practice, but worth keeping in mind. If rate limiting becomes a concern, a light caching layer (e.g. a 60-second in-memory cache keyed on |
|
@claude review this |
|
Claude finished @DIodide's task in 2m 46s —— View job PR Review: skills.sh Catalog Integration
Note: The TanStack migration proposed in the previous review is already applied in commit Bugs / Correctness1. When the HuggingFace top-5 search doesn't contain an exact name match, Fix: either skip insertion when 2. Chat-time HuggingFace fallback has the same exact-match fragility (
3. Hardcoded ngrok URL committed in "https://aab7-140-180-240-231.ngrok-free.app",This URL will expire and is a dev artifact. It should either be in env config or removed. Also Architecture4. if (existing) return existing._id; // always skips if any record existsIf a skill's 5. A custom 6. skills: Array<{ name: string; description: string }>;The shared Minor7. 8. What's Good
|
|
@cole-ramer status? |
|
I haven't really had a chance to work on it again. I expect to work on it more tomorrow or Tuesday. I can easily switch to skills. When they search for skills, but I still don't know how to get the list of skills for the browser. I have looked at the npx skills repo and had Claude look at it, and I haven't found a way that we can get the list of all the skills. |
|
Abandoned the hugging face approach; now there is a script that searches the skills.sh api with a bunch of keywords and letter combinations and adds them to a skillsIndex table in convex, which contains the necessary metadata. Skills browser now sorts by installs descending, and the search feature directly calls the skill. The search feature. If there is a new skill that wasn't caught before, found in the search, it is added to skillsIndex. When a skill is added for the first time, it gets the .md file directly from GitHub. Also, with skills.sh api there is no accessible description of each skill, so I just dropped it. A future fix could be allowing users to open the MD file before adding it to their harness. |
|
@claude review this |
|
Claude finished @cole-ramer's task in 2m 28s —— View job PR Review: skills.sh Catalog Integration (Revised Approach)
The pivot from HuggingFace to the skills.sh API + GitHub SKILL.md fetch is a solid direction — the scraper approach makes sense for bootstrap, and live search-and-discover keeps the index fresh. A few significant issues to address before merge: Bugs / Correctness1. const all = await ctx.db.query("skillsIndex").collect();
return all.length;With ~50k skills, this loads every document to count them on every paginated browse. Convex will hit document read limits here. The 2. const rows = await ctx.db.query("skillsIndex")
.withIndex("by_installs").order("desc")
.take(args.offset + args.limit);
return { rows: rows.slice(args.offset), ... };
3. A 4. Any authenticated user can call Architecture5. Search index is over
6. The 7. Still unfixed from the previous review — Minor8. Hardcoded ngrok URL still in 9. 10. What's Good
|
|
@claude review this |
|
Claude finished @cole-ramer's task in 6m 40s —— View job PR Review: skills.sh Catalog Integration (Latest State)
|
|
@claude review this |
|
@claude review this |
|
Claude finished @cole-ramer's task in 5m 56s —— View job PR Review: skills.sh Catalog Integration (Latest State)
What's Fixed Since Last ReviewAll major items from the previous round are resolved:
Bugs / Correctness1. Skill summary Every chat request that has skills now fires a synchronous Convex query ( Architecture2. Ephemeral dev ports still in CORS ( "http://127.0.0.1:57177",
"http://127.0.0.1:57609",Flagged in the previous two reviews. Still present. These are meaningless outside the specific machine where they were added. Remove them (or replace with an 3. No GitHub API authentication ( Both the Convex action and FastAPI fallback still make unauthenticated GitHub API requests. The unauthenticated rate limit is 60 requests/hour per IP. The scraper, 4. Non-standard Radix Tooltip/DropdownMenu nesting ( <DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild> {/* ← non-standard */}
<button ...>The standard Radix pattern for a tooltip-wrapped dropdown trigger is the reverse: <DropdownMenu>
<DropdownMenuTrigger asChild>
<Tooltip>
<TooltipTrigger asChild>
<button ...>The current nesting works because both use Minor5. What's Good in This Revision
Summary: Two items from previous reviews remain unaddressed (ephemeral dev ports, GitHub auth). One new latency concern with the |
|
@claude review this |
|
Claude finished @cole-ramer's task in 2m 0s —— View job Claude finished @cole-ramer's task in 5m — PR Review: skills.sh Catalog Integration (Latest State)
What's Fixed Since Last ReviewSignificant progress — all but two items from the previous round are resolved:
Remaining Issues1. skill_details_list = await query_convex(
http_client,
"skills:getByNames",
{"names": [s.name for s in skill_refs]},
)This was flagged in the last two reviews and is still unaddressed. The Convex query has a 10-second timeout ( 2. No GitHub API authentication ( Both the Convex Adding a # In chat.py _fetch_skill_md_from_repo:
gh_headers = {"Accept": "application/vnd.github.v3+json"}
if settings.github_token:
gh_headers["Authorization"] = f"Bearer {settings.github_token}"// In skills.ts fetchSkillMdFromRepo:
const token = process.env.GITHUB_TOKEN;
const ghHeaders: Record<string, string> = { Accept: "application/vnd.github.v3+json" };
if (token) ghHeaders["Authorization"] = `Bearer ${token}`;Minor3. .inputValidator((data: { q: string; limit: number }) => data)The TanStack Start docs use 4. The counts will drift over time. Since these skills will be in 5. Browse-mode cursor stack grows unboundedly ( The cursor stack is trimmed on search-term changes ( What's Good
Summary: Two substantive items remain — the Convex query latency on the chat critical path (#1) and GitHub API rate limiting (#2). Both are low-effort fixes with meaningful reliability impact. Items #3–#5 are polish/low priority. |
|
@claude review this |
|
Claude finished @cole-ramer's task in 2m 13s —— View job PR Review: skills.sh Catalog Integration (Latest State)
What's Fixed Since Last ReviewAll items from the previous round are resolved except GitHub auth:
Remaining Issue1. No GitHub API authentication ( Both the Convex # chat.py:139
gh_headers = {"Accept": "application/vnd.github.v3+json"}
# no Authorization header// skills.ts:211
const ghHeaders = { Accept: "application/vnd.github.v3+json" };
// no Authorization headerThis has been flagged in the last three reviews. The unauthenticated rate limit is 60 requests/hour per server IP — shared across all users, the scraper, Minor (Low Priority)2. Install counts like 3. Browse-mode cursor stack grows unboundedly ( The What's GoodThis PR is in solid shape. Everything from the previous four review rounds has been addressed except the GitHub token. The quality of recent fixes has been high:
Summary: One substantive item remains — GitHub API auth (#1). Items #2 and #3 are polish. This is ready to merge once #1 is addressed. |
|
@claude this commit should hopefully be the result of the merged staging and feak/skills.sh (that way it is super easy to merge into staging) make sure nothing broke |
|
Claude finished @cole-ramer's task in 3m 48s —— View job Merge Verification
The merge looks clean overall. No leftover conflict markers, and the two feature sets (skills + sandbox) integrate without stepping on each other. What Was MergedCommit Merge Result: Looks GoodFastAPI FastAPI FastAPI Convex Convex Frontend One Regression from the Merge
# packages/fastapi/app/main.py:53 — should be removed
"http://127.0.0.1:57177",SummaryThe merge is functionally sound — no conflicts, no broken imports, no missing symbols. The only issue is the dev port CORS origin re-introduced from staging. Everything else that was previously fixed in the skills review cycle remains fixed. |

PR: Skills.sh Catalog Integration
Branch:
feak/skills.sh→stagingSummary
Integrates the full skills.sh catalog (~50,000 skills) into the platform. Users can browse, search, and add skills to their harnesses. Selected skills are passed to the LLM during chat as a manifest, and a custom
get_skill_contenttool lets the model retrieve full skill markdown on demand.What Changed
New: Skills Catalog Proxy (FastAPI)
packages/fastapi/app/routes/skills.py— Three new endpoints (/api/skills,/api/skills/search,/api/skills/detail) that proxy the HuggingFace Dataset Server API. List and search responses strip thedetailfield to keep payloads small for the browser.main.pyunder/api/skills.New: Convex Skills Backend
packages/convex-backend/convex/skills.ts— Queries (getByName,getByNames) and anensureSkillDetailsaction that lazily fetches and stores full skill markdown from HuggingFace into a newskillDetailstable. Skill detail is only persisted when a harness containing that skill is saved, avoiding unnecessary storage.packages/convex-backend/convex/schema.ts— AddedskillDetailstable (name, skillName, description, detail, code) with aby_nameindex. Changedharnesses.skillsfromv.array(v.string())tov.array(v.object({ name, description }))so descriptions travel with the harness and don't require extra queries at chat time.Updated: Harness Mutations
packages/convex-backend/convex/harnesses.ts—create,update, andduplicatemutations updated for the new skills shape. Includes a one-timemigrateSkillsToObjectsinternal mutation that was used to migrate existing documents from the oldstring[]format.packages/convex-backend/convex/seed.ts— Seed data updated to use{ name, description }skill objects.New: Frontend Skills Components
apps/web/src/components/skills-browser.tsx— Paginated, searchable skill catalog browser using TanStack Query. Checkbox-style selection matching the MCP preset grid pattern.apps/web/src/components/recommended-skills-grid.tsx— Curated grid of 9 recommended skills (vercel-react-best-practices, brainstorming, browser-automation, pdf-vision-reader, systematic-debugging, xlsx, code-review, database-schema-design, git-best-practices).apps/web/src/lib/skills.ts— SharedSkillEntrytype andRECOMMENDED_SKILLSdata.0xbigboss/claude-code), and description.Updated: Harness Editor (
$harnessId.tsx)ensureSkillDetailsfires in the background to cache markdown for newly added skills.skillDetailsConvex query for display — descriptions now come directly from the stored skill objects, eliminating flicker.Updated: Onboarding (
onboarding.tsx){ name, description }objects on harness creation.ensureSkillDetailscalled after harness creation to backfill markdown.Updated: Chat Page (
chat/index.tsx)SkillsStatuscomponent in the chat header showing "X Skills" next to the MCP status indicator. Clicking it reveals a dropdown listing all skills in the active harness with their display name and description.skills: Array<{ name, description }>to FastAPI.Updated: Chat Loop (FastAPI
chat.py)get_skill_contenttool definition is added to the tools list sent to the LLM.get_skill_contentare intercepted and handled locally — the backend queries Convex for the cached markdown, falling back to HuggingFace if not yet cached.Updated: FastAPI Models (
models.py)SkillRefmodel (name + description).HarnessConfig.skillschanged fromlist[str]tolist[SkillRef].Updated: Convex Service (
services/convex.py)query_convexhelper to execute Convex queries via the HTTP API using the deploy key. Used by the chat route to fetch skill detail markdown.Minor Fixes
message-attachments.tsx— Replaced ESLint ignore comment with biome-ignore foruseMediaCaption.chat/index.tsx— FixedforEachreturn value lint (useIterableCallbackReturn) by switching tofor...of. Added biome-ignore fornoStaticElementInteractionson the drag-and-drop zone.organizeImportsrule.Files Changed (22 files, +1204 / -136)
routes/skills.py(new),routes/chat.py,models.py,services/convex.py,main.pyschema.ts,harnesses.ts,skills.ts(new),seed.tsskills-browser.tsx(new),recommended-skills-grid.tsx(new)skills.ts(new),use-chat-stream.ts,models.ts,multimodal.tschat/index.tsx,harnesses/$harnessId.tsx,onboarding.tsxattachment-chip.tsx,message-attachments.tsx,use-file-attachments.ts