@@ -81,6 +81,49 @@ function buildValidationSets(catalog) {
8181 return { validPairs, validWorkflows } ;
8282}
8383
84+ function extractCommandCandidates ( content ) {
85+ const lines = content . split ( / \r ? \n / u) ;
86+ const candidates = [ ] ;
87+ const inlineCodeRegex = / ` ( [ ^ ` \n ] + ) ` / g;
88+ const fenceHeaderRegex = / ^ \s * ` ` ` ( [ a - z 0 - 9 _ - ] * ) \s * $ / iu;
89+ const codeFenceLanguages = new Set ( [ '' , 'bash' , 'sh' , 'zsh' , 'shell' , 'console' ] ) ;
90+
91+ let inFence = false ;
92+ let shouldScanFence = false ;
93+
94+ for ( let lineNumber = 1 ; lineNumber <= lines . length ; lineNumber += 1 ) {
95+ const line = lines [ lineNumber - 1 ] ;
96+ const fenceMatch = line . match ( fenceHeaderRegex ) ;
97+
98+ if ( fenceMatch ) {
99+ if ( ! inFence ) {
100+ inFence = true ;
101+ shouldScanFence = codeFenceLanguages . has ( fenceMatch [ 1 ] . toLowerCase ( ) ) ;
102+ } else {
103+ inFence = false ;
104+ shouldScanFence = false ;
105+ }
106+ continue ;
107+ }
108+
109+ if ( inFence ) {
110+ if ( shouldScanFence ) {
111+ candidates . push ( { lineNumber, text : line } ) ;
112+ }
113+ continue ;
114+ }
115+
116+ inlineCodeRegex . lastIndex = 0 ;
117+ let inlineMatch = inlineCodeRegex . exec ( line ) ;
118+ while ( inlineMatch ) {
119+ candidates . push ( { lineNumber, text : inlineMatch [ 1 ] } ) ;
120+ inlineMatch = inlineCodeRegex . exec ( line ) ;
121+ }
122+ }
123+
124+ return candidates ;
125+ }
126+
84127function findInvalidCommands ( files , validPairs , validWorkflows ) {
85128 const validTopLevel = new Set ( [ 'mcp' , 'tools' , 'daemon' ] ) ;
86129 const validDaemonActions = new Set ( [ 'status' , 'start' , 'stop' , 'restart' , 'list' ] ) ;
@@ -92,12 +135,12 @@ function findInvalidCommands(files, validPairs, validWorkflows) {
92135 for ( const absoluteFilePath of files ) {
93136 const relativePath = path . relative ( repoRoot , absoluteFilePath ) || absoluteFilePath ;
94137 const content = readFileSync ( absoluteFilePath , 'utf8' ) ;
95- const lines = content . split ( / \r ? \n / u ) ;
138+ const candidates = extractCommandCandidates ( content ) ;
96139
97- for ( let lineNumber = 1 ; lineNumber <= lines . length ; lineNumber += 1 ) {
98- const line = lines [ lineNumber - 1 ] ;
140+ for ( const candidate of candidates ) {
141+ const { lineNumber, text } = candidate ;
99142 commandRegex . lastIndex = 0 ;
100- let match = commandRegex . exec ( line ) ;
143+ let match = commandRegex . exec ( text ) ;
101144
102145 while ( match ) {
103146 const first = match [ 1 ] ;
@@ -118,7 +161,7 @@ function findInvalidCommands(files, validPairs, validWorkflows) {
118161 findings . push ( `${ relativePath } :${ lineNumber } : ${ command } ` ) ;
119162 }
120163
121- match = commandRegex . exec ( line ) ;
164+ match = commandRegex . exec ( text ) ;
122165 }
123166 }
124167 }
0 commit comments