bktide 1.0.1755266193 → 1.0.1755547716

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.
Files changed (75) hide show
  1. package/README.md +107 -1
  2. package/WORKFLOW_README.md +1 -1
  3. package/completions/bktide-dynamic.fish +171 -0
  4. package/completions/bktide.bash +124 -0
  5. package/completions/bktide.fish +107 -0
  6. package/completions/bktide.zsh +139 -0
  7. package/dist/commands/BaseCommand.js +7 -7
  8. package/dist/commands/BaseCommand.js.map +1 -1
  9. package/dist/commands/GenerateCompletions.js +238 -0
  10. package/dist/commands/GenerateCompletions.js.map +1 -0
  11. package/dist/commands/ListAnnotations.js +7 -0
  12. package/dist/commands/ListAnnotations.js.map +1 -1
  13. package/dist/commands/ListBuilds.js +67 -3
  14. package/dist/commands/ListBuilds.js.map +1 -1
  15. package/dist/commands/ListOrganizations.js +6 -0
  16. package/dist/commands/ListOrganizations.js.map +1 -1
  17. package/dist/commands/ListPipelines.js +87 -12
  18. package/dist/commands/ListPipelines.js.map +1 -1
  19. package/dist/commands/ManageToken.js +32 -9
  20. package/dist/commands/ManageToken.js.map +1 -1
  21. package/dist/commands/ShowViewer.js +7 -1
  22. package/dist/commands/ShowViewer.js.map +1 -1
  23. package/dist/commands/index.js +1 -0
  24. package/dist/commands/index.js.map +1 -1
  25. package/dist/formatters/annotations/PlainTextFormatter.js +37 -9
  26. package/dist/formatters/annotations/PlainTextFormatter.js.map +1 -1
  27. package/dist/formatters/builds/PlainTextFormatter.js +82 -60
  28. package/dist/formatters/builds/PlainTextFormatter.js.map +1 -1
  29. package/dist/formatters/errors/AlfredFormatter.js +20 -0
  30. package/dist/formatters/errors/AlfredFormatter.js.map +1 -1
  31. package/dist/formatters/errors/PlainTextFormatter.js +121 -23
  32. package/dist/formatters/errors/PlainTextFormatter.js.map +1 -1
  33. package/dist/formatters/organizations/PlainTextFormatter.js +37 -6
  34. package/dist/formatters/organizations/PlainTextFormatter.js.map +1 -1
  35. package/dist/formatters/pipelines/AlfredFormatter.js.map +1 -1
  36. package/dist/formatters/pipelines/Formatter.js.map +1 -1
  37. package/dist/formatters/pipelines/JsonFormatter.js.map +1 -1
  38. package/dist/formatters/pipelines/PlainTextFormatter.js +165 -19
  39. package/dist/formatters/pipelines/PlainTextFormatter.js.map +1 -1
  40. package/dist/formatters/token/AlfredFormatter.js +15 -2
  41. package/dist/formatters/token/AlfredFormatter.js.map +1 -1
  42. package/dist/formatters/token/PlainTextFormatter.js +56 -18
  43. package/dist/formatters/token/PlainTextFormatter.js.map +1 -1
  44. package/dist/formatters/viewer/PlainTextFormatter.js +8 -7
  45. package/dist/formatters/viewer/PlainTextFormatter.js.map +1 -1
  46. package/dist/index.js +47 -6
  47. package/dist/index.js.map +1 -1
  48. package/dist/services/CredentialManager.js +80 -10
  49. package/dist/services/CredentialManager.js.map +1 -1
  50. package/dist/ui/help.js +69 -0
  51. package/dist/ui/help.js.map +1 -0
  52. package/dist/ui/progress.js +356 -0
  53. package/dist/ui/progress.js.map +1 -0
  54. package/dist/ui/reporter.js +111 -0
  55. package/dist/ui/reporter.js.map +1 -0
  56. package/dist/ui/responsive-table.js +183 -0
  57. package/dist/ui/responsive-table.js.map +1 -0
  58. package/dist/ui/spinner.js +20 -0
  59. package/dist/ui/spinner.js.map +1 -0
  60. package/dist/ui/symbols.js +46 -0
  61. package/dist/ui/symbols.js.map +1 -0
  62. package/dist/ui/table.js +32 -0
  63. package/dist/ui/table.js.map +1 -0
  64. package/dist/ui/theme.js +280 -0
  65. package/dist/ui/theme.js.map +1 -0
  66. package/dist/ui/width.js +111 -0
  67. package/dist/ui/width.js.map +1 -0
  68. package/dist/utils/alfred.js +6 -0
  69. package/dist/utils/alfred.js.map +1 -0
  70. package/dist/utils/cli-error-handler.js +35 -20
  71. package/dist/utils/cli-error-handler.js.map +1 -1
  72. package/dist/utils/pagination.js +92 -0
  73. package/dist/utils/pagination.js.map +1 -0
  74. package/info.plist +51 -218
  75. package/package.json +23 -5
@@ -1,6 +1,8 @@
1
1
  import { BaseErrorFormatter } from './Formatter.js';
2
+ import { COLORS, SYMBOLS, formatTips, TipStyle } from '../../ui/theme.js';
3
+ import { wrapText, termWidth } from '../../ui/width.js';
2
4
  /**
3
- * Plain text formatter for errors
5
+ * Plain text formatter for errors with structured sections
4
6
  */
5
7
  export class PlainTextFormatter extends BaseErrorFormatter {
6
8
  name = 'plain';
@@ -13,40 +15,136 @@ export class PlainTextFormatter extends BaseErrorFormatter {
13
15
  */
14
16
  formatError(errors, options) {
15
17
  const errorArray = Array.isArray(errors) ? errors : [errors];
16
- let output = '';
18
+ const sections = [];
19
+ const width = termWidth();
20
+ const contentWidth = Math.min(width - 4, 76); // Leave some margin, cap at 76 chars
17
21
  for (const error of errorArray) {
18
- if (output)
19
- output += '\n\n'; // Add spacing between multiple errors
20
- // Add error header
21
- output += `\x1b[31m${this.getErrorName(error)}: ${this.getErrorMessage(error)}\x1b[0m\n`;
22
- // Add stack trace if available and debug is enabled
23
- const stack = this.getStackTrace(error);
24
- if (stack && options?.debug) {
25
- output += `\n\x1b[33mStack Trace:\x1b[0m\n${stack}\n`;
22
+ if (sections.length > 0)
23
+ sections.push(''); // Add spacing between multiple errors
24
+ // Title section with icon
25
+ const errorName = this.getErrorName(error);
26
+ const errorMessage = this.getErrorMessage(error);
27
+ sections.push(COLORS.error(`${SYMBOLS.error} ERROR ${errorName}`));
28
+ // Message section (wrapped for readability)
29
+ if (errorMessage && errorMessage !== errorName) {
30
+ const wrappedMessage = wrapText(errorMessage, contentWidth);
31
+ wrappedMessage.forEach(line => {
32
+ sections.push(` ${line}`);
33
+ });
34
+ }
35
+ // Cause section (if available)
36
+ const cause = this.getErrorCause(error);
37
+ if (cause) {
38
+ sections.push('');
39
+ sections.push(COLORS.muted(`CAUSE ${cause}`));
26
40
  }
27
- // Add API errors if present
41
+ // API errors section
28
42
  const apiErrors = this.getApiErrors(error);
29
43
  if (apiErrors?.length) {
30
- output += '\n\x1b[33mAPI Errors:\x1b[0m\n';
31
- apiErrors.forEach((apiError, index) => {
32
- output += ` Error ${index + 1}: ${apiError.message || 'Unknown error'}\n`;
33
- if (apiError.path)
34
- output += ` Path: ${apiError.path.join('.')}\n`;
35
- if (apiError.locations)
36
- output += ` Locations: ${JSON.stringify(apiError.locations)}\n`;
44
+ sections.push('');
45
+ sections.push(COLORS.warn(`${SYMBOLS.warn} DETAILS`));
46
+ apiErrors.forEach((apiError) => {
47
+ const message = apiError.message || 'Unknown error';
48
+ sections.push(` ${SYMBOLS.bullet} ${message}`);
49
+ if (apiError.path) {
50
+ sections.push(COLORS.muted(` Path: ${apiError.path.join('.')}`));
51
+ }
37
52
  });
38
53
  }
39
- // Add request details if available and debug is enabled
54
+ // Stack trace (debug only)
55
+ const stack = this.getStackTrace(error);
56
+ if (stack && options?.debug) {
57
+ sections.push('');
58
+ sections.push(COLORS.muted('STACK'));
59
+ // Indent stack trace lines
60
+ stack.split('\n').forEach(line => {
61
+ sections.push(COLORS.muted(` ${line}`));
62
+ });
63
+ }
64
+ // Request details (debug only)
40
65
  const request = this.getRequestDetails(error);
41
66
  if (request && options?.debug) {
42
- output += '\n\x1b[36mRequest Details:\x1b[0m\n';
67
+ sections.push('');
68
+ sections.push(COLORS.muted('REQUEST'));
43
69
  if (request.url)
44
- output += ` URL: ${request.url}\n`;
70
+ sections.push(COLORS.muted(` URL: ${request.url}`));
45
71
  if (request.method)
46
- output += ` Method: ${request.method}\n`;
72
+ sections.push(COLORS.muted(` Method: ${request.method}`));
73
+ }
74
+ // Hints section - context-aware suggestions
75
+ const hints = this.getContextualHints(error, errorMessage);
76
+ if (hints.length > 0) {
77
+ sections.push('');
78
+ sections.push(formatTips(hints, TipStyle.FIXES));
79
+ }
80
+ }
81
+ return sections.join('\n');
82
+ }
83
+ /**
84
+ * Get contextual hints based on the error
85
+ */
86
+ getContextualHints(_error, message) {
87
+ const hints = [];
88
+ const lowerMessage = message.toLowerCase();
89
+ // Authentication errors
90
+ if (lowerMessage.includes('auth') || lowerMessage.includes('unauthorized') ||
91
+ lowerMessage.includes('401') || lowerMessage.includes('token')) {
92
+ hints.push('Check your token: bktide token --check');
93
+ hints.push('Store a new token: bktide token --store');
94
+ hints.push('Set token via: export BUILDKITE_API_TOKEN=<your-token>');
95
+ }
96
+ // Network errors
97
+ else if (lowerMessage.includes('econnrefused') || lowerMessage.includes('network') ||
98
+ lowerMessage.includes('etimedout') || lowerMessage.includes('dns')) {
99
+ hints.push('Check your internet connection');
100
+ hints.push('Verify Buildkite API is accessible');
101
+ hints.push('Try again with --no-cache to bypass cache');
102
+ }
103
+ // Permission errors
104
+ else if (lowerMessage.includes('permission') || lowerMessage.includes('forbidden') ||
105
+ lowerMessage.includes('403')) {
106
+ hints.push('Verify you have access to this resource');
107
+ hints.push('Check organization permissions');
108
+ hints.push('List accessible orgs: bktide orgs');
109
+ }
110
+ // Not found errors
111
+ else if (lowerMessage.includes('not found') || lowerMessage.includes('404')) {
112
+ hints.push('Verify the resource exists');
113
+ hints.push('Check spelling of organization/pipeline/build names');
114
+ hints.push('List available resources with appropriate list command');
115
+ }
116
+ // Rate limit errors
117
+ else if (lowerMessage.includes('rate limit') || lowerMessage.includes('429')) {
118
+ hints.push('Wait a moment before retrying');
119
+ hints.push('Use --no-cache to avoid repeated API calls');
120
+ hints.push('Consider using --count with a smaller value');
121
+ }
122
+ // Generic hints
123
+ else {
124
+ hints.push('Run with --debug for detailed error information');
125
+ hints.push('Check command syntax: bktide <command> --help');
126
+ hints.push('Report issues: https://github.com/your-repo/issues');
127
+ }
128
+ // Always include debug hint if not already in debug mode
129
+ if (!hints.some(h => h.includes('--debug'))) {
130
+ hints.push('Use --debug flag for stack trace');
131
+ }
132
+ return hints;
133
+ }
134
+ /**
135
+ * Try to extract a cause from the error
136
+ */
137
+ getErrorCause(error) {
138
+ if (error && typeof error === 'object' && 'cause' in error) {
139
+ const cause = error.cause;
140
+ if (cause instanceof Error) {
141
+ return cause.message;
142
+ }
143
+ if (typeof cause === 'string') {
144
+ return cause;
47
145
  }
48
146
  }
49
- return output;
147
+ return null;
50
148
  }
51
149
  }
52
150
  //# sourceMappingURL=PlainTextFormatter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/errors/PlainTextFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAyC,MAAM,gBAAgB,CAAC;AAE3F;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IACxD,IAAI,GAAG,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,WAAW,CAAC,MAA2B,EAAE,OAA+B;QACtE,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,IAAI,MAAM;gBAAE,MAAM,IAAI,MAAM,CAAC,CAAC,sCAAsC;YAEpE,mBAAmB;YACnB,MAAM,IAAI,WAAW,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC;YAEzF,oDAAoD;YACpD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,KAAK,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBAC5B,MAAM,IAAI,kCAAkC,KAAK,IAAI,CAAC;YACxD,CAAC;YAED,4BAA4B;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,SAAS,EAAE,MAAM,EAAE,CAAC;gBACtB,MAAM,IAAI,gCAAgC,CAAC;gBAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;oBACpC,MAAM,IAAI,WAAW,KAAK,GAAG,CAAC,KAAK,QAAQ,CAAC,OAAO,IAAI,eAAe,IAAI,CAAC;oBAC3E,IAAI,QAAQ,CAAC,IAAI;wBAAE,MAAM,IAAI,WAAW,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;oBACpE,IAAI,QAAQ,CAAC,SAAS;wBAAE,MAAM,IAAI,gBAAgB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC3F,CAAC,CAAC,CAAC;YACL,CAAC;YAED,wDAAwD;YACxD,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,OAAO,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBAC9B,MAAM,IAAI,qCAAqC,CAAC;gBAChD,IAAI,OAAO,CAAC,GAAG;oBAAE,MAAM,IAAI,UAAU,OAAO,CAAC,GAAG,IAAI,CAAC;gBACrD,IAAI,OAAO,CAAC,MAAM;oBAAE,MAAM,IAAI,aAAa,OAAO,CAAC,MAAM,IAAI,CAAC;YAChE,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import { BaseErrorFormatter, ErrorFormatter, ErrorFormatterOptions } from './Formatter.js';\n\n/**\n * Plain text formatter for errors\n */\nexport class PlainTextFormatter extends BaseErrorFormatter implements ErrorFormatter {\n name = 'plain';\n\n /**\n * Format one or more errors for display in plain text\n * \n * @param errors The error(s) to format\n * @param options Formatting options\n * @returns Formatted error message\n */\n formatError(errors: unknown | unknown[], options?: ErrorFormatterOptions): string {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n let output = '';\n\n for (const error of errorArray) {\n if (output) output += '\\n\\n'; // Add spacing between multiple errors\n\n // Add error header\n output += `\\x1b[31m${this.getErrorName(error)}: ${this.getErrorMessage(error)}\\x1b[0m\\n`;\n\n // Add stack trace if available and debug is enabled\n const stack = this.getStackTrace(error);\n if (stack && options?.debug) {\n output += `\\n\\x1b[33mStack Trace:\\x1b[0m\\n${stack}\\n`;\n }\n\n // Add API errors if present\n const apiErrors = this.getApiErrors(error);\n if (apiErrors?.length) {\n output += '\\n\\x1b[33mAPI Errors:\\x1b[0m\\n';\n apiErrors.forEach((apiError, index) => {\n output += ` Error ${index + 1}: ${apiError.message || 'Unknown error'}\\n`;\n if (apiError.path) output += ` Path: ${apiError.path.join('.')}\\n`;\n if (apiError.locations) output += ` Locations: ${JSON.stringify(apiError.locations)}\\n`;\n });\n }\n\n // Add request details if available and debug is enabled\n const request = this.getRequestDetails(error);\n if (request && options?.debug) {\n output += '\\n\\x1b[36mRequest Details:\\x1b[0m\\n';\n if (request.url) output += ` URL: ${request.url}\\n`;\n if (request.method) output += ` Method: ${request.method}\\n`;\n }\n }\n\n return output;\n }\n} "]}
1
+ {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/errors/PlainTextFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseErrorFormatter, ErrorFormatter, ErrorFormatterOptions } from './Formatter.js';\nimport { COLORS, SYMBOLS, formatTips, TipStyle } from '../../ui/theme.js';\nimport { wrapText, termWidth } from '../../ui/width.js';\n\n/**\n * Plain text formatter for errors with structured sections\n */\nexport class PlainTextFormatter extends BaseErrorFormatter implements ErrorFormatter {\n name = 'plain';\n\n /**\n * Format one or more errors for display in plain text\n * \n * @param errors The error(s) to format\n * @param options Formatting options\n * @returns Formatted error message\n */\n formatError(errors: unknown | unknown[], options?: ErrorFormatterOptions): string {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n const sections: string[] = [];\n const width = termWidth();\n const contentWidth = Math.min(width - 4, 76); // Leave some margin, cap at 76 chars\n \n for (const error of errorArray) {\n if (sections.length > 0) sections.push(''); // Add spacing between multiple errors\n \n // Title section with icon\n const errorName = this.getErrorName(error);\n const errorMessage = this.getErrorMessage(error);\n sections.push(COLORS.error(`${SYMBOLS.error} ERROR ${errorName}`));\n \n // Message section (wrapped for readability)\n if (errorMessage && errorMessage !== errorName) {\n const wrappedMessage = wrapText(errorMessage, contentWidth);\n wrappedMessage.forEach(line => {\n sections.push(` ${line}`);\n });\n }\n \n // Cause section (if available)\n const cause = this.getErrorCause(error);\n if (cause) {\n sections.push('');\n sections.push(COLORS.muted(`CAUSE ${cause}`));\n }\n \n // API errors section\n const apiErrors = this.getApiErrors(error);\n if (apiErrors?.length) {\n sections.push('');\n sections.push(COLORS.warn(`${SYMBOLS.warn} DETAILS`));\n apiErrors.forEach((apiError) => {\n const message = apiError.message || 'Unknown error';\n sections.push(` ${SYMBOLS.bullet} ${message}`);\n if (apiError.path) {\n sections.push(COLORS.muted(` Path: ${apiError.path.join('.')}`));\n }\n });\n }\n \n // Stack trace (debug only)\n const stack = this.getStackTrace(error);\n if (stack && options?.debug) {\n sections.push('');\n sections.push(COLORS.muted('STACK'));\n // Indent stack trace lines\n stack.split('\\n').forEach(line => {\n sections.push(COLORS.muted(` ${line}`));\n });\n }\n \n // Request details (debug only)\n const request = this.getRequestDetails(error);\n if (request && options?.debug) {\n sections.push('');\n sections.push(COLORS.muted('REQUEST'));\n if (request.url) sections.push(COLORS.muted(` URL: ${request.url}`));\n if (request.method) sections.push(COLORS.muted(` Method: ${request.method}`));\n }\n \n // Hints section - context-aware suggestions\n const hints = this.getContextualHints(error, errorMessage);\n if (hints.length > 0) {\n sections.push('');\n sections.push(formatTips(hints, TipStyle.FIXES));\n }\n }\n \n return sections.join('\\n');\n }\n \n /**\n * Get contextual hints based on the error\n */\n private getContextualHints(_error: unknown, message: string): string[] {\n const hints: string[] = [];\n const lowerMessage = message.toLowerCase();\n \n // Authentication errors\n if (lowerMessage.includes('auth') || lowerMessage.includes('unauthorized') || \n lowerMessage.includes('401') || lowerMessage.includes('token')) {\n hints.push('Check your token: bktide token --check');\n hints.push('Store a new token: bktide token --store');\n hints.push('Set token via: export BUILDKITE_API_TOKEN=<your-token>');\n }\n // Network errors\n else if (lowerMessage.includes('econnrefused') || lowerMessage.includes('network') ||\n lowerMessage.includes('etimedout') || lowerMessage.includes('dns')) {\n hints.push('Check your internet connection');\n hints.push('Verify Buildkite API is accessible');\n hints.push('Try again with --no-cache to bypass cache');\n }\n // Permission errors\n else if (lowerMessage.includes('permission') || lowerMessage.includes('forbidden') ||\n lowerMessage.includes('403')) {\n hints.push('Verify you have access to this resource');\n hints.push('Check organization permissions');\n hints.push('List accessible orgs: bktide orgs');\n }\n // Not found errors\n else if (lowerMessage.includes('not found') || lowerMessage.includes('404')) {\n hints.push('Verify the resource exists');\n hints.push('Check spelling of organization/pipeline/build names');\n hints.push('List available resources with appropriate list command');\n }\n // Rate limit errors\n else if (lowerMessage.includes('rate limit') || lowerMessage.includes('429')) {\n hints.push('Wait a moment before retrying');\n hints.push('Use --no-cache to avoid repeated API calls');\n hints.push('Consider using --count with a smaller value');\n }\n // Generic hints\n else {\n hints.push('Run with --debug for detailed error information');\n hints.push('Check command syntax: bktide <command> --help');\n hints.push('Report issues: https://github.com/your-repo/issues');\n }\n \n // Always include debug hint if not already in debug mode\n if (!hints.some(h => h.includes('--debug'))) {\n hints.push('Use --debug flag for stack trace');\n }\n \n return hints;\n }\n \n /**\n * Try to extract a cause from the error\n */\n private getErrorCause(error: unknown): string | null {\n if (error && typeof error === 'object' && 'cause' in error) {\n const cause = (error as any).cause;\n if (cause instanceof Error) {\n return cause.message;\n }\n if (typeof cause === 'string') {\n return cause;\n }\n }\n return null;\n }\n} "]}
@@ -1,15 +1,46 @@
1
1
  import { BaseFormatter } from './Formatter.js';
2
+ import { renderTable } from '../../ui/table.js';
3
+ import { isMobileTerminal } from '../../ui/responsive-table.js';
4
+ import { SEMANTIC_COLORS, formatEmptyState } from '../../ui/theme.js';
2
5
  export class PlainTextFormatter extends BaseFormatter {
3
6
  name = 'plain-text';
4
7
  formatOrganizations(organizations, _options) {
5
8
  if (!organizations || organizations.length === 0) {
6
- return 'No organizations found.';
9
+ return formatEmptyState('No organizations found', [
10
+ 'Check your API token has the correct permissions',
11
+ 'Run "bktide token --check" to verify your access'
12
+ ]);
7
13
  }
8
- let output = 'Your organizations:\n';
9
- organizations.forEach(org => {
10
- output += `- ${org.name} (${org.slug})\n`;
11
- });
12
- return output.trim();
14
+ const lines = [];
15
+ if (isMobileTerminal()) {
16
+ // For very narrow terminals, use a vertical list format
17
+ organizations.forEach((org, i) => {
18
+ if (i > 0)
19
+ lines.push(''); // Separator between items
20
+ lines.push(SEMANTIC_COLORS.heading(org.name || 'Unknown'));
21
+ lines.push(` ${SEMANTIC_COLORS.label('Slug:')} ${org.slug || '-'}`);
22
+ });
23
+ }
24
+ else {
25
+ // Build table with enhanced headers
26
+ const rows = [];
27
+ // Bold + underlined headers for emphasis
28
+ const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));
29
+ rows.push(headers);
30
+ organizations.forEach((org) => {
31
+ rows.push([
32
+ org.name || SEMANTIC_COLORS.muted('-'),
33
+ org.slug || SEMANTIC_COLORS.muted('-')
34
+ ]);
35
+ });
36
+ lines.push(renderTable(rows, { preserveWidths: true }));
37
+ }
38
+ // Add summary line (dimmed)
39
+ lines.push('');
40
+ lines.push(SEMANTIC_COLORS.dim(organizations.length === 1
41
+ ? '1 organization accessible'
42
+ : `${SEMANTIC_COLORS.count(organizations.length.toString())} organizations accessible`));
43
+ return lines.join('\n');
13
44
  }
14
45
  }
15
46
  //# sourceMappingURL=PlainTextFormatter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/organizations/PlainTextFormatter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,IAAI,GAAG,YAAY,CAAC;IAEpB,mBAAmB,CAAC,aAA6B,EAAE,QAA2B;QAC5E,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,yBAAyB,CAAC;QACnC,CAAC;QAED,IAAI,MAAM,GAAG,uBAAuB,CAAC;QACrC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YAC1B,MAAM,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;CACF","sourcesContent":["import { FormatterOptions } from '../BaseFormatter.js';\nimport { BaseFormatter } from './Formatter.js';\nimport { Organization } from '../../types/index.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'plain-text';\n \n formatOrganizations(organizations: Organization[], _options?: FormatterOptions): string {\n if (!organizations || organizations.length === 0) {\n return 'No organizations found.';\n }\n\n let output = 'Your organizations:\\n';\n organizations.forEach(org => {\n output += `- ${org.name} (${org.slug})\\n`;\n });\n \n return output.trim();\n }\n} "]}
1
+ {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/organizations/PlainTextFormatter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,IAAI,GAAG,YAAY,CAAC;IAEpB,mBAAmB,CAAC,aAA6B,EAAE,QAA2B;QAC5E,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,gBAAgB,CACrB,wBAAwB,EACxB;gBACE,kDAAkD;gBAClD,kDAAkD;aACnD,CACF,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,gBAAgB,EAAE,EAAE,CAAC;YACvB,wDAAwD;YACxD,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBAC/B,IAAI,CAAC,GAAG,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,0BAA0B;gBACrD,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC;gBAC3D,KAAK,CAAC,IAAI,CAAC,KAAK,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,oCAAoC;YACpC,MAAM,IAAI,GAAe,EAAE,CAAC;YAE5B,yCAAyC;YACzC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACtE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEnB,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC;oBACR,GAAG,CAAC,IAAI,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC;oBACtC,GAAG,CAAC,IAAI,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;QAED,4BAA4B;QAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAC5B,aAAa,CAAC,MAAM,KAAK,CAAC;YACxB,CAAC,CAAC,2BAA2B;YAC7B,CAAC,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,2BAA2B,CACzF,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF","sourcesContent":["import { FormatterOptions } from '../BaseFormatter.js';\nimport { BaseFormatter } from './Formatter.js';\nimport { Organization } from '../../types/index.js';\nimport { renderTable } from '../../ui/table.js';\nimport { isMobileTerminal } from '../../ui/responsive-table.js';\nimport { SEMANTIC_COLORS, formatEmptyState } from '../../ui/theme.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'plain-text';\n \n formatOrganizations(organizations: Organization[], _options?: FormatterOptions): string {\n if (!organizations || organizations.length === 0) {\n return formatEmptyState(\n 'No organizations found',\n [\n 'Check your API token has the correct permissions',\n 'Run \"bktide token --check\" to verify your access'\n ]\n );\n }\n\n const lines: string[] = [];\n\n if (isMobileTerminal()) {\n // For very narrow terminals, use a vertical list format\n organizations.forEach((org, i) => {\n if (i > 0) lines.push(''); // Separator between items\n lines.push(SEMANTIC_COLORS.heading(org.name || 'Unknown'));\n lines.push(` ${SEMANTIC_COLORS.label('Slug:')} ${org.slug || '-'}`);\n });\n } else {\n // Build table with enhanced headers\n const rows: string[][] = [];\n \n // Bold + underlined headers for emphasis\n const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));\n rows.push(headers);\n \n organizations.forEach((org) => {\n rows.push([\n org.name || SEMANTIC_COLORS.muted('-'),\n org.slug || SEMANTIC_COLORS.muted('-')\n ]);\n });\n\n lines.push(renderTable(rows, { preserveWidths: true }));\n }\n \n // Add summary line (dimmed)\n lines.push('');\n lines.push(SEMANTIC_COLORS.dim(\n organizations.length === 1 \n ? '1 organization accessible'\n : `${SEMANTIC_COLORS.count(organizations.length.toString())} organizations accessible`\n ));\n \n return lines.join('\\n');\n }\n} "]}
@@ -1 +1 @@
1
- {"version":3,"file":"AlfredFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/AlfredFormatter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,MAAM,OAAO,eAAgB,SAAQ,aAAa;IAChD,IAAI,GAAG,QAAQ,CAAC;IAEhB,eAAe,CAAC,SAAqB,EAAE,cAAwB,EAAE,QAA2B;QAC1F,mDAAmD;QACnD,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAkB,EAAE,EAAE;YACvD,6DAA6D;YAC7D,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG;gBAC9B,yBAAyB,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEpE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;YAC5C,MAAM,YAAY,GAAG,GAAG,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAE1D,OAAO;gBACL,GAAG,EAAE,GAAG;gBACR,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,KAAK;gBACZ,GAAG,EAAE,WAAW;gBAChB,YAAY,EAAE,YAAY;gBAC1B,IAAI,EAAE;oBACJ,GAAG,EAAE;wBACH,QAAQ,EAAE,iBAAiB,QAAQ,CAAC,YAAY,EAAE;wBAClD,GAAG,EAAE,WAAW;qBACjB;oBACD,GAAG,EAAE;wBACH,QAAQ,EAAE,SAAS,QAAQ,CAAC,IAAI,EAAE;wBAClC,GAAG,EAAE,WAAW;qBACjB;iBACF;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,WAAW;oBACjB,SAAS,EAAE,GAAG,QAAQ,CAAC,IAAI,mBAAmB,QAAQ,CAAC,YAAY,WAAW,QAAQ,CAAC,IAAI,EAAE;iBAC9F;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,mCAAmC;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;CACF","sourcesContent":["import { FormatterOptions } from '../BaseFormatter.js';\nimport { BaseFormatter } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\n\nexport class AlfredFormatter extends BaseFormatter {\n name = 'alfred';\n \n formatPipelines(pipelines: Pipeline[], _organizations: string[], _options?: FormatterOptions): string {\n // Format pipelines as Alfred-compatible JSON items\n const alfredItems = pipelines.map((pipeline: Pipeline) => {\n // Generate web URL for the pipeline (if not already present)\n const pipelineUrl = pipeline.url || \n `https://buildkite.com/${pipeline.organization}/${pipeline.slug}`;\n \n const uid = pipeline.uuid;\n const title = pipeline.slug;\n const subtitle = pipeline.description || '';\n const autocomplete = `${pipeline.organization}/${pipeline.name}`;\n const match = `${pipeline.organization}/${pipeline.slug}`;\n\n return {\n uid: uid,\n title: title,\n subtitle: subtitle,\n match: match,\n arg: pipelineUrl,\n autocomplete: autocomplete,\n mods: {\n alt: {\n subtitle: `Organization: ${pipeline.organization}`,\n arg: pipelineUrl\n },\n cmd: {\n subtitle: `Name: ${pipeline.name}`,\n arg: pipelineUrl\n }\n },\n text: {\n copy: pipelineUrl,\n largetype: `${pipeline.name}\\nOrganization: ${pipeline.organization}\\nSlug: ${pipeline.slug}`\n }\n };\n });\n \n // Return formatted JSON for Alfred\n return JSON.stringify({ items: alfredItems }, null, 2);\n }\n} "]}
1
+ {"version":3,"file":"AlfredFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/AlfredFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA4B,MAAM,gBAAgB,CAAC;AAGzE,MAAM,OAAO,eAAgB,SAAQ,aAAa;IAChD,IAAI,GAAG,QAAQ,CAAC;IAEhB,eAAe,CAAC,SAAqB,EAAE,cAAwB,EAAE,QAAmC;QAClG,mDAAmD;QACnD,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAkB,EAAE,EAAE;YACvD,6DAA6D;YAC7D,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG;gBAC9B,yBAAyB,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEpE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;YAC5C,MAAM,YAAY,GAAG,GAAG,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAE1D,OAAO;gBACL,GAAG,EAAE,GAAG;gBACR,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,KAAK;gBACZ,GAAG,EAAE,WAAW;gBAChB,YAAY,EAAE,YAAY;gBAC1B,IAAI,EAAE;oBACJ,GAAG,EAAE;wBACH,QAAQ,EAAE,iBAAiB,QAAQ,CAAC,YAAY,EAAE;wBAClD,GAAG,EAAE,WAAW;qBACjB;oBACD,GAAG,EAAE;wBACH,QAAQ,EAAE,SAAS,QAAQ,CAAC,IAAI,EAAE;wBAClC,GAAG,EAAE,WAAW;qBACjB;iBACF;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,WAAW;oBACjB,SAAS,EAAE,GAAG,QAAQ,CAAC,IAAI,mBAAmB,QAAQ,CAAC,YAAY,WAAW,QAAQ,CAAC,IAAI,EAAE;iBAC9F;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,mCAAmC;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;CACF","sourcesContent":["import { BaseFormatter, PipelineFormatterOptions } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\n\nexport class AlfredFormatter extends BaseFormatter {\n name = 'alfred';\n \n formatPipelines(pipelines: Pipeline[], _organizations: string[], _options?: PipelineFormatterOptions): string {\n // Format pipelines as Alfred-compatible JSON items\n const alfredItems = pipelines.map((pipeline: Pipeline) => {\n // Generate web URL for the pipeline (if not already present)\n const pipelineUrl = pipeline.url || \n `https://buildkite.com/${pipeline.organization}/${pipeline.slug}`;\n \n const uid = pipeline.uuid;\n const title = pipeline.slug;\n const subtitle = pipeline.description || '';\n const autocomplete = `${pipeline.organization}/${pipeline.name}`;\n const match = `${pipeline.organization}/${pipeline.slug}`;\n\n return {\n uid: uid,\n title: title,\n subtitle: subtitle,\n match: match,\n arg: pipelineUrl,\n autocomplete: autocomplete,\n mods: {\n alt: {\n subtitle: `Organization: ${pipeline.organization}`,\n arg: pipelineUrl\n },\n cmd: {\n subtitle: `Name: ${pipeline.name}`,\n arg: pipelineUrl\n }\n },\n text: {\n copy: pipelineUrl,\n largetype: `${pipeline.name}\\nOrganization: ${pipeline.organization}\\nSlug: ${pipeline.slug}`\n }\n };\n });\n \n // Return formatted JSON for Alfred\n return JSON.stringify({ items: alfredItems }, null, 2);\n }\n} "]}
@@ -1 +1 @@
1
- {"version":3,"file":"Formatter.js","sourceRoot":"/","sources":["formatters/pipelines/Formatter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAMlD,MAAM,OAAgB,aAAa;IAKjC,MAAM,CAAI,IAAS,EAAE,QAA2D,EAAE,OAA0B;QAC1G,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;CACF","sourcesContent":["import { BaseFormatter as BaseFormatterInterface, FormatterOptions } from '../BaseFormatter.js';\nimport { Pipeline } from '../../types/index.js';\nimport { logger } from '../../services/logger.js';\n\nexport interface PipelineFormatter extends BaseFormatterInterface {\n formatPipelines(pipelines: Pipeline[], organizations: string[], options?: FormatterOptions): string;\n}\n\nexport abstract class BaseFormatter implements PipelineFormatter {\n abstract name: string;\n \n abstract formatPipelines(pipelines: Pipeline[], organizations: string[], options?: FormatterOptions): string;\n \n format<T>(data: T[], formatFn: (data: T[], options?: FormatterOptions) => string, options?: FormatterOptions): string {\n if (options?.debug) {\n logger.debug(`Formatting with ${this.name} formatter`);\n }\n return formatFn(data, options);\n }\n} "]}
1
+ {"version":3,"file":"Formatter.js","sourceRoot":"/","sources":["formatters/pipelines/Formatter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAoBlD,MAAM,OAAgB,aAAa;IAKjC,MAAM,CAAI,IAAS,EAAE,QAA2D,EAAE,OAA0B;QAC1G,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;CACF","sourcesContent":["import { BaseFormatter as BaseFormatterInterface, FormatterOptions } from '../BaseFormatter.js';\nimport { Pipeline } from '../../types/index.js';\nimport { logger } from '../../services/logger.js';\n\nexport interface PipelineFormatterOptions extends FormatterOptions {\n filterActive?: boolean;\n filterText?: string;\n truncated?: boolean;\n hasMoreAvailable?: boolean; // Whether there are more pipelines on the server\n totalBeforeFilter?: number;\n requestedLimit?: number; // The limit that was requested (via --count or default)\n organizationsCount?: number;\n orgSpecified?: boolean;\n hasError?: boolean;\n errorType?: 'access' | 'not_found' | 'api' | 'generic';\n errorMessage?: string;\n}\n\nexport interface PipelineFormatter extends BaseFormatterInterface {\n formatPipelines(pipelines: Pipeline[], organizations: string[], options?: PipelineFormatterOptions): string;\n}\n\nexport abstract class BaseFormatter implements PipelineFormatter {\n abstract name: string;\n \n abstract formatPipelines(pipelines: Pipeline[], organizations: string[], options?: PipelineFormatterOptions): string;\n \n format<T>(data: T[], formatFn: (data: T[], options?: FormatterOptions) => string, options?: FormatterOptions): string {\n if (options?.debug) {\n logger.debug(`Formatting with ${this.name} formatter`);\n }\n return formatFn(data, options);\n }\n} "]}
@@ -1 +1 @@
1
- {"version":3,"file":"JsonFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/JsonFormatter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,IAAI,GAAG,MAAM,CAAC;IAEd,eAAe,CAAC,SAAqB,EAAE,aAAuB,EAAE,QAA2B;QACzF,MAAM,MAAM,GAAG;YACb,KAAK,EAAE,SAAS,CAAC,MAAM;YACvB,aAAa;YACb,SAAS;SACV,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF","sourcesContent":["import { FormatterOptions } from '../BaseFormatter.js';\nimport { BaseFormatter } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\n\nexport class JsonFormatter extends BaseFormatter {\n name = 'json';\n \n formatPipelines(pipelines: Pipeline[], organizations: string[], _options?: FormatterOptions): string {\n const result = {\n count: pipelines.length,\n organizations,\n pipelines\n };\n \n return JSON.stringify(result, null, 2);\n }\n} "]}
1
+ {"version":3,"file":"JsonFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/JsonFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA4B,MAAM,gBAAgB,CAAC;AAGzE,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,IAAI,GAAG,MAAM,CAAC;IAEd,eAAe,CAAC,SAAqB,EAAE,aAAuB,EAAE,QAAmC;QACjG,MAAM,MAAM,GAAG;YACb,KAAK,EAAE,SAAS,CAAC,MAAM;YACvB,aAAa;YACb,SAAS;SACV,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF","sourcesContent":["import { BaseFormatter, PipelineFormatterOptions } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\n\nexport class JsonFormatter extends BaseFormatter {\n name = 'json';\n \n formatPipelines(pipelines: Pipeline[], organizations: string[], _options?: PipelineFormatterOptions): string {\n const result = {\n count: pipelines.length,\n organizations,\n pipelines\n };\n \n return JSON.stringify(result, null, 2);\n }\n} "]}
@@ -1,4 +1,7 @@
1
1
  import { AbstractFormatter } from '../BaseFormatter.js';
2
+ import { renderTable } from '../../ui/table.js';
3
+ import { renderResponsiveTable, isNarrowTerminal, isMobileTerminal } from '../../ui/responsive-table.js';
4
+ import { SEMANTIC_COLORS, formatEmptyState, formatTips, TipStyle } from '../../ui/theme.js';
2
5
  export class PlainTextFormatter extends AbstractFormatter {
3
6
  constructor() {
4
7
  super('plain-text');
@@ -6,40 +9,183 @@ export class PlainTextFormatter extends AbstractFormatter {
6
9
  formatPipelines(pipelines, organizations, options) {
7
10
  // If no organizations are found, handle that case
8
11
  if (organizations.length === 0) {
9
- return 'No organizations found.';
12
+ return formatEmptyState('No organizations found', ['Check your API token has the correct permissions']);
10
13
  }
11
14
  return this.format(pipelines, this.formatPipelinesImpl.bind(this, organizations), options);
12
15
  }
13
- formatPipelinesImpl(organizations, pipelines, _options) {
16
+ formatPipelinesImpl(organizations, pipelines, options) {
14
17
  const output = [];
15
18
  if (pipelines.length === 0) {
16
- output.push('No pipelines found.');
17
- if (organizations.length === 1) {
18
- output.push(`No pipelines found in organization ${organizations[0]}.`);
19
+ // Build context-aware empty state message
20
+ let message;
21
+ const suggestions = [];
22
+ if (options?.filterActive && options?.filterText) {
23
+ // Filter returned no results
24
+ message = `No pipelines match filter '${SEMANTIC_COLORS.label(options.filterText)}'`;
25
+ suggestions.push('Try a broader search term');
26
+ suggestions.push('The filter searches pipeline names, slugs, and descriptions');
27
+ suggestions.push('Remove the filter to see all pipelines');
28
+ }
29
+ else if (organizations.length === 1) {
30
+ // Specific organization has no pipelines
31
+ message = `No pipelines found in organization ${SEMANTIC_COLORS.label(organizations[0])}`;
32
+ suggestions.push('Check the organization name is correct');
33
+ suggestions.push('Verify you have access to pipelines in this organization');
34
+ suggestions.push('Run "bktide orgs" to see available organizations');
19
35
  }
20
36
  else {
21
- output.push(`No pipelines found across ${organizations.length} organizations.`);
37
+ // No pipelines across all organizations
38
+ message = `No pipelines found across ${organizations.length === 0 ? 'any accessible' : organizations.length.toString()} organizations`;
39
+ suggestions.push('Check your API token has the correct permissions');
40
+ suggestions.push('Run "bktide token --check" to verify your access');
41
+ if (organizations.length > 0) {
42
+ suggestions.push('Some organizations may not have any pipelines configured');
43
+ }
22
44
  }
23
- return output.join('\n');
45
+ return formatEmptyState(message, suggestions);
24
46
  }
25
- if (organizations.length === 1) {
26
- output.push(`Pipelines for ${organizations[0]} (${pipelines.length} total):`);
47
+ // Build table rows
48
+ // Check if we're on a mobile terminal
49
+ if (isMobileTerminal()) {
50
+ // Use a vertical list format for very narrow terminals
51
+ const items = pipelines.map(p => ({
52
+ name: p.name || 'Unknown',
53
+ slug: p.slug || '-',
54
+ org: p.organization || '-'
55
+ }));
56
+ items.forEach((item, i) => {
57
+ if (i > 0)
58
+ output.push(''); // Separator between items
59
+ output.push(SEMANTIC_COLORS.heading(item.name));
60
+ output.push(` ${SEMANTIC_COLORS.label('Slug:')} ${SEMANTIC_COLORS.dim(item.slug)}`);
61
+ if (organizations.length > 1) {
62
+ output.push(` ${SEMANTIC_COLORS.label('Org:')} ${item.org}`);
63
+ }
64
+ });
27
65
  }
28
- else {
29
- output.push(`Pipelines across your organizations (${pipelines.length} total):`);
66
+ else if (isNarrowTerminal()) {
67
+ // Use responsive table for narrow terminals
68
+ if (organizations.length > 1) {
69
+ const headers = ['ORGANIZATION', 'NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));
70
+ const dataRows = pipelines.map((p) => [
71
+ p.organization || SEMANTIC_COLORS.muted('-'),
72
+ p.name || SEMANTIC_COLORS.muted('-'),
73
+ p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')
74
+ ]);
75
+ const columns = [
76
+ { header: 'ORGANIZATION', priority: 2, minWidth: 8, truncate: true },
77
+ { header: 'NAME', priority: 10, minWidth: 10, truncate: true },
78
+ { header: 'SLUG', priority: 5, minWidth: 8, truncate: true }
79
+ ];
80
+ output.push(renderResponsiveTable(headers, dataRows, { columns }));
81
+ }
82
+ else {
83
+ const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));
84
+ const dataRows = pipelines.map((p) => [
85
+ p.name || SEMANTIC_COLORS.muted('-'),
86
+ p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')
87
+ ]);
88
+ const columns = [
89
+ { header: 'NAME', priority: 10, minWidth: 15, truncate: true },
90
+ { header: 'SLUG', priority: 5, minWidth: 10, truncate: true }
91
+ ];
92
+ output.push(renderResponsiveTable(headers, dataRows, { columns }));
93
+ }
30
94
  }
31
- pipelines.forEach((pipeline) => {
95
+ else {
96
+ // Use standard table for wide terminals
97
+ const rows = [];
32
98
  if (organizations.length > 1) {
33
- output.push(`- [${pipeline.organization}] ${pipeline.name} (${pipeline.slug})`);
99
+ const headers = ['ORGANIZATION', 'NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));
100
+ rows.push(headers);
101
+ pipelines.forEach((p) => {
102
+ rows.push([
103
+ p.organization || SEMANTIC_COLORS.muted('-'),
104
+ p.name || SEMANTIC_COLORS.muted('-'),
105
+ p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')
106
+ ]);
107
+ });
108
+ }
109
+ else {
110
+ const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));
111
+ rows.push(headers);
112
+ pipelines.forEach((p) => {
113
+ rows.push([
114
+ p.name || SEMANTIC_COLORS.muted('-'),
115
+ p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')
116
+ ]);
117
+ });
118
+ }
119
+ output.push(renderTable(rows, { preserveWidths: true }));
120
+ }
121
+ // Summary line (dimmed)
122
+ output.push('');
123
+ // Check if we're showing a subset (we hit the limit and there might be more)
124
+ // But NOT when filtering (filtered results are already a subset)
125
+ const isShowingSubset = !options?.filterActive && (options?.hasMoreAvailable ||
126
+ (options?.truncated && pipelines.length >= (options?.requestedLimit || 0)));
127
+ if (organizations.length === 1) {
128
+ if (isShowingSubset) {
129
+ output.push(SEMANTIC_COLORS.dim(`Showing ${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines from ${organizations[0]}`));
34
130
  }
35
131
  else {
36
- output.push(`- ${pipeline.name} (${pipeline.slug})`);
132
+ output.push(SEMANTIC_COLORS.dim(`${SEMANTIC_COLORS.count(pipelines.length.toString())} ${pipelines.length === 1 ? 'pipeline' : 'pipelines'} in ${organizations[0]}`));
37
133
  }
38
- });
39
- // Summary line showing total pipelines listed
40
- output.push(`Showing ${pipelines.length} pipelines.`);
41
- if (organizations.length > 1) {
42
- output.push(`\nSearched across ${organizations.length} organizations. Use --org to filter to a specific organization.`);
134
+ }
135
+ else {
136
+ if (isShowingSubset) {
137
+ output.push(SEMANTIC_COLORS.dim(`Showing ${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines from ${organizations.length} organizations`));
138
+ }
139
+ else {
140
+ output.push(SEMANTIC_COLORS.dim(`${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines across ${organizations.length} organizations`));
141
+ }
142
+ }
143
+ // Add contextual hints based on results
144
+ const hints = [];
145
+ // Add filter hints
146
+ if (options?.filterActive && options?.filterText) {
147
+ output.push(SEMANTIC_COLORS.dim(`Showing pipelines matching '${options.filterText}'`));
148
+ hints.push('Remove --filter to see all pipelines');
149
+ }
150
+ else if (pipelines.length > 20) {
151
+ // Many pipelines - suggest filtering
152
+ hints.push('Use --filter <text> to search by name or description');
153
+ }
154
+ // Add organization hints
155
+ if (organizations.length > 1 && !options?.orgSpecified) {
156
+ hints.push('Use --org <name> to focus on a specific organization');
157
+ if (pipelines.length > 10) {
158
+ hints.push('Pipeline slugs are unique within each organization');
159
+ }
160
+ }
161
+ // Add pagination hints
162
+ if (options?.truncated || options?.hasMoreAvailable) {
163
+ const currentCount = pipelines.length;
164
+ const requestedLimit = options?.requestedLimit || currentCount;
165
+ // Only show pagination hints if we actually hit the limit
166
+ // (not if we got fewer results than requested)
167
+ if (currentCount >= requestedLimit) {
168
+ // Calculate reasonable next count suggestion
169
+ let nextCount;
170
+ if (requestedLimit < 100) {
171
+ nextCount = Math.min(requestedLimit * 2, 100); // Double up to 100
172
+ }
173
+ else if (requestedLimit < 500) {
174
+ nextCount = Math.min(requestedLimit + 100, 500); // Add 100 up to 500
175
+ }
176
+ else {
177
+ nextCount = requestedLimit + 250; // Add 250 for larger requests
178
+ }
179
+ // Show pagination hint without duplicating the count
180
+ if (options?.hasMoreAvailable || options?.truncated) {
181
+ hints.push(`Use --count ${nextCount} to see more`);
182
+ }
183
+ }
184
+ }
185
+ // Display hints if any
186
+ if (hints.length > 0) {
187
+ output.push('');
188
+ output.push(formatTips(hints, TipStyle.GROUPED));
43
189
  }
44
190
  return output.join('\n');
45
191
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/PlainTextFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI1E,MAAM,OAAO,kBAAmB,SAAQ,iBAAiB;IACvD;QACE,KAAK,CAAC,YAAY,CAAC,CAAC;IACtB,CAAC;IAED,eAAe,CAAC,SAAqB,EAAE,aAAuB,EAAE,OAA0B;QACxF,kDAAkD;QAClD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,yBAAyB,CAAC;QACnC,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7F,CAAC;IAEO,mBAAmB,CAAC,aAAuB,EAAE,SAAqB,EAAE,QAA2B;QACrG,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACnC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,sCAAsC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,6BAA6B,aAAa,CAAC,MAAM,iBAAiB,CAAC,CAAC;YAClF,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,iBAAiB,aAAa,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,MAAM,UAAU,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,wCAAwC,SAAS,CAAC,MAAM,UAAU,CAAC,CAAC;QAClF,CAAC;QAED,SAAS,CAAC,OAAO,CAAC,CAAC,QAAkB,EAAE,EAAE;YACvC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;YAClF,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;YACvD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,MAAM,CAAC,IAAI,CAAC,WAAW,SAAS,CAAC,MAAM,aAAa,CAAC,CAAC;QAEtD,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,qBAAqB,aAAa,CAAC,MAAM,iEAAiE,CAAC,CAAC;QAC1H,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;CACF","sourcesContent":["import { FormatterOptions, AbstractFormatter } from '../BaseFormatter.js';\nimport { PipelineFormatter } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\n\nexport class PlainTextFormatter extends AbstractFormatter implements PipelineFormatter {\n constructor() {\n super('plain-text');\n }\n\n formatPipelines(pipelines: Pipeline[], organizations: string[], options?: FormatterOptions): string {\n // If no organizations are found, handle that case\n if (organizations.length === 0) {\n return 'No organizations found.';\n }\n \n return this.format(pipelines, this.formatPipelinesImpl.bind(this, organizations), options);\n }\n\n private formatPipelinesImpl(organizations: string[], pipelines: Pipeline[], _options?: FormatterOptions): string {\n const output: string[] = [];\n \n if (pipelines.length === 0) {\n output.push('No pipelines found.');\n if (organizations.length === 1) {\n output.push(`No pipelines found in organization ${organizations[0]}.`);\n } else {\n output.push(`No pipelines found across ${organizations.length} organizations.`);\n }\n return output.join('\\n');\n }\n \n if (organizations.length === 1) {\n output.push(`Pipelines for ${organizations[0]} (${pipelines.length} total):`);\n } else {\n output.push(`Pipelines across your organizations (${pipelines.length} total):`);\n }\n \n pipelines.forEach((pipeline: Pipeline) => {\n if (organizations.length > 1) {\n output.push(`- [${pipeline.organization}] ${pipeline.name} (${pipeline.slug})`);\n } else {\n output.push(`- ${pipeline.name} (${pipeline.slug})`);\n }\n });\n \n // Summary line showing total pipelines listed\n output.push(`Showing ${pipelines.length} pipelines.`);\n \n if (organizations.length > 1) {\n output.push(`\\nSearched across ${organizations.length} organizations. Use --org to filter to a specific organization.`);\n }\n \n return output.join('\\n');\n }\n} "]}
1
+ {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/pipelines/PlainTextFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { AbstractFormatter } from '../BaseFormatter.js';\nimport { PipelineFormatter, PipelineFormatterOptions } from './Formatter.js';\nimport { Pipeline } from '../../types/index.js';\nimport { renderTable } from '../../ui/table.js';\nimport { renderResponsiveTable, isNarrowTerminal, isMobileTerminal } from '../../ui/responsive-table.js';\nimport { SEMANTIC_COLORS, formatEmptyState, formatTips, TipStyle } from '../../ui/theme.js';\n\nexport class PlainTextFormatter extends AbstractFormatter implements PipelineFormatter {\n constructor() {\n super('plain-text');\n }\n\n formatPipelines(pipelines: Pipeline[], organizations: string[], options?: PipelineFormatterOptions): string {\n // If no organizations are found, handle that case\n if (organizations.length === 0) {\n return formatEmptyState(\n 'No organizations found',\n ['Check your API token has the correct permissions']\n );\n }\n \n return this.format(pipelines, this.formatPipelinesImpl.bind(this, organizations), options);\n }\n\n private formatPipelinesImpl(organizations: string[], pipelines: Pipeline[], options?: PipelineFormatterOptions): string {\n const output: string[] = [];\n \n if (pipelines.length === 0) {\n // Build context-aware empty state message\n let message: string;\n const suggestions: string[] = [];\n \n if (options?.filterActive && options?.filterText) {\n // Filter returned no results\n message = `No pipelines match filter '${SEMANTIC_COLORS.label(options.filterText)}'`;\n suggestions.push('Try a broader search term');\n suggestions.push('The filter searches pipeline names, slugs, and descriptions');\n suggestions.push('Remove the filter to see all pipelines');\n } else if (organizations.length === 1) {\n // Specific organization has no pipelines\n message = `No pipelines found in organization ${SEMANTIC_COLORS.label(organizations[0])}`;\n suggestions.push('Check the organization name is correct');\n suggestions.push('Verify you have access to pipelines in this organization');\n suggestions.push('Run \"bktide orgs\" to see available organizations');\n } else {\n // No pipelines across all organizations\n message = `No pipelines found across ${organizations.length === 0 ? 'any accessible' : organizations.length.toString()} organizations`;\n suggestions.push('Check your API token has the correct permissions');\n suggestions.push('Run \"bktide token --check\" to verify your access');\n if (organizations.length > 0) {\n suggestions.push('Some organizations may not have any pipelines configured');\n }\n }\n \n return formatEmptyState(message, suggestions);\n }\n\n // Build table rows\n // Check if we're on a mobile terminal\n if (isMobileTerminal()) {\n // Use a vertical list format for very narrow terminals\n const items = pipelines.map(p => ({\n name: p.name || 'Unknown',\n slug: p.slug || '-',\n org: p.organization || '-'\n }));\n \n items.forEach((item, i) => {\n if (i > 0) output.push(''); // Separator between items\n output.push(SEMANTIC_COLORS.heading(item.name));\n output.push(` ${SEMANTIC_COLORS.label('Slug:')} ${SEMANTIC_COLORS.dim(item.slug)}`);\n if (organizations.length > 1) {\n output.push(` ${SEMANTIC_COLORS.label('Org:')} ${item.org}`);\n }\n });\n } else if (isNarrowTerminal()) {\n // Use responsive table for narrow terminals\n if (organizations.length > 1) {\n const headers = ['ORGANIZATION', 'NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));\n const dataRows: string[][] = pipelines.map((p: Pipeline) => [\n p.organization || SEMANTIC_COLORS.muted('-'),\n p.name || SEMANTIC_COLORS.muted('-'),\n p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')\n ]);\n \n const columns = [\n { header: 'ORGANIZATION', priority: 2, minWidth: 8, truncate: true },\n { header: 'NAME', priority: 10, minWidth: 10, truncate: true },\n { header: 'SLUG', priority: 5, minWidth: 8, truncate: true }\n ];\n \n output.push(renderResponsiveTable(headers, dataRows, { columns }));\n } else {\n const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));\n const dataRows: string[][] = pipelines.map((p: Pipeline) => [\n p.name || SEMANTIC_COLORS.muted('-'),\n p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')\n ]);\n \n const columns = [\n { header: 'NAME', priority: 10, minWidth: 15, truncate: true },\n { header: 'SLUG', priority: 5, minWidth: 10, truncate: true }\n ];\n \n output.push(renderResponsiveTable(headers, dataRows, { columns }));\n }\n } else {\n // Use standard table for wide terminals\n const rows: string[][] = [];\n if (organizations.length > 1) {\n const headers = ['ORGANIZATION', 'NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));\n rows.push(headers);\n pipelines.forEach((p: Pipeline) => {\n rows.push([\n p.organization || SEMANTIC_COLORS.muted('-'),\n p.name || SEMANTIC_COLORS.muted('-'),\n p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')\n ]);\n });\n } else {\n const headers = ['NAME', 'SLUG'].map(h => SEMANTIC_COLORS.heading(h));\n rows.push(headers);\n pipelines.forEach((p: Pipeline) => {\n rows.push([\n p.name || SEMANTIC_COLORS.muted('-'),\n p.slug ? SEMANTIC_COLORS.dim(p.slug) : SEMANTIC_COLORS.muted('-')\n ]);\n });\n }\n output.push(renderTable(rows, { preserveWidths: true }));\n }\n \n // Summary line (dimmed)\n output.push('');\n \n // Check if we're showing a subset (we hit the limit and there might be more)\n // But NOT when filtering (filtered results are already a subset)\n const isShowingSubset = !options?.filterActive && (\n options?.hasMoreAvailable || \n (options?.truncated && pipelines.length >= (options?.requestedLimit || 0))\n );\n \n if (organizations.length === 1) {\n if (isShowingSubset) {\n output.push(SEMANTIC_COLORS.dim(\n `Showing ${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines from ${organizations[0]}`\n ));\n } else {\n output.push(SEMANTIC_COLORS.dim(\n `${SEMANTIC_COLORS.count(pipelines.length.toString())} ${pipelines.length === 1 ? 'pipeline' : 'pipelines'} in ${organizations[0]}`\n ));\n }\n } else {\n if (isShowingSubset) {\n output.push(SEMANTIC_COLORS.dim(\n `Showing ${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines from ${organizations.length} organizations`\n ));\n } else {\n output.push(SEMANTIC_COLORS.dim(\n `${SEMANTIC_COLORS.count(pipelines.length.toString())} pipelines across ${organizations.length} organizations`\n ));\n }\n }\n \n // Add contextual hints based on results\n const hints: string[] = [];\n \n // Add filter hints\n if (options?.filterActive && options?.filterText) {\n output.push(SEMANTIC_COLORS.dim(`Showing pipelines matching '${options.filterText}'`));\n hints.push('Remove --filter to see all pipelines');\n } else if (pipelines.length > 20) {\n // Many pipelines - suggest filtering\n hints.push('Use --filter <text> to search by name or description');\n }\n \n // Add organization hints\n if (organizations.length > 1 && !options?.orgSpecified) {\n hints.push('Use --org <name> to focus on a specific organization');\n if (pipelines.length > 10) {\n hints.push('Pipeline slugs are unique within each organization');\n }\n }\n \n // Add pagination hints\n if (options?.truncated || options?.hasMoreAvailable) {\n const currentCount = pipelines.length;\n const requestedLimit = options?.requestedLimit || currentCount;\n \n // Only show pagination hints if we actually hit the limit\n // (not if we got fewer results than requested)\n if (currentCount >= requestedLimit) {\n // Calculate reasonable next count suggestion\n let nextCount: number;\n if (requestedLimit < 100) {\n nextCount = Math.min(requestedLimit * 2, 100); // Double up to 100\n } else if (requestedLimit < 500) {\n nextCount = Math.min(requestedLimit + 100, 500); // Add 100 up to 500\n } else {\n nextCount = requestedLimit + 250; // Add 250 for larger requests\n }\n \n // Show pagination hint without duplicating the count\n if (options?.hasMoreAvailable || options?.truncated) {\n hints.push(`Use --count ${nextCount} to see more`);\n }\n }\n }\n \n // Display hints if any\n if (hints.length > 0) {\n output.push('');\n output.push(formatTips(hints, TipStyle.GROUPED));\n }\n \n return output.join('\\n');\n }\n} "]}
@@ -1,3 +1,4 @@
1
+ import { isRunningInAlfred } from '../../utils/alfred.js';
1
2
  import { BaseTokenFormatter } from './Formatter.js';
2
3
  /**
3
4
  * Alfred formatter for tokens
@@ -108,10 +109,22 @@ export class AlfredFormatter extends BaseTokenFormatter {
108
109
  }
109
110
  formatTokenStatusAsItems(status) {
110
111
  const items = [];
111
- // Add token status item
112
+ const inAlfred = isRunningInAlfred();
113
+ // Alfred-first UX: if no token, present actionable item to open config
114
+ if (inAlfred && !status.hasToken) {
115
+ items.push({
116
+ title: 'Set Buildkite token',
117
+ subtitle: 'Open Workflow Configuration to set BUILDKITE_API_TOKEN',
118
+ icon: 'icons/info.png',
119
+ arg: 'alfred:open-config'
120
+ });
121
+ }
122
+ // Add token status item with context-aware subtitle
112
123
  items.push({
113
124
  title: `Token Status: ${status.hasToken ? 'Present' : 'Not Present'}`,
114
- subtitle: status.hasToken ? 'Token is stored in system keychain' : 'No token found in system keychain',
125
+ subtitle: status.hasToken
126
+ ? (inAlfred ? 'Token provided via Workflow Configuration' : 'Token is stored in system keychain')
127
+ : (inAlfred ? 'No token set in Workflow Configuration' : 'No token found in system keychain'),
115
128
  icon: this.getIcon(status.hasToken),
116
129
  arg: status.hasToken ? 'token:present' : 'token:not-present'
117
130
  });