@@ -26,6 +26,26 @@ function quoteShellWord(value: string): string {
2626 return `'${ value . replaceAll ( "'" , `'"'"'` ) } '` ;
2727}
2828
29+ function stripUrlQueryAndFragment ( value : string ) : string {
30+ const queryIndex = value . indexOf ( "?" ) ;
31+ const fragmentIndex = value . indexOf ( "#" ) ;
32+ if ( queryIndex === - 1 ) {
33+ return fragmentIndex === - 1 ? value : value . slice ( 0 , fragmentIndex ) ;
34+ }
35+ if ( fragmentIndex === - 1 ) return value . slice ( 0 , queryIndex ) ;
36+ return value . slice ( 0 , Math . min ( queryIndex , fragmentIndex ) ) ;
37+ }
38+
39+ function stripGitSuffix ( value : string ) : string {
40+ return value . toLowerCase ( ) . endsWith ( ".git" ) ? value . slice ( 0 , - 4 ) : value ;
41+ }
42+
43+ function lastRemotePathSegment ( value : string ) : string {
44+ const lastSlash = value . lastIndexOf ( "/" ) ;
45+ const lastColon = value . lastIndexOf ( ":" ) ;
46+ return value . slice ( Math . max ( lastSlash , lastColon ) + 1 ) . trim ( ) ;
47+ }
48+
2949export function providerMountPrefix ( provider : string ) : string | undefined {
3050 return PROVIDER_MOUNT_PREFIXES [ provider ] ;
3151}
@@ -62,12 +82,7 @@ export function resolveRepositoryMountPath(provider: string, resource: SessionGi
6282 return resource . mount_path ;
6383 }
6484 // Accept URL remotes as well as scp-like SSH remotes such as git@host:team/repo.git.
65- const repositoryName = resource . url
66- . replace ( / [ ? # ] .* $ / , "" )
67- . split ( / [ / : ] / )
68- . filter ( Boolean )
69- . at ( - 1 )
70- ?. replace ( / \. g i t $ / i, "" ) ;
85+ const repositoryName = stripGitSuffix ( lastRemotePathSegment ( stripUrlQueryAndFragment ( resource . url ) ) ) ;
7186 if ( ! repositoryName ) {
7287 throw new UserError ( `Cannot derive a ${ provider } Git repository mount path from URL '${ resource . url } '.` ) ;
7388 }
@@ -95,13 +110,32 @@ export function prependFileHint(prompt: string, files: MountedFile[] | undefined
95110 return `${ hint } \n\n${ prompt } ` ;
96111}
97112
98- const FILE_MENTION_SENTINEL_RE = / \u27E6 f i l e : ( .+ ?) \u27E7 / g;
113+ const FILE_MENTION_START = "\u27E6file:" ;
114+ const FILE_MENTION_END = "\u27E7" ;
99115
100116/** 将 prompt 内 ⟦file:mountPath⟧ 占位符替换为 provider 感知的真实 sandbox 路径 */
101117export function rewriteFileMentions ( prompt : string , provider : string ) : string {
102- return prompt . replace ( FILE_MENTION_SENTINEL_RE , ( _match , mountPath : string ) =>
103- resolveSandboxMountPath ( provider , mountPath ) ,
104- ) ;
118+ let cursor = 0 ;
119+ let rewritten = "" ;
120+
121+ while ( cursor < prompt . length ) {
122+ const start = prompt . indexOf ( FILE_MENTION_START , cursor ) ;
123+ if ( start === - 1 ) {
124+ rewritten += prompt . slice ( cursor ) ;
125+ break ;
126+ }
127+ const mountPathStart = start + FILE_MENTION_START . length ;
128+ const end = prompt . indexOf ( FILE_MENTION_END , mountPathStart ) ;
129+ if ( end === - 1 ) {
130+ rewritten += prompt . slice ( cursor ) ;
131+ break ;
132+ }
133+ rewritten += prompt . slice ( cursor , start ) ;
134+ rewritten += resolveSandboxMountPath ( provider , prompt . slice ( mountPathStart , end ) ) ;
135+ cursor = end + FILE_MENTION_END . length ;
136+ }
137+
138+ return rewritten ;
105139}
106140
107141/** 发送给 provider 前统一处理:先替换 mention 占位符,再 prepend 文件 hint */
0 commit comments