@@ -208,6 +208,50 @@ enum WriteAction {
208208 /// Archived note path (e.g., "04-Archive/01-Projects/note.md").
209209 file : String ,
210210 } ,
211+ /// Edit a specific section of a note.
212+ Edit {
213+ /// Target note (path, basename, or #docid).
214+ #[ arg( long) ]
215+ file : String ,
216+ /// Section heading to edit (case-insensitive).
217+ #[ arg( long) ]
218+ heading : String ,
219+ /// Content to add/replace in the section.
220+ #[ arg( long) ]
221+ content : String ,
222+ /// Edit mode: "replace", "prepend", or "append" (default: "append").
223+ #[ arg( long, default_value = "append" ) ]
224+ mode : String ,
225+ } ,
226+ /// Rewrite a note's body content (preserves frontmatter by default).
227+ Rewrite {
228+ /// Target note (path, basename, or #docid).
229+ #[ arg( long) ]
230+ file : String ,
231+ /// New body content.
232+ #[ arg( long) ]
233+ content : String ,
234+ /// Preserve existing frontmatter (default: true).
235+ #[ arg( long, default_value_t = true ) ]
236+ preserve_frontmatter : bool ,
237+ } ,
238+ /// Edit a note's frontmatter properties.
239+ EditFrontmatter {
240+ /// Target note (path, basename, or #docid).
241+ #[ arg( long) ]
242+ file : String ,
243+ /// Operations as JSON string: [{"op":"add_tag","value":"rust"},{"op":"set","key":"status","value":"done"}]
244+ #[ arg( long) ]
245+ operations : String ,
246+ } ,
247+ /// Delete a note.
248+ Delete {
249+ /// Target note (path, basename, or #docid).
250+ file : String ,
251+ /// Delete mode: "soft" (archive, default) or "hard" (permanent).
252+ #[ arg( long, default_value = "soft" ) ]
253+ mode : String ,
254+ } ,
211255}
212256
213257#[ derive( Subcommand , Debug ) ]
@@ -975,6 +1019,136 @@ async fn main() -> Result<()> {
9751019 println ! ( "Restored: {} → {}" , file, result. path) ;
9761020 }
9771021 }
1022+ WriteAction :: Edit {
1023+ file,
1024+ heading,
1025+ content,
1026+ mode,
1027+ } => {
1028+ let edit_mode = match mode. as_str ( ) {
1029+ "replace" => engraph:: writer:: EditMode :: Replace ,
1030+ "prepend" => engraph:: writer:: EditMode :: Prepend ,
1031+ _ => engraph:: writer:: EditMode :: Append ,
1032+ } ;
1033+ let input = engraph:: writer:: EditInput {
1034+ file,
1035+ heading,
1036+ content,
1037+ mode : edit_mode,
1038+ modified_by : "cli" . into ( ) ,
1039+ } ;
1040+ let result =
1041+ engraph:: writer:: edit_note ( & store, & vault_path, & input, None ) ?;
1042+ if cli. json {
1043+ println ! ( "{}" , serde_json:: to_string_pretty( & result) ?) ;
1044+ } else {
1045+ println ! (
1046+ "Edited: {} section \" {}\" ({})" ,
1047+ result. path, result. heading, result. mode
1048+ ) ;
1049+ }
1050+ }
1051+ WriteAction :: Rewrite {
1052+ file,
1053+ content,
1054+ preserve_frontmatter,
1055+ } => {
1056+ let input = engraph:: writer:: RewriteInput {
1057+ file,
1058+ content,
1059+ preserve_frontmatter,
1060+ modified_by : "cli" . into ( ) ,
1061+ } ;
1062+ let result =
1063+ engraph:: writer:: rewrite_note ( & store, & vault_path, & input) ?;
1064+ if cli. json {
1065+ println ! ( "{}" , serde_json:: to_string_pretty( & result) ?) ;
1066+ } else {
1067+ println ! (
1068+ "Rewrote: {} (frontmatter {})" ,
1069+ result. path,
1070+ if preserve_frontmatter { "preserved" } else { "replaced" }
1071+ ) ;
1072+ }
1073+ }
1074+ WriteAction :: EditFrontmatter { file, operations } => {
1075+ let raw_ops: Vec < serde_json:: Value > = serde_json:: from_str ( & operations)
1076+ . map_err ( |e| anyhow:: anyhow!( "invalid JSON operations: {}" , e) ) ?;
1077+ let mut ops = Vec :: new ( ) ;
1078+ for raw in & raw_ops {
1079+ let op = raw. get ( "op" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) ;
1080+ match op {
1081+ "set" => {
1082+ let key = raw. get ( "key" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1083+ let value = raw. get ( "value" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1084+ ops. push ( engraph:: writer:: FrontmatterOp :: Set ( key, value) ) ;
1085+ }
1086+ "remove" => {
1087+ let key = raw. get ( "key" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1088+ ops. push ( engraph:: writer:: FrontmatterOp :: Remove ( key) ) ;
1089+ }
1090+ "add_tag" => {
1091+ let value = raw. get ( "value" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1092+ ops. push ( engraph:: writer:: FrontmatterOp :: AddTag ( value) ) ;
1093+ }
1094+ "remove_tag" => {
1095+ let value = raw. get ( "value" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1096+ ops. push ( engraph:: writer:: FrontmatterOp :: RemoveTag ( value) ) ;
1097+ }
1098+ "add_alias" => {
1099+ let value = raw. get ( "value" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1100+ ops. push ( engraph:: writer:: FrontmatterOp :: AddAlias ( value) ) ;
1101+ }
1102+ "remove_alias" => {
1103+ let value = raw. get ( "value" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "" ) . to_string ( ) ;
1104+ ops. push ( engraph:: writer:: FrontmatterOp :: RemoveAlias ( value) ) ;
1105+ }
1106+ _ => {
1107+ return Err ( anyhow:: anyhow!( "unknown frontmatter op: {:?}" , op) ) ;
1108+ }
1109+ }
1110+ }
1111+ let input = engraph:: writer:: EditFrontmatterInput {
1112+ file,
1113+ operations : ops,
1114+ modified_by : "cli" . into ( ) ,
1115+ } ;
1116+ let result =
1117+ engraph:: writer:: edit_frontmatter ( & store, & vault_path, & input) ?;
1118+ if cli. json {
1119+ println ! ( "{}" , serde_json:: to_string_pretty( & result) ?) ;
1120+ } else {
1121+ println ! ( "Frontmatter updated: {}" , result. path) ;
1122+ }
1123+ }
1124+ WriteAction :: Delete { file, mode } => {
1125+ let delete_mode = match mode. as_str ( ) {
1126+ "hard" => engraph:: writer:: DeleteMode :: Hard ,
1127+ _ => engraph:: writer:: DeleteMode :: Soft ,
1128+ } ;
1129+ let archive_folder = profile
1130+ . as_ref ( )
1131+ . and_then ( |p| p. structure . folders . archive . as_deref ( ) )
1132+ . unwrap_or ( "04-Archive" ) ;
1133+ engraph:: writer:: delete_note (
1134+ & store,
1135+ & vault_path,
1136+ & file,
1137+ delete_mode,
1138+ archive_folder,
1139+ ) ?;
1140+ if cli. json {
1141+ println ! (
1142+ "{}" ,
1143+ serde_json:: to_string_pretty( & serde_json:: json!( {
1144+ "deleted" : file,
1145+ "mode" : mode
1146+ } ) ) ?
1147+ ) ;
1148+ } else {
1149+ println ! ( "Deleted: {} ({})" , file, mode) ;
1150+ }
1151+ }
9781152 }
9791153 }
9801154
0 commit comments