@@ -12,6 +12,7 @@ import type { FetchImpl } from '../lib/http.js';
1212import type { HttpClient } from '../lib/http.js' ;
1313import { GLOBAL_OPTS_HINT , Output , resolveOutputMode , type OutputMode } from '../lib/output.js' ;
1414import { assertNotLocal } from '../lib/target-url.js' ;
15+ import { renderTextTable , resolveTextColumns , type TextTableColumn } from '../lib/text-table.js' ;
1516import { assertIdempotencyKey } from '../lib/validate.js' ;
1617import {
1718 fetchSinglePage ,
@@ -44,6 +45,8 @@ interface ListOptions extends CommonOptions {
4445 pageSize ?: number ;
4546 startingToken ?: string ;
4647 maxItems ?: number ;
48+ columns ?: string ;
49+ noHeader ?: boolean ;
4750}
4851
4952export async function runList (
@@ -57,6 +60,9 @@ export async function runList(
5760 startingToken : opts . startingToken ,
5861 maxItems : opts . maxItems ,
5962 } ) ;
63+ if ( opts . output === 'text' ) {
64+ resolveTextColumns ( opts . columns , PROJECT_LIST_COLUMNS ) ;
65+ }
6066 const client = makeClient ( opts , deps ) ;
6167
6268 // When the user explicitly passed a page-size flag and did NOT ask
@@ -84,7 +90,7 @@ export async function runList(
8490
8591 out . print ( page , data => {
8692 const p = data as Page < CliProject > ;
87- return renderProjectListText ( p ) ;
93+ return renderProjectListText ( p , { columns : opts . columns , noHeader : opts . noHeader } ) ;
8894 } ) ;
8995 return page ;
9096}
@@ -663,6 +669,8 @@ export function createProjectCommand(deps: ProjectDeps = {}): Command {
663669 . option ( '--page-size <n>' , 'service page-size hint (1-100, default 25)' )
664670 . option ( '--starting-token <token>' , 'opaque cursor from a previous list response' )
665671 . option ( '--max-items <n>' , 'stop after this many items across auto-paged pages' )
672+ . option ( '--columns <list>' , 'select/reorder text table columns (comma-separated keys)' )
673+ . option ( '--no-header' , 'suppress the text table header row' )
666674 . addHelpText ( 'after' , GLOBAL_OPTS_HINT )
667675 . action ( async ( cmdOpts : ListFlagOpts , command : Command ) => {
668676 // Don't parse numeric flags via Commander — its parser throws a
@@ -676,6 +684,8 @@ export function createProjectCommand(deps: ProjectDeps = {}): Command {
676684 pageSize : parseFlag ( cmdOpts . pageSize , 'page-size' ) ,
677685 startingToken : cmdOpts . startingToken ,
678686 maxItems : parseFlag ( cmdOpts . maxItems , 'max-items' ) ,
687+ columns : cmdOpts . columns ,
688+ noHeader : cmdOpts . header === false ,
679689 } ,
680690 deps ,
681691 ) ;
@@ -895,6 +905,8 @@ interface ListFlagOpts {
895905 pageSize ?: string ;
896906 startingToken ?: string ;
897907 maxItems ?: string ;
908+ columns ?: string ;
909+ header ?: boolean ;
898910}
899911
900912interface CreateFlagOpts {
@@ -1001,45 +1013,37 @@ function makeOutput(mode: OutputMode, deps: ProjectDeps): Output {
10011013 return new Output ( mode , { stdout : deps . stdout , stderr : deps . stderr } ) ;
10021014}
10031015
1004- function renderProjectListText ( page : Page < CliProject > ) : string {
1016+ const PROJECT_LIST_COLUMNS : ReadonlyArray < TextTableColumn < CliProject > > = [
1017+ {
1018+ header : 'ID' ,
1019+ width : rows => Math . max ( 2 , ...rows . map ( project => project . id . length ) ) ,
1020+ render : project => project . id ,
1021+ } ,
1022+ {
1023+ header : 'NAME' ,
1024+ width : rows => Math . max ( 4 , ...rows . map ( project => project . name . length ) ) ,
1025+ render : project => project . name ,
1026+ } ,
1027+ { header : 'TYPE' , width : 8 , render : project => project . type } ,
1028+ { header : 'FROM' , width : 6 , render : project => project . createdFrom } ,
1029+ { header : 'CREATED' , width : 0 , render : project => project . createdAt } ,
1030+ ] ;
1031+
1032+ function renderProjectListText (
1033+ page : Page < CliProject > ,
1034+ options : { columns ?: string ; noHeader ?: boolean } = { } ,
1035+ ) : string {
10051036 if ( page . items . length === 0 ) {
10061037 return page . nextToken
10071038 ? `No projects on this page.\nnextToken: ${ page . nextToken } `
10081039 : 'No projects.' ;
10091040 }
1010- // Compact, AWS-CLI-grade columnar output. Column widths are computed
1011- // per-call so a single absurdly long project name doesn't push the
1012- // whole table off-screen.
1013- const idWidth = Math . max ( 2 , ...page . items . map ( p => p . id . length ) ) ;
1014- const nameWidth = Math . max ( 4 , ...page . items . map ( p => p . name . length ) ) ;
1015- const typeWidth = 8 ;
1016- const fromWidth = 6 ;
1017-
1018- const header =
1019- pad ( 'ID' , idWidth ) +
1020- ' ' +
1021- pad ( 'NAME' , nameWidth ) +
1022- ' ' +
1023- pad ( 'TYPE' , typeWidth ) +
1024- ' ' +
1025- pad ( 'FROM' , fromWidth ) +
1026- ' ' +
1027- 'CREATED' ;
1028-
1029- const rows = page . items . map (
1030- p =>
1031- pad ( p . id , idWidth ) +
1032- ' ' +
1033- pad ( p . name , nameWidth ) +
1034- ' ' +
1035- pad ( p . type , typeWidth ) +
1036- ' ' +
1037- pad ( p . createdFrom , fromWidth ) +
1038- ' ' +
1039- p . createdAt ,
1040- ) ;
1041-
1042- const lines = [ header , ...rows ] ;
1041+ const lines = [
1042+ renderTextTable ( page . items , PROJECT_LIST_COLUMNS , {
1043+ columns : options . columns ,
1044+ noHeader : options . noHeader ,
1045+ } ) ,
1046+ ] ;
10431047 if ( page . nextToken ) lines . push ( '' , `nextToken: ${ page . nextToken } ` ) ;
10441048 return lines . join ( '\n' ) ;
10451049}
@@ -1055,11 +1059,6 @@ function renderProjectText(p: CliProject): string {
10551059 ] . join ( '\n' ) ;
10561060}
10571061
1058- function pad ( s : string , width : number ) : string {
1059- if ( s . length >= width ) return s ;
1060- return s + ' ' . repeat ( width - s . length ) ;
1061- }
1062-
10631062function renderUpdateText ( r : CliUpdateProjectResponse ) : string {
10641063 return [
10651064 `id: ${ r . id } ` ,
0 commit comments