@@ -20,7 +20,6 @@ import {
2020import { parseOrgProjectArg } from "../../../lib/arg-parsing.js" ;
2121import { openInBrowser } from "../../../lib/browser.js" ;
2222import { ContextError } from "../../../lib/errors.js" ;
23- import { filterFields } from "../../../lib/formatters/json.js" ;
2423import {
2524 colorTag ,
2625 escapeMarkdownCell ,
@@ -30,10 +29,13 @@ import { type Column, writeTable } from "../../../lib/formatters/table.js";
3029import {
3130 buildListCommand ,
3231 buildListLimitFlag ,
32+ LIST_BASE_ALIASES ,
33+ LIST_TARGET_POSITIONAL ,
3334 targetPatternExplanation ,
3435} from "../../../lib/list-command.js" ;
3536import {
3637 dispatchOrgScopedList ,
38+ jsonTransformListResult ,
3739 type ListCommandMeta ,
3840 type ListResult ,
3941} from "../../../lib/org-list.js" ;
@@ -57,11 +59,18 @@ type ListFlags = {
5759 readonly cursor ?: string ;
5860 readonly json : boolean ;
5961 readonly fields ?: string [ ] ;
62+ readonly query ?: string ;
6063} ;
6164
62- type AlertRuleWithTarget = {
63- rule : IssueAlertRule ;
64- target : ResolvedTarget ;
65+ /** Display row carrying per-rule project context for the human formatter. */
66+ type AlertRuleRow = { rule : IssueAlertRule ; target : ResolvedTarget } ;
67+
68+ /**
69+ * Extended result type: raw rules in `items` (for JSON), display rows in
70+ * `displayRows` (for human output). Mirrors the IssueListResult pattern.
71+ */
72+ type IssueAlertListResult = ListResult < IssueAlertRule > & {
73+ displayRows ?: AlertRuleRow [ ] ;
6574} ;
6675
6776const issueAlertListMeta : ListCommandMeta = {
@@ -119,7 +128,7 @@ async function fetchFromTargets(
119128 targets : ResolvedTarget [ ] ,
120129 limit : number ,
121130 json : boolean
122- ) : Promise < AlertRuleWithTarget [ ] > {
131+ ) : Promise < AlertRuleRow [ ] > {
123132 const perTargetLimit = Math . max ( limit , Math . ceil ( limit / targets . length ) * 2 ) ;
124133 const results = await withProgress (
125134 {
@@ -140,14 +149,26 @@ async function fetchFromTargets(
140149 return results . flat ( ) . slice ( 0 , limit ) ;
141150}
142151
152+ /** Client-side name filter applied after fetch (API has no query param). */
153+ function applyQueryFilter (
154+ rows : AlertRuleRow [ ] ,
155+ query : string | undefined
156+ ) : AlertRuleRow [ ] {
157+ if ( ! query ) {
158+ return rows ;
159+ }
160+ const q = query . toLowerCase ( ) ;
161+ return rows . filter ( ( r ) => r . rule . name . toLowerCase ( ) . includes ( q ) ) ;
162+ }
163+
143164// ---------------------------------------------------------------------------
144165// Mode handlers
145166// ---------------------------------------------------------------------------
146167
147168async function handleAutoDetectIssueAlerts (
148169 cwd : string ,
149170 flags : ListFlags
150- ) : Promise < ListResult < AlertRuleWithTarget > > {
171+ ) : Promise < IssueAlertListResult > {
151172 const { targets, footer } = await withProgress (
152173 { message : "Resolving targets..." , json : flags . json } ,
153174 ( ) =>
@@ -159,15 +180,23 @@ async function handleAutoDetectIssueAlerts(
159180 if ( targets . length === 0 ) {
160181 throw new ContextError ( "Organization and project" , USAGE_HINT ) ;
161182 }
162- const items = await fetchFromTargets ( targets , flags . limit , flags . json ) ;
163- return { items, hasMore : false , hint : footer } ;
183+ const displayRows = applyQueryFilter (
184+ await fetchFromTargets ( targets , flags . limit , flags . json ) ,
185+ flags . query
186+ ) ;
187+ return {
188+ items : displayRows . map ( ( r ) => r . rule ) ,
189+ displayRows,
190+ hasMore : false ,
191+ hint : footer ,
192+ } ;
164193}
165194
166195async function handleExplicitIssueAlerts (
167196 org : string ,
168197 project : string ,
169198 flags : ListFlags
170- ) : Promise < ListResult < AlertRuleWithTarget > > {
199+ ) : Promise < IssueAlertListResult > {
171200 const target : ResolvedTarget = {
172201 org,
173202 project,
@@ -181,8 +210,13 @@ async function handleExplicitIssueAlerts(
181210 } ,
182211 ( ) => fetchIssueRulesForTarget ( target , flags . limit )
183212 ) ;
213+ const displayRows = applyQueryFilter (
214+ rules . map ( ( rule ) => ( { rule, target } ) ) ,
215+ flags . query
216+ ) ;
184217 return {
185- items : rules . map ( ( rule ) => ( { rule, target } ) ) ,
218+ items : displayRows . map ( ( r ) => r . rule ) ,
219+ displayRows,
186220 hasMore : false ,
187221 hint : `Alert rules: ${ buildIssueAlertsUrl ( org , project ) } ` ,
188222 } ;
@@ -191,7 +225,7 @@ async function handleExplicitIssueAlerts(
191225async function handleOrgAllIssueAlerts (
192226 org : string ,
193227 flags : ListFlags
194- ) : Promise < ListResult < AlertRuleWithTarget > > {
228+ ) : Promise < IssueAlertListResult > {
195229 // org-all: list all projects in the org, then fetch alerts for each
196230 const { targets } = await withProgress (
197231 { message : `Listing projects in ${ org } ...` , json : flags . json } ,
@@ -201,9 +235,13 @@ async function handleOrgAllIssueAlerts(
201235 { cwd : "" , usageHint : USAGE_HINT }
202236 )
203237 ) ;
204- const items = await fetchFromTargets ( targets , flags . limit , flags . json ) ;
238+ const displayRows = applyQueryFilter (
239+ await fetchFromTargets ( targets , flags . limit , flags . json ) ,
240+ flags . query
241+ ) ;
205242 return {
206- items,
243+ items : displayRows . map ( ( r ) => r . rule ) ,
244+ displayRows,
207245 hasMore : false ,
208246 hint :
209247 targets . length > 1
@@ -216,7 +254,7 @@ async function handleProjectSearchIssueAlerts(
216254 projectSlug : string ,
217255 cwd : string ,
218256 flags : ListFlags
219- ) : Promise < ListResult < AlertRuleWithTarget > > {
257+ ) : Promise < IssueAlertListResult > {
220258 const { targets } = await withProgress (
221259 { message : `Searching for project '${ projectSlug } '...` , json : flags . json } ,
222260 ( ) =>
@@ -225,23 +263,25 @@ async function handleProjectSearchIssueAlerts(
225263 { cwd, usageHint : USAGE_HINT }
226264 )
227265 ) ;
228- const items = await fetchFromTargets ( targets , flags . limit , flags . json ) ;
229- return { items, hasMore : false } ;
266+ const displayRows = applyQueryFilter (
267+ await fetchFromTargets ( targets , flags . limit , flags . json ) ,
268+ flags . query
269+ ) ;
270+ return { items : displayRows . map ( ( r ) => r . rule ) , displayRows, hasMore : false } ;
230271}
231272
232273// ---------------------------------------------------------------------------
233274// Human output
234275// ---------------------------------------------------------------------------
235276
236- function formatIssueAlertListHuman (
237- result : ListResult < AlertRuleWithTarget >
238- ) : string {
277+ function formatIssueAlertListHuman ( result : IssueAlertListResult ) : string {
239278 if ( result . items . length === 0 ) {
240279 return result . hint ?? "No issue alert rules found." ;
241280 }
242281
282+ const rows = result . displayRows ?? [ ] ;
243283 const uniqueProjects = new Set (
244- result . items . map ( ( r ) => `${ r . target . org } /${ r . target . project } ` )
284+ rows . map ( ( r ) => `${ r . target . org } /${ r . target . project } ` )
245285 ) ;
246286 const isMultiProject = uniqueProjects . size > 1 ;
247287
@@ -255,7 +295,7 @@ function formatIssueAlertListHuman(
255295 status : string ;
256296 } ;
257297
258- const rows : Row [ ] = result . items . map ( ( { rule : r , target } ) => ( {
298+ const tableRows : Row [ ] = rows . map ( ( { rule : r , target } ) => ( {
259299 id : r . id ,
260300 name : escapeMarkdownCell ( r . name ) ,
261301 ...( isMultiProject && {
@@ -284,36 +324,11 @@ function formatIssueAlertListHuman(
284324
285325 const parts : string [ ] = [ ] ;
286326 const buffer : Writer = { write : ( s ) => parts . push ( s ) } ;
287- writeTable ( buffer , rows , columns ) ;
327+ writeTable ( buffer , tableRows , columns ) ;
288328
289329 return parts . join ( "" ) . trimEnd ( ) ;
290330}
291331
292- // ---------------------------------------------------------------------------
293- // JSON transform
294- // ---------------------------------------------------------------------------
295-
296- function jsonTransformIssueAlertList (
297- result : ListResult < AlertRuleWithTarget > ,
298- fields ?: string [ ]
299- ) : unknown {
300- const rules = result . items . map ( ( { rule } ) => rule ) ;
301- const items =
302- fields && fields . length > 0
303- ? rules . map ( ( r ) => filterFields ( r , fields ) )
304- : rules ;
305-
306- const envelope : Record < string , unknown > = {
307- data : items ,
308- hasMore : ! ! result . hasMore ,
309- hasPrev : ! ! result . hasPrev ,
310- } ;
311- if ( result . nextCursor ) {
312- envelope . nextCursor = result . nextCursor ;
313- }
314- return envelope ;
315- }
316-
317332// ---------------------------------------------------------------------------
318333// Command
319334// ---------------------------------------------------------------------------
@@ -334,30 +349,25 @@ export const listCommand = buildListCommand("alert", {
334349 } ,
335350 output : {
336351 human : formatIssueAlertListHuman ,
337- jsonTransform : jsonTransformIssueAlertList ,
352+ jsonTransform : jsonTransformListResult ,
338353 } ,
339354 parameters : {
340- positional : {
341- kind : "tuple" ,
342- parameters : [
343- {
344- placeholder : "org/project" ,
345- brief :
346- "<org>/<project>, <org>/ (all), <project> (search), or omit to auto-detect" ,
347- parse : String ,
348- optional : true ,
349- } ,
350- ] ,
351- } ,
355+ positional : LIST_TARGET_POSITIONAL ,
352356 flags : {
353357 web : {
354358 kind : "boolean" ,
355359 brief : "Open in browser" ,
356360 default : false ,
357361 } ,
358362 limit : buildListLimitFlag ( "issue alert rules" ) ,
363+ query : {
364+ kind : "parsed" ,
365+ parse : String ,
366+ brief : "Filter rules by name" ,
367+ optional : true ,
368+ } ,
359369 } ,
360- aliases : { w : "web" , n : "limit " } ,
370+ aliases : { ... LIST_BASE_ALIASES , w : "web" , q : "query " } ,
361371 } ,
362372 async * func ( this : SentryContext , flags : ListFlags , target ?: string ) {
363373 const { cwd } = this ;
@@ -390,7 +400,7 @@ export const listCommand = buildListCommand("alert", {
390400 flags
391401 ) ,
392402 } ,
393- } ) ) as ListResult < AlertRuleWithTarget > ;
403+ } ) ) as IssueAlertListResult ;
394404
395405 yield new CommandOutput ( result ) ;
396406 return { hint : result . hint } ;
0 commit comments