@@ -90,6 +90,8 @@ const CLI_COMMAND_SPEC = {
9090 "contributor-profile" : [ ] ,
9191 "monitor-open-prs" : [ ] ,
9292 "pr-outcomes" : [ ] ,
93+ notifications : [ ] ,
94+ "notifications-read" : [ ] ,
9395 "analyze-branch" : [ ] ,
9496 preflight : [ ] ,
9597 "review-pr" : [ ] ,
@@ -3399,6 +3401,8 @@ async function runCli(args) {
33993401 if ( command === "contributor-profile" ) return contributorProfileCli ( options ) ;
34003402 if ( command === "monitor-open-prs" ) return monitorOpenPrsCli ( options ) ;
34013403 if ( command === "pr-outcomes" ) return prOutcomesCli ( options ) ;
3404+ if ( command === "notifications" ) return notificationsCli ( options ) ;
3405+ if ( command === "notifications-read" ) return notificationsReadCli ( options ) ;
34023406 if ( command === "review-pr" ) return reviewPrCli ( options ) ;
34033407 if ( command !== "analyze-branch" && command !== "preflight" ) {
34043408 const suggestion = suggestCommand ( command ) ;
@@ -3900,6 +3904,66 @@ async function prOutcomesCli(options) {
39003904 }
39013905}
39023906
3907+ function printNotificationsHelp ( ) {
3908+ process . stdout . write (
3909+ [
3910+ "Usage: loopover-mcp notifications --login <github-login> [--json]" ,
3911+ "" ,
3912+ "Your own badge notification feed (newest first) with an unread count, self-scoped." ,
3913+ "Mirrors the loopover_list_notifications MCP tool and GET /v1/contributors/{login}/notifications. No source upload." ,
3914+ "" ,
3915+ "Pass --json for machine-readable output." ,
3916+ ] . join ( "\n" ) + "\n" ,
3917+ ) ;
3918+ }
3919+
3920+ // #6745: CLI mirror of loopover_list_notifications. Login resolves from --login / the active session /
3921+ // LOOPOVER_LOGIN / GITHUB_LOGIN, like the sibling contributor commands.
3922+ async function notificationsCli ( options ) {
3923+ if ( options . help === true ) return printNotificationsHelp ( ) ;
3924+ const login = options . login ?? activeProfile . session ?. login ?? process . env . LOOPOVER_LOGIN ?? process . env . GITHUB_LOGIN ;
3925+ if ( ! login ) throw new Error ( "Pass --login <github-login>, log in with `loopover-mcp login`, or set LOOPOVER_LOGIN." ) ;
3926+ const payload = await getNotifications ( login ) ;
3927+ if ( options . json ) {
3928+ process . stdout . write ( `${ JSON . stringify ( payload , null , 2 ) } \n` ) ;
3929+ return ;
3930+ }
3931+ process . stdout . write ( `LoopOver notifications for ${ login } : ${ payload . unreadCount } unread.\n` ) ;
3932+ for ( const item of payload . notifications ?? [ ] ) {
3933+ // `login` is the user's own value; the API chooses the title text, so it is sanitized before the terminal.
3934+ const flag = item . status === "delivered" ? "*" : " " ;
3935+ process . stdout . write ( `${ sanitizePlainTextTerminalOutput ( `${ flag } ${ item . repoFullName } #${ item . pullNumber } ${ item . title } ` ) } \n` ) ;
3936+ }
3937+ }
3938+
3939+ function printNotificationsReadHelp ( ) {
3940+ process . stdout . write (
3941+ [
3942+ "Usage: loopover-mcp notifications-read --login <github-login> [--id <delivery-id>]... [--json]" ,
3943+ "" ,
3944+ "Mark your delivered notifications read. With no --id, marks all of them." ,
3945+ "Mirrors the loopover_mark_notifications_read MCP tool and POST /v1/contributors/{login}/notifications/read." ,
3946+ "" ,
3947+ "Pass --json for machine-readable output." ,
3948+ ] . join ( "\n" ) + "\n" ,
3949+ ) ;
3950+ }
3951+
3952+ // #6745: CLI mirror of loopover_mark_notifications_read. Repeated --id flags collect into an ids array; omitting
3953+ // them marks every delivered notification read (mirrors the route's absent-body behavior).
3954+ async function notificationsReadCli ( options ) {
3955+ if ( options . help === true ) return printNotificationsReadHelp ( ) ;
3956+ const login = options . login ?? activeProfile . session ?. login ?? process . env . LOOPOVER_LOGIN ?? process . env . GITHUB_LOGIN ;
3957+ if ( ! login ) throw new Error ( "Pass --login <github-login>, log in with `loopover-mcp login`, or set LOOPOVER_LOGIN." ) ;
3958+ const ids = Array . isArray ( options . id ) ? options . id : options . id ? [ options . id ] : undefined ;
3959+ const payload = await postMarkNotificationsRead ( login , ids ) ;
3960+ if ( options . json ) {
3961+ process . stdout . write ( `${ JSON . stringify ( payload , null , 2 ) } \n` ) ;
3962+ return ;
3963+ }
3964+ process . stdout . write ( `Marked ${ payload . marked } LoopOver notification(s) read for ${ login } .\n` ) ;
3965+ }
3966+
39033967function printRepoDecisionHelp ( ) {
39043968 process . stdout . write (
39053969 [
@@ -4380,6 +4444,8 @@ function printHelp() {
43804444 loopover-mcp repo-decision --login <github-login> --repo owner/repo [--json]
43814445 loopover-mcp monitor-open-prs --login <github-login> [--json]
43824446 loopover-mcp pr-outcomes --login <github-login> [--limit N] [--json]
4447+ loopover-mcp notifications --login <github-login> [--json]
4448+ loopover-mcp notifications-read --login <github-login> [--id <delivery-id>]... [--json]
43834449 loopover-mcp analyze-branch --login <github-login> [--repo owner/repo] [--base origin/main] [--branch-eligibility eligible|ineligible|unknown] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--scenario-note "..."] [--validation "passed|npm test|summary"] [--format table] [--json]
43844450 loopover-mcp preflight --login <github-login> [--repo owner/repo] [--base origin/main] [--branch-eligibility eligible|ineligible|unknown] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--validation "passed|npm test|summary"] [--format table] [--json]
43854451 loopover-mcp review-pr --login <github-login> [--repo owner/repo] [--base origin/main] [--commit <message>]... [--body <text>] [--body-file <path>] [--linked-issue <number>] [--json]
@@ -4398,7 +4464,7 @@ function printHelp() {
43984464 LOOPOVER_PROFILE
43994465 LOOPOVER_CONFIG_PATH or LOOPOVER_CONFIG_DIR
44004466 LOOPOVER_API_TOKEN, LOOPOVER_MCP_TOKEN, LOOPOVER_TOKEN, or a session from loopover-mcp login
4401- LOOPOVER_LOGIN or GITHUB_LOGIN (default --login for analyze-branch, preflight, review-pr, decision-pack, repo-decision, monitor-open-prs, pr-outcomes, and agent plan/packet)
4467+ LOOPOVER_LOGIN or GITHUB_LOGIN (default --login for analyze-branch, preflight, review-pr, decision-pack, repo-decision, monitor-open-prs, pr-outcomes, notifications, notifications-read, and agent plan/packet)
44024468 GITHUB_TOKEN for non-interactive login bootstrap
44034469 GITTENSOR_SCORE_PREVIEW_CMD
44044470 GITTENSOR_ROOT
@@ -4443,7 +4509,7 @@ Use --profile <name> or LOOPOVER_PROFILE to run login, logout, whoami, status, d
44434509
44444510function parseOptions ( args ) {
44454511 const options = { } ;
4446- const repeatable = new Set ( [ "label" , "issue" , "commit" , "changedFile" , "test" , "testFile" , "validation" , "validationCommand" , "validationStatus" , "validationSummary" , "validationDuration" , "scenarioNote" ] ) ;
4512+ const repeatable = new Set ( [ "label" , "issue" , "id" , " commit", "changedFile" , "test" , "testFile" , "validation" , "validationCommand" , "validationStatus" , "validationSummary" , "validationDuration" , "scenarioNote" ] ) ;
44474513 for ( let index = 0 ; index < args . length ; index += 1 ) {
44484514 const arg = args [ index ] ;
44494515 if ( arg === "--json" ) {
@@ -5533,6 +5599,15 @@ function getPrOutcomes(login, limit) {
55335599 return apiGet ( `/v1/contributors/${ encodeURIComponent ( login ) } /pr-outcomes${ suffix } ` ) ;
55345600}
55355601
5602+ // #6745: contributor notification feed + mark-read. `postMarkNotificationsRead` sends no ids to mark all
5603+ // delivered notifications read, mirroring markNotificationsReadShape's optional ids.
5604+ function getNotifications ( login ) {
5605+ return apiGet ( `/v1/contributors/${ encodeURIComponent ( login ) } /notifications` ) ;
5606+ }
5607+ function postMarkNotificationsRead ( login , ids ) {
5608+ return apiPost ( `/v1/contributors/${ encodeURIComponent ( login ) } /notifications/read` , ids ? { ids } : { } ) ;
5609+ }
5610+
55365611// Mirror the API's own `summary` when it sends one, so the CLI and the loopover_monitor_open_prs MCP
55375612// tool (which returns monitor.summary verbatim) never drift into two different sentences for one payload.
55385613function openPrMonitorToolSummary ( login , payload ) {
0 commit comments