trello-cli-unofficial 0.11.6 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [0.12.0](https://github.com/JaegerCaiser/trello-cli-unofficial/compare/v0.11.6...v0.12.0) (2025-11-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * **cards:** add cards show command ([a98fa9f](https://github.com/JaegerCaiser/trello-cli-unofficial/commit/a98fa9fe2244bbd8ba754e725b867ee22cfd4c33))
7
+
1
8
  ## [0.11.6](https://github.com/JaegerCaiser/trello-cli-unofficial/compare/v0.11.5...v0.11.6) (2025-11-14)
2
9
 
3
10
 
package/README.md CHANGED
@@ -252,6 +252,13 @@ tcu
252
252
  - **Delete**: Confirm before removing
253
253
  - **Move**: Select destination list
254
254
 
255
+ ### Show Card Details
256
+
257
+ ```bash
258
+ # Show detailed information about a card, including checklists, members, labels and attachments
259
+ tcu cards show <cardId>
260
+ ```
261
+
255
262
  ## 🤖 CI/CD & Automation
256
263
 
257
264
  This project uses automated CI/CD with semantic versioning based on **commit messages**:
package/dist/main.js CHANGED
@@ -2588,6 +2588,12 @@ var require_en = __commonJS((exports, module) => {
2588
2588
  delete: "\uD83D\uDDD1\uFE0F Delete Card",
2589
2589
  back: "\u2B05\uFE0F Back",
2590
2590
  moveCard: "\uD83D\uDCE6 Move card"
2591
+ },
2592
+ show: {
2593
+ labels: "\uD83C\uDFF7\uFE0F Labels:",
2594
+ members: "\uD83D\uDC65 Members:",
2595
+ checklists: "\u2705 Checklists:",
2596
+ attachments: "\uD83D\uDCCE Attachments:"
2591
2597
  }
2592
2598
  },
2593
2599
  errors: {
@@ -2724,6 +2730,9 @@ var require_en = __commonJS((exports, module) => {
2724
2730
  update: {
2725
2731
  description: "Update an existing card"
2726
2732
  },
2733
+ show: {
2734
+ description: "Show detailed information about a specific card"
2735
+ },
2727
2736
  delete: {
2728
2737
  description: "Delete a card"
2729
2738
  }
@@ -2863,6 +2872,12 @@ var require_pt_BR = __commonJS((exports, module) => {
2863
2872
  delete: "\uD83D\uDDD1\uFE0F Deletar Card",
2864
2873
  back: "\u2B05\uFE0F Voltar",
2865
2874
  moveCard: "\uD83D\uDCE6 Mover cart\xE3o"
2875
+ },
2876
+ show: {
2877
+ labels: "\uD83C\uDFF7\uFE0F Labels:",
2878
+ members: "\uD83D\uDC65 Membros:",
2879
+ checklists: "\u2705 Checklists:",
2880
+ attachments: "\uD83D\uDCCE Anexos:"
2866
2881
  }
2867
2882
  },
2868
2883
  errors: {
@@ -2999,6 +3014,9 @@ var require_pt_BR = __commonJS((exports, module) => {
2999
3014
  update: {
3000
3015
  description: "Atualiza um card existente"
3001
3016
  },
3017
+ show: {
3018
+ description: "Mostra informa\xE7\xF5es detalhadas sobre um card espec\xEDfico"
3019
+ },
3002
3020
  delete: {
3003
3021
  description: "Deleta um card"
3004
3022
  }
@@ -3251,6 +3269,17 @@ class GetCardsUseCase {
3251
3269
  }
3252
3270
  }
3253
3271
 
3272
+ // src/application/use-cases/GetCardUseCase.ts
3273
+ class GetCardUseCase {
3274
+ trelloRepository;
3275
+ constructor(trelloRepository) {
3276
+ this.trelloRepository = trelloRepository;
3277
+ }
3278
+ async execute(cardId) {
3279
+ return await this.trelloRepository.getCard(cardId);
3280
+ }
3281
+ }
3282
+
3254
3283
  // src/application/use-cases/GetListsUseCase.ts
3255
3284
  class GetListsUseCase {
3256
3285
  trelloRepository;
@@ -26134,6 +26163,7 @@ class CardController {
26134
26163
  updateCardUseCase;
26135
26164
  deleteCardUseCase;
26136
26165
  moveCardUseCase;
26166
+ getCardUseCase;
26137
26167
  constructor(trelloRepository, boardController, outputFormatter) {
26138
26168
  this.trelloRepository = trelloRepository;
26139
26169
  this.boardController = boardController;
@@ -26142,6 +26172,7 @@ class CardController {
26142
26172
  this.updateCardUseCase = new UpdateCardUseCase(trelloRepository);
26143
26173
  this.deleteCardUseCase = new DeleteCardUseCase(trelloRepository);
26144
26174
  this.moveCardUseCase = new MoveCardUseCase(trelloRepository);
26175
+ this.getCardUseCase = new GetCardUseCase(trelloRepository);
26145
26176
  }
26146
26177
  async createCardInteractive() {
26147
26178
  const boards = await this.boardController.getBoards();
@@ -26425,6 +26456,46 @@ class CardController {
26425
26456
  console.log(t2("card.updated"));
26426
26457
  console.log(t2("card.cardName", { name: updatedCard.name }));
26427
26458
  }
26459
+ async showCard(cardId) {
26460
+ const card = await this.getCardUseCase.execute(cardId);
26461
+ const boards = await this.boardController.getBoards();
26462
+ let boardName;
26463
+ let listName;
26464
+ for (const board of boards) {
26465
+ const lists = await this.boardController.getLists(board.id);
26466
+ const list = lists.find((l) => l.id === card.idList);
26467
+ if (list) {
26468
+ boardName = board.name;
26469
+ listName = list.name;
26470
+ break;
26471
+ }
26472
+ }
26473
+ this.outputFormatter.message(t2("card.cardName", { name: card.name }));
26474
+ if (card.desc) {
26475
+ this.outputFormatter.message(t2("card.cardDescription", { description: card.desc }));
26476
+ }
26477
+ this.outputFormatter.message(t2("card.cardUrl", { url: card.url }));
26478
+ this.outputFormatter.message(t2("card.cardId", { id: card.id }));
26479
+ if (boardName && listName) {
26480
+ this.outputFormatter.message(`${t2("board.boardName", { name: boardName })} / ${t2("list.boardLists", { boardName: listName })}`);
26481
+ }
26482
+ if (card.labels && card.labels.length > 0) {
26483
+ this.outputFormatter.message(t2("card.show.labels"));
26484
+ this.outputFormatter.output(card.labels);
26485
+ }
26486
+ if (card.members && card.members.length > 0) {
26487
+ this.outputFormatter.message(t2("card.show.members"));
26488
+ this.outputFormatter.output(card.members);
26489
+ }
26490
+ if (card.checklists && card.checklists.length > 0) {
26491
+ this.outputFormatter.message(t2("card.show.checklists"));
26492
+ this.outputFormatter.output(card.checklists);
26493
+ }
26494
+ if (card.attachments && card.attachments.length > 0) {
26495
+ this.outputFormatter.message(t2("card.show.attachments"));
26496
+ this.outputFormatter.output(card.attachments);
26497
+ }
26498
+ }
26428
26499
  async moveCardToList(cardId, targetListId) {
26429
26500
  const boards = await this.trelloRepository.getBoards();
26430
26501
  let card;
@@ -26494,15 +26565,23 @@ class CardEntity {
26494
26565
  idList;
26495
26566
  desc;
26496
26567
  url;
26497
- constructor(id, name, idList, desc, url) {
26568
+ labels;
26569
+ members;
26570
+ checklists;
26571
+ attachments;
26572
+ constructor(id, name, idList, desc, url, labels, members, checklists, attachments) {
26498
26573
  this.id = id;
26499
26574
  this.name = name;
26500
26575
  this.idList = idList;
26501
26576
  this.desc = desc;
26502
26577
  this.url = url;
26578
+ this.labels = labels;
26579
+ this.members = members;
26580
+ this.checklists = checklists;
26581
+ this.attachments = attachments;
26503
26582
  }
26504
26583
  static fromApiResponse(data) {
26505
- return new CardEntity(data.id, data.name, data.idList, data.desc, data.url);
26584
+ return new CardEntity(data.id, data.name, data.idList, data.desc, data.url, data.labels, data.members, data.checklists, data.attachments);
26506
26585
  }
26507
26586
  static create(data) {
26508
26587
  return {
@@ -28688,7 +28767,8 @@ class TrelloApiRepository {
28688
28767
  this.token = token;
28689
28768
  }
28690
28769
  async request(endpoint, options) {
28691
- const url = `${this.baseUrl}${endpoint}?key=${this.apiKey}&token=${this.token}`;
28770
+ const separator = endpoint.includes("?") ? "&" : "?";
28771
+ const url = `${this.baseUrl}${endpoint}${separator}key=${this.apiKey}&token=${this.token}`;
28692
28772
  const response = await fetch(url, options);
28693
28773
  if (!response.ok) {
28694
28774
  const errorText = await response.text();
@@ -28829,6 +28909,11 @@ ${errorText}`);
28829
28909
  async moveCard(cardId, targetListId) {
28830
28910
  return this.updateCard(cardId, { idList: targetListId });
28831
28911
  }
28912
+ async getCard(cardId) {
28913
+ const endpoint = `/cards/${cardId}?checklists=all&members=true&attachments=true`;
28914
+ const data = await this.request(endpoint);
28915
+ return CardEntity.fromApiResponse(data);
28916
+ }
28832
28917
  }
28833
28918
  var init_TrelloApiRepository = __esm(() => {
28834
28919
  init_entities();
@@ -31138,18 +31223,20 @@ class CommandController {
31138
31223
  authController;
31139
31224
  boardController;
31140
31225
  cardController;
31141
- program = null;
31226
+ program;
31142
31227
  outputFormatter;
31143
31228
  constructor() {
31144
31229
  const configRepository = new FileConfigRepository;
31145
31230
  this.authController = new AuthController(configRepository);
31231
+ this.program = new Command;
31146
31232
  this.outputFormatter = new OutputFormatter;
31233
+ this.initializeProgram();
31234
+ this.setupCommands();
31147
31235
  }
31148
- getProgram() {
31236
+ initializeProgram() {
31149
31237
  if (!this.program) {
31150
31238
  this.program = new Command;
31151
31239
  }
31152
- return this.program;
31153
31240
  }
31154
31241
  getVersion() {
31155
31242
  const cwdPackageJson = join(process.cwd(), "package.json");
@@ -31162,13 +31249,13 @@ class CommandController {
31162
31249
  try {
31163
31250
  const currentFilePath = fileURLToPath2(import.meta.url);
31164
31251
  const currentDir = dirname(currentFilePath);
31165
- const installedPackageJson = join(currentDir, "..", "..", "package.json");
31252
+ const installedPackageJson = join(currentDir, "..", "..", "..", "..", "package.json");
31166
31253
  if (existsSync(installedPackageJson)) {
31167
31254
  const packageJson = JSON.parse(readFileSync2(installedPackageJson, "utf-8"));
31168
31255
  return packageJson.version;
31169
31256
  }
31170
31257
  } catch {}
31171
- return "0.11.3";
31258
+ return "0.11.10";
31172
31259
  }
31173
31260
  async initializeTrelloControllers() {
31174
31261
  await this.authController.ensureAuthenticated();
@@ -31178,9 +31265,12 @@ class CommandController {
31178
31265
  this.boardController = new BoardController(trelloRepository, this.outputFormatter);
31179
31266
  this.cardController = new CardController(trelloRepository, this.boardController, this.outputFormatter);
31180
31267
  }
31181
- async setupCommands() {
31268
+ setupCommands() {
31269
+ if (!this.program) {
31270
+ throw new Error(t2("errors.programNotInitialized"));
31271
+ }
31182
31272
  const version = this.getVersion();
31183
- this.getProgram().name("trello-cli-unofficial").description(t2("commands.description")).version(version).option("-f, --format <format>", t2("commands.formatOption"), "table").option("-v", t2("commands.versionOption")).option("--verbose", t2("commands.verboseOption")).on("option:format", (format) => {
31273
+ this.program.name("trello-cli-unofficial").description(t2("commands.description")).version(version).option("-f, --format <format>", t2("commands.formatOption"), "table").option("-v", t2("commands.versionOption")).option("--verbose", t2("commands.verboseOption")).on("option:format", (format) => {
31184
31274
  this.outputFormatter.setFormat(format);
31185
31275
  }).on("option:v", () => {
31186
31276
  console.log(version);
@@ -31188,15 +31278,15 @@ class CommandController {
31188
31278
  }).on("option:verbose", () => {
31189
31279
  process.env.VERBOSE_ERRORS = "true";
31190
31280
  });
31191
- this.getProgram().command("interactive").alias("i").description(t2("commands.interactive.description")).action(async () => {
31281
+ this.program.command("interactive").alias("i").description(t2("commands.interactive.description")).action(async () => {
31192
31282
  const configRepository = new FileConfigRepository;
31193
31283
  const cli = new (await Promise.resolve().then(() => (init_TrelloCliController(), exports_TrelloCliController))).TrelloCliController(configRepository, this.outputFormatter);
31194
31284
  await cli.run();
31195
31285
  });
31196
- this.getProgram().command("setup").description(t2("commands.setup.description")).action(async () => {
31286
+ this.program.command("setup").description(t2("commands.setup.description")).action(async () => {
31197
31287
  await this.authController.setupToken();
31198
31288
  });
31199
- const boardsCmd = this.getProgram().command("boards").description(t2("commands.boards.manage"));
31289
+ const boardsCmd = this.program.command("boards").description(t2("commands.boards.manage"));
31200
31290
  boardsCmd.command("list").description(t2("commands.boards.description")).option("-f, --format <format>", t2("commands.formatOption"), "table").action(async (options) => {
31201
31291
  try {
31202
31292
  await this.initializeTrelloControllers();
@@ -31205,7 +31295,7 @@ class CommandController {
31205
31295
  }
31206
31296
  await this.boardController.showBoards();
31207
31297
  } catch (error) {
31208
- console.error(t2("commands.errors.genericError"), error.message);
31298
+ console.error(t2("commands.commandErrors.genericError"), error.message);
31209
31299
  }
31210
31300
  });
31211
31301
  boardsCmd.command("show <boardId>").description(t2("commands.boards.show.description")).option("-f, --format <format>", t2("commands.formatOption"), "table").action(async (boardId, options) => {
@@ -31216,7 +31306,7 @@ class CommandController {
31216
31306
  }
31217
31307
  await this.boardController.showBoardDetails(boardId);
31218
31308
  } catch (error) {
31219
- console.error(t2("commands.errors.genericError"), error.message);
31309
+ console.error(t2("commands.commandErrors.genericError"), error.message);
31220
31310
  }
31221
31311
  });
31222
31312
  boardsCmd.command("create <name>").description(t2("commands.boards.create.description")).option("-d, --desc <description>", t2("commands.boards.create.descOption")).action(async (name, options) => {
@@ -31227,7 +31317,7 @@ class CommandController {
31227
31317
  console.error("\u274C Erro:", error.message);
31228
31318
  }
31229
31319
  });
31230
- this.getProgram().command("boards-legacy").description(t2("commands.deprecated.boardsLegacyDescription")).action(async () => {
31320
+ this.program.command("boards-legacy").description(t2("commands.deprecated.boardsLegacyDescription")).action(async () => {
31231
31321
  console.warn(t2("commands.deprecated.boardsLegacyWarning"));
31232
31322
  try {
31233
31323
  await this.initializeTrelloControllers();
@@ -31236,7 +31326,7 @@ class CommandController {
31236
31326
  console.error(t2("commands.deprecated.boardsLegacyError"), error.message);
31237
31327
  }
31238
31328
  });
31239
- const listsCmd = this.getProgram().command("lists").description(t2("commands.lists.description"));
31329
+ const listsCmd = this.program.command("lists").description(t2("commands.lists.description"));
31240
31330
  listsCmd.command("list <boardId>").description(t2("commands.lists.list.description")).option("-f, --format <format>", t2("commands.formatOption"), "table").action(async (boardId, options) => {
31241
31331
  try {
31242
31332
  await this.initializeTrelloControllers();
@@ -31245,7 +31335,7 @@ class CommandController {
31245
31335
  }
31246
31336
  await this.boardController.showListsById(boardId);
31247
31337
  } catch (error) {
31248
- console.error(t2("commands.errors.genericError"), error.message);
31338
+ console.error(t2("commands.commandErrors.genericError"), error.message);
31249
31339
  }
31250
31340
  });
31251
31341
  listsCmd.command("create <boardId> <name>").description(t2("commands.lists.create.description")).action(async (boardId, name) => {
@@ -31272,7 +31362,7 @@ class CommandController {
31272
31362
  console.error(t2("commands.commandErrors.genericError"), error.message);
31273
31363
  }
31274
31364
  });
31275
- this.getProgram().command("lists-legacy <boardName>").description(t2("commands.deprecated.listsLegacyDescription")).action(async (boardName) => {
31365
+ this.program.command("lists-legacy <boardName>").description(t2("commands.deprecated.listsLegacyDescription")).action(async (boardName) => {
31276
31366
  console.warn(t2("commands.deprecated.listsLegacyWarning"));
31277
31367
  try {
31278
31368
  await this.initializeTrelloControllers();
@@ -31281,7 +31371,7 @@ class CommandController {
31281
31371
  console.error(t2("commands.deprecated.listsLegacyError"), error.message);
31282
31372
  }
31283
31373
  });
31284
- const cardsCmd = this.getProgram().command("cards").description(t2("commands.cards.manage"));
31374
+ const cardsCmd = this.program.command("cards").description(t2("commands.cards.manage"));
31285
31375
  cardsCmd.command("list <listId>").description(t2("commands.cards.list.description")).option("-f, --format <format>", t2("commands.formatOption"), "table").action(async (listId, options) => {
31286
31376
  try {
31287
31377
  await this.initializeTrelloControllers();
@@ -31290,7 +31380,7 @@ class CommandController {
31290
31380
  }
31291
31381
  await this.boardController.showCardsByListId(listId);
31292
31382
  } catch (error) {
31293
- console.error(t2("commands.errors.genericError"), error.message);
31383
+ console.error(t2("commands.commandErrors.genericError"), error.message);
31294
31384
  }
31295
31385
  });
31296
31386
  cardsCmd.command("create <listId> <name>").description(t2("commands.cards.create.description")).option("-d, --desc <description>", t2("commands.options.cardDescription")).action(async (listId, name, options) => {
@@ -31325,7 +31415,18 @@ class CommandController {
31325
31415
  console.error(t2("commands.commandErrors.genericError"), error.message);
31326
31416
  }
31327
31417
  });
31328
- this.getProgram().command("cards-legacy <boardName> <listName>").description(t2("commands.deprecated.cardsLegacyDescription")).action(async (boardName, listName) => {
31418
+ cardsCmd.command("show <cardId>").description(t2("commands.cards.show.description")).option("-f, --format <format>", t2("commands.formatOption"), "table").action(async (cardId, options) => {
31419
+ try {
31420
+ await this.initializeTrelloControllers();
31421
+ if (options.format) {
31422
+ this.outputFormatter.setFormat(options.format);
31423
+ }
31424
+ await this.cardController.showCard(cardId);
31425
+ } catch (error) {
31426
+ console.error(t2("commands.commandErrors.genericError"), error.message);
31427
+ }
31428
+ });
31429
+ this.program.command("cards-legacy <boardName> <listName>").description(t2("commands.deprecated.cardsLegacyDescription")).action(async (boardName, listName) => {
31329
31430
  console.warn(t2("commands.deprecated.cardsLegacyWarning"));
31330
31431
  try {
31331
31432
  await this.initializeTrelloControllers();
@@ -31334,7 +31435,7 @@ class CommandController {
31334
31435
  console.error(t2("commands.deprecated.cardsLegacyError"), error.message);
31335
31436
  }
31336
31437
  });
31337
- this.getProgram().command("create-card-legacy <boardName> <listName> <cardName>").description(t2("commands.deprecated.createCardLegacyDescription")).option("-d, --desc <description>", t2("commands.options.cardDescription")).action(async (boardName, listName, cardName, options) => {
31438
+ this.program.command("create-card-legacy <boardName> <listName> <cardName>").description(t2("commands.deprecated.createCardLegacyDescription")).option("-d, --desc <description>", t2("commands.options.cardDescription")).action(async (boardName, listName, cardName, options) => {
31338
31439
  console.warn(t2("commands.deprecated.createCardLegacyWarning"));
31339
31440
  try {
31340
31441
  await this.initializeTrelloControllers();
@@ -31343,7 +31444,7 @@ class CommandController {
31343
31444
  console.error(t2("commands.commandErrors.genericError"), error.message);
31344
31445
  }
31345
31446
  });
31346
- this.getProgram().command("move-card-legacy <cardId> <listName>").description(t2("commands.deprecated.moveCardLegacyDescription")).action(async (cardId, listName) => {
31447
+ this.program.command("move-card-legacy <cardId> <listName>").description(t2("commands.deprecated.moveCardLegacyDescription")).action(async (cardId, listName) => {
31347
31448
  console.warn(t2("commands.deprecated.moveCardLegacyWarning"));
31348
31449
  try {
31349
31450
  await this.initializeTrelloControllers();
@@ -31352,7 +31453,7 @@ class CommandController {
31352
31453
  console.error(t2("commands.commandErrors.genericError"), error.message);
31353
31454
  }
31354
31455
  });
31355
- this.getProgram().command("delete-card-legacy <cardId>").description(t2("commands.deprecated.deleteCardLegacyDescription")).action(async (cardId) => {
31456
+ this.program.command("delete-card-legacy <cardId>").description(t2("commands.deprecated.deleteCardLegacyDescription")).action(async (cardId) => {
31356
31457
  console.warn(t2("commands.deprecated.deleteCardLegacyWarning"));
31357
31458
  try {
31358
31459
  await this.initializeTrelloControllers();
@@ -31363,13 +31464,16 @@ class CommandController {
31363
31464
  });
31364
31465
  }
31365
31466
  async run() {
31366
- await this.setupCommands();
31467
+ if (!this.program) {
31468
+ this.initializeProgram();
31469
+ this.setupCommands();
31470
+ }
31367
31471
  if (process.argv.length === 2) {
31368
31472
  const configRepository = new FileConfigRepository;
31369
31473
  const cli = new (await Promise.resolve().then(() => (init_TrelloCliController(), exports_TrelloCliController))).TrelloCliController(configRepository, this.outputFormatter);
31370
31474
  await cli.run();
31371
31475
  } else {
31372
- await this.getProgram().parseAsync();
31476
+ this.program.parse();
31373
31477
  }
31374
31478
  }
31375
31479
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trello-cli-unofficial",
3
3
  "type": "module",
4
- "version": "0.11.6",
4
+ "version": "0.12.0",
5
5
  "private": false,
6
6
  "description": "Unofficial Trello CLI using Power-Up authentication, built with Bun for maximum performance",
7
7
  "author": "Matheus Caiser <matheus.kaiser@gmail.com> (https://www.mrdeveloper.com.br/)",
@@ -0,0 +1,10 @@
1
+ import type { CardEntity } from '@domain/entities';
2
+ import type { TrelloRepository } from '@domain/repositories';
3
+
4
+ export class GetCardUseCase {
5
+ constructor(private trelloRepository: TrelloRepository) {}
6
+
7
+ async execute(cardId: string): Promise<CardEntity> {
8
+ return await this.trelloRepository.getCard(cardId);
9
+ }
10
+ }
@@ -6,6 +6,7 @@ export * from './DeleteCardUseCase';
6
6
  export * from './GetBoardDetailsUseCase';
7
7
  export * from './GetBoardsUseCase';
8
8
  export * from './GetCardsUseCase';
9
+ export * from './GetCardUseCase';
9
10
  export * from './GetListsUseCase';
10
11
  export * from './MoveCardUseCase';
11
12
  export * from './UpdateCardUseCase';
@@ -4,6 +4,10 @@ export interface Card {
4
4
  desc?: string;
5
5
  idList: string;
6
6
  url?: string;
7
+ labels?: Array<{ id: string; name?: string; color?: string }>;
8
+ members?: Array<{ id: string; username?: string; fullName?: string }>;
9
+ checklists?: Array<Record<string, unknown>>;
10
+ attachments?: Array<Record<string, unknown>>;
7
11
  }
8
12
 
9
13
  export interface CreateCardData {
@@ -26,6 +30,10 @@ interface TrelloCardResponse {
26
30
  idList: string;
27
31
  pos: number;
28
32
  url?: string;
33
+ labels?: Array<{ id: string; name?: string; color?: string }>;
34
+ members?: Array<{ id: string; username?: string; fullName?: string }>;
35
+ checklists?: Array<Record<string, unknown>>;
36
+ attachments?: Array<Record<string, unknown>>;
29
37
  [key: string]: unknown;
30
38
  }
31
39
 
@@ -36,10 +44,24 @@ export class CardEntity implements Card {
36
44
  public readonly idList: string,
37
45
  public readonly desc?: string,
38
46
  public readonly url?: string,
47
+ public readonly labels?: Array<{ id: string; name?: string; color?: string }>,
48
+ public readonly members?: Array<{ id: string; username?: string; fullName?: string }>,
49
+ public readonly checklists?: Array<Record<string, unknown>>,
50
+ public readonly attachments?: Array<Record<string, unknown>>,
39
51
  ) {}
40
52
 
41
53
  static fromApiResponse(data: TrelloCardResponse): CardEntity {
42
- return new CardEntity(data.id, data.name, data.idList, data.desc, data.url);
54
+ return new CardEntity(
55
+ data.id,
56
+ data.name,
57
+ data.idList,
58
+ data.desc,
59
+ data.url,
60
+ data.labels,
61
+ data.members,
62
+ data.checklists,
63
+ data.attachments,
64
+ );
43
65
  }
44
66
 
45
67
  static create(
@@ -14,6 +14,7 @@ export interface TrelloRepository {
14
14
  deleteList: (listId: string) => Promise<void>;
15
15
  moveList: (listId: string, position: number) => Promise<ListEntity>;
16
16
  getCards: (listId: string) => Promise<CardEntity[]>;
17
+ getCard: (cardId: string) => Promise<CardEntity>;
17
18
  createCard: (data: CreateCardData) => Promise<CardEntity>;
18
19
  updateCard: (cardId: string, data: UpdateCardData) => Promise<CardEntity>;
19
20
  deleteCard: (cardId: string) => Promise<void>;
@@ -127,6 +127,12 @@
127
127
  "delete": "🗑️ Delete Card",
128
128
  "back": "⬅️ Back",
129
129
  "moveCard": "📦 Move card"
130
+ },
131
+ "show": {
132
+ "labels": "🏷️ Labels:",
133
+ "members": "👥 Members:",
134
+ "checklists": "✅ Checklists:",
135
+ "attachments": "📎 Attachments:"
130
136
  }
131
137
  },
132
138
  "errors": {
@@ -263,6 +269,9 @@
263
269
  "update": {
264
270
  "description": "Update an existing card"
265
271
  },
272
+ "show": {
273
+ "description": "Show detailed information about a specific card"
274
+ },
266
275
  "delete": {
267
276
  "description": "Delete a card"
268
277
  }
@@ -127,6 +127,12 @@
127
127
  "delete": "🗑️ Deletar Card",
128
128
  "back": "⬅️ Voltar",
129
129
  "moveCard": "📦 Mover cartão"
130
+ },
131
+ "show": {
132
+ "labels": "🏷️ Labels:",
133
+ "members": "👥 Membros:",
134
+ "checklists": "✅ Checklists:",
135
+ "attachments": "📎 Anexos:"
130
136
  }
131
137
  },
132
138
  "errors": {
@@ -263,6 +269,9 @@
263
269
  "update": {
264
270
  "description": "Atualiza um card existente"
265
271
  },
272
+ "show": {
273
+ "description": "Mostra informações detalhadas sobre um card específico"
274
+ },
266
275
  "delete": {
267
276
  "description": "Deleta um card"
268
277
  }
@@ -40,7 +40,9 @@ export class TrelloApiRepository implements TrelloRepository {
40
40
  endpoint: string,
41
41
  options?: RequestInit,
42
42
  ): Promise<unknown> {
43
- const url = `${this.baseUrl}${endpoint}?key=${this.apiKey}&token=${this.token}`;
43
+ // If the endpoint already contains query params, add key/token with & otherwise use ?
44
+ const separator = endpoint.includes('?') ? '&' : '?';
45
+ const url = `${this.baseUrl}${endpoint}${separator}key=${this.apiKey}&token=${this.token}`;
44
46
 
45
47
  const response = await fetch(url, options);
46
48
 
@@ -235,4 +237,11 @@ export class TrelloApiRepository implements TrelloRepository {
235
237
  async moveCard(cardId: string, targetListId: string): Promise<CardEntity> {
236
238
  return this.updateCard(cardId, { idList: targetListId });
237
239
  }
240
+
241
+ async getCard(cardId: string): Promise<CardEntity> {
242
+ // Request the card with details: members, checklists and attachments
243
+ const endpoint = `/cards/${cardId}?checklists=all&members=true&attachments=true`;
244
+ const data = await this.request(endpoint);
245
+ return CardEntity.fromApiResponse(data as TrelloCardResponse);
246
+ }
238
247
  }
@@ -6,6 +6,7 @@ import type { OutputFormatter } from '@/shared';
6
6
  import {
7
7
  CreateCardUseCase,
8
8
  DeleteCardUseCase,
9
+ GetCardUseCase,
9
10
  MoveCardUseCase,
10
11
  UpdateCardUseCase,
11
12
  } from '@application/use-cases';
@@ -19,6 +20,7 @@ export class CardController {
19
20
  private updateCardUseCase: UpdateCardUseCase;
20
21
  private deleteCardUseCase: DeleteCardUseCase;
21
22
  private moveCardUseCase: MoveCardUseCase;
23
+ private getCardUseCase: GetCardUseCase;
22
24
 
23
25
  constructor(
24
26
  private trelloRepository: TrelloRepository,
@@ -29,6 +31,7 @@ export class CardController {
29
31
  this.updateCardUseCase = new UpdateCardUseCase(trelloRepository);
30
32
  this.deleteCardUseCase = new DeleteCardUseCase(trelloRepository);
31
33
  this.moveCardUseCase = new MoveCardUseCase(trelloRepository);
34
+ this.getCardUseCase = new GetCardUseCase(trelloRepository);
32
35
  }
33
36
 
34
37
  async createCardInteractive(): Promise<void> {
@@ -405,6 +408,56 @@ export class CardController {
405
408
  console.log(t('card.cardName', { name: updatedCard.name }));
406
409
  }
407
410
 
411
+ async showCard(cardId: string): Promise<void> {
412
+ // Get detailed card from the repository
413
+ const card = await this.getCardUseCase.execute(cardId);
414
+
415
+ // Try to find board and list names
416
+ const boards = await this.boardController.getBoards();
417
+ let boardName: string | undefined;
418
+ let listName: string | undefined;
419
+ for (const board of boards) {
420
+ const lists = await this.boardController.getLists(board.id);
421
+ const list = lists.find(l => l.id === card.idList);
422
+ if (list) {
423
+ boardName = board.name;
424
+ listName = list.name;
425
+ break;
426
+ }
427
+ }
428
+
429
+ this.outputFormatter.message(t('card.cardName', { name: card.name }));
430
+ if (card.desc) {
431
+ this.outputFormatter.message(t('card.cardDescription', { description: card.desc }));
432
+ }
433
+ this.outputFormatter.message(t('card.cardUrl', { url: card.url }));
434
+ this.outputFormatter.message(t('card.cardId', { id: card.id }));
435
+ if (boardName && listName) {
436
+ this.outputFormatter.message(`${t('board.boardName', { name: boardName })} / ${t('list.boardLists', { boardName: listName })}`);
437
+ }
438
+
439
+ // Show labels, members and checklists if present
440
+ if (card.labels && card.labels.length > 0) {
441
+ this.outputFormatter.message(t('card.show.labels'));
442
+ this.outputFormatter.output(card.labels);
443
+ }
444
+
445
+ if (card.members && card.members.length > 0) {
446
+ this.outputFormatter.message(t('card.show.members'));
447
+ this.outputFormatter.output(card.members);
448
+ }
449
+
450
+ if (card.checklists && card.checklists.length > 0) {
451
+ this.outputFormatter.message(t('card.show.checklists'));
452
+ this.outputFormatter.output(card.checklists);
453
+ }
454
+
455
+ if (card.attachments && card.attachments.length > 0) {
456
+ this.outputFormatter.message(t('card.show.attachments'));
457
+ this.outputFormatter.output(card.attachments);
458
+ }
459
+ }
460
+
408
461
  async moveCardToList(cardId: string, targetListId: string): Promise<void> {
409
462
  // Primeiro precisamos encontrar o cartão para mostrar informações
410
463
  const boards = await this.trelloRepository.getBoards();
@@ -8,31 +8,33 @@ import {
8
8
  FileConfigRepository,
9
9
  TrelloApiRepository,
10
10
  } from '@infrastructure/repositories';
11
-
12
11
  import { Command } from 'commander';
12
+
13
13
  import { t } from '@/i18n';
14
14
  import { OutputFormatter } from '@/shared';
15
-
16
15
  import { AuthController, BoardController, CardController } from './index';
17
16
 
18
17
  export class CommandController {
19
18
  private authController: AuthController;
20
19
  private boardController!: BoardController;
21
20
  private cardController!: CardController;
22
- private program: Command | null = null;
21
+ private program: Command;
23
22
  private outputFormatter: OutputFormatter;
24
23
 
25
24
  constructor() {
26
25
  const configRepository = new FileConfigRepository();
27
26
  this.authController = new AuthController(configRepository);
27
+ this.program = new Command();
28
28
  this.outputFormatter = new OutputFormatter();
29
+ this.initializeProgram();
30
+ this.setupCommands();
29
31
  }
30
32
 
31
- private getProgram(): Command {
33
+ private initializeProgram(): void {
34
+ // Ensure program is properly initialized
32
35
  if (!this.program) {
33
36
  this.program = new Command();
34
37
  }
35
- return this.program;
36
38
  }
37
39
 
38
40
  private getVersion(): string {
@@ -44,7 +46,8 @@ export class CommandController {
44
46
  try {
45
47
  const packageJson = JSON.parse(readFileSync(cwdPackageJson, 'utf-8'));
46
48
  return packageJson.version;
47
- } catch {
49
+ }
50
+ catch {
48
51
  // Continue to next approach
49
52
  }
50
53
  }
@@ -53,18 +56,19 @@ export class CommandController {
53
56
  try {
54
57
  const currentFilePath = fileURLToPath(import.meta.url);
55
58
  const currentDir = dirname(currentFilePath);
56
- const installedPackageJson = join(currentDir, '..', '..', 'package.json');
59
+ const installedPackageJson = join(currentDir, '..', '..', '..', '..', 'package.json');
57
60
 
58
61
  if (existsSync(installedPackageJson)) {
59
62
  const packageJson = JSON.parse(readFileSync(installedPackageJson, 'utf-8'));
60
63
  return packageJson.version;
61
64
  }
62
- } catch {
65
+ }
66
+ catch {
63
67
  // Continue to fallback
64
68
  }
65
69
 
66
- // 3. Fallback to hardcoded version from package.json
67
- return '0.11.3';
70
+ // 3. Fallback to hardcoded version
71
+ return '0.11.10';
68
72
  }
69
73
 
70
74
  private async initializeTrelloControllers(): Promise<void> {
@@ -88,11 +92,16 @@ export class CommandController {
88
92
  );
89
93
  }
90
94
 
91
- private async setupCommands(): Promise<void> {
95
+ private setupCommands(): void {
96
+ // Ensure program is initialized
97
+ if (!this.program) {
98
+ throw new Error(t('errors.programNotInitialized'));
99
+ }
100
+
92
101
  // Get version using robust method
93
102
  const version = this.getVersion();
94
103
 
95
- this.getProgram()
104
+ this.program
96
105
  .name('trello-cli-unofficial')
97
106
  .description(t('commands.description'))
98
107
  .version(version)
@@ -112,7 +121,7 @@ export class CommandController {
112
121
  });
113
122
 
114
123
  // Interactive mode
115
- this.getProgram()
124
+ this.program
116
125
  .command('interactive')
117
126
  .alias('i')
118
127
  .description(t('commands.interactive.description'))
@@ -125,7 +134,7 @@ export class CommandController {
125
134
  });
126
135
 
127
136
  // Setup command
128
- this.getProgram()
137
+ this.program
129
138
  .command('setup')
130
139
  .description(t('commands.setup.description'))
131
140
  .action(async () => {
@@ -133,7 +142,7 @@ export class CommandController {
133
142
  });
134
143
 
135
144
  // Boards subcommands
136
- const boardsCmd = this.getProgram()
145
+ const boardsCmd = this.program
137
146
  .command('boards')
138
147
  .description(t('commands.boards.manage'));
139
148
 
@@ -149,7 +158,10 @@ export class CommandController {
149
158
  }
150
159
  await this.boardController.showBoards();
151
160
  } catch (error) {
152
- console.error(t('commands.errors.genericError'), (error as Error).message);
161
+ console.error(
162
+ t('commands.commandErrors.genericError'),
163
+ (error as Error).message,
164
+ );
153
165
  }
154
166
  });
155
167
 
@@ -165,7 +177,7 @@ export class CommandController {
165
177
  }
166
178
  await this.boardController.showBoardDetails(boardId);
167
179
  } catch (error) {
168
- console.error(t('commands.errors.genericError'), (error as Error).message);
180
+ console.error(t('commands.commandErrors.genericError'), (error as Error).message);
169
181
  }
170
182
  });
171
183
 
@@ -186,7 +198,7 @@ export class CommandController {
186
198
  });
187
199
 
188
200
  // Legacy boards command with deprecation warning
189
- this.getProgram()
201
+ this.program
190
202
  .command('boards-legacy')
191
203
  .description(t('commands.deprecated.boardsLegacyDescription'))
192
204
  .action(async () => {
@@ -203,7 +215,7 @@ export class CommandController {
203
215
  });
204
216
 
205
217
  // Lists subcommands
206
- const listsCmd = this.getProgram()
218
+ const listsCmd = this.program
207
219
  .command('lists')
208
220
  .description(t('commands.lists.description'));
209
221
 
@@ -219,7 +231,7 @@ export class CommandController {
219
231
  }
220
232
  await this.boardController.showListsById(boardId);
221
233
  } catch (error) {
222
- console.error(t('commands.errors.genericError'), (error as Error).message);
234
+ console.error(t('commands.commandErrors.genericError'), (error as Error).message);
223
235
  }
224
236
  });
225
237
 
@@ -272,7 +284,7 @@ export class CommandController {
272
284
  });
273
285
 
274
286
  // Legacy lists command with deprecation warning
275
- this.getProgram()
287
+ this.program
276
288
  .command('lists-legacy <boardName>')
277
289
  .description(t('commands.deprecated.listsLegacyDescription'))
278
290
  .action(async (boardName: string) => {
@@ -289,7 +301,7 @@ export class CommandController {
289
301
  });
290
302
 
291
303
  // Cards subcommands
292
- const cardsCmd = this.getProgram()
304
+ const cardsCmd = this.program
293
305
  .command('cards')
294
306
  .description(t('commands.cards.manage'));
295
307
 
@@ -305,7 +317,7 @@ export class CommandController {
305
317
  }
306
318
  await this.boardController.showCardsByListId(listId);
307
319
  } catch (error) {
308
- console.error(t('commands.errors.genericError'), (error as Error).message);
320
+ console.error(t('commands.commandErrors.genericError'), (error as Error).message);
309
321
  }
310
322
  });
311
323
 
@@ -388,8 +400,27 @@ export class CommandController {
388
400
  },
389
401
  );
390
402
 
403
+ cardsCmd
404
+ .command('show <cardId>')
405
+ .description(t('commands.cards.show.description'))
406
+ .option('-f, --format <format>', t('commands.formatOption'), 'table')
407
+ .action(async (cardId: string, options: { format?: string }) => {
408
+ try {
409
+ await this.initializeTrelloControllers();
410
+ if (options.format) {
411
+ this.outputFormatter.setFormat(options.format as OutputFormat);
412
+ }
413
+ await this.cardController.showCard(cardId);
414
+ } catch (error) {
415
+ console.error(
416
+ t('commands.commandErrors.genericError'),
417
+ (error as Error).message,
418
+ );
419
+ }
420
+ });
421
+
391
422
  // Legacy commands with deprecation warnings
392
- this.getProgram()
423
+ this.program
393
424
  .command('cards-legacy <boardName> <listName>')
394
425
  .description(t('commands.deprecated.cardsLegacyDescription'))
395
426
  .action(async (boardName: string, listName: string) => {
@@ -405,7 +436,7 @@ export class CommandController {
405
436
  }
406
437
  });
407
438
 
408
- this.getProgram()
439
+ this.program
409
440
  .command('create-card-legacy <boardName> <listName> <cardName>')
410
441
  .description(t('commands.deprecated.createCardLegacyDescription'))
411
442
  .option('-d, --desc <description>', t('commands.options.cardDescription'))
@@ -434,7 +465,7 @@ export class CommandController {
434
465
  },
435
466
  );
436
467
 
437
- this.getProgram()
468
+ this.program
438
469
  .command('move-card-legacy <cardId> <listName>')
439
470
  .description(t('commands.deprecated.moveCardLegacyDescription'))
440
471
  .action(async (cardId: string, listName: string) => {
@@ -450,7 +481,7 @@ export class CommandController {
450
481
  }
451
482
  });
452
483
 
453
- this.getProgram()
484
+ this.program
454
485
  .command('delete-card-legacy <cardId>')
455
486
  .description(t('commands.deprecated.deleteCardLegacyDescription'))
456
487
  .action(async (cardId: string) => {
@@ -468,7 +499,11 @@ export class CommandController {
468
499
  }
469
500
 
470
501
  async run(): Promise<void> {
471
- await this.setupCommands();
502
+ // Ensure program is initialized before parsing
503
+ if (!this.program) {
504
+ this.initializeProgram();
505
+ this.setupCommands();
506
+ }
472
507
 
473
508
  // Fallback to interactive mode if no command specified
474
509
  if (process.argv.length === 2) {
@@ -478,7 +513,7 @@ export class CommandController {
478
513
  ).TrelloCliController(configRepository, this.outputFormatter);
479
514
  await cli.run();
480
515
  } else {
481
- await this.getProgram().parseAsync();
516
+ this.program.parse();
482
517
  }
483
518
  }
484
519
  }