Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 68 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,26 @@ resource_list({ category: 'Documentation' });
resource_list({ tag: 'api' });
```

### resource_search
### resource_search (tool)

Search with intelligent relevance scoring.
Search resources by keyword with intelligent relevance scoring. This tool performs a full-text, multi-field search across resource names, tool names, tags, descriptions, and content.

Arguments and validation:

- `query` (required): Non-empty search string (case-insensitive). An empty query will be rejected.
- `type` (optional): Filter by resource type (agent, checklist, command, knowledge-base, task, template, all). Defaults to `all` when omitted.
- `max_results` (optional): Maximum number of results to return. Defaults to `10`.

Cache semantics:

- Cache key format: `${query}:${type || 'all'}:${max_results}`
- Cache TTL: 5 minutes (300000 ms). Cached responses (including cached empty results) are returned for this duration.
- Note: cached empty searches now return the same no-results response shape as non-cached empty searches. This ensures a consistent response shape for consumers and tests (see PR #6).

Usage examples:

```typescript
// Search across all fields
// Search by keyword across all fields
resource_search({ query: 'api documentation' });

// Search within specific type
Expand All @@ -168,6 +182,57 @@ resource_search({ query: 'deployment', type: 'task' });
resource_search({ query: 'security', max_results: 5 });
```

Example successful response (formatted):

```json
{
"count": 2,
"results": [
{
"score": 57,
"toolName": "checklist_api_documentation",
"name": "API Documentation Checklist",
"type": "checklist",
"description": "Checklist for producing API documentation",
"category": "Documentation",
"tags": ["api", "documentation"],
"matchedFields": ["name", "tags", "description"],
"snippet": "...comprehensive API documentation checklist for REST and GraphQL..."
},
{
"score": 34,
"toolName": "knowledge_base_api_examples",
"name": "API Examples",
"type": "knowledge-base",
"description": "Sample API requests and patterns",
"category": "Development",
"tags": ["api", "examples"],
"matchedFields": ["content"],
"snippet": "...example request showing authentication headers..."
}
]
}
```

Example no-results response (returned for both cached and non-cached empty searches):

```json
{
"results": [],
"message": "No resources found matching 'documentation'",
"suggestions": [
"Try broader search terms",
"Check spelling",
"Use resource_list({ type: 'all' }) to see all available resources"
],
"availableCategories": []
}
```

Why this matters

- Returning a consistent shape for empty responses (cached or not) prevents consumer and test breakage when callers assume a fixed schema. See the fix in PR #6 which merged into `fix/docs-linking-robustness`.

### resource_info

Get detailed resource metadata.
Expand Down
8 changes: 6 additions & 2 deletions pages/fix-links.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ const BASE_PATH = '/opencode-warcraft-notifications';
// Matches: href="/something/" but not href="/opencode-warcraft-notifications/something/"
// Also matches: href="/something/#anchor" and href="/"
// But not href="/opencode-warcraft-notifications" (exact match)
// Match href="/..." that does not already include the base path
// Allow anchors and query strings; preserve file extensions. Exclude protocol-relative and external links.
const INTERNAL_LINK_PATTERN =
/href="(\/(?!opencode-warcraft-notifications(?:\/|"|$))(?:[^"#\s][^"]*?)?)"/g;
/href="(\/(?!opencode-warcraft-notifications(?:\/|"|$))(?:[^"\s]*?))"/g;

/**
* Fix links in a single HTML file
Expand All @@ -51,11 +53,13 @@ async function fixLinksInFile(filePath) {
return match;
}

// If path is base path without trailing slash, add trailing slash
// If path is exactly base path, ensure trailing slash
if (path === BASE_PATH) {
return `href="${BASE_PATH}/"`;
}

// Preserve anchors and query strings while prefixing base path
// e.g. "/foo#bar" -> "/base/foo#bar"
modified = true;
return `href="${BASE_PATH}${path}"`;
});
Expand Down
60 changes: 51 additions & 9 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,17 +243,23 @@ resource_list({ type: 'all' });

**Performance:** Fast, low context impact (metadata only)

### resource_search
### resource_search (tool)

Search resources by keyword with relevance scoring.
Search resources by keyword with multi-field relevance scoring. The tool accepts the following arguments and performs input validation (non-empty `query` required).

**Arguments:**
Arguments and validation:

- `query` (required): Search query (case-insensitive)
- `type` (optional): Filter by type
- `max_results` (optional): Maximum results to return (default: 10)
- `query` (required): Non-empty search string (case-insensitive). Empty queries are rejected.
- `type` (optional): Resource type filter (agent, checklist, command, knowledge-base, task, template, all). Defaults to `all` when omitted.
- `max_results` (optional): Maximum number of results to return. Defaults to `10`.

**Usage:**
Cache details:

- Cache key format: `${query}:${type || 'all'}:${max_results}`
- Cache TTL: 5 minutes (300000 ms)
- Cached empty results now return the same no-results response shape as non-cached empty searches for consistency (see PR #6).

Usage:

```typescript
// Search by keyword across all fields
Expand All @@ -266,9 +272,45 @@ resource_search({ query: 'deployment', type: 'task' });
resource_search({ query: 'security', max_results: 5 });
```

**Performance:** Fast (<100ms for 100+ resources), cached (5-min TTL)
Example successful response:

```json
{
"count": 1,
"results": [
{
"score": 50,
"toolName": "checklist_api_documentation",
"name": "API Documentation Checklist",
"type": "checklist",
"description": "Checklist for producing API documentation",
"category": "Documentation",
"tags": ["api", "documentation"],
"matchedFields": ["name", "tags"],
"snippet": "...API documentation checklist with REST and GraphQL examples..."
}
]
}
```

Example no-results response:

```json
{
"results": [],
"message": "No resources found matching 'documentation'",
"suggestions": [
"Try broader search terms",
"Check spelling",
"Use resource_list({ type: 'all' }) to see all available resources"
],
"availableCategories": []
}
```

Performance: Fast (<100ms for 100+ resources), cached (5-min TTL)

**Scoring:** Multi-field relevance scoring (exact name: 20, tags: 15, description: 5, content: 2)
Scoring: Multi-field relevance scoring (exact name: 20, tags: 15, description: 5, content: 2)

### resource_info

Expand Down
15 changes: 15 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,21 @@ export const ResourceLoaderPlugin: Plugin = async (ctx) => {
const cacheKey = `${query}:${type || 'all'}:${max_results}`;
const cached = getCachedSearch(cacheKey);
if (cached) {
// Ensure cached empty results use the same "no results" response shape
if (cached.length === 0) {
const categories = Array.from(index.byCategory.keys());
return JSON.stringify({
results: [],
message: `No resources found matching '${query}'`,
suggestions: [
'Try broader search terms',
'Check spelling',
"Use resource_list({ type: 'all' }) to see all available resources",
],
availableCategories: categories,
});
}

return JSON.stringify(formatSearchResults(cached));
}

Expand Down
Loading