Skip to content

Commit 7b26b2a

Browse files
feat: add pagination support with --limit and --offset flags
- Add --limit flag to control number of results (default: 20) - Add --offset flag for pagination (skip N results) - Include pagination metadata in JSON response - Support page navigation with has_next/has_previous indicators - Update help examples to demonstrate pagination usage - Enables LLMs to access all search results, not just first 20 Made-with: Cursor
1 parent 44e7fca commit 7b26b2a

2 files changed

Lines changed: 60 additions & 16 deletions

File tree

cmd/docs/docs.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232

3333
var searchMode bool
3434
var outputFormat string
35+
var searchLimit int
36+
var searchOffset int
3537

3638
func NewCommand(clients *shared.ClientFactory) *cobra.Command {
3739
cmd := &cobra.Command{
@@ -52,8 +54,12 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command {
5254
Command: "docs --search \"Block Kit\" --output=json",
5355
},
5456
{
55-
Meaning: "Search and open in browser (default)",
56-
Command: "docs --search \"Block Kit\" --output=browser",
57+
Meaning: "Search with custom limit",
58+
Command: "docs --search \"Block Kit\" --output=json --limit=50",
59+
},
60+
{
61+
Meaning: "Search with pagination",
62+
Command: "docs --search \"Block Kit\" --output=json --limit=20 --offset=20",
5763
},
5864
}),
5965
RunE: func(cmd *cobra.Command, args []string) error {
@@ -63,6 +69,8 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command {
6369

6470
cmd.Flags().BoolVar(&searchMode, "search", false, "search Slack docs with optional query")
6571
cmd.Flags().StringVar(&outputFormat, "output", "browser", "output format: browser, json")
72+
cmd.Flags().IntVar(&searchLimit, "limit", 20, "maximum number of results to return")
73+
cmd.Flags().IntVar(&searchOffset, "offset", 0, "number of results to skip (for pagination)")
6674

6775
return cmd
6876
}
@@ -85,7 +93,7 @@ func findDocsRepo() string {
8593
// runProgrammaticSearch executes the local search
8694
func runProgrammaticSearch(query string, docsPath string) (*ProgrammaticSearchOutput, error) {
8795
contentDir := filepath.Join(docsPath, "content")
88-
return search.SearchDocs(query, "", 20, contentDir)
96+
return search.SearchDocs(query, "", searchLimit, searchOffset, contentDir)
8997
}
9098

9199
// runDocsCommand opens Slack developer docs in the browser or performs programmatic search

internal/search/search.go

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,21 @@ type SearchResult struct {
3737

3838
// SearchResponse represents the complete search response
3939
type SearchResponse struct {
40-
Query string `json:"query"`
41-
Filter string `json:"filter"`
42-
Results []SearchResult `json:"results"`
43-
Total int `json:"total"`
44-
Showing int `json:"showing"`
40+
Query string `json:"query"`
41+
Filter string `json:"filter"`
42+
Results []SearchResult `json:"results"`
43+
Total int `json:"total"`
44+
Showing int `json:"showing"`
45+
Pagination *PaginationInfo `json:"pagination,omitempty"`
46+
}
47+
48+
// PaginationInfo provides pagination metadata
49+
type PaginationInfo struct {
50+
Limit int `json:"limit"`
51+
Offset int `json:"offset"`
52+
Page int `json:"page"` // 1-based page number
53+
HasNext bool `json:"has_next"`
54+
HasPrevious bool `json:"has_previous"`
4555
}
4656

4757
// FrontMatter represents the YAML frontmatter in markdown files
@@ -302,7 +312,7 @@ func findMarkdownFiles(dir string) ([]string, error) {
302312
}
303313

304314
// SearchDocs performs a programmatic search of documentation files
305-
func SearchDocs(query, filter string, limit int, contentDir string) (*SearchResponse, error) {
315+
func SearchDocs(query, filter string, limit, offset int, contentDir string) (*SearchResponse, error) {
306316
if contentDir == "" {
307317
return nil, fmt.Errorf("content directory not specified")
308318
}
@@ -385,22 +395,48 @@ func SearchDocs(query, filter string, limit int, contentDir string) (*SearchResp
385395
return results[i].Score > results[j].Score
386396
})
387397

388-
// Limit results
398+
// Apply pagination
389399
total := len(results)
400+
401+
// Handle offset
402+
if offset >= total {
403+
results = []SearchResult{} // No results if offset too high
404+
} else if offset > 0 {
405+
results = results[offset:]
406+
}
407+
408+
// Handle limit
390409
if limit > 0 && limit < len(results) {
391410
results = results[:limit]
392411
}
393-
412+
394413
if filter == "" {
395414
filter = "all"
396415
}
397416

417+
// Calculate pagination info
418+
var pagination *PaginationInfo
419+
if limit > 0 {
420+
page := (offset / limit) + 1
421+
hasNext := offset+len(results) < total
422+
hasPrevious := offset > 0
423+
424+
pagination = &PaginationInfo{
425+
Limit: limit,
426+
Offset: offset,
427+
Page: page,
428+
HasNext: hasNext,
429+
HasPrevious: hasPrevious,
430+
}
431+
}
432+
398433
response := &SearchResponse{
399-
Query: query,
400-
Filter: filter,
401-
Results: results,
402-
Total: total,
403-
Showing: len(results),
434+
Query: query,
435+
Filter: filter,
436+
Results: results,
437+
Total: total,
438+
Showing: len(results),
439+
Pagination: pagination,
404440
}
405441

406442
return response, nil

0 commit comments

Comments
 (0)