@@ -4,133 +4,171 @@ import { Command } from "commander";
44import path from "node:path" ;
55import os from "node:os" ;
66import { VectorStoreService } from "./store/VectorStoreService.js" ;
7- import { findVersion , listLibraries } from "./tools/library.js" ;
8- import { search } from "./tools/search.js" ;
9- import { scrape } from "./tools/scrape.js" ;
7+ import {
8+ FindVersionTool ,
9+ ListLibrariesTool ,
10+ ScrapeTool ,
11+ SearchTool ,
12+ } from "./tools" ;
1013
1114const formatOutput = ( data : unknown ) => JSON . stringify ( data , null , 2 ) ;
1215
13- const program = new Command ( ) ;
16+ async function main ( ) {
17+ const store = new VectorStoreService ( ) ;
1418
15- // Initialize the store manager
16- const store = new VectorStoreService ( ) ;
19+ try {
20+ await store . initialize ( ) ;
1721
18- // Function to ensure store is shutdown before exiting
19- const cleanupAndExit = async ( error ?: unknown ) => {
20- await store . shutdown ( ) ;
21- if ( error ) {
22- console . error (
23- "Error:" ,
24- error instanceof Error ? error . message : String ( error )
25- ) ;
26- process . exit ( 1 ) ;
27- }
28- process . exit ( 0 ) ;
29- } ;
22+ const tools = {
23+ listLibraries : new ListLibrariesTool ( store ) ,
24+ findVersion : new FindVersionTool ( store ) ,
25+ scrape : new ScrapeTool ( store ) ,
26+ search : new SearchTool ( store ) ,
27+ } ;
3028
31- // Handle cleanup on SIGINT
32- process . on ( "SIGINT" , ( ) => {
33- cleanupAndExit ( ) ;
34- } ) ;
29+ const program = new Command ( ) ;
3530
36- program
37- . name ( "docs-mcp" )
38- . description ( "CLI for managing documentation vector store" )
39- . version ( "1.0.0" ) ;
31+ // Handle cleanup on SIGINT
32+ process . on ( "SIGINT" , async ( ) => {
33+ await store . shutdown ( ) ;
34+ process . exit ( 0 ) ;
35+ } ) ;
4036
41- program
42- . command ( "scrape <library> <version> <url>" )
43- . description ( "Scrape and index documentation from a URL" )
44- . option ( "-p, --max-pages <number>" , "Maximum pages to scrape" , "100" )
45- . option ( "-d, --max-depth <number>" , "Maximum navigation depth" , "3" )
46- . option (
47- "--subpages-only" ,
48- "Allow scraping pages outside the initial URL path" ,
49- true
50- )
51- . action ( async ( library , version , url , options ) => {
52- try {
53- const result = await scrape ( {
54- storeService : store ,
55- url,
56- library,
57- version,
58- options : {
59- maxPages : Number . parseInt ( options . maxPages ) ,
60- maxDepth : Number . parseInt ( options . maxDepth ) ,
61- } ,
37+ program
38+ . name ( "docs-mcp" )
39+ . description ( "CLI for managing documentation vector store" )
40+ . version ( "1.0.0" ) ;
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+ "--subpages-only" ,
49+ "Allow scraping pages outside the initial URL path" ,
50+ true
51+ )
52+ . action ( async ( library , version , url , options ) => {
53+ try {
54+ const result = await tools . scrape . execute ( {
55+ url,
56+ library,
57+ version,
58+ options : {
59+ maxPages : Number . parseInt ( options . maxPages ) ,
60+ maxDepth : Number . parseInt ( options . maxDepth ) ,
61+ } ,
62+ } ) ;
63+ console . log ( `✅ Successfully scraped ${ result . pagesScraped } pages` ) ;
64+ await store . shutdown ( ) ;
65+ process . exit ( 0 ) ;
66+ } catch ( error ) {
67+ await store . shutdown ( ) ;
68+ console . error (
69+ "Error:" ,
70+ error instanceof Error ? error . message : String ( error )
71+ ) ;
72+ process . exit ( 1 ) ;
73+ }
6274 } ) ;
63- console . log ( `✅ Successfully scraped ${ result . pagesScraped } pages` ) ;
64- await cleanupAndExit ( ) ;
65- } catch ( error ) {
66- await cleanupAndExit ( error ) ;
67- }
68- } ) ;
6975
70- program
71- . command ( "search <library> <version> <query>" )
72- . description (
73- "Search documents in a library. Version matching examples:\n" +
74- " - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" +
75- " - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" +
76- " - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" +
77- " - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version"
78- )
79- . option ( "-l, --limit <number>" , "Maximum number of results" , "5" )
80- . option (
81- "-e, --exact-match" ,
82- "Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)" ,
83- false
84- )
85- . action ( async ( library , version , query , options ) => {
86- try {
87- const result = await search ( {
88- library,
89- version,
90- query,
91- limit : Number . parseInt ( options . limit ) ,
92- exactMatch : options . exactMatch ,
93- storeService : store ,
76+ program
77+ . command ( "search <library> <version> <query>" )
78+ . description (
79+ "Search documents in a library. Version matching examples:\n" +
80+ " - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" +
81+ " - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" +
82+ " - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" +
83+ " - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version"
84+ )
85+ . option ( "-l, --limit <number>" , "Maximum number of results" , "5" )
86+ . option (
87+ "-e, --exact-match" ,
88+ "Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)" ,
89+ false
90+ )
91+ . action ( async ( library , version , query , options ) => {
92+ try {
93+ const result = await tools . search . execute ( {
94+ library,
95+ version,
96+ query,
97+ limit : Number . parseInt ( options . limit ) ,
98+ exactMatch : options . exactMatch ,
99+ } ) ;
100+ console . log ( formatOutput ( result . results ) ) ;
101+ await store . shutdown ( ) ;
102+ process . exit ( 0 ) ;
103+ } catch ( error ) {
104+ await store . shutdown ( ) ;
105+ console . error (
106+ "Error:" ,
107+ error instanceof Error ? error . message : String ( error )
108+ ) ;
109+ process . exit ( 1 ) ;
110+ }
94111 } ) ;
95- console . log ( formatOutput ( result . results ) ) ;
96- await cleanupAndExit ( ) ;
97- } catch ( error ) {
98- await cleanupAndExit ( error ) ;
99- }
100- } ) ;
101112
102- program
103- . command ( "list-libraries" )
104- . description ( "List all available libraries and their versions" )
105- . action ( async ( ) => {
106- try {
107- const result = await listLibraries ( { storeService : store } ) ;
108- console . log ( formatOutput ( result . libraries ) ) ;
109- await cleanupAndExit ( ) ;
110- } catch ( error ) {
111- await cleanupAndExit ( error ) ;
112- }
113- } ) ;
113+ program
114+ . command ( "list-libraries" )
115+ . description ( "List all available libraries and their versions" )
116+ . action ( async ( ) => {
117+ try {
118+ const result = await tools . listLibraries . execute ( ) ;
119+ console . log ( formatOutput ( result . libraries ) ) ;
120+ await store . shutdown ( ) ;
121+ process . exit ( 0 ) ;
122+ } catch ( error ) {
123+ await store . shutdown ( ) ;
124+ console . error (
125+ "Error:" ,
126+ error instanceof Error ? error . message : String ( error )
127+ ) ;
128+ process . exit ( 1 ) ;
129+ }
130+ } ) ;
114131
115- program
116- . command ( "find-version <library> [targetVersion]" )
117- . description ( "Find the best matching version for a library" )
118- . action ( async ( library , targetVersion ) => {
119- try {
120- const version = await findVersion ( {
121- storeService : store ,
122- library,
123- targetVersion,
132+ program
133+ . command ( "find-version <library> [targetVersion]" )
134+ . description ( "Find the best matching version for a library" )
135+ . action ( async ( library , targetVersion ) => {
136+ try {
137+ const version = await tools . findVersion . execute ( {
138+ library,
139+ targetVersion,
140+ } ) ;
141+ if ( version ) {
142+ console . log ( version ) ;
143+ await store . shutdown ( ) ;
144+ process . exit ( 0 ) ;
145+ } else {
146+ await store . shutdown ( ) ;
147+ console . error ( "Error: No matching version found" ) ;
148+ process . exit ( 1 ) ;
149+ }
150+ } catch ( error ) {
151+ await store . shutdown ( ) ;
152+ console . error (
153+ "Error:" ,
154+ error instanceof Error ? error . message : String ( error )
155+ ) ;
156+ process . exit ( 1 ) ;
157+ }
124158 } ) ;
125- if ( version ) {
126- console . log ( version ) ;
127- await cleanupAndExit ( ) ;
128- } else {
129- await cleanupAndExit ( "No matching version found" ) ;
130- }
131- } catch ( error ) {
132- await cleanupAndExit ( error ) ;
133- }
134- } ) ;
135159
136- program . parse ( ) ;
160+ await program . parseAsync ( ) ;
161+ } catch ( error ) {
162+ await store . shutdown ( ) ;
163+ console . error (
164+ "Error:" ,
165+ error instanceof Error ? error . message : String ( error )
166+ ) ;
167+ process . exit ( 1 ) ;
168+ }
169+ }
170+
171+ main ( ) . catch ( ( error ) => {
172+ console . error ( "Fatal error:" , error ) ;
173+ process . exit ( 1 ) ;
174+ } ) ;
0 commit comments