|
1 | 1 | #!/usr/bin/env node |
2 | 2 | import "dotenv/config"; |
3 | | -import { Command } from "commander"; |
4 | | -import path from "node:path"; |
5 | 3 | import os from "node:os"; |
| 4 | +import path from "node:path"; |
| 5 | +import { Command } from "commander"; |
6 | 6 | import { VectorStoreService } from "./store/VectorStoreService"; |
7 | 7 | import { |
8 | | - FindVersionTool, |
9 | | - ListLibrariesTool, |
10 | | - ScrapeTool, |
11 | | - SearchTool, |
| 8 | + FindVersionTool, |
| 9 | + ListLibrariesTool, |
| 10 | + ScrapeTool, |
| 11 | + SearchTool, |
12 | 12 | } from "./tools"; |
13 | 13 |
|
14 | 14 | const formatOutput = (data: unknown) => JSON.stringify(data, null, 2); |
15 | 15 |
|
16 | 16 | async function main() { |
17 | | - const storeService = new VectorStoreService(); |
| 17 | + const storeService = new VectorStoreService(); |
18 | 18 |
|
19 | | - try { |
20 | | - await storeService.initialize(); |
| 19 | + try { |
| 20 | + await storeService.initialize(); |
21 | 21 |
|
22 | | - const tools = { |
23 | | - listLibraries: new ListLibrariesTool(storeService), |
24 | | - findVersion: new FindVersionTool(storeService), |
25 | | - scrape: new ScrapeTool(storeService), |
26 | | - search: new SearchTool(storeService), |
27 | | - }; |
| 22 | + const tools = { |
| 23 | + listLibraries: new ListLibrariesTool(storeService), |
| 24 | + findVersion: new FindVersionTool(storeService), |
| 25 | + scrape: new ScrapeTool(storeService), |
| 26 | + search: new SearchTool(storeService), |
| 27 | + }; |
28 | 28 |
|
29 | | - const program = new Command(); |
| 29 | + const program = new Command(); |
30 | 30 |
|
31 | | - // Handle cleanup on SIGINT |
32 | | - process.on("SIGINT", async () => { |
33 | | - await storeService.shutdown(); |
34 | | - process.exit(0); |
35 | | - }); |
| 31 | + // Handle cleanup on SIGINT |
| 32 | + process.on("SIGINT", async () => { |
| 33 | + await storeService.shutdown(); |
| 34 | + process.exit(0); |
| 35 | + }); |
36 | 36 |
|
37 | | - program |
38 | | - .name("docs-mcp") |
39 | | - .description("CLI for managing documentation vector store") |
40 | | - .version("1.0.0"); |
| 37 | + program |
| 38 | + .name("docs-mcp") |
| 39 | + .description("CLI for managing documentation vector store") |
| 40 | + .version("1.0.0"); |
41 | 41 |
|
42 | | - program |
43 | | - .command("scrape <library> <version> <url>") |
44 | | - .description("Scrape and index documentation from a URL") |
45 | | - .option("-p, --max-pages <number>", "Maximum pages to scrape", "100") |
46 | | - .option("-d, --max-depth <number>", "Maximum navigation depth", "3") |
47 | | - .option( |
48 | | - "-c, --max-concurrency <number>", |
49 | | - "Maximum concurrent page requests", |
50 | | - "3" |
51 | | - ) |
52 | | - .option( |
53 | | - "--subpages-only", |
54 | | - "Allow scraping pages outside the initial URL path", |
55 | | - true |
56 | | - ) |
57 | | - .action(async (library, version, url, options) => { |
58 | | - const result = await tools.scrape.execute({ |
59 | | - url, |
60 | | - library, |
61 | | - version, |
62 | | - options: { |
63 | | - maxPages: Number.parseInt(options.maxPages), |
64 | | - maxDepth: Number.parseInt(options.maxDepth), |
65 | | - maxConcurrency: Number.parseInt(options.maxConcurrency), |
66 | | - }, |
67 | | - }); |
68 | | - console.log(`✅ Successfully scraped ${result.pagesScraped} pages`); |
69 | | - }); |
| 42 | + program |
| 43 | + .command("scrape <library> <version> <url>") |
| 44 | + .description("Scrape and index documentation from a URL") |
| 45 | + .option("-p, --max-pages <number>", "Maximum pages to scrape", "100") |
| 46 | + .option("-d, --max-depth <number>", "Maximum navigation depth", "3") |
| 47 | + .option( |
| 48 | + "-c, --max-concurrency <number>", |
| 49 | + "Maximum concurrent page requests", |
| 50 | + "3", |
| 51 | + ) |
| 52 | + .option( |
| 53 | + "--subpages-only", |
| 54 | + "Allow scraping pages outside the initial URL path", |
| 55 | + true, |
| 56 | + ) |
| 57 | + .action(async (library, version, url, options) => { |
| 58 | + const result = await tools.scrape.execute({ |
| 59 | + url, |
| 60 | + library, |
| 61 | + version, |
| 62 | + options: { |
| 63 | + maxPages: Number.parseInt(options.maxPages), |
| 64 | + maxDepth: Number.parseInt(options.maxDepth), |
| 65 | + maxConcurrency: Number.parseInt(options.maxConcurrency), |
| 66 | + }, |
| 67 | + }); |
| 68 | + console.log(`✅ Successfully scraped ${result.pagesScraped} pages`); |
| 69 | + }); |
70 | 70 |
|
71 | | - program |
72 | | - .command("search <library> <version> <query>") |
73 | | - .description( |
74 | | - "Search documents in a library. Version matching examples:\n" + |
75 | | - " - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" + |
76 | | - " - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" + |
77 | | - " - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" + |
78 | | - " - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version" |
79 | | - ) |
80 | | - .option("-l, --limit <number>", "Maximum number of results", "5") |
81 | | - .option( |
82 | | - "-e, --exact-match", |
83 | | - "Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)", |
84 | | - false |
85 | | - ) |
86 | | - .action(async (library, version, query, options) => { |
87 | | - const result = await tools.search.execute({ |
88 | | - library, |
89 | | - version, |
90 | | - query, |
91 | | - limit: Number.parseInt(options.limit), |
92 | | - exactMatch: options.exactMatch, |
93 | | - }); |
94 | | - console.log(formatOutput(result.results)); |
95 | | - }); |
| 71 | + program |
| 72 | + .command("search <library> <version> <query>") |
| 73 | + .description( |
| 74 | + "Search documents in a library. Version matching examples:\n" + |
| 75 | + " - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" + |
| 76 | + " - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" + |
| 77 | + " - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" + |
| 78 | + " - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version", |
| 79 | + ) |
| 80 | + .option("-l, --limit <number>", "Maximum number of results", "5") |
| 81 | + .option( |
| 82 | + "-e, --exact-match", |
| 83 | + "Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)", |
| 84 | + false, |
| 85 | + ) |
| 86 | + .action(async (library, version, query, options) => { |
| 87 | + const result = await tools.search.execute({ |
| 88 | + library, |
| 89 | + version, |
| 90 | + query, |
| 91 | + limit: Number.parseInt(options.limit), |
| 92 | + exactMatch: options.exactMatch, |
| 93 | + }); |
| 94 | + console.log(formatOutput(result.results)); |
| 95 | + }); |
96 | 96 |
|
97 | | - program |
98 | | - .command("list-libraries") |
99 | | - .description("List all available libraries and their versions") |
100 | | - .action(async () => { |
101 | | - const result = await tools.listLibraries.execute(); |
102 | | - console.log(formatOutput(result.libraries)); |
103 | | - }); |
| 97 | + program |
| 98 | + .command("list-libraries") |
| 99 | + .description("List all available libraries and their versions") |
| 100 | + .action(async () => { |
| 101 | + const result = await tools.listLibraries.execute(); |
| 102 | + console.log(formatOutput(result.libraries)); |
| 103 | + }); |
104 | 104 |
|
105 | | - program |
106 | | - .command("find-version <library> [targetVersion]") |
107 | | - .description("Find the best matching version for a library") |
108 | | - .action(async (library, targetVersion) => { |
109 | | - const version = await tools.findVersion.execute({ |
110 | | - library, |
111 | | - targetVersion, |
112 | | - }); |
113 | | - if (!version) { |
114 | | - throw new Error("No matching version found"); |
115 | | - } |
116 | | - console.log(version); |
117 | | - }); |
| 105 | + program |
| 106 | + .command("find-version <library> [targetVersion]") |
| 107 | + .description("Find the best matching version for a library") |
| 108 | + .action(async (library, targetVersion) => { |
| 109 | + const version = await tools.findVersion.execute({ |
| 110 | + library, |
| 111 | + targetVersion, |
| 112 | + }); |
| 113 | + if (!version) { |
| 114 | + throw new Error("No matching version found"); |
| 115 | + } |
| 116 | + console.log(version); |
| 117 | + }); |
118 | 118 |
|
119 | | - await program.parseAsync(); |
120 | | - } catch (error) { |
121 | | - console.error( |
122 | | - "Error:", |
123 | | - error instanceof Error ? error.message : String(error) |
124 | | - ); |
125 | | - await storeService.shutdown(); |
126 | | - process.exit(1); |
127 | | - } |
| 119 | + await program.parseAsync(); |
| 120 | + } catch (error) { |
| 121 | + console.error( |
| 122 | + "Error:", |
| 123 | + error instanceof Error ? error.message : String(error), |
| 124 | + ); |
| 125 | + await storeService.shutdown(); |
| 126 | + process.exit(1); |
| 127 | + } |
128 | 128 |
|
129 | | - // Clean shutdown after successful execution |
130 | | - await storeService.shutdown(); |
131 | | - process.exit(0); |
| 129 | + // Clean shutdown after successful execution |
| 130 | + await storeService.shutdown(); |
| 131 | + process.exit(0); |
132 | 132 | } |
133 | 133 |
|
134 | 134 | main().catch((error) => { |
135 | | - console.error("Fatal error:", error); |
136 | | - process.exit(1); |
| 135 | + console.error("Fatal error:", error); |
| 136 | + process.exit(1); |
137 | 137 | }); |
0 commit comments