1
+ import { z } from 'zod' ;
2
+ import { getRuleHierarchy , getRulesForLibrary } from '../data/rulesProvider' ;
3
+
4
+ // Define recursive schema for the hierarchy structure
5
+ export interface HierarchyNode {
6
+ id : string ;
7
+ name : string ;
8
+ children ?: HierarchyNode [ ] ;
9
+ }
10
+
11
+ const hierarchyNodeSchema : z . ZodType < HierarchyNode > = z . lazy ( ( ) =>
12
+ z . object ( {
13
+ id : z . string ( ) ,
14
+ name : z . string ( ) ,
15
+ children : z . array ( hierarchyNodeSchema ) . optional ( ) ,
16
+ } )
17
+ ) ;
18
+
19
+ // New schema for the listAvailableRulesTool output
20
+ const libraryInfoSchema = z . object ( {
21
+ identifier : z . string ( ) ,
22
+ name : z . string ( ) ,
23
+ stack : z . array ( z . string ( ) ) , // e.g., ["Frontend", "React"]
24
+ } ) ;
25
+
26
+ const listAvailableRulesOutputSchema = z . object ( {
27
+ availableLibraries : z . array ( libraryInfoSchema ) ,
28
+ reminder : z . string ( ) ,
29
+ } ) ;
30
+
31
+ const getRuleContentInputSchema = z . object ( { libraryIdentifier : z . string ( ) } ) ;
32
+ const getRuleContentOutputSchema = z . union ( [
33
+ z . object ( { rules : z . array ( z . string ( ) ) } ) ,
34
+ z . object ( { error : z . string ( ) } ) ,
35
+ ] ) ;
36
+
37
+ // Helper function to find leaf nodes (libraries)
38
+ type LibraryInfo = z . infer < typeof libraryInfoSchema > ;
39
+
40
+ function findLibraries ( nodes : HierarchyNode [ ] , currentStack : string [ ] = [ ] ) : LibraryInfo [ ] {
41
+ let libraries : LibraryInfo [ ] = [ ] ;
42
+ if ( ! nodes ) return libraries ;
43
+
44
+ nodes . forEach ( node => {
45
+ const newStack = [ ...currentStack , node . name ] ;
46
+ if ( ! node . children || node . children . length === 0 ) {
47
+ // This is a leaf node (a library)
48
+ // We only add it if it seems like a rule identifier (heuristic: contains '_')
49
+ // or if it's explicitly marked somehow (adjust logic if needed based on preparedRules.json structure)
50
+ // For now, let's assume all leaf nodes with actual rules in preparedRules.json are valid identifiers.
51
+ // A better approach might be to cross-reference with preparedRules.rules keys if necessary.
52
+ libraries . push ( {
53
+ identifier : node . id ,
54
+ name : node . name ,
55
+ stack : currentStack , // Stack leading *to* this node's parent category
56
+ } ) ;
57
+ } else {
58
+ // Recurse into children
59
+ libraries = libraries . concat ( findLibraries ( node . children , newStack ) ) ;
60
+ }
61
+ } ) ;
62
+ return libraries ;
63
+ }
64
+
65
+ // Updated listAvailableRulesTool
66
+ export const listAvailableRulesTool = {
67
+ name : 'listAvailableRules' ,
68
+ description : 'Lists available AI library identifiers and their stacks, with instructions on how to get rules.' ,
69
+ inputSchema : z . object ( { } ) . optional ( ) ,
70
+ outputSchema : listAvailableRulesOutputSchema , // Use the new output schema
71
+ async execute ( ) : Promise < z . infer < typeof listAvailableRulesOutputSchema > > {
72
+ const hierarchy = getRuleHierarchy ( ) ;
73
+ const availableLibraries = findLibraries ( hierarchy ) ;
74
+
75
+ const result = {
76
+ availableLibraries : availableLibraries ,
77
+ reminder : "Use the 'getRuleContent' tool with one of the 'identifier' values (e.g., 'REACT_CODING_STANDARDS') to get specific rules."
78
+ } ;
79
+
80
+ // Validate the final output structure
81
+ const validation = listAvailableRulesOutputSchema . safeParse ( result ) ;
82
+ if ( ! validation . success ) {
83
+ console . error ( 'Output validation failed for listAvailableRules:' , validation . error ) ;
84
+ // Fallback or throw error
85
+ throw new Error ( 'Internal server error: Failed to prepare available libraries list.' ) ;
86
+ }
87
+ return validation . data ;
88
+ } ,
89
+ } ;
90
+
91
+ export const getRuleContentTool = {
92
+ name : 'getRuleContent' ,
93
+ description : 'Gets the AI rules for a specific library identifier.' ,
94
+ inputSchema : getRuleContentInputSchema ,
95
+ outputSchema : getRuleContentOutputSchema ,
96
+ async execute ( input : z . infer < typeof getRuleContentInputSchema > ) : Promise < z . infer < typeof getRuleContentOutputSchema > > {
97
+ const rules = getRulesForLibrary ( input . libraryIdentifier ) ;
98
+ let result : z . infer < typeof getRuleContentOutputSchema > ;
99
+ if ( rules ) {
100
+ result = { rules } ;
101
+ } else {
102
+ result = { error : `Rules not found for library identifier: ${ input . libraryIdentifier } ` } ;
103
+ }
104
+ // Validate output before returning
105
+ const validation = getRuleContentOutputSchema . safeParse ( result ) ;
106
+ if ( ! validation . success ) {
107
+ console . error ( `Output validation failed for getRuleContent (input: ${ input . libraryIdentifier } ):` , validation . error ) ;
108
+ // Even if rules were found, if they don't match schema, it's an internal error
109
+ throw new Error ( 'Internal server error: Failed to prepare rule content.' ) ;
110
+ }
111
+ return validation . data ;
112
+ } ,
113
+ } ;
114
+
115
+ // Export all tools in a map for easy lookup
116
+ export const tools = {
117
+ [ listAvailableRulesTool . name ] : listAvailableRulesTool ,
118
+ [ getRuleContentTool . name ] : getRuleContentTool ,
119
+ } ;
120
+
121
+ export type Tool = typeof listAvailableRulesTool | typeof getRuleContentTool ;
122
+ export type ToolName = keyof typeof tools ;
0 commit comments