@@ -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 , 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 (
@@ -84,7 +87,7 @@ export async function runList(
8487
8588 out . print ( page , data => {
8689 const p = data as Page < CliProject > ;
87- return renderProjectListText ( p ) ;
90+ return renderProjectListText ( p , { columns : opts . columns , noHeader : opts . noHeader } ) ;
8891 } ) ;
8992 return page ;
9093}
@@ -584,6 +587,8 @@ export function createProjectCommand(deps: ProjectDeps = {}): Command {
584587 . option ( '--page-size <n>' , 'service page-size hint (1-100, default 25)' )
585588 . option ( '--starting-token <token>' , 'opaque cursor from a previous list response' )
586589 . option ( '--max-items <n>' , 'stop after this many items across auto-paged pages' )
590+ . option ( '--columns <list>' , 'select/reorder text table columns (comma-separated keys)' )
591+ . option ( '--no-header' , 'suppress the text table header row' )
587592 . addHelpText ( 'after' , GLOBAL_OPTS_HINT )
588593 . action ( async ( cmdOpts : ListFlagOpts , command : Command ) => {
589594 // Don't parse numeric flags via Commander — its parser throws a
@@ -597,6 +602,8 @@ export function createProjectCommand(deps: ProjectDeps = {}): Command {
597602 pageSize : parseFlag ( cmdOpts . pageSize , 'page-size' ) ,
598603 startingToken : cmdOpts . startingToken ,
599604 maxItems : parseFlag ( cmdOpts . maxItems , 'max-items' ) ,
605+ columns : cmdOpts . columns ,
606+ noHeader : cmdOpts . header === false ,
600607 } ,
601608 deps ,
602609 ) ;
@@ -784,6 +791,8 @@ interface ListFlagOpts {
784791 pageSize ?: string ;
785792 startingToken ?: string ;
786793 maxItems ?: string ;
794+ columns ?: string ;
795+ header ?: boolean ;
787796}
788797
789798interface CreateFlagOpts {
@@ -885,45 +894,37 @@ function makeOutput(mode: OutputMode, deps: ProjectDeps): Output {
885894 return new Output ( mode , { stdout : deps . stdout , stderr : deps . stderr } ) ;
886895}
887896
888- function renderProjectListText ( page : Page < CliProject > ) : string {
897+ const PROJECT_LIST_COLUMNS : ReadonlyArray < TextTableColumn < CliProject > > = [
898+ {
899+ header : 'ID' ,
900+ width : rows => Math . max ( 2 , ...rows . map ( project => project . id . length ) ) ,
901+ render : project => project . id ,
902+ } ,
903+ {
904+ header : 'NAME' ,
905+ width : rows => Math . max ( 4 , ...rows . map ( project => project . name . length ) ) ,
906+ render : project => project . name ,
907+ } ,
908+ { header : 'TYPE' , width : 8 , render : project => project . type } ,
909+ { header : 'FROM' , width : 6 , render : project => project . createdFrom } ,
910+ { header : 'CREATED' , width : 0 , render : project => project . createdAt } ,
911+ ] ;
912+
913+ function renderProjectListText (
914+ page : Page < CliProject > ,
915+ options : { columns ?: string ; noHeader ?: boolean } = { } ,
916+ ) : string {
889917 if ( page . items . length === 0 ) {
890918 return page . nextToken
891919 ? `No projects on this page.\nnextToken: ${ page . nextToken } `
892920 : 'No projects.' ;
893921 }
894- // Compact, AWS-CLI-grade columnar output. Column widths are computed
895- // per-call so a single absurdly long project name doesn't push the
896- // whole table off-screen.
897- const idWidth = Math . max ( 2 , ...page . items . map ( p => p . id . length ) ) ;
898- const nameWidth = Math . max ( 4 , ...page . items . map ( p => p . name . length ) ) ;
899- const typeWidth = 8 ;
900- const fromWidth = 6 ;
901-
902- const header =
903- pad ( 'ID' , idWidth ) +
904- ' ' +
905- pad ( 'NAME' , nameWidth ) +
906- ' ' +
907- pad ( 'TYPE' , typeWidth ) +
908- ' ' +
909- pad ( 'FROM' , fromWidth ) +
910- ' ' +
911- 'CREATED' ;
912-
913- const rows = page . items . map (
914- p =>
915- pad ( p . id , idWidth ) +
916- ' ' +
917- pad ( p . name , nameWidth ) +
918- ' ' +
919- pad ( p . type , typeWidth ) +
920- ' ' +
921- pad ( p . createdFrom , fromWidth ) +
922- ' ' +
923- p . createdAt ,
924- ) ;
925-
926- const lines = [ header , ...rows ] ;
922+ const lines = [
923+ renderTextTable ( page . items , PROJECT_LIST_COLUMNS , {
924+ columns : options . columns ,
925+ noHeader : options . noHeader ,
926+ } ) ,
927+ ] ;
927928 if ( page . nextToken ) lines . push ( '' , `nextToken: ${ page . nextToken } ` ) ;
928929 return lines . join ( '\n' ) ;
929930}
@@ -939,11 +940,6 @@ function renderProjectText(p: CliProject): string {
939940 ] . join ( '\n' ) ;
940941}
941942
942- function pad ( s : string , width : number ) : string {
943- if ( s . length >= width ) return s ;
944- return s + ' ' . repeat ( width - s . length ) ;
945- }
946-
947943function renderUpdateText ( r : CliUpdateProjectResponse ) : string {
948944 return [
949945 `id: ${ r . id } ` ,
0 commit comments