bktide 1.0.1755267617 → 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,23 +1,29 @@
1
1
  import { BaseCommand } from './BaseCommand.js';
2
2
  import { getViewerFormatter } from '../formatters/index.js';
3
3
  import { logger } from '../services/logger.js';
4
+ import { Progress } from '../ui/progress.js';
4
5
  export class ShowViewer extends BaseCommand {
5
6
  constructor(options) {
6
7
  super(options);
7
8
  }
8
9
  async execute(options) {
9
10
  await this.ensureInitialized();
11
+ const format = options.format || 'plain';
12
+ const spinner = Progress.spinner('Fetching viewer…', { format });
10
13
  try {
11
14
  const data = await this.client.getViewer();
15
+ spinner.stop();
12
16
  if (!data?.viewer) {
13
17
  throw new Error('Invalid response format: missing viewer data');
14
18
  }
15
- const formatter = getViewerFormatter(options.format || 'plain');
19
+ const formatter = getViewerFormatter(format);
16
20
  const output = formatter.formatViewer(data, { debug: options.debug });
17
21
  logger.console(output);
22
+ // Success is implicit - data display confirms retrieval
18
23
  return 0; // Success
19
24
  }
20
25
  catch (error) {
26
+ spinner.stop();
21
27
  this.handleError(error, options.debug);
22
28
  return 1; // Error
23
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ShowViewer.js","sourceRoot":"/","sources":["commands/ShowViewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAK/C,MAAM,OAAO,UAAW,SAAQ,WAAW;IACzC,YAAY,OAAgC;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAsB;QAClC,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAE3C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,IAA6B,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;YAE/F,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACvB,OAAO,CAAC,CAAC,CAAC,UAAU;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACvC,OAAO,CAAC,CAAC,CAAC,QAAQ;QACpB,CAAC;IACH,CAAC;CACF","sourcesContent":["import { BaseCommand, BaseCommandOptions } from './BaseCommand.js';\nimport { getViewerFormatter } from '../formatters/index.js';\nimport { ViewerData } from '../types/index.js';\nimport { logger } from '../services/logger.js';\n\nexport interface ViewerOptions extends BaseCommandOptions {\n}\n\nexport class ShowViewer extends BaseCommand {\n constructor(options?: Partial<ViewerOptions>) {\n super(options);\n }\n \n async execute(options: ViewerOptions): Promise<number> {\n await this.ensureInitialized();\n \n try {\n const data = await this.client.getViewer();\n \n if (!data?.viewer) {\n throw new Error('Invalid response format: missing viewer data');\n }\n \n const formatter = getViewerFormatter(options.format || 'plain');\n const output = formatter.formatViewer(data as unknown as ViewerData, { debug: options.debug });\n \n logger.console(output);\n return 0; // Success\n } catch (error) {\n this.handleError(error, options.debug);\n return 1; // Error\n }\n }\n} "]}
1
+ {"version":3,"file":"ShowViewer.js","sourceRoot":"/","sources":["commands/ShowViewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAK7C,MAAM,OAAO,UAAW,SAAQ,WAAW;IACzC,YAAY,OAAgC;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAsB;QAClC,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAEjE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC3C,OAAO,CAAC,IAAI,EAAE,CAAC;YAEf,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,IAA6B,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;YAE/F,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACvB,wDAAwD;YACxD,OAAO,CAAC,CAAC,CAAC,UAAU;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACvC,OAAO,CAAC,CAAC,CAAC,QAAQ;QACpB,CAAC;IACH,CAAC;CACF","sourcesContent":["import { BaseCommand, BaseCommandOptions } from './BaseCommand.js';\nimport { getViewerFormatter } from '../formatters/index.js';\nimport { ViewerData } from '../types/index.js';\nimport { logger } from '../services/logger.js';\n\nimport { Progress } from '../ui/progress.js';\n\nexport interface ViewerOptions extends BaseCommandOptions {\n}\n\nexport class ShowViewer extends BaseCommand {\n constructor(options?: Partial<ViewerOptions>) {\n super(options);\n }\n \n async execute(options: ViewerOptions): Promise<number> {\n await this.ensureInitialized();\n \n const format = options.format || 'plain';\n const spinner = Progress.spinner('Fetching viewer…', { format });\n \n try {\n const data = await this.client.getViewer();\n spinner.stop();\n \n if (!data?.viewer) {\n throw new Error('Invalid response format: missing viewer data');\n }\n \n const formatter = getViewerFormatter(format);\n const output = formatter.formatViewer(data as unknown as ViewerData, { debug: options.debug });\n \n logger.console(output);\n // Success is implicit - data display confirms retrieval\n return 0; // Success\n } catch (error) {\n spinner.stop();\n this.handleError(error, options.debug);\n return 1; // Error\n }\n }\n} "]}
@@ -5,4 +5,5 @@ export * from './ListBuilds.js';
5
5
  export * from './ListPipelines.js';
6
6
  export * from './ManageToken.js';
7
7
  export * from './ListAnnotations.js';
8
+ export * from './GenerateCompletions.js';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC","sourcesContent":["export * from './BaseCommand.js';\nexport * from './ShowViewer.js';\nexport * from './ListOrganizations.js';\nexport * from './ListBuilds.js';\nexport * from './ListPipelines.js';\nexport * from './ManageToken.js';\nexport * from './ListAnnotations.js'; "]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC","sourcesContent":["export * from './BaseCommand.js';\nexport * from './ShowViewer.js';\nexport * from './ListOrganizations.js';\nexport * from './ListBuilds.js';\nexport * from './ListPipelines.js';\nexport * from './ManageToken.js';\nexport * from './ListAnnotations.js';\nexport * from './GenerateCompletions.js';"]}
@@ -1,25 +1,48 @@
1
1
  import { BaseFormatter } from './Formatter.js';
2
2
  import { formatAnnotationBody } from '../../utils/textFormatter.js';
3
+ import { SEMANTIC_COLORS, formatEmptyState, formatError } from '../../ui/theme.js';
3
4
  export class PlainTextFormatter extends BaseFormatter {
4
5
  name = 'PlainText';
5
6
  formatAnnotations(annotations, options) {
6
7
  if (options?.hasError) {
7
- return `Error: ${options.errorMessage || 'Unknown error occurred'}`;
8
+ return formatError(options.errorMessage || 'Failed to fetch annotations', {
9
+ showHelp: true,
10
+ helpCommand: 'bktide annotations --help'
11
+ });
8
12
  }
9
13
  if (!annotations || annotations.length === 0) {
10
- if (options?.contextFilter) {
11
- return `No annotations found for this build with context '${options.contextFilter}'.`;
12
- }
13
- return 'No annotations found for this build.';
14
+ const message = options?.contextFilter
15
+ ? `No annotations found for this build with context '${options.contextFilter}'`
16
+ : 'No annotations found for this build';
17
+ return formatEmptyState(message, [
18
+ 'Annotations are created by build steps',
19
+ 'Check the build has completed and has annotation steps'
20
+ ]);
14
21
  }
15
22
  const lines = [];
23
+ // Style symbols for different annotation types
24
+ const styleSymbols = {
25
+ error: '✖',
26
+ warning: '⚠',
27
+ info: 'ℹ',
28
+ success: '✓'
29
+ };
30
+ // Style colors for different annotation types
31
+ const styleColors = {
32
+ error: SEMANTIC_COLORS.error,
33
+ warning: SEMANTIC_COLORS.warning,
34
+ info: SEMANTIC_COLORS.info,
35
+ success: SEMANTIC_COLORS.success
36
+ };
16
37
  annotations.forEach((annotation, index) => {
17
38
  if (index > 0) {
18
39
  lines.push(''); // Add blank line between annotations
19
40
  }
20
- lines.push(`Annotation ${index + 1}:`);
21
- lines.push(` Context: ${annotation.context}`);
22
- lines.push(` Style: ${annotation.style}`);
41
+ const symbol = styleSymbols[annotation.style] || '•';
42
+ const colorFn = styleColors[annotation.style] || ((s) => s);
43
+ // Style with symbol and color
44
+ lines.push(colorFn(`${symbol} ${annotation.context} (${annotation.style})`));
45
+ lines.push('');
23
46
  // Format the body HTML with proper HTML/markdown handling
24
47
  const formattedBody = formatAnnotationBody(annotation.body.html);
25
48
  // Indent the formatted body properly
@@ -27,8 +50,13 @@ export class PlainTextFormatter extends BaseFormatter {
27
50
  .split('\n')
28
51
  .map(line => ` ${line}`)
29
52
  .join('\n');
30
- lines.push(` Body: ${indentedBody}`);
53
+ lines.push(indentedBody);
31
54
  });
55
+ // Add summary if multiple annotations
56
+ if (annotations.length > 1) {
57
+ lines.push('');
58
+ lines.push(SEMANTIC_COLORS.dim(`${SEMANTIC_COLORS.count(annotations.length.toString())} annotations found`));
59
+ }
32
60
  return lines.join('\n');
33
61
  }
34
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/annotations/PlainTextFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA8B,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,IAAI,GAAG,WAAW,CAAC;IAEnB,iBAAiB,CAAC,WAAyB,EAAE,OAAoC;QAC/E,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;YACtB,OAAO,UAAU,OAAO,CAAC,YAAY,IAAI,wBAAwB,EAAE,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7C,IAAI,OAAO,EAAE,aAAa,EAAE,CAAC;gBAC3B,OAAO,qDAAqD,OAAO,CAAC,aAAa,IAAI,CAAC;YACxF,CAAC;YACD,OAAO,sCAAsC,CAAC;QAChD,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;YACxC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,qCAAqC;YACvD,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,cAAc,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,cAAc,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;YAE3C,0DAA0D;YAC1D,MAAM,aAAa,GAAG,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEjE,qCAAqC;YACrC,MAAM,YAAY,GAAG,aAAa;iBAC/B,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;iBACxB,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,KAAK,CAAC,IAAI,CAAC,WAAW,YAAY,EAAE,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF","sourcesContent":["import { BaseFormatter, AnnotationFormatterOptions } from './Formatter.js';\nimport { Annotation } from '../../types/index.js';\nimport { formatAnnotationBody } from '../../utils/textFormatter.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'PlainText';\n\n formatAnnotations(annotations: Annotation[], options?: AnnotationFormatterOptions): string {\n if (options?.hasError) {\n return `Error: ${options.errorMessage || 'Unknown error occurred'}`;\n }\n\n if (!annotations || annotations.length === 0) {\n if (options?.contextFilter) {\n return `No annotations found for this build with context '${options.contextFilter}'.`;\n }\n return 'No annotations found for this build.';\n }\n\n const lines: string[] = [];\n \n annotations.forEach((annotation, index) => {\n if (index > 0) {\n lines.push(''); // Add blank line between annotations\n }\n \n lines.push(`Annotation ${index + 1}:`);\n lines.push(` Context: ${annotation.context}`);\n lines.push(` Style: ${annotation.style}`);\n \n // Format the body HTML with proper HTML/markdown handling\n const formattedBody = formatAnnotationBody(annotation.body.html);\n \n // Indent the formatted body properly\n const indentedBody = formattedBody\n .split('\\n')\n .map(line => ` ${line}`)\n .join('\\n');\n \n lines.push(` Body: ${indentedBody}`);\n });\n\n return lines.join('\\n');\n }\n}\n"]}
1
+ {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/annotations/PlainTextFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseFormatter, AnnotationFormatterOptions } from './Formatter.js';\nimport { Annotation } from '../../types/index.js';\nimport { formatAnnotationBody } from '../../utils/textFormatter.js';\nimport { SEMANTIC_COLORS, formatEmptyState, formatError } from '../../ui/theme.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'PlainText';\n\n formatAnnotations(annotations: Annotation[], options?: AnnotationFormatterOptions): string {\n if (options?.hasError) {\n return formatError(options.errorMessage || 'Failed to fetch annotations', {\n showHelp: true,\n helpCommand: 'bktide annotations --help'\n });\n }\n\n if (!annotations || annotations.length === 0) {\n const message = options?.contextFilter\n ? `No annotations found for this build with context '${options.contextFilter}'`\n : 'No annotations found for this build';\n \n return formatEmptyState(message, [\n 'Annotations are created by build steps',\n 'Check the build has completed and has annotation steps'\n ]);\n }\n\n const lines: string[] = [];\n \n // Style symbols for different annotation types\n const styleSymbols: Record<string, string> = {\n error: '✖',\n warning: '⚠',\n info: 'ℹ',\n success: '✓'\n };\n \n // Style colors for different annotation types\n const styleColors: Record<string, (s: string) => string> = {\n error: SEMANTIC_COLORS.error,\n warning: SEMANTIC_COLORS.warning,\n info: SEMANTIC_COLORS.info,\n success: SEMANTIC_COLORS.success\n };\n \n annotations.forEach((annotation, index) => {\n if (index > 0) {\n lines.push(''); // Add blank line between annotations\n }\n \n const symbol = styleSymbols[annotation.style] || '•';\n const colorFn = styleColors[annotation.style] || ((s: string) => s);\n \n // Style with symbol and color\n lines.push(colorFn(`${symbol} ${annotation.context} (${annotation.style})`));\n lines.push('');\n \n // Format the body HTML with proper HTML/markdown handling\n const formattedBody = formatAnnotationBody(annotation.body.html);\n \n // Indent the formatted body properly\n const indentedBody = formattedBody\n .split('\\n')\n .map(line => ` ${line}`)\n .join('\\n');\n \n lines.push(indentedBody);\n });\n \n // Add summary if multiple annotations\n if (annotations.length > 1) {\n lines.push('');\n lines.push(SEMANTIC_COLORS.dim(`${SEMANTIC_COLORS.count(annotations.length.toString())} annotations found`));\n }\n\n return lines.join('\\n');\n }\n}\n"]}
@@ -1,4 +1,7 @@
1
1
  import { BaseFormatter } from './Formatter.js';
2
+ import { renderTable } from '../../ui/table.js';
3
+ import { renderResponsiveTable, isNarrowTerminal, isMobileTerminal } from '../../ui/responsive-table.js';
4
+ import { SEMANTIC_COLORS, formatBuildStatus, formatEmptyState, formatError, formatTips, TipStyle } from '../../ui/theme.js';
2
5
  export class PlainTextFormatter extends BaseFormatter {
3
6
  name = 'plain-text';
4
7
  formatBuilds(builds, options) {
@@ -19,95 +22,114 @@ export class PlainTextFormatter extends BaseFormatter {
19
22
  }
20
23
  // Handle empty results (no error, just no data)
21
24
  if (builds.length === 0) {
22
- let output = 'No builds found.';
25
+ let message = 'No builds found';
26
+ const suggestions = [];
23
27
  // Add user info if provided
24
28
  if (options?.userName) {
25
- output = `No builds found for ${options.userName}`;
29
+ message = `No builds found for ${SEMANTIC_COLORS.label(options.userName)}`;
26
30
  if (options?.userEmail || options?.userId) {
27
- output += ` (${options.userEmail || options.userId})`;
31
+ message += ` ${SEMANTIC_COLORS.dim(`(${options.userEmail || options.userId})`)}`;
28
32
  }
29
- output += '.';
30
33
  }
31
- // Add organization suggestion if applicable
34
+ // Add suggestions based on context
32
35
  if (!options?.orgSpecified) {
33
- output += '\nTry specifying an organization with --org to narrow your search.';
36
+ suggestions.push('Try specifying an organization with --org <name>');
34
37
  }
35
- return output;
38
+ suggestions.push('Use --count to increase the number of results');
39
+ return formatEmptyState(message, suggestions);
36
40
  }
37
- let output = `Found ${builds.length} builds:\n`;
38
- output += '==============================================\n';
39
- builds.forEach((build) => {
40
- try {
41
- output += `${build.pipeline?.slug || 'Unknown pipeline'} #${build.number}\n`;
42
- output += `State: ${build.state || 'Unknown'}\n`;
43
- output += `Branch: ${build.branch || 'Unknown'}\n`;
44
- output += `Message: ${build.message || 'No message'}\n`;
45
- const createdDate = (build.created_at || build.createdAt) ?
46
- new Date(build.created_at || build.createdAt).toLocaleString() : 'Unknown';
47
- const startedDate = (build.started_at || build.startedAt) ?
48
- new Date(build.started_at || build.startedAt).toLocaleString() : 'Not started';
49
- const finishedDate = (build.finished_at || build.finishedAt) ?
50
- new Date(build.finished_at || build.finishedAt).toLocaleString() : 'Not finished';
51
- output += `Created: ${createdDate}\n`;
52
- output += `Started: ${startedDate}\n`;
53
- output += `Finished: ${finishedDate}\n`;
54
- output += `URL: ${build.web_url || build.url || 'No URL'}\n`;
55
- output += '------------------\n';
56
- }
57
- catch (error) {
58
- output += `Error displaying build: ${error}\n`;
59
- if (options?.debug) {
60
- output += `Build data: ${JSON.stringify(build, null, 2)}\n`;
61
- }
62
- output += '------------------\n';
63
- }
41
+ const lines = [];
42
+ // Build a tabular summary view for scan-ability
43
+ const headers = ['PIPELINE', 'NUMBER', 'STATE', 'BRANCH'].map(h => SEMANTIC_COLORS.heading(h));
44
+ const dataRows = [];
45
+ builds.forEach((b) => {
46
+ dataRows.push([
47
+ b.pipeline?.slug || SEMANTIC_COLORS.muted('unknown'),
48
+ SEMANTIC_COLORS.identifier(`#${b.number}`),
49
+ formatBuildStatus(b.state || 'UNKNOWN', { useSymbol: false }),
50
+ b.branch || SEMANTIC_COLORS.dim('(no branch)')
51
+ ]);
64
52
  });
65
- // Summary and guidance lines
66
- output += `\nShowing ${builds.length} builds. Use --count and --page options to see more.\n`;
53
+ // Use responsive table for narrow terminals
54
+ if (isNarrowTerminal()) {
55
+ // Configure columns with priorities for narrow displays
56
+ const columns = [
57
+ { header: 'PIPELINE', priority: 3, minWidth: 10, truncate: true },
58
+ { header: 'NUMBER', priority: 10, minWidth: 6, align: 'right' },
59
+ { header: 'STATE', priority: 9, minWidth: 8 },
60
+ { header: 'BRANCH', priority: 1, minWidth: 6, truncate: true }
61
+ ];
62
+ if (isMobileTerminal()) {
63
+ // For very narrow terminals, show only most important columns
64
+ columns[0].priority = 2; // Lower pipeline priority
65
+ columns[3].priority = 0; // Hide branch on mobile
66
+ }
67
+ lines.push(renderResponsiveTable(headers, dataRows, { columns }));
68
+ }
69
+ else {
70
+ // Use standard table for wide terminals
71
+ const rows = [headers, ...dataRows];
72
+ lines.push(renderTable(rows, { preserveWidths: true }));
73
+ }
74
+ // Summary line (dimmed as auxiliary info)
75
+ lines.push('');
76
+ lines.push(SEMANTIC_COLORS.dim(`Found ${SEMANTIC_COLORS.count(builds.length.toString())} builds`));
77
+ // Add contextual hints if searching multiple orgs
67
78
  if (options?.organizationsCount && options.organizationsCount > 1 && !options.orgSpecified) {
68
- output += `Searched across ${options.organizationsCount} organizations. Use --org to filter to a specific organization.\n`;
79
+ const hints = [`Searched across ${options.organizationsCount} organizations. Use --org to filter to a specific organization.`];
80
+ lines.push('');
81
+ lines.push(formatTips(hints, TipStyle.INDIVIDUAL));
69
82
  }
70
- return output;
83
+ return lines.join('\n');
71
84
  }
72
85
  formatAccessError(options) {
73
- let output = '';
74
- if (options?.userName) {
75
- output = `No builds found for ${options.userName}`;
76
- if (options?.userEmail || options?.userId) {
77
- output += ` (${options.userEmail || options.userId})`;
78
- }
79
- output += '. ';
80
- }
86
+ let message = 'Access Denied';
81
87
  if (options?.orgSpecified && options?.accessErrors && options.accessErrors.length > 0) {
82
- output += options.accessErrors[0];
88
+ message = options.accessErrors[0];
83
89
  }
84
90
  else {
85
- output += 'You don\'t have access to the specified organization(s).';
91
+ message = 'You don\'t have access to the specified organization(s).';
86
92
  }
87
- return output;
93
+ return formatError(message, {
94
+ showHelp: true,
95
+ helpCommand: 'bktide orgs',
96
+ suggestions: [
97
+ 'Check your organization name is correct',
98
+ 'Run "bktide orgs" to see available organizations',
99
+ 'Verify your token has the correct permissions'
100
+ ]
101
+ });
88
102
  }
89
103
  formatNotFoundError(options) {
90
- let output = '';
104
+ let message = 'No builds found';
91
105
  if (options?.userName) {
92
- output = `No builds found for ${options.userName}`;
106
+ message = `No builds found for ${options.userName}`;
93
107
  if (options?.userEmail || options?.userId) {
94
- output += ` (${options.userEmail || options.userId})`;
108
+ message += ` (${options.userEmail || options.userId})`;
95
109
  }
96
- output += '.';
97
- }
98
- else {
99
- output = 'No builds found.';
100
110
  }
111
+ const suggestions = [];
101
112
  if (!options?.orgSpecified) {
102
- output += '\nTry specifying an organization with --org to narrow your search.';
113
+ suggestions.push('Try specifying an organization with --org <name>');
103
114
  }
104
- return output;
115
+ suggestions.push('Check your filters are correct');
116
+ suggestions.push('Try broadening your search');
117
+ return formatEmptyState(message, suggestions);
105
118
  }
106
119
  formatApiError(options) {
107
- return options?.errorMessage || 'An API error occurred while fetching builds.';
120
+ const message = options?.errorMessage || 'Failed to fetch builds from Buildkite';
121
+ return formatError(message, {
122
+ showHelp: true,
123
+ helpCommand: 'bktide builds --help',
124
+ suggestions: ['This might be a temporary issue. Try again in a moment.']
125
+ });
108
126
  }
109
127
  formatGenericError(options) {
110
- return options?.errorMessage || 'An error occurred while fetching builds.';
128
+ const message = options?.errorMessage || 'An unexpected error occurred';
129
+ return formatError(message, {
130
+ showHelp: true,
131
+ helpCommand: 'bktide builds --help'
132
+ });
111
133
  }
112
134
  }
113
135
  //# sourceMappingURL=PlainTextFormatter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/builds/PlainTextFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseFormatter, BuildFormatterOptions } from './Formatter.js';\nimport { Build } from '../../types/index.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'plain-text';\n \n formatBuilds(builds: Build[], options?: BuildFormatterOptions): string {\n // Handle error cases first\n if (options?.hasError) {\n if (options.errorType === 'access') {\n return this.formatAccessError(options);\n } else if (options.errorType === 'not_found') {\n return this.formatNotFoundError(options);\n } else if (options.errorType === 'api') {\n return this.formatApiError(options);\n } else {\n return this.formatGenericError(options);\n }\n }\n \n // Handle empty results (no error, just no data)\n if (builds.length === 0) {\n let output = 'No builds found.';\n \n // Add user info if provided\n if (options?.userName) {\n output = `No builds found for ${options.userName}`;\n if (options?.userEmail || options?.userId) {\n output += ` (${options.userEmail || options.userId})`;\n }\n output += '.';\n }\n \n // Add organization suggestion if applicable\n if (!options?.orgSpecified) {\n output += '\\nTry specifying an organization with --org to narrow your search.';\n }\n \n return output;\n }\n\n let output = `Found ${builds.length} builds:\\n`;\n output += '==============================================\\n';\n \n builds.forEach((build: Build) => {\n try {\n output += `${build.pipeline?.slug || 'Unknown pipeline'} #${build.number}\\n`;\n output += `State: ${build.state || 'Unknown'}\\n`;\n output += `Branch: ${build.branch || 'Unknown'}\\n`;\n output += `Message: ${build.message || 'No message'}\\n`;\n \n const createdDate = (build.created_at || build.createdAt) ? \n new Date(build.created_at || build.createdAt as string).toLocaleString() : 'Unknown';\n \n const startedDate = (build.started_at || build.startedAt) ? \n new Date(build.started_at || build.startedAt as string).toLocaleString() : 'Not started';\n \n const finishedDate = (build.finished_at || build.finishedAt) ? \n new Date(build.finished_at || build.finishedAt as string).toLocaleString() : 'Not finished';\n \n output += `Created: ${createdDate}\\n`;\n output += `Started: ${startedDate}\\n`;\n output += `Finished: ${finishedDate}\\n`;\n output += `URL: ${build.web_url || build.url || 'No URL'}\\n`;\n output += '------------------\\n';\n } catch (error) {\n output += `Error displaying build: ${error}\\n`;\n if (options?.debug) {\n output += `Build data: ${JSON.stringify(build, null, 2)}\\n`;\n }\n output += '------------------\\n';\n }\n });\n \n // Summary and guidance lines\n output += `\\nShowing ${builds.length} builds. Use --count and --page options to see more.\\n`;\n if (options?.organizationsCount && options.organizationsCount > 1 && !options.orgSpecified) {\n output += `Searched across ${options.organizationsCount} organizations. Use --org to filter to a specific organization.\\n`;\n }\n \n return output;\n }\n \n private formatAccessError(options?: BuildFormatterOptions): string {\n let output = '';\n \n if (options?.userName) {\n output = `No builds found for ${options.userName}`;\n if (options?.userEmail || options?.userId) {\n output += ` (${options.userEmail || options.userId})`;\n }\n output += '. ';\n }\n \n if (options?.orgSpecified && options?.accessErrors && options.accessErrors.length > 0) {\n output += options.accessErrors[0];\n } else {\n output += 'You don\\'t have access to the specified organization(s).';\n }\n \n return output;\n }\n \n private formatNotFoundError(options?: BuildFormatterOptions): string {\n let output = '';\n \n if (options?.userName) {\n output = `No builds found for ${options.userName}`;\n if (options?.userEmail || options?.userId) {\n output += ` (${options.userEmail || options.userId})`;\n }\n output += '.';\n } else {\n output = 'No builds found.';\n }\n \n if (!options?.orgSpecified) {\n output += '\\nTry specifying an organization with --org to narrow your search.';\n }\n \n return output;\n }\n \n private formatApiError(options?: BuildFormatterOptions): string {\n return options?.errorMessage || 'An API error occurred while fetching builds.';\n }\n \n private formatGenericError(options?: BuildFormatterOptions): string {\n return options?.errorMessage || 'An error occurred while fetching builds.';\n }\n} "]}
1
+ {"version":3,"file":"PlainTextFormatter.js","sourceRoot":"/","sources":["formatters/builds/PlainTextFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseFormatter, BuildFormatterOptions } from './Formatter.js';\nimport { Build } from '../../types/index.js';\nimport { renderTable } from '../../ui/table.js';\nimport { renderResponsiveTable, isNarrowTerminal, isMobileTerminal } from '../../ui/responsive-table.js';\nimport { \n SEMANTIC_COLORS, \n formatBuildStatus,\n formatEmptyState,\n formatError,\n formatTips,\n TipStyle\n} from '../../ui/theme.js';\n\nexport class PlainTextFormatter extends BaseFormatter {\n name = 'plain-text';\n \n formatBuilds(builds: Build[], options?: BuildFormatterOptions): string {\n // Handle error cases first\n if (options?.hasError) {\n if (options.errorType === 'access') {\n return this.formatAccessError(options);\n } else if (options.errorType === 'not_found') {\n return this.formatNotFoundError(options);\n } else if (options.errorType === 'api') {\n return this.formatApiError(options);\n } else {\n return this.formatGenericError(options);\n }\n }\n \n // Handle empty results (no error, just no data)\n if (builds.length === 0) {\n let message = 'No builds found';\n const suggestions: string[] = [];\n \n // Add user info if provided\n if (options?.userName) {\n message = `No builds found for ${SEMANTIC_COLORS.label(options.userName)}`;\n if (options?.userEmail || options?.userId) {\n message += ` ${SEMANTIC_COLORS.dim(`(${options.userEmail || options.userId})`)}`;\n }\n }\n \n // Add suggestions based on context\n if (!options?.orgSpecified) {\n suggestions.push('Try specifying an organization with --org <name>');\n }\n suggestions.push('Use --count to increase the number of results');\n \n return formatEmptyState(message, suggestions);\n }\n\n const lines: string[] = [];\n \n // Build a tabular summary view for scan-ability\n const headers = ['PIPELINE', 'NUMBER', 'STATE', 'BRANCH'].map(\n h => SEMANTIC_COLORS.heading(h)\n );\n \n const dataRows: string[][] = [];\n builds.forEach((b: Build) => {\n dataRows.push([\n b.pipeline?.slug || SEMANTIC_COLORS.muted('unknown'),\n SEMANTIC_COLORS.identifier(`#${b.number}`),\n formatBuildStatus(b.state || 'UNKNOWN', { useSymbol: false }),\n b.branch || SEMANTIC_COLORS.dim('(no branch)')\n ]);\n });\n \n // Use responsive table for narrow terminals\n if (isNarrowTerminal()) {\n // Configure columns with priorities for narrow displays\n const columns = [\n { header: 'PIPELINE', priority: 3, minWidth: 10, truncate: true },\n { header: 'NUMBER', priority: 10, minWidth: 6, align: 'right' as const },\n { header: 'STATE', priority: 9, minWidth: 8 },\n { header: 'BRANCH', priority: 1, minWidth: 6, truncate: true }\n ];\n \n if (isMobileTerminal()) {\n // For very narrow terminals, show only most important columns\n columns[0].priority = 2; // Lower pipeline priority\n columns[3].priority = 0; // Hide branch on mobile\n }\n \n lines.push(renderResponsiveTable(headers, dataRows, { columns }));\n } else {\n // Use standard table for wide terminals\n const rows: string[][] = [headers, ...dataRows];\n lines.push(renderTable(rows, { preserveWidths: true }));\n }\n \n // Summary line (dimmed as auxiliary info)\n lines.push('');\n lines.push(SEMANTIC_COLORS.dim(`Found ${SEMANTIC_COLORS.count(builds.length.toString())} builds`));\n \n // Add contextual hints if searching multiple orgs\n if (options?.organizationsCount && options.organizationsCount > 1 && !options.orgSpecified) {\n const hints = [`Searched across ${options.organizationsCount} organizations. Use --org to filter to a specific organization.`];\n lines.push('');\n lines.push(formatTips(hints, TipStyle.INDIVIDUAL));\n }\n \n return lines.join('\\n');\n }\n \n private formatAccessError(options?: BuildFormatterOptions): string {\n let message = 'Access Denied';\n \n if (options?.orgSpecified && options?.accessErrors && options.accessErrors.length > 0) {\n message = options.accessErrors[0];\n } else {\n message = 'You don\\'t have access to the specified organization(s).';\n }\n \n return formatError(message, {\n showHelp: true,\n helpCommand: 'bktide orgs',\n suggestions: [\n 'Check your organization name is correct',\n 'Run \"bktide orgs\" to see available organizations',\n 'Verify your token has the correct permissions'\n ]\n });\n }\n \n private formatNotFoundError(options?: BuildFormatterOptions): string {\n let message = 'No builds found';\n \n if (options?.userName) {\n message = `No builds found for ${options.userName}`;\n if (options?.userEmail || options?.userId) {\n message += ` (${options.userEmail || options.userId})`;\n }\n }\n \n const suggestions: string[] = [];\n if (!options?.orgSpecified) {\n suggestions.push('Try specifying an organization with --org <name>');\n }\n suggestions.push('Check your filters are correct');\n suggestions.push('Try broadening your search');\n \n return formatEmptyState(message, suggestions);\n }\n \n private formatApiError(options?: BuildFormatterOptions): string {\n const message = options?.errorMessage || 'Failed to fetch builds from Buildkite';\n return formatError(message, {\n showHelp: true,\n helpCommand: 'bktide builds --help',\n suggestions: ['This might be a temporary issue. Try again in a moment.']\n });\n }\n \n private formatGenericError(options?: BuildFormatterOptions): string {\n const message = options?.errorMessage || 'An unexpected error occurred';\n return formatError(message, {\n showHelp: true,\n helpCommand: 'bktide builds --help'\n });\n }\n} "]}
@@ -1,4 +1,5 @@
1
1
  import { BaseErrorFormatter } from './Formatter.js';
2
+ import { isRunningInAlfred } from '../../utils/alfred.js';
2
3
  /**
3
4
  * Alfred formatter for errors
4
5
  *
@@ -16,6 +17,25 @@ export class AlfredFormatter extends BaseErrorFormatter {
16
17
  */
17
18
  formatError(errors, options) {
18
19
  const errorArray = Array.isArray(errors) ? errors : [errors];
20
+ // Alfred first-run UX: if running in Alfred and token is missing, show setup item
21
+ const missingToken = errorArray.some(err => {
22
+ const msg = err instanceof Error ? err.message : String(err);
23
+ return /API token required|No token/i.test(msg);
24
+ });
25
+ if (isRunningInAlfred() && missingToken && !process.env.BUILDKITE_API_TOKEN && !process.env.BK_TOKEN) {
26
+ return JSON.stringify({
27
+ items: [
28
+ {
29
+ uid: 'set-token',
30
+ title: 'Set Buildkite token',
31
+ subtitle: 'Open Workflow Configuration to paste your token',
32
+ arg: 'alfred:open-config',
33
+ icon: { path: 'icons/info.png' },
34
+ valid: true
35
+ }
36
+ ]
37
+ });
38
+ }
19
39
  const items = [];
20
40
  for (const error of errorArray) {
21
41
  // Add main error item
@@ -1 +1 @@
1
- {"version":3,"file":"AlfredFormatter.js","sourceRoot":"/","sources":["formatters/errors/AlfredFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseErrorFormatter, ErrorFormatter, ErrorFormatterOptions } from './Formatter.js';\n\n/**\n * Alfred formatter for errors\n * \n * This formatter provides Alfred-compatible JSON output for error conditions\n * that can be processed by Alfred workflows\n */\nexport class AlfredFormatter extends BaseErrorFormatter implements ErrorFormatter {\n name = 'alfred';\n\n /**\n * Format one or more errors for display in Alfred JSON format\n * \n * @param errors The error(s) to format\n * @param options Formatting options\n * @returns Alfred JSON formatted error message\n */\n formatError(errors: unknown | unknown[], options?: ErrorFormatterOptions): string {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n const items = [];\n\n for (const error of errorArray) {\n // Add main error item\n items.push({\n uid: 'error',\n title: `Error: ${this.getErrorMessage(error)}`,\n subtitle: this.getErrorSubtitle(error),\n arg: this.getErrorMessage(error),\n icon: {\n path: 'icons/error.png'\n },\n valid: true\n });\n\n // Add stack trace items if debug is enabled\n if (options?.debug) {\n const stack = this.getStackTrace(error);\n if (stack) {\n const stackItems = stack.split('\\n').map((line, index) => ({\n uid: `stack-${index}`,\n title: line.trim(),\n subtitle: 'Stack trace line',\n arg: line,\n icon: {\n path: 'icons/stack.png'\n },\n valid: true\n }));\n items.push(...stackItems);\n }\n\n // Add API errors if present\n const apiErrors = this.getApiErrors(error);\n if (apiErrors?.length) {\n apiErrors.forEach((apiError, index) => {\n items.push({\n uid: `api-error-${index}`,\n title: `API Error: ${apiError.message || 'Unknown error'}`,\n subtitle: apiError.path ? `Path: ${apiError.path.join('.')}` : 'API Error',\n arg: apiError.message || 'Unknown error',\n icon: {\n path: 'icons/api-error.png'\n },\n valid: true\n });\n });\n }\n\n // Add request details if available\n const request = this.getRequestDetails(error);\n if (request) {\n items.push({\n uid: 'request-details',\n title: 'Request Details',\n subtitle: `${request.method || 'Unknown'} ${request.url || 'Unknown URL'}`,\n arg: `${request.method || 'Unknown'} ${request.url || 'Unknown URL'}`,\n icon: {\n path: 'icons/request.png'\n },\n valid: false\n });\n }\n }\n }\n\n // Add system info if debug is enabled\n if (options?.debug) {\n items.push({\n uid: 'system-info',\n title: 'System Information',\n subtitle: `Node ${process.version} on ${process.platform} (${process.arch})`,\n arg: `Node ${process.version} on ${process.platform} (${process.arch})`,\n icon: {\n path: 'icons/info.png'\n },\n valid: false\n });\n }\n\n return JSON.stringify({ items });\n }\n\n /**\n * Get a subtitle for the error to display in Alfred\n * @param error The error object\n * @returns A subtitle string\n */\n private getErrorSubtitle(error: unknown): string {\n const errorName = this.getErrorName(error);\n const apiErrors = this.getApiErrors(error);\n \n if (apiErrors?.length) {\n const firstError = apiErrors[0];\n return `API Error: ${firstError.message || 'Unknown error'}`;\n }\n \n return `${errorName} - Press Enter to copy error message`;\n }\n} "]}
1
+ {"version":3,"file":"AlfredFormatter.js","sourceRoot":"/","sources":["formatters/errors/AlfredFormatter.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseErrorFormatter, ErrorFormatter, ErrorFormatterOptions } from './Formatter.js';\nimport { isRunningInAlfred } from '../../utils/alfred.js';\n\n/**\n * Alfred formatter for errors\n * \n * This formatter provides Alfred-compatible JSON output for error conditions\n * that can be processed by Alfred workflows\n */\nexport class AlfredFormatter extends BaseErrorFormatter implements ErrorFormatter {\n name = 'alfred';\n\n /**\n * Format one or more errors for display in Alfred JSON format\n * \n * @param errors The error(s) to format\n * @param options Formatting options\n * @returns Alfred JSON formatted error message\n */\n formatError(errors: unknown | unknown[], options?: ErrorFormatterOptions): string {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n\n // Alfred first-run UX: if running in Alfred and token is missing, show setup item\n const missingToken = errorArray.some(err => {\n const msg = err instanceof Error ? err.message : String(err);\n return /API token required|No token/i.test(msg);\n });\n if (isRunningInAlfred() && missingToken && !process.env.BUILDKITE_API_TOKEN && !process.env.BK_TOKEN) {\n return JSON.stringify({\n items: [\n {\n uid: 'set-token',\n title: 'Set Buildkite token',\n subtitle: 'Open Workflow Configuration to paste your token',\n arg: 'alfred:open-config',\n icon: { path: 'icons/info.png' },\n valid: true\n }\n ]\n });\n }\n const items = [];\n\n for (const error of errorArray) {\n // Add main error item\n items.push({\n uid: 'error',\n title: `Error: ${this.getErrorMessage(error)}`,\n subtitle: this.getErrorSubtitle(error),\n arg: this.getErrorMessage(error),\n icon: {\n path: 'icons/error.png'\n },\n valid: true\n });\n\n // Add stack trace items if debug is enabled\n if (options?.debug) {\n const stack = this.getStackTrace(error);\n if (stack) {\n const stackItems = stack.split('\\n').map((line, index) => ({\n uid: `stack-${index}`,\n title: line.trim(),\n subtitle: 'Stack trace line',\n arg: line,\n icon: {\n path: 'icons/stack.png'\n },\n valid: true\n }));\n items.push(...stackItems);\n }\n\n // Add API errors if present\n const apiErrors = this.getApiErrors(error);\n if (apiErrors?.length) {\n apiErrors.forEach((apiError, index) => {\n items.push({\n uid: `api-error-${index}`,\n title: `API Error: ${apiError.message || 'Unknown error'}`,\n subtitle: apiError.path ? `Path: ${apiError.path.join('.')}` : 'API Error',\n arg: apiError.message || 'Unknown error',\n icon: {\n path: 'icons/api-error.png'\n },\n valid: true\n });\n });\n }\n\n // Add request details if available\n const request = this.getRequestDetails(error);\n if (request) {\n items.push({\n uid: 'request-details',\n title: 'Request Details',\n subtitle: `${request.method || 'Unknown'} ${request.url || 'Unknown URL'}`,\n arg: `${request.method || 'Unknown'} ${request.url || 'Unknown URL'}`,\n icon: {\n path: 'icons/request.png'\n },\n valid: false\n });\n }\n }\n }\n\n // Add system info if debug is enabled\n if (options?.debug) {\n items.push({\n uid: 'system-info',\n title: 'System Information',\n subtitle: `Node ${process.version} on ${process.platform} (${process.arch})`,\n arg: `Node ${process.version} on ${process.platform} (${process.arch})`,\n icon: {\n path: 'icons/info.png'\n },\n valid: false\n });\n }\n\n return JSON.stringify({ items });\n }\n\n /**\n * Get a subtitle for the error to display in Alfred\n * @param error The error object\n * @returns A subtitle string\n */\n private getErrorSubtitle(error: unknown): string {\n const errorName = this.getErrorName(error);\n const apiErrors = this.getApiErrors(error);\n \n if (apiErrors?.length) {\n const firstError = apiErrors[0];\n return `API Error: ${firstError.message || 'Unknown error'}`;\n }\n \n return `${errorName} - Press Enter to copy error message`;\n }\n} "]}