@@ -58,6 +58,70 @@ ipcMain.handle('project:analyze', async (event, folderPath): Promise<LinguistRes
5858 } )
5959} )
6060
61+ // 读取项目许可证的前若干行
62+ ipcMain . handle ( 'project:read-license' , async ( _evt , folderPath : string , maxLines = 20 ) => {
63+ try {
64+ if ( ! folderPath || typeof folderPath !== 'string' )
65+ return { success : false , message : 'Invalid project path' }
66+
67+ const stat = await fs . stat ( folderPath ) . catch ( ( ) => null )
68+ if ( ! stat || ! stat . isDirectory ( ) )
69+ return { success : false , message : 'Project path does not exist or is not a directory' }
70+
71+ const entries = await fs . readdir ( folderPath )
72+ const candidates = entries . filter ( ( name ) => {
73+ const lower = name . toLowerCase ( )
74+ return (
75+ // 常见文件名:license/licence/copying/unlicense,允许有扩展名
76+ lower . startsWith ( 'license' )
77+ || lower . startsWith ( 'licence' )
78+ || lower . startsWith ( 'copying' )
79+ || lower . startsWith ( 'unlicense' )
80+ )
81+ } )
82+
83+ if ( ! candidates . length )
84+ return { success : false , message : 'License file not found' }
85+
86+ const rank = ( filename : string ) => {
87+ const f = filename . toLowerCase ( )
88+ const ext = path . extname ( f )
89+ const base = f . replace ( ext , '' )
90+ // 优先级:license > licence > copying > unlicense;无扩展名 > .md > .txt > 其他
91+ const baseScore = base . startsWith ( 'license' )
92+ ? 0
93+ : base . startsWith ( 'licence' )
94+ ? 1
95+ : base . startsWith ( 'copying' )
96+ ? 2
97+ : base . startsWith ( 'unlicense' )
98+ ? 3
99+ : 9
100+ const extScore = ext === '' ? 0 : ext === '.md' ? 1 : ext === '.txt' ? 2 : 3
101+ return baseScore * 10 + extScore
102+ }
103+
104+ const picked = candidates . sort ( ( a , b ) => rank ( a ) - rank ( b ) ) [ 0 ]
105+ const full = path . join ( folderPath , picked )
106+ const fileStat = await fs . stat ( full )
107+ if ( ! fileStat . isFile ( ) )
108+ return { success : false , message : 'License file is not a regular file' }
109+
110+ const content = await fs . readFile ( full , 'utf8' )
111+ const lines = content . split ( / \r ? \n / ) . slice ( 0 , Math . max ( 1 , Math . min ( 100 , maxLines ) ) )
112+
113+ return {
114+ success : true ,
115+ filename : picked ,
116+ snippet : lines . join ( '\n' ) ,
117+ lines : lines . length ,
118+ }
119+ }
120+ catch ( e : any ) {
121+ return { success : false , message : String ( e ?. message || e ) }
122+ }
123+ } )
124+
61125// 使用IDE打开项目
62126ipcMain . handle ( 'project:open' , async ( _ , idePath : string , projectPath : string ) : Promise < string > => {
63127 if ( ! idePath || ! projectPath ) {
0 commit comments