Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 98 additions & 37 deletions src/commands/agent/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,117 @@
output?: string;
}

// Column widths (NAME is dynamic, takes remaining space)
const COL_VERSION = 18;
const COL_VISIBILITY = 10;
const COL_ID = 30;
const COL_CREATED = 10;
const FIXED_WIDTH = COL_VERSION + COL_VISIBILITY + COL_ID + COL_CREATED + 4; // 4 for spacing

function truncate(str: string, maxLen: number): string {
if (str.length <= maxLen) return str;
return str.slice(0, maxLen - 1) + "…";
interface ColumnDef {
header: string;
raw: (agent: Agent) => string;
styled: (agent: Agent) => string;
}

function printTable(agents: Agent[]): void {
const columns: ColumnDef[] = [
{
header: "NAME",
raw: (a) => a.name,
styled(a) {
return this.raw(a);
},
},
{
header: "SOURCE",
raw: (a) => (a as any).source?.type || "-",

Check warning on line 35 in src/commands/agent/list.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
styled(a) {
return this.raw(a);
},
},
{
header: "VERSION",
raw: (a) => {
const pkg =
(a as any).source?.npm?.package_name ||

Check warning on line 44 in src/commands/agent/list.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
(a as any).source?.pip?.package_name;

Check warning on line 45 in src/commands/agent/list.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
return pkg ? `${pkg}@${a.version}` : a.version;
},
styled(a) {
const pkg =
(a as any).source?.npm?.package_name ||

Check warning on line 50 in src/commands/agent/list.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
(a as any).source?.pip?.package_name;

Check warning on line 51 in src/commands/agent/list.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
return pkg ? chalk.dim(pkg + "@") + a.version : a.version;
},
},
{
header: "ID",
raw: (a) => a.id,
styled(a) {
return chalk.dim(a.id);
},
},
{
header: "CREATED",
raw: (a) => formatTimeAgo(a.create_time_ms),
styled(a) {
return chalk.dim(this.raw(a));
},
},
];

function computeColumnWidths(agents: Agent[]): number[] {
const minPad = 2;
const maxPad = 4;
const termWidth = process.stdout.columns || 120;

// Min width per column: max of header and all row values, plus minimum padding
const minWidths = columns.map((col) => {
const maxContent = agents.reduce(
(w, a) => Math.max(w, col.raw(a).length),
col.header.length,
);
return maxContent + minPad;
});

const totalMin = minWidths.reduce((s, w) => s + w, 0);
const slack = termWidth - totalMin;
const extraPerCol = Math.min(
maxPad - minPad,
Math.max(0, Math.floor(slack / columns.length)),
);

return minWidths.map((w) => w + extraPerCol);
}

function padStyled(raw: string, styled: string, width: number): string {
return styled + " ".repeat(Math.max(0, width - raw.length));
}

function printTable(agents: Agent[], isPublic: boolean): void {
if (isPublic) {
console.log(
chalk.dim("Showing PUBLIC agents. Use --private to see private agents"),
);
} else {
console.log(
chalk.dim("Showing PRIVATE agents. Use --public to see public agents"),
);
}
console.log();

if (agents.length === 0) {
console.log(chalk.dim("No agents found"));
return;
}

const widths = computeColumnWidths(agents);
const termWidth = process.stdout.columns || 120;
const nameWidth = Math.max(10, termWidth - FIXED_WIDTH);

// Header
const header =
"NAME".padEnd(nameWidth) +
" " +
"VERSION".padEnd(COL_VERSION) +
" " +
"VISIBILITY".padEnd(COL_VISIBILITY) +
" " +
"ID".padEnd(COL_ID) +
" " +
"CREATED".padEnd(COL_CREATED);
const header = columns.map((col, i) => col.header.padEnd(widths[i])).join("");
console.log(chalk.bold(header));
console.log(chalk.dim("─".repeat(Math.min(header.length, termWidth))));

// Rows
for (const agent of agents) {
const name = truncate(agent.name, nameWidth).padEnd(nameWidth);
const version = truncate(agent.version, COL_VERSION).padEnd(COL_VERSION);
const visibility = (agent.is_public ? "public" : "private").padEnd(
COL_VISIBILITY,
);
const visibilityColored = agent.is_public
? chalk.green(visibility)
: chalk.dim(visibility);
const id = truncate(agent.id, COL_ID).padEnd(COL_ID);
const created = formatTimeAgo(agent.create_time_ms).padEnd(COL_CREATED);

console.log(
`${name} ${version} ${visibilityColored} ${chalk.dim(id)} ${chalk.dim(created)}`,
);
const line = columns
.map((col, i) => padStyled(col.raw(agent), col.styled(agent), widths[i]))
.join("");
console.log(line);
}

console.log();
Expand Down Expand Up @@ -105,7 +166,7 @@
if (format !== "text") {
output(agents, { format, defaultFormat: "json" });
} else {
printTable(agents);
printTable(agents, !!options.public);
}
} catch (error) {
outputError("Failed to list agents", error);
Expand Down
Loading