@@ -79,9 +79,11 @@ export class UpdateChecker {
7979 return ;
8080 }
8181
82- // Parse version from output like "qsv 0.132.0"
83- const match = output . match ( / q s v \s + ( \d + \. \d + \. \d + ) / ) ;
82+ // Parse version from output like "qsv 0.132.0" or "qsv 0.132.0-beta"
83+ // Handle multiple spaces, extra text, and pre-release tags
84+ const match = output . match ( / q s v \s + ( \d + \. \d + \. \d + ) (?: - [ \w . ] + ) ? / ) ;
8485 if ( match ) {
86+ // Only use the main version number (ignore pre-release tags for now)
8587 resolve ( match [ 1 ] ) ;
8688 } else {
8789 reject ( new Error ( `Could not parse qsv version from: ${ output } ` ) ) ;
@@ -98,19 +100,33 @@ export class UpdateChecker {
98100 * Get version that skills were generated with (from skill JSON files)
99101 */
100102 getSkillsVersion ( ) : string {
101- try {
102- // Read version from any skill file (they all have the same version)
103- const sampleSkillPath = join ( this . skillsDir , 'qsv-stats.json' ) ;
104- if ( ! existsSync ( sampleSkillPath ) ) {
105- return 'unknown' ;
106- }
103+ // Try multiple skill files as fallbacks for resilience
104+ const skillFilesToTry = [
105+ 'qsv-stats.json' ,
106+ 'qsv-select.json' ,
107+ 'qsv-count.json' ,
108+ 'qsv-search.json'
109+ ] ;
110+
111+ for ( const skillFile of skillFilesToTry ) {
112+ try {
113+ const skillPath = join ( this . skillsDir , skillFile ) ;
114+ if ( ! existsSync ( skillPath ) ) {
115+ continue ;
116+ }
107117
108- const skill = JSON . parse ( readFileSync ( sampleSkillPath , 'utf-8' ) ) ;
109- return skill . version || 'unknown' ;
110- } catch ( error ) {
111- console . error ( '[UpdateChecker] Failed to read skills version:' , error ) ;
112- return 'unknown' ;
118+ const skill = JSON . parse ( readFileSync ( skillPath , 'utf-8' ) ) ;
119+ if ( skill . version ) {
120+ return skill . version ;
121+ }
122+ } catch ( error ) {
123+ // Try next file
124+ continue ;
125+ }
113126 }
127+
128+ console . warn ( '[UpdateChecker] Could not determine skills version from any skill file' ) ;
129+ return 'unknown' ;
114130 }
115131
116132 /**
@@ -153,6 +169,11 @@ export class UpdateChecker {
153169 writeFileSync ( this . versionFilePath , JSON . stringify ( info , null , 2 ) , 'utf-8' ) ;
154170 } catch ( error ) {
155171 console . error ( '[UpdateChecker] Failed to save version info:' , error ) ;
172+ console . warn (
173+ '[UpdateChecker] WARNING: Version info could not be persisted at' ,
174+ this . versionFilePath ,
175+ '- update checks may be repeated or version tracking may be inaccurate.'
176+ ) ;
156177 }
157178 }
158179
@@ -177,9 +198,19 @@ export class UpdateChecker {
177198 return null ;
178199 }
179200
180- const data = await response . json ( ) as { tag_name : string } ;
201+ const data : unknown = await response . json ( ) ;
202+ if (
203+ ! data ||
204+ typeof data !== 'object' ||
205+ data === null ||
206+ typeof ( data as { tag_name ?: unknown } ) . tag_name !== 'string'
207+ ) {
208+ console . error ( '[UpdateChecker] GitHub API response missing valid tag_name field' ) ;
209+ return null ;
210+ }
211+ const tagName = ( data as { tag_name : string } ) . tag_name ;
181212 // Tag format is typically "v0.132.0" or "0.132.0"
182- const version = data . tag_name . replace ( / ^ v / , '' ) ;
213+ const version = tagName . replace ( / ^ v / , '' ) ;
183214 return version ;
184215 } catch ( error ) {
185216 console . error ( '[UpdateChecker] Failed to check GitHub releases:' , error ) ;
@@ -189,11 +220,20 @@ export class UpdateChecker {
189220
190221 /**
191222 * Compare semantic versions (simple implementation)
223+ * Handles only numeric versions like "1.2.3" (pre-release tags are ignored)
192224 */
193225 private compareVersions ( v1 : string , v2 : string ) : number {
194226 const parts1 = v1 . split ( '.' ) . map ( Number ) ;
195227 const parts2 = v2 . split ( '.' ) . map ( Number ) ;
196228
229+ // Validate that all parts are valid numbers
230+ if ( parts1 . some ( isNaN ) || parts2 . some ( isNaN ) ) {
231+ console . warn (
232+ `[UpdateChecker] Invalid version format: "${ v1 } " or "${ v2 } " - comparison may be incorrect`
233+ ) ;
234+ return 0 ; // Treat as equal if we can't compare
235+ }
236+
197237 for ( let i = 0 ; i < Math . max ( parts1 . length , parts2 . length ) ; i ++ ) {
198238 const part1 = parts1 [ i ] || 0 ;
199239 const part2 = parts2 [ i ] || 0 ;
0 commit comments