@@ -65,7 +65,7 @@ import { DefaultAmazonQAppInitContext } from '../../../amazonq/apps/initContext'
6565import globals from '../../../shared/extensionGlobals'
6666import { MynahIconsType , MynahUIDataModel , QuickActionCommand } from '@aws/mynah-ui'
6767import { LspClient } from '../../../amazonq/lsp/lspClient'
68- import { ContextCommandItem } from '../../../amazonq/lsp/types'
68+ import { ContextCommandItem , ContextCommandItemType } from '../../../amazonq/lsp/types'
6969import { createPromptCommand , workspaceCommand } from '../../../amazonq/webview/ui/tabs/constants'
7070import fs from '../../../shared/fs/fs'
7171import * as vscode from 'vscode'
@@ -121,12 +121,16 @@ export interface ChatControllerMessageListeners {
121121 readonly processFileClick : MessageListener < FileClick >
122122}
123123
124- const promptFileExtension = '.prompt'
124+ const promptFileExtension = '.prompt.md '
125125
126126const additionalContentInnerContextLimit = 8192
127127
128128const aditionalContentNameLimit = 1024
129129
130+ const getUserPromptsDirectory = ( ) => {
131+ return path . join ( fs . getUserHomeDir ( ) , '.aws' , 'amazonq' , 'prompts' )
132+ }
133+
130134export class ChatController {
131135 private readonly sessionStorage : ChatSessionStorage
132136 private readonly triggerEventsStorage : TriggerEventsStorage
@@ -458,35 +462,19 @@ export class ChatController {
458462 }
459463 const promptsCmd : QuickActionCommand = contextCommand [ 0 ] . commands ?. [ 3 ]
460464
461- // Check .aws/prompts for prompt files in workspace
462- const workspacePromptFiles = await vscode . workspace . findFiles ( `.aws/prompts/*${ promptFileExtension } ` )
463-
464- if ( workspacePromptFiles . length > 0 ) {
465- promptsCmd . children ?. [ 0 ] . commands . push (
466- ...workspacePromptFiles . map ( ( file ) => {
467- const workspacePath = vscode . workspace . getWorkspaceFolder ( file ) ?. uri . path || path . dirname ( file . path )
468- const relativePath = path . relative ( workspacePath , file . path )
469- return {
470- command : path . basename ( file . path , promptFileExtension ) ,
471- icon : 'magic' as MynahIconsType ,
472- route : [ workspacePath , relativePath ] ,
473- }
474- } )
475- )
476- }
477- // Check ~/.aws/prompts for global prompt files
465+ // Check for user prompts
478466 try {
479- const systemPromptsDirectory = path . join ( fs . getUserHomeDir ( ) , '.aws' , 'prompts' )
480- const directoryExists = await fs . exists ( systemPromptsDirectory )
467+ const userPromptsDirectory = getUserPromptsDirectory ( )
468+ const directoryExists = await fs . exists ( userPromptsDirectory )
481469 if ( directoryExists ) {
482- const systemPromptFiles = await fs . readdir ( systemPromptsDirectory )
470+ const systemPromptFiles = await fs . readdir ( userPromptsDirectory )
483471 promptsCmd . children ?. [ 0 ] . commands . push (
484472 ...systemPromptFiles
485473 . filter ( ( [ name ] ) => name . endsWith ( promptFileExtension ) )
486474 . map ( ( [ name ] ) => ( {
487475 command : path . basename ( name , promptFileExtension ) ,
488476 icon : 'magic' as MynahIconsType ,
489- route : [ systemPromptsDirectory , name ] ,
477+ route : [ userPromptsDirectory , name ] ,
490478 } ) )
491479 )
492480 }
@@ -536,19 +524,7 @@ export class ChatController {
536524 mandatory : true ,
537525 title : 'Prompt name' ,
538526 placeholder : 'Enter prompt name' ,
539- description : 'Use this prompt in the chat by typing `@` followed by the prompt name.' ,
540- } ,
541- {
542- id : 'shared-scope' ,
543- type : 'select' ,
544- title : 'Save globally for all projects?' ,
545- mandatory : true ,
546- value : 'system' ,
547- description : `If yes is selected, ${ promptFileExtension } file will be saved in ~/.aws/prompts.` ,
548- options : [
549- { value : 'project' , label : 'No' } ,
550- { value : 'system' , label : 'Yes' } ,
551- ] ,
527+ description : `Use this prompt by typing \`@\` followed by the prompt name. Prompt will be saved in ${ getUserPromptsDirectory ( ) } .` ,
552528 } ,
553529 ] ,
554530 [
@@ -568,18 +544,11 @@ export class ChatController {
568544 private async processCustomFormAction ( message : CustomFormActionMessage ) {
569545 if ( message . tabID ) {
570546 if ( message . action . id === 'submit-create-prompt' ) {
571- let promptsDirectory = path . join ( fs . getUserHomeDir ( ) , '.aws' , 'prompts' )
572- if (
573- vscode . workspace . workspaceFolders ?. [ 0 ] &&
574- message . action . formItemValues ?. [ 'shared-scope' ] === 'project'
575- ) {
576- const workspaceUri = vscode . workspace . workspaceFolders [ 0 ] . uri
577- promptsDirectory = vscode . Uri . joinPath ( workspaceUri , '.aws' , 'prompts' ) . fsPath
578- }
547+ const userPromptsDirectory = getUserPromptsDirectory ( )
579548
580549 const title = message . action . formItemValues ?. [ 'prompt-name' ]
581550 const newFilePath = path . join (
582- promptsDirectory ,
551+ userPromptsDirectory ,
583552 title ? `${ title } ${ promptFileExtension } ` : `default${ promptFileExtension } `
584553 )
585554 const newFileContent = new Uint8Array ( Buffer . from ( '' ) )
@@ -610,8 +579,16 @@ export class ChatController {
610579 if ( ! projectRoot ) {
611580 return
612581 }
613-
614- const absoluteFilePath = path . join ( projectRoot , message . filePath )
582+ let absoluteFilePath = path . join ( projectRoot , message . filePath )
583+
584+ // Handle clicking on a user prompt outside the workspace
585+ if ( message . filePath . endsWith ( promptFileExtension ) ) {
586+ try {
587+ await vscode . workspace . fs . stat ( vscode . Uri . file ( absoluteFilePath ) )
588+ } catch {
589+ absoluteFilePath = path . join ( getUserPromptsDirectory ( ) , message . filePath )
590+ }
591+ }
615592
616593 try {
617594 // Open the file in VSCode
@@ -887,18 +864,36 @@ export class ChatController {
887864 }
888865
889866 private async resolveContextCommandPayload ( triggerPayload : TriggerPayload ) : Promise < string [ ] > {
890- if ( triggerPayload . context === undefined || triggerPayload . context . length === 0 ) {
891- return [ ]
892- }
893867 const contextCommands : ContextCommandItem [ ] = [ ]
894868 const relativePaths : string [ ] = [ ]
895- for ( const context of triggerPayload . context ) {
896- if ( typeof context !== 'string' && context . route && context . route . length === 2 ) {
897- contextCommands . push ( {
898- workspaceFolder : context . route [ 0 ] || '' ,
899- type : context . icon === 'folder' ? 'folder' : 'file' ,
900- relativePath : context . route [ 1 ] || '' ,
869+
870+ // Check for workspace rules to add to context
871+ const workspaceRules = await vscode . workspace . findFiles ( `.amazonq/rules/*${ promptFileExtension } ` )
872+ if ( workspaceRules . length > 0 ) {
873+ contextCommands . push (
874+ ...workspaceRules . map ( ( rule ) => {
875+ const workspaceFolderPath = vscode . workspace . getWorkspaceFolder ( rule ) ?. uri ?. path || ''
876+ return {
877+ workspaceFolder : workspaceFolderPath ,
878+ // todo: add 'prompt' type to LSP model
879+ type : 'file' as ContextCommandItemType ,
880+ relativePath : path . relative ( workspaceFolderPath , rule . path ) ,
881+ }
901882 } )
883+ )
884+ }
885+
886+ // Add context commands added by user to context
887+ if ( triggerPayload . context !== undefined && triggerPayload . context . length > 0 ) {
888+ for ( const context of triggerPayload . context ) {
889+ // todo: add handling of 'prompt' type (dependent on LSP changes)
890+ if ( typeof context !== 'string' && context . route && context . route . length === 2 ) {
891+ contextCommands . push ( {
892+ workspaceFolder : context . route [ 0 ] || '' ,
893+ type : context . icon === 'folder' ? 'folder' : 'file' ,
894+ relativePath : context . route [ 1 ] || '' ,
895+ } )
896+ }
902897 }
903898 }
904899 if ( contextCommands . length === 0 ) {
@@ -916,7 +911,11 @@ export class ChatController {
916911 description : prompt . description . substring ( 0 , aditionalContentNameLimit ) ,
917912 innerContext : prompt . content . substring ( 0 , additionalContentInnerContextLimit ) ,
918913 } )
919- const relativePath = path . relative ( workspaceFolder , prompt . filePath )
914+ let relativePath = path . relative ( workspaceFolder , prompt . filePath )
915+ // Handle user prompts outside the workspace
916+ if ( prompt . filePath . startsWith ( getUserPromptsDirectory ( ) ) ) {
917+ relativePath = path . basename ( prompt . filePath )
918+ }
920919 relativePaths . push ( relativePath )
921920 }
922921 }
0 commit comments