From c8228e913b993e16b2671e743f29cd76a25059b3 Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:22:03 +0200 Subject: [PATCH] feat: show brief title when listing brief instead of uuid - add search for brief selection --- apps/cli/package.json | 17 +++- apps/cli/src/commands/context.command.ts | 74 +++++++++++----- package-lock.json | 86 +++++++++++++++++++ .../src/services/organization.service.ts | 40 ++++++++- .../tm-core/src/storage/storage-factory.ts | 5 +- 5 files changed, 193 insertions(+), 29 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 8c3561ed..4ccdbda1 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -8,7 +8,10 @@ "exports": { ".": "./src/index.ts" }, - "files": ["dist", "README.md"], + "files": [ + "dist", + "README.md" + ], "scripts": { "typecheck": "tsc --noEmit", "lint": "biome check src", @@ -22,6 +25,7 @@ "test:ci": "vitest run --coverage --reporter=dot" }, "dependencies": { + "@inquirer/search": "^3.2.0", "@tm/core": "*", "boxen": "^8.0.1", "chalk": "5.6.2", @@ -41,12 +45,19 @@ "engines": { "node": ">=18.0.0" }, - "keywords": ["task-master", "cli", "task-management", "productivity"], + "keywords": [ + "task-master", + "cli", + "task-management", + "productivity" + ], "author": "", "license": "MIT", "typesVersions": { "*": { - "*": ["src/*"] + "*": [ + "src/*" + ] } }, "version": "" diff --git a/apps/cli/src/commands/context.command.ts b/apps/cli/src/commands/context.command.ts index 2bd175d5..630ba21f 100644 --- a/apps/cli/src/commands/context.command.ts +++ b/apps/cli/src/commands/context.command.ts @@ -6,6 +6,7 @@ import { Command } from 'commander'; import chalk from 'chalk'; import inquirer from 'inquirer'; +import search from '@inquirer/search'; import ora, { Ora } from 'ora'; import { AuthManager, @@ -156,10 +157,14 @@ export class ContextCommand extends Command { if (context.briefName || context.briefId) { console.log(chalk.green('\n✓ Brief')); - if (context.briefName) { + if (context.briefName && context.briefId) { + const shortId = context.briefId.slice(0, 8); + console.log( + chalk.white(` ${context.briefName} `) + chalk.gray(`(${shortId})`) + ); + } else if (context.briefName) { console.log(chalk.white(` ${context.briefName}`)); - } - if (context.briefId) { + } else if (context.briefId) { console.log(chalk.gray(` ID: ${context.briefId}`)); } } @@ -324,25 +329,53 @@ export class ContextCommand extends Command { }; } - // Prompt for selection - const { selectedBrief } = await inquirer.prompt([ - { - type: 'list', - name: 'selectedBrief', - message: 'Select a brief:', - choices: [ - { name: '(No brief - organization level)', value: null }, - ...briefs.map((brief) => ({ - name: `Brief ${brief.id} (${new Date(brief.createdAt).toLocaleDateString()})`, - value: brief - })) - ] + // Prompt for selection with search + const selectedBrief = await search<(typeof briefs)[0] | null>({ + message: 'Search for a brief:', + source: async (input) => { + const searchTerm = input?.toLowerCase() || ''; + + // Static option for no brief + const noBriefOption = { + name: '(No brief - organization level)', + value: null as any, + description: 'Clear brief selection' + }; + + // Filter and map brief options + const briefOptions = briefs + .filter((brief) => { + if (!searchTerm) return true; + + const title = brief.document?.title || ''; + const shortId = brief.id.slice(0, 8); + + // Search by title first, then by UUID + return ( + title.toLowerCase().includes(searchTerm) || + brief.id.toLowerCase().includes(searchTerm) || + shortId.toLowerCase().includes(searchTerm) + ); + }) + .map((brief) => { + const title = + brief.document?.title || `Brief ${brief.id.slice(0, 8)}`; + const shortId = brief.id.slice(0, 8); + return { + name: `${title} ${chalk.gray(`(${shortId})`)}`, + value: brief + }; + }); + + return [noBriefOption, ...briefOptions]; } - ]); + }); if (selectedBrief) { // Update context with brief - const briefName = `Brief ${selectedBrief.id.slice(0, 8)}`; + const briefName = + selectedBrief.document?.title || + `Brief ${selectedBrief.id.slice(0, 8)}`; this.authManager.updateContext({ briefId: selectedBrief.id, briefName: briefName @@ -354,7 +387,7 @@ export class ContextCommand extends Command { success: true, action: 'select-brief', context: this.authManager.getContext() || undefined, - message: `Selected brief: ${selectedBrief.name}` + message: `Selected brief: ${selectedBrief.document?.title}` }; } else { // Clear brief selection @@ -490,7 +523,8 @@ export class ContextCommand extends Command { } // Update context: set org and brief - const briefName = `Brief ${brief.id.slice(0, 8)}`; + const briefName = + brief.document?.title || `Brief ${brief.id.slice(0, 8)}`; this.authManager.updateContext({ orgId: brief.accountId, orgName, diff --git a/package-lock.json b/package-lock.json index 3c1a3197..00e71c65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,6 +104,7 @@ "name": "@tm/cli", "license": "MIT", "dependencies": { + "@inquirer/search": "^3.2.0", "@tm/core": "*", "boxen": "^8.0.1", "chalk": "5.6.2", @@ -124,6 +125,91 @@ "node": ">=18.0.0" } }, + "apps/cli/node_modules/@inquirer/ansi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", + "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "apps/cli/node_modules/@inquirer/figures": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", + "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "apps/cli/node_modules/@inquirer/search": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", + "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/cli/node_modules/@inquirer/search/node_modules/@inquirer/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", + "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/cli/node_modules/@inquirer/search/node_modules/@inquirer/type": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", + "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "apps/docs": { "version": "0.0.6", "devDependencies": { diff --git a/packages/tm-core/src/services/organization.service.ts b/packages/tm-core/src/services/organization.service.ts index fadbd781..8d126301 100644 --- a/packages/tm-core/src/services/organization.service.ts +++ b/packages/tm-core/src/services/organization.service.ts @@ -27,6 +27,12 @@ export interface Brief { status: string; createdAt: string; updatedAt: string; + document?: { + id: string; + title: string; + document_name: string; + description?: string; + }; } /** @@ -171,7 +177,12 @@ export class OrganizationService { document_id, status, created_at, - updated_at + updated_at, + document:document_id ( + id, + document_name, + title + ) `) .eq('account_id', orgId); @@ -196,7 +207,14 @@ export class OrganizationService { documentId: brief.document_id, status: brief.status, createdAt: brief.created_at, - updatedAt: brief.updated_at + updatedAt: brief.updated_at, + document: brief.document + ? { + id: brief.document.id, + document_name: brief.document.document_name, + title: brief.document.title + } + : undefined })); } catch (error) { if (error instanceof TaskMasterError) { @@ -224,7 +242,13 @@ export class OrganizationService { document_id, status, created_at, - updated_at + updated_at, + document:document_id ( + id, + document_name, + title, + description + ) `) .eq('id', briefId) .single(); @@ -253,7 +277,15 @@ export class OrganizationService { documentId: briefData.document_id, status: briefData.status, createdAt: briefData.created_at, - updatedAt: briefData.updated_at + updatedAt: briefData.updated_at, + document: briefData.document + ? { + id: briefData.document.id, + document_name: briefData.document.document_name, + title: briefData.document.title, + description: briefData.document.description + } + : undefined }; } catch (error) { if (error instanceof TaskMasterError) { diff --git a/packages/tm-core/src/storage/storage-factory.ts b/packages/tm-core/src/storage/storage-factory.ts index 5c65ba54..a08d3df2 100644 --- a/packages/tm-core/src/storage/storage-factory.ts +++ b/packages/tm-core/src/storage/storage-factory.ts @@ -82,8 +82,8 @@ export class StorageFactory { apiAccessToken: credentials.token, apiEndpoint: config.storage?.apiEndpoint || - process.env.TM_PUBLIC_BASE_DOMAIN || - 'https://tryhamster.com/api' + process.env.TM_BASE_DOMAIN || + process.env.TM_PUBLIC_BASE_DOMAIN }; config.storage = nextStorage; } @@ -112,6 +112,7 @@ export class StorageFactory { apiAccessToken: credentials.token, apiEndpoint: config.storage?.apiEndpoint || + process.env.TM_BASE_DOMAIN || process.env.TM_PUBLIC_BASE_DOMAIN || 'https://tryhamster.com/api' };