upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/bin/ngit/sub_commands/list.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-03-05 13:31:35 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-03-05 13:31:35 +0000
commit609f3c3db02d437222e2c8e171189179d06c3e9c (patch)
tree23747f31010721fa11c76cca467a4ef89d95eda5 /src/bin/ngit/sub_commands/list.rs
parentb143abb25d6ece32412629baa3e75e94d139979f (diff)
feat(json): emit nevent1 bech32 IDs in --json output
All id and reply_to fields in --json output now use nevent1... bech32 encoding (with relay hint when available) instead of raw hex, making them directly usable as nostr: URI references in --body text. Update SKILL.md to document the nevent1 ID format and enforce use of nostr:nevent1... URIs when cross-referencing events in body text.
Diffstat (limited to 'src/bin/ngit/sub_commands/list.rs')
-rw-r--r--src/bin/ngit/sub_commands/list.rs40
1 files changed, 31 insertions, 9 deletions
diff --git a/src/bin/ngit/sub_commands/list.rs b/src/bin/ngit/sub_commands/list.rs
index df147bc..ee9840e 100644
--- a/src/bin/ngit/sub_commands/list.rs
+++ b/src/bin/ngit/sub_commands/list.rs
@@ -24,9 +24,9 @@ use ngit::{
24use nostr::{ 24use nostr::{
25 FromBech32, ToBech32, 25 FromBech32, ToBech32,
26 filter::{Alphabet, SingleLetterTag}, 26 filter::{Alphabet, SingleLetterTag},
27 nips::nip19::Nip19, 27 nips::nip19::{Nip19, Nip19Event},
28}; 28};
29use nostr_sdk::Kind; 29use nostr_sdk::{Kind, RelayUrl};
30 30
31use crate::{ 31use crate::{
32 cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms}, 32 cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms},
@@ -246,6 +246,7 @@ pub async fn launch(
246 .await? 246 .await?
247 .len() 247 .len()
248 }; 248 };
249 let relay_hint = repo_ref.relays.first();
249 return show_proposal_details( 250 return show_proposal_details(
250 &filtered_proposals, 251 &filtered_proposals,
251 event_id_or_nevent, 252 event_id_or_nevent,
@@ -253,11 +254,13 @@ pub async fn launch(
253 show_comments, 254 show_comments,
254 comment_count, 255 comment_count,
255 &comments, 256 &comments,
257 relay_hint,
256 ); 258 );
257 } 259 }
258 260
261 let relay_hint = repo_ref.relays.first();
259 if json { 262 if json {
260 output_json(&filtered_proposals)?; 263 output_json(&filtered_proposals, relay_hint)?;
261 } else { 264 } else {
262 output_table(&filtered_proposals, &status, &label_filter); 265 output_table(&filtered_proposals, &status, &label_filter);
263 } 266 }
@@ -384,11 +387,28 @@ fn output_table(
384 ); 387 );
385} 388}
386 389
387fn output_json(proposals: &[(&nostr::Event, Kind, Vec<String>, Option<String>)]) -> Result<()> { 390/// Convert an event ID to a `nevent1…` bech32 string, including a relay hint
391/// when one is available. Falls back to the plain hex string on error.
392fn event_id_to_nevent(event_id: nostr::EventId, relay: Option<&RelayUrl>) -> String {
393 let relays = relay.map(|r| vec![r.clone()]).unwrap_or_default();
394 Nip19Event {
395 event_id,
396 relays,
397 author: None,
398 kind: None,
399 }
400 .to_bech32()
401 .unwrap_or_else(|_| event_id.to_hex())
402}
403
404fn output_json(
405 proposals: &[(&nostr::Event, Kind, Vec<String>, Option<String>)],
406 relay_hint: Option<&RelayUrl>,
407) -> Result<()> {
388 let json_output: Vec<serde_json::Value> = proposals 408 let json_output: Vec<serde_json::Value> = proposals
389 .iter() 409 .iter()
390 .map(|(proposal, status_kind, proposal_labels, subject_override)| { 410 .map(|(proposal, status_kind, proposal_labels, subject_override)| {
391 let id = proposal.id.to_string(); 411 let id = event_id_to_nevent(proposal.id, relay_hint);
392 let status = status_kind_to_str(*status_kind).to_string(); 412 let status = status_kind_to_str(*status_kind).to_string();
393 let (title, author, branch) = if let Ok(cl) = event_to_cover_letter(proposal) { 413 let (title, author, branch) = if let Ok(cl) = event_to_cover_letter(proposal) {
394 ( 414 (
@@ -460,6 +480,7 @@ fn show_proposal_details(
460 show_comments: bool, 480 show_comments: bool,
461 comment_count: usize, 481 comment_count: usize,
462 comments: &[nostr::Event], 482 comments: &[nostr::Event],
483 relay_hint: Option<&RelayUrl>,
463) -> Result<()> { 484) -> Result<()> {
464 use nostr::ToBech32; 485 use nostr::ToBech32;
465 486
@@ -484,9 +505,10 @@ fn show_proposal_details(
484 let comments_json: Vec<serde_json::Value> = comments 505 let comments_json: Vec<serde_json::Value> = comments
485 .iter() 506 .iter()
486 .map(|c| { 507 .map(|c| {
487 let reply_to = comment_reply_to(c).map(|id| id.to_string()); 508 let reply_to =
509 comment_reply_to(c).map(|id| event_id_to_nevent(id, relay_hint));
488 serde_json::json!({ 510 serde_json::json!({
489 "id": c.id.to_string(), 511 "id": event_id_to_nevent(c.id, relay_hint),
490 "author": c.pubkey.to_bech32().unwrap_or_default(), 512 "author": c.pubkey.to_bech32().unwrap_or_default(),
491 "created_at": c.created_at.as_secs(), 513 "created_at": c.created_at.as_secs(),
492 "reply_to": reply_to, 514 "reply_to": reply_to,
@@ -495,7 +517,7 @@ fn show_proposal_details(
495 }) 517 })
496 .collect(); 518 .collect();
497 serde_json::json!({ 519 serde_json::json!({
498 "id": proposal.id.to_string(), 520 "id": event_id_to_nevent(proposal.id, relay_hint),
499 "status": status_kind_to_str(*status_kind), 521 "status": status_kind_to_str(*status_kind),
500 "subject": display_title, 522 "subject": display_title,
501 "author": proposal.pubkey.to_bech32().unwrap_or_default(), 523 "author": proposal.pubkey.to_bech32().unwrap_or_default(),
@@ -507,7 +529,7 @@ fn show_proposal_details(
507 }) 529 })
508 } else { 530 } else {
509 serde_json::json!({ 531 serde_json::json!({
510 "id": proposal.id.to_string(), 532 "id": event_id_to_nevent(proposal.id, relay_hint),
511 "status": status_kind_to_str(*status_kind), 533 "status": status_kind_to_str(*status_kind),
512 "subject": display_title, 534 "subject": display_title,
513 "author": proposal.pubkey.to_bech32().unwrap_or_default(), 535 "author": proposal.pubkey.to_bech32().unwrap_or_default(),