@@ -32,6 +32,13 @@ use thiserror::Error;
3232use unicode_width:: UnicodeWidthStr ;
3333
3434use crate :: cli:: chat:: cli:: editor:: open_editor_file;
35+ use crate :: cli:: chat:: prompt_args:: {
36+ count_arguments,
37+ has_args_placeholder,
38+ substitute_arguments,
39+ validate_placeholders,
40+ ArgumentError ,
41+ } ;
3542use crate :: cli:: chat:: tool_manager:: PromptBundle ;
3643use crate :: cli:: chat:: {
3744 ChatError ,
@@ -110,6 +117,13 @@ impl Prompt {
110117
111118 /// Save content to the prompt file
112119 fn save_content ( & self , content : & str ) -> Result < ( ) , GetPromptError > {
120+ // Validate argument placeholders before saving
121+ if let Err ( arg_error) = validate_placeholders ( content) {
122+ return Err ( GetPromptError :: General (
123+ eyre:: eyre!( "Invalid argument placeholders: {}" , arg_error)
124+ ) ) ;
125+ }
126+
113127 // Ensure parent directory exists
114128 if let Some ( parent) = self . path . parent ( ) {
115129 fs:: create_dir_all ( parent) . map_err ( GetPromptError :: Io ) ?;
@@ -741,7 +755,22 @@ impl PromptsArgs {
741755 style:: Print ( "\n " ) ,
742756 ) ?;
743757 for name in & global_prompts {
758+ let prompts = Prompts :: new ( name, os) . map_err ( |e| ChatError :: Custom ( e. to_string ( ) . into ( ) ) ) ?;
759+ let arg_count = if let Some ( ( content, _) ) = prompts. load_existing ( ) . map_err ( |e| ChatError :: Custom ( e. to_string ( ) . into ( ) ) ) ? {
760+ count_arguments ( & content)
761+ } else {
762+ 0
763+ } ;
764+
744765 queue ! ( session. stderr, style:: Print ( "- " ) , style:: Print ( name) ) ?;
766+ if arg_count > 0 {
767+ queue ! (
768+ session. stderr,
769+ StyledText :: secondary_fg( ) ,
770+ style:: Print ( & format!( " ({} arguments)" , arg_count) ) ,
771+ StyledText :: reset( ) ,
772+ ) ?;
773+ }
745774 queue ! ( session. stderr, style:: Print ( "\n " ) ) ?;
746775 }
747776 }
@@ -759,7 +788,22 @@ impl PromptsArgs {
759788 ) ?;
760789 for name in & local_prompts {
761790 let has_global_version = overridden_globals. contains ( name) ;
791+ let prompts = Prompts :: new ( name, os) . map_err ( |e| ChatError :: Custom ( e. to_string ( ) . into ( ) ) ) ?;
792+ let arg_count = if let Some ( ( content, _) ) = prompts. load_existing ( ) . map_err ( |e| ChatError :: Custom ( e. to_string ( ) . into ( ) ) ) ? {
793+ count_arguments ( & content)
794+ } else {
795+ 0
796+ } ;
797+
762798 queue ! ( session. stderr, style:: Print ( "- " ) , style:: Print ( name) , ) ?;
799+ if arg_count > 0 {
800+ queue ! (
801+ session. stderr,
802+ StyledText :: secondary_fg( ) ,
803+ style:: Print ( & format!( " ({} arguments)" , arg_count) ) ,
804+ StyledText :: reset( ) ,
805+ ) ?;
806+ }
763807 if has_global_version {
764808 queue ! (
765809 session. stderr,
@@ -1245,6 +1289,8 @@ impl PromptsSubcommand {
12451289 ) ?;
12461290
12471291 // Display usage example
1292+ let arg_count = count_arguments ( content) ;
1293+ let has_args = has_args_placeholder ( content) ;
12481294 queue ! (
12491295 session. stderr,
12501296 style:: SetAttribute ( Attribute :: Bold ) ,
@@ -1253,10 +1299,73 @@ impl PromptsSubcommand {
12531299 StyledText :: success_fg( ) ,
12541300 style:: Print ( "@" ) ,
12551301 style:: Print ( name) ,
1302+ ) ?;
1303+
1304+ // Show argument placeholders if any exist
1305+ if arg_count > 0 {
1306+ if has_args {
1307+ queue ! ( session. stderr, style:: Print ( " <arguments>" ) ) ?;
1308+ }
1309+ if let Ok ( positions) = validate_placeholders ( content) {
1310+ for pos in positions {
1311+ queue ! (
1312+ session. stderr,
1313+ style:: Print ( " <arg" ) ,
1314+ style:: Print ( pos. to_string( ) ) ,
1315+ style:: Print ( ">" ) ,
1316+ ) ?;
1317+ }
1318+ }
1319+ }
1320+
1321+ queue ! (
1322+ session. stderr,
12561323 StyledText :: reset( ) ,
12571324 style:: Print ( "\n \n " ) ,
12581325 ) ?;
12591326
1327+ // Display argument information
1328+ if arg_count > 0 {
1329+ queue ! (
1330+ session. stderr,
1331+ style:: SetAttribute ( Attribute :: Bold ) ,
1332+ style:: Print ( "Arguments:" ) ,
1333+ StyledText :: reset_attributes( ) ,
1334+ style:: Print ( "\n " ) ,
1335+ ) ?;
1336+
1337+ if has_args {
1338+ queue ! (
1339+ session. stderr,
1340+ style:: Print ( " " ) ,
1341+ StyledText :: error_fg( ) ,
1342+ style:: Print ( "(required) " ) ,
1343+ StyledText :: brand_fg( ) ,
1344+ style:: Print ( "$ARGS or ${@}" ) ,
1345+ StyledText :: reset( ) ,
1346+ style:: Print ( " - All provided arguments\n " ) ,
1347+ ) ?;
1348+ }
1349+
1350+ if let Ok ( positions) = validate_placeholders ( content) {
1351+ for pos in positions {
1352+ queue ! (
1353+ session. stderr,
1354+ style:: Print ( " " ) ,
1355+ StyledText :: error_fg( ) ,
1356+ style:: Print ( "(required) " ) ,
1357+ StyledText :: brand_fg( ) ,
1358+ style:: Print ( & format!( "arg{}" , pos) ) ,
1359+ StyledText :: reset( ) ,
1360+ style:: Print ( " - Positional argument " ) ,
1361+ style:: Print ( pos. to_string( ) ) ,
1362+ style:: Print ( "\n " ) ,
1363+ ) ?;
1364+ }
1365+ }
1366+ queue ! ( session. stderr, style:: Print ( "\n " ) ) ?;
1367+ }
1368+
12601369 // Display content preview (first few lines)
12611370 queue ! (
12621371 session. stderr,
@@ -1330,16 +1439,52 @@ impl PromptsSubcommand {
13301439 execute ! ( session. stderr) ?;
13311440 }
13321441
1442+ // Handle argument substitution for file-based prompt
1443+ let final_content = if let Some ( ref args) = arguments {
1444+ match substitute_arguments ( & content, args) {
1445+ Ok ( ( substituted, has_excess) ) => {
1446+ if has_excess {
1447+ queue ! (
1448+ session. stderr,
1449+ style:: Print ( "\n " ) ,
1450+ StyledText :: warning_fg( ) ,
1451+ style:: Print ( "⚠ Warning: More arguments provided than expected. Ignoring extra arguments.\n " ) ,
1452+ StyledText :: reset( ) ,
1453+ ) ?;
1454+ execute ! ( session. stderr) ?;
1455+ }
1456+ substituted
1457+ } ,
1458+ Err ( arg_error) => {
1459+ queue ! (
1460+ session. stderr,
1461+ style:: Print ( "\n " ) ,
1462+ StyledText :: error_fg( ) ,
1463+ style:: Print ( "Error processing arguments: " ) ,
1464+ style:: Print ( arg_error. to_string( ) ) ,
1465+ StyledText :: reset( ) ,
1466+ style:: Print ( "\n " ) ,
1467+ ) ?;
1468+ execute ! ( session. stderr) ?;
1469+ return Ok ( ChatState :: PromptUser {
1470+ skip_printing_tools : true ,
1471+ } ) ;
1472+ }
1473+ }
1474+ } else {
1475+ content. clone ( )
1476+ } ;
1477+
13331478 // Display the file-based prompt content to the user
1334- display_file_prompt_content ( & name, & content , session) ?;
1479+ display_file_prompt_content ( & name, & final_content , session) ?;
13351480
13361481 // Handle local prompt
13371482 session. pending_prompts . clear ( ) ;
13381483
1339- // Create a PromptMessage from the local prompt content
1484+ // Create a PromptMessage from the processed prompt content
13401485 let prompt_message = PromptMessage {
13411486 role : PromptMessageRole :: User ,
1342- content : PromptMessageContent :: Text { text : content . clone ( ) } ,
1487+ content : PromptMessageContent :: Text { text : final_content } ,
13431488 } ;
13441489 session. pending_prompts . push_back ( prompt_message) ;
13451490
0 commit comments