diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js
index c928de9..8710f08 100644
--- a/.dependency-cruiser.js
+++ b/.dependency-cruiser.js
@@ -143,6 +143,10 @@ export default {
doNotFollow: {
path: ['node_modules', '\\.next', 'dist', '\\.turbo', 'storybook-static'],
},
+ exclude: {
+ // Exclude railway-executor - it's a Deno app with HTTP imports that can't be resolved
+ path: '^apps/railway-executor',
+ },
tsPreCompilationDeps: true,
tsConfig: {
fileName: './tsconfig.json',
diff --git a/apps/web/src/app/how-it-works/page.tsx b/apps/web/src/app/how-it-works/page.tsx
index b993af4..4b80bab 100644
--- a/apps/web/src/app/how-it-works/page.tsx
+++ b/apps/web/src/app/how-it-works/page.tsx
@@ -642,7 +642,7 @@ const result = await streamText({
Why collections? They let you
compose specialized sub-agents without manually curating tool lists. Think of
- them as "skill packs" for your AI.
+ them as “skill packs” for your AI.
@@ -654,8 +654,8 @@ const result = await streamText({
Try It in the Playground
- Ask the playground agent to "search for tools about X" and watch it discover,
- load, and execute tools dynamically!
+ Ask the playground agent to “search for tools about X” and watch it
+ discover, load, and execute tools dynamically!
diff --git a/apps/web/src/app/tool/tool-search/page.tsx b/apps/web/src/app/tool/tool-search/page.tsx
index d42b31c..b139085 100644
--- a/apps/web/src/app/tool/tool-search/page.tsx
+++ b/apps/web/src/app/tool/tool-search/page.tsx
@@ -34,6 +34,25 @@ interface Tool {
type SortOption = 'downloads' | 'recent';
+/** Sort tools by criterion, pushing broken tools to the bottom */
+function sortTools(tools: Tool[], sortBy: SortOption): Tool[] {
+ return [...tools].sort((a, b) => {
+ const aIsBroken = a.importHealth === 'BROKEN' || a.executionHealth === 'BROKEN';
+ const bIsBroken = b.importHealth === 'BROKEN' || b.executionHealth === 'BROKEN';
+ // Always push broken tools to bottom
+ if (aIsBroken && !bIsBroken) return 1;
+ if (!aIsBroken && bIsBroken) return -1;
+ // Within same broken status, sort by selected criterion
+ if (sortBy === 'downloads') {
+ const aDownloads = a.package.npmDownloadsLastMonth ?? 0;
+ const bDownloads = b.package.npmDownloadsLastMonth ?? 0;
+ return bDownloads - aDownloads;
+ }
+ // Sort by recent (createdAt descending)
+ return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+ });
+}
+
/**
* Tool Registry Search Page
*
@@ -79,23 +98,7 @@ export default function ToolSearchPage(): React.ReactElement {
if (toolsData.success) {
const fetchedTools = toolsData.data;
- // Sort by selected criterion, then push broken tools to bottom
- const sortedTools = [...fetchedTools].sort((a: Tool, b: Tool) => {
- const aIsBroken = a.importHealth === 'BROKEN' || a.executionHealth === 'BROKEN';
- const bIsBroken = b.importHealth === 'BROKEN' || b.executionHealth === 'BROKEN';
- // Always push broken tools to bottom
- if (aIsBroken && !bIsBroken) return 1;
- if (!aIsBroken && bIsBroken) return -1;
- // Within same broken status, sort by selected criterion
- if (sortBy === 'downloads') {
- const aDownloads = a.package.npmDownloadsLastMonth ?? 0;
- const bDownloads = b.package.npmDownloadsLastMonth ?? 0;
- return bDownloads - aDownloads;
- }
- // Sort by recent (createdAt descending)
- return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
- });
- setTools(sortedTools);
+ setTools(sortTools(fetchedTools, sortBy));
setError(null);
// Extract unique categories from all tools
@@ -321,7 +324,8 @@ export default function ToolSearchPage(): React.ReactElement {
{/* Install command */}
- {/* biome-ignore lint/a11y/useKeyWithClickEvents: onClick only prevents propagation, not an interactive element */}
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
+ {/* biome-ignore lint/a11y/useKeyWithClickEvents: onClick only prevents propagation */}
e.stopPropagation()}>