@hyperdrive.bot/gut 0.1.6 → 0.1.9

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 (111) hide show
  1. package/README.md +1 -1
  2. package/dist/base-command.d.ts +22 -0
  3. package/dist/base-command.js +99 -0
  4. package/dist/commands/add.d.ts +14 -0
  5. package/dist/commands/add.js +70 -0
  6. package/dist/commands/affected.d.ts +23 -0
  7. package/dist/commands/affected.js +323 -0
  8. package/dist/commands/audit.d.ts +33 -0
  9. package/dist/commands/audit.js +594 -0
  10. package/dist/commands/back.d.ts +6 -0
  11. package/dist/commands/back.js +29 -0
  12. package/dist/commands/checkout.d.ts +14 -0
  13. package/dist/commands/checkout.js +124 -0
  14. package/dist/commands/commit.d.ts +11 -0
  15. package/dist/commands/commit.js +107 -0
  16. package/dist/commands/context.d.ts +6 -0
  17. package/dist/commands/context.js +32 -0
  18. package/dist/commands/contexts.d.ts +7 -0
  19. package/dist/commands/contexts.js +88 -0
  20. package/dist/commands/deps.d.ts +10 -0
  21. package/dist/commands/deps.js +100 -0
  22. package/dist/commands/entity/add.d.ts +16 -0
  23. package/dist/commands/entity/add.js +103 -0
  24. package/dist/commands/entity/clone-all.d.ts +17 -0
  25. package/dist/commands/entity/clone-all.js +127 -0
  26. package/dist/commands/entity/clone.d.ts +15 -0
  27. package/dist/commands/entity/clone.js +106 -0
  28. package/dist/commands/entity/list.d.ts +11 -0
  29. package/dist/commands/entity/list.js +80 -0
  30. package/dist/commands/entity/remove.d.ts +12 -0
  31. package/dist/commands/entity/remove.js +54 -0
  32. package/dist/commands/extract.d.ts +35 -0
  33. package/dist/commands/extract.js +483 -0
  34. package/dist/commands/focus.d.ts +19 -0
  35. package/dist/commands/focus.js +137 -0
  36. package/dist/commands/graph.d.ts +18 -0
  37. package/dist/commands/graph.js +273 -0
  38. package/dist/commands/init.d.ts +11 -0
  39. package/dist/commands/init.js +75 -0
  40. package/dist/commands/insights.d.ts +21 -0
  41. package/dist/commands/insights.js +465 -0
  42. package/dist/commands/patterns.d.ts +40 -0
  43. package/dist/commands/patterns.js +405 -0
  44. package/dist/commands/pull.d.ts +11 -0
  45. package/dist/commands/pull.js +121 -0
  46. package/dist/commands/push.d.ts +11 -0
  47. package/dist/commands/push.js +97 -0
  48. package/dist/commands/quick-setup.d.ts +20 -0
  49. package/dist/commands/quick-setup.js +417 -0
  50. package/dist/commands/recent.d.ts +9 -0
  51. package/dist/commands/recent.js +51 -0
  52. package/dist/commands/related.d.ts +23 -0
  53. package/dist/commands/related.js +255 -0
  54. package/dist/commands/repos.d.ts +17 -0
  55. package/dist/commands/repos.js +184 -0
  56. package/dist/commands/stack.d.ts +10 -0
  57. package/dist/commands/stack.js +78 -0
  58. package/dist/commands/status.d.ts +13 -0
  59. package/dist/commands/status.js +193 -0
  60. package/dist/commands/sync.d.ts +11 -0
  61. package/dist/commands/sync.js +139 -0
  62. package/dist/commands/ticket/focus.d.ts +20 -0
  63. package/dist/commands/ticket/focus.js +217 -0
  64. package/dist/commands/ticket/get.d.ts +15 -0
  65. package/dist/commands/ticket/get.js +168 -0
  66. package/dist/commands/ticket/hint.d.ts +16 -0
  67. package/dist/commands/ticket/hint.js +147 -0
  68. package/dist/commands/ticket/index.d.ts +10 -0
  69. package/dist/commands/ticket/index.js +60 -0
  70. package/dist/commands/ticket/list.d.ts +13 -0
  71. package/dist/commands/ticket/list.js +120 -0
  72. package/dist/commands/ticket/sync.d.ts +14 -0
  73. package/dist/commands/ticket/sync.js +85 -0
  74. package/dist/commands/ticket/update.d.ts +17 -0
  75. package/dist/commands/ticket/update.js +142 -0
  76. package/dist/commands/unfocus.d.ts +6 -0
  77. package/dist/commands/unfocus.js +19 -0
  78. package/dist/commands/used-by.d.ts +13 -0
  79. package/dist/commands/used-by.js +110 -0
  80. package/dist/commands/workspace.d.ts +22 -0
  81. package/dist/commands/workspace.js +372 -0
  82. package/dist/index.d.ts +14 -0
  83. package/dist/index.js +16 -0
  84. package/dist/models/entity.model.d.ts +234 -0
  85. package/dist/models/entity.model.js +1 -0
  86. package/dist/models/ticket.model.d.ts +117 -0
  87. package/dist/models/ticket.model.js +43 -0
  88. package/dist/services/auth.service.d.ts +15 -0
  89. package/dist/services/auth.service.js +26 -0
  90. package/dist/services/config.service.d.ts +34 -0
  91. package/dist/services/config.service.js +234 -0
  92. package/dist/services/entity.service.d.ts +20 -0
  93. package/dist/services/entity.service.js +127 -0
  94. package/dist/services/focus.service.d.ts +71 -0
  95. package/dist/services/focus.service.js +614 -0
  96. package/dist/services/git.service.d.ts +39 -0
  97. package/dist/services/git.service.js +188 -0
  98. package/dist/services/gut-api.service.d.ts +53 -0
  99. package/dist/services/gut-api.service.js +99 -0
  100. package/dist/services/ticket.service.d.ts +84 -0
  101. package/dist/services/ticket.service.js +207 -0
  102. package/dist/utils/display.d.ts +26 -0
  103. package/dist/utils/display.js +145 -0
  104. package/dist/utils/filesystem.d.ts +32 -0
  105. package/dist/utils/filesystem.js +198 -0
  106. package/dist/utils/index.d.ts +13 -0
  107. package/dist/utils/index.js +14 -0
  108. package/dist/utils/validation.d.ts +22 -0
  109. package/dist/utils/validation.js +192 -0
  110. package/oclif.manifest.json +2008 -0
  111. package/package.json +11 -2
@@ -0,0 +1,168 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ import { getStatusEmoji, getPriorityEmoji } from '../../models/ticket.model.js';
5
+ import { TicketService } from '../../services/ticket.service.js';
6
+ export default class TicketGet extends BaseCommand {
7
+ static args = {
8
+ ticketId: Args.string({
9
+ description: 'ticket ID (e.g., PROJ-1234)',
10
+ name: 'ticketId',
11
+ required: true
12
+ })
13
+ };
14
+ static description = 'Get details for a gut ticket';
15
+ static examples = [
16
+ '<%= config.bin %> <%= command.id %> PROJ-1234',
17
+ '<%= config.bin %> <%= command.id %> PROJ-1234 --json'
18
+ ];
19
+ static flags = {
20
+ json: Flags.boolean({
21
+ char: 'j',
22
+ description: 'output as JSON'
23
+ })
24
+ };
25
+ get requiresInit() {
26
+ return false;
27
+ }
28
+ async run() {
29
+ const { args, flags } = await this.parse(TicketGet);
30
+ const ticketService = new TicketService(this.configService);
31
+ if (!ticketService.isConfigured()) {
32
+ this.error('API not configured. Set GUT_API_ENDPOINT and GUT_TENANT_ID environment variables.');
33
+ }
34
+ try {
35
+ const ticket = await ticketService.getTicket(args.ticketId);
36
+ if (!ticket) {
37
+ this.error(`Ticket ${args.ticketId} not found`);
38
+ }
39
+ if (flags.json) {
40
+ this.log(JSON.stringify(ticket, null, 2));
41
+ return;
42
+ }
43
+ this.printTicketDetails(ticket);
44
+ }
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ this.error(`Failed to get ticket: ${message}`);
48
+ }
49
+ }
50
+ printTicketDetails(ticket) {
51
+ const statusEmoji = getStatusEmoji(ticket.status);
52
+ const priorityEmoji = getPriorityEmoji(ticket.priority);
53
+ this.log('');
54
+ this.log(chalk.bold(`📋 ${ticket.ticketId}: ${ticket.summary}`));
55
+ this.log('');
56
+ // Status section
57
+ this.log(chalk.dim('─'.repeat(60)));
58
+ this.log(chalk.bold('Status'));
59
+ this.log(` ${statusEmoji} Status: ${ticket.status}`);
60
+ this.log(` 📍 Phase: ${ticket.phase}`);
61
+ this.log(` 📊 Confidence: ${this.formatConfidence(ticket.confidence)}`);
62
+ this.log(` ${priorityEmoji} Priority: ${ticket.priority}`);
63
+ this.log(` 🏷️ Type: ${ticket.type}`);
64
+ // Source section
65
+ this.log('');
66
+ this.log(chalk.dim('─'.repeat(60)));
67
+ this.log(chalk.bold('Source'));
68
+ this.log(` 🔗 Source: ${ticket.source.type}`);
69
+ this.log(` 🌐 URL: ${ticket.source.externalUrl}`);
70
+ if (ticket.source.projectKey) {
71
+ this.log(` 📁 Project: ${ticket.source.projectKey}`);
72
+ }
73
+ // Git section
74
+ this.log('');
75
+ this.log(chalk.dim('─'.repeat(60)));
76
+ this.log(chalk.bold('Git'));
77
+ this.log(` 🌿 Branch: ${ticket.branch}`);
78
+ // Entities section
79
+ if (ticket.requiredEntities.length > 0) {
80
+ this.log('');
81
+ this.log(chalk.dim('─'.repeat(60)));
82
+ this.log(chalk.bold('Required Entities'));
83
+ for (const entity of ticket.requiredEntities) {
84
+ this.log(` • ${entity}`);
85
+ }
86
+ }
87
+ if (ticket.relatedEntities && ticket.relatedEntities.length > 0) {
88
+ this.log('');
89
+ this.log(chalk.bold('Related Entities'));
90
+ for (const entity of ticket.relatedEntities) {
91
+ this.log(` • ${chalk.dim(entity)}`);
92
+ }
93
+ }
94
+ // Affected files section
95
+ if (ticket.affectedFiles && ticket.affectedFiles.length > 0) {
96
+ this.log('');
97
+ this.log(chalk.dim('─'.repeat(60)));
98
+ this.log(chalk.bold('Affected Files'));
99
+ for (const file of ticket.affectedFiles.slice(0, 10)) {
100
+ this.log(` • ${file}`);
101
+ }
102
+ if (ticket.affectedFiles.length > 10) {
103
+ this.log(chalk.dim(` ... and ${ticket.affectedFiles.length - 10} more`));
104
+ }
105
+ }
106
+ // Labels section
107
+ if (ticket.labels.length > 0) {
108
+ this.log('');
109
+ this.log(chalk.dim('─'.repeat(60)));
110
+ this.log(chalk.bold('Labels'));
111
+ this.log(` ${ticket.labels.map(l => chalk.cyan(l)).join(', ')}`);
112
+ }
113
+ // Handoff section (if blocked)
114
+ if (ticket.handoff?.required) {
115
+ this.log('');
116
+ this.log(chalk.dim('─'.repeat(60)));
117
+ this.log(chalk.bold.red('⚠️ Handoff Required'));
118
+ if (ticket.handoff.reason) {
119
+ this.log(` Reason: ${ticket.handoff.reason}`);
120
+ }
121
+ if (ticket.handoff.triggeredAt) {
122
+ this.log(` Triggered: ${new Date(ticket.handoff.triggeredAt).toLocaleString()}`);
123
+ }
124
+ if (ticket.handoff.confidenceAtTrigger !== undefined) {
125
+ this.log(` Confidence at trigger: ${ticket.handoff.confidenceAtTrigger}%`);
126
+ }
127
+ // Previous hints
128
+ if (ticket.handoff.humanHints && ticket.handoff.humanHints.length > 0) {
129
+ this.log('');
130
+ this.log(chalk.bold(' Previous Hints:'));
131
+ for (const hint of ticket.handoff.humanHints) {
132
+ this.log(` • "${hint.hint}" (by ${hint.givenBy})`);
133
+ }
134
+ }
135
+ // Suggested next steps
136
+ if (ticket.handoff.suggestedNextSteps && ticket.handoff.suggestedNextSteps.length > 0) {
137
+ this.log('');
138
+ this.log(chalk.bold(' Suggested Next Steps:'));
139
+ for (const step of ticket.handoff.suggestedNextSteps) {
140
+ this.log(` • ${step}`);
141
+ }
142
+ }
143
+ }
144
+ // Description section
145
+ this.log('');
146
+ this.log(chalk.dim('─'.repeat(60)));
147
+ this.log(chalk.bold('Description'));
148
+ this.log(ticket.description || chalk.dim('No description'));
149
+ // Timestamps
150
+ this.log('');
151
+ this.log(chalk.dim('─'.repeat(60)));
152
+ this.log(chalk.dim(`Created: ${new Date(ticket.createdAt).toLocaleString()}`));
153
+ this.log(chalk.dim(`Updated: ${new Date(ticket.updatedAt).toLocaleString()}`));
154
+ this.log('');
155
+ }
156
+ formatConfidence(confidence) {
157
+ if (confidence >= 90) {
158
+ return chalk.green(`${confidence}%`);
159
+ }
160
+ if (confidence >= 70) {
161
+ return chalk.yellow(`${confidence}%`);
162
+ }
163
+ if (confidence >= 50) {
164
+ return chalk.hex('#FFA500')(`${confidence}%`);
165
+ }
166
+ return chalk.red(`${confidence}%`);
167
+ }
168
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class TicketHint extends BaseCommand {
3
+ static args: {
4
+ hint: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ ticketId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ editor: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'given-by': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ };
14
+ protected get requiresInit(): boolean;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,147 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import ora from 'ora';
5
+ import { BaseCommand } from '../../base-command.js';
6
+ import { getStatusEmoji } from '../../models/ticket.model.js';
7
+ import { TicketService } from '../../services/ticket.service.js';
8
+ export default class TicketHint extends BaseCommand {
9
+ static args = {
10
+ hint: Args.string({
11
+ description: 'hint text to add (if not provided, will prompt)',
12
+ name: 'hint',
13
+ required: false
14
+ }),
15
+ ticketId: Args.string({
16
+ description: 'ticket ID to add hint to',
17
+ name: 'ticketId',
18
+ required: true
19
+ })
20
+ };
21
+ static description = 'Add a hint to a blocked or needs_clarity ticket to help AI retry';
22
+ static examples = [
23
+ '<%= config.bin %> <%= command.id %> PROJ-1234 "The auth token is in the Authorization header"',
24
+ '<%= config.bin %> <%= command.id %> PROJ-1234 --editor',
25
+ '<%= config.bin %> <%= command.id %> PROJ-1234'
26
+ ];
27
+ static flags = {
28
+ editor: Flags.boolean({
29
+ char: 'e',
30
+ description: 'open editor for hint text'
31
+ }),
32
+ 'given-by': Flags.string({
33
+ char: 'g',
34
+ description: 'identifier of who is giving the hint',
35
+ default: process.env.USER || 'human'
36
+ }),
37
+ json: Flags.boolean({
38
+ char: 'j',
39
+ description: 'output as JSON'
40
+ })
41
+ };
42
+ get requiresInit() {
43
+ return false;
44
+ }
45
+ async run() {
46
+ const { args, flags } = await this.parse(TicketHint);
47
+ const ticketService = new TicketService(this.configService);
48
+ if (!ticketService.isConfigured()) {
49
+ this.error('API not configured. Set GUT_API_ENDPOINT and GUT_TENANT_ID environment variables.');
50
+ }
51
+ // Get current ticket to show context
52
+ const spinner = ora('Getting ticket details...').start();
53
+ try {
54
+ const ticket = await ticketService.getTicket(args.ticketId);
55
+ spinner.stop();
56
+ if (!ticket) {
57
+ this.error(`Ticket ${args.ticketId} not found`);
58
+ }
59
+ // Show ticket context
60
+ if (!flags.json) {
61
+ this.log('');
62
+ this.log(chalk.bold(`📋 ${ticket.ticketId}: ${ticket.summary}`));
63
+ this.log(` ${getStatusEmoji(ticket.status)} Status: ${ticket.status}`);
64
+ if (ticket.handoff?.reason) {
65
+ this.log('');
66
+ this.log(chalk.bold.yellow('⚠️ Block Reason:'));
67
+ this.log(` ${ticket.handoff.reason}`);
68
+ }
69
+ if (ticket.handoff?.suggestedNextSteps && ticket.handoff.suggestedNextSteps.length > 0) {
70
+ this.log('');
71
+ this.log(chalk.bold('💡 Suggested Next Steps:'));
72
+ for (const step of ticket.handoff.suggestedNextSteps) {
73
+ this.log(` • ${step}`);
74
+ }
75
+ }
76
+ // Show previous hints
77
+ if (ticket.handoff?.humanHints && ticket.handoff.humanHints.length > 0) {
78
+ this.log('');
79
+ this.log(chalk.bold('📝 Previous Hints:'));
80
+ for (const hint of ticket.handoff.humanHints) {
81
+ const date = new Date(hint.givenAt).toLocaleString();
82
+ this.log(` • "${hint.hint}"`);
83
+ this.log(chalk.dim(` by ${hint.givenBy} at ${date}`));
84
+ }
85
+ }
86
+ this.log('');
87
+ }
88
+ // Get hint text
89
+ let hintText = args.hint;
90
+ if (!hintText) {
91
+ if (flags.editor) {
92
+ // Open editor for hint
93
+ const result = await inquirer.prompt([
94
+ {
95
+ type: 'editor',
96
+ name: 'hint',
97
+ message: 'Enter your hint (this will open your editor):',
98
+ default: `# Add your hint below to help the AI understand what to do next.\n# The AI will retry with this context.\n\n`
99
+ }
100
+ ]);
101
+ hintText = result.hint.replace(/^#.*$/gm, '').trim();
102
+ }
103
+ else {
104
+ // Prompt for hint
105
+ const result = await inquirer.prompt([
106
+ {
107
+ type: 'input',
108
+ name: 'hint',
109
+ message: 'Enter your hint:',
110
+ validate: (input) => input.trim().length > 0 || 'Hint cannot be empty'
111
+ }
112
+ ]);
113
+ hintText = result.hint;
114
+ }
115
+ }
116
+ if (!hintText || hintText.trim().length === 0) {
117
+ this.error('Hint cannot be empty');
118
+ }
119
+ // Submit hint
120
+ spinner.start('Adding hint...');
121
+ const response = await ticketService.addHint(args.ticketId, hintText.trim(), flags['given-by']);
122
+ spinner.succeed('Hint added');
123
+ if (flags.json) {
124
+ this.log(JSON.stringify(response, null, 2));
125
+ return;
126
+ }
127
+ this.log('');
128
+ this.log(chalk.green('✓ Hint added successfully'));
129
+ this.log(` ${getStatusEmoji(response.status)} New status: ${response.status}`);
130
+ if (response.retryTriggered) {
131
+ this.log('');
132
+ this.log(chalk.cyan('🔄 Retry triggered - AI will process with your hint'));
133
+ this.log(chalk.dim(' Run `gut ticket get ' + args.ticketId + '` to check progress'));
134
+ }
135
+ else {
136
+ this.log('');
137
+ this.log(chalk.dim(' No automatic retry triggered'));
138
+ }
139
+ this.log('');
140
+ }
141
+ catch (error) {
142
+ spinner.fail('Failed');
143
+ const message = error instanceof Error ? error.message : String(error);
144
+ this.error(`Failed to add hint: ${message}`);
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class Ticket extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
7
+ };
8
+ protected get requiresInit(): boolean;
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,60 @@
1
+ import { Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ export default class Ticket extends BaseCommand {
5
+ static description = 'Manage gut tickets from the ADHB (Autonomous Development with Human Backstop) system';
6
+ static examples = [
7
+ '<%= config.bin %> ticket list',
8
+ '<%= config.bin %> ticket get PROJ-1234',
9
+ '<%= config.bin %> ticket focus PROJ-1234',
10
+ '<%= config.bin %> ticket hint PROJ-1234 "Check the config file"',
11
+ '<%= config.bin %> ticket sync PROJ-1234',
12
+ '<%= config.bin %> ticket update PROJ-1234 --status in_progress'
13
+ ];
14
+ static flags = {
15
+ help: Flags.help({ char: 'h' })
16
+ };
17
+ get requiresInit() {
18
+ return false;
19
+ }
20
+ async run() {
21
+ this.log('');
22
+ this.log(chalk.bold('🎫 Gut Ticket Commands'));
23
+ this.log('');
24
+ this.log('Manage tickets from the ADHB (Autonomous Development with Human Backstop) system.');
25
+ this.log('These commands interact with the gut backend API to manage AI-enriched tickets.');
26
+ this.log('');
27
+ this.log(chalk.bold('Available Commands:'));
28
+ this.log('');
29
+ this.log(` ${chalk.cyan('gut ticket list')} List all tickets`);
30
+ this.log(` ${chalk.cyan('gut ticket get <id>')} Get details for a specific ticket`);
31
+ this.log(` ${chalk.cyan('gut ticket focus <id>')} Focus on a ticket (download manifest, clone entities)`);
32
+ this.log(` ${chalk.cyan('gut ticket hint <id>')} Add a hint to help AI retry a blocked ticket`);
33
+ this.log(` ${chalk.cyan('gut ticket sync <id>')} Sync ticket state with external source`);
34
+ this.log(` ${chalk.cyan('gut ticket update <id>')} Update ticket status or confidence`);
35
+ this.log('');
36
+ this.log(chalk.bold('Workflow:'));
37
+ this.log('');
38
+ this.log(' 1. Tickets are created from JIRA/GitHub and enriched by AI');
39
+ this.log(' 2. Use `gut ticket list` to see available tickets');
40
+ this.log(' 3. Use `gut ticket focus <id>` to set up your workspace');
41
+ this.log(' 4. If blocked, use `gut ticket hint <id>` to help AI');
42
+ this.log(' 5. Use `gut ticket sync <id>` to sync status back to source');
43
+ this.log('');
44
+ this.log(chalk.bold('Configuration:'));
45
+ this.log('');
46
+ this.log(' Set these environment variables:');
47
+ this.log(` ${chalk.cyan('GUT_API_ENDPOINT')} - API base URL (e.g., https://api.devsquad.com)`);
48
+ this.log(` ${chalk.cyan('GUT_TENANT_ID')} - Your tenant ID`);
49
+ this.log(` ${chalk.cyan('GUT_AUTH_TOKEN')} - Authentication token (optional)`);
50
+ this.log('');
51
+ this.log(' Or create `.gut/api.json` in your workspace:');
52
+ this.log(chalk.dim(' {'));
53
+ this.log(chalk.dim(' "apiEndpoint": "https://api.devsquad.com",'));
54
+ this.log(chalk.dim(' "tenantId": "your-tenant-id"'));
55
+ this.log(chalk.dim(' }'));
56
+ this.log('');
57
+ this.log(chalk.dim('Run `gut ticket <command> --help` for more information on a specific command.'));
58
+ this.log('');
59
+ }
60
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class TicketList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ protected get requiresInit(): boolean;
11
+ run(): Promise<void>;
12
+ private formatConfidence;
13
+ }
@@ -0,0 +1,120 @@
1
+ import { Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import Table from 'cli-table3';
4
+ import { BaseCommand } from '../../base-command.js';
5
+ import { getStatusEmoji, getPriorityEmoji } from '../../models/ticket.model.js';
6
+ import { TicketService } from '../../services/ticket.service.js';
7
+ const VALID_STATUSES = [
8
+ 'enriching',
9
+ 'needs_clarity',
10
+ 'ready',
11
+ 'in_progress',
12
+ 'testing',
13
+ 'in_review',
14
+ 'blocked',
15
+ 'ready_to_merge',
16
+ 'deploying',
17
+ 'validating',
18
+ 'done'
19
+ ];
20
+ export default class TicketList extends BaseCommand {
21
+ static description = 'List gut tickets';
22
+ static examples = [
23
+ '<%= config.bin %> <%= command.id %>',
24
+ '<%= config.bin %> <%= command.id %> --status ready',
25
+ '<%= config.bin %> <%= command.id %> --status blocked --limit 10',
26
+ '<%= config.bin %> <%= command.id %> --json'
27
+ ];
28
+ static flags = {
29
+ json: Flags.boolean({
30
+ char: 'j',
31
+ description: 'output as JSON'
32
+ }),
33
+ limit: Flags.integer({
34
+ char: 'l',
35
+ default: 20,
36
+ description: 'maximum number of tickets to show'
37
+ }),
38
+ status: Flags.string({
39
+ char: 's',
40
+ description: `filter by status (${VALID_STATUSES.join(', ')})`,
41
+ options: VALID_STATUSES
42
+ })
43
+ };
44
+ get requiresInit() {
45
+ return false; // Ticket commands work without gut init
46
+ }
47
+ async run() {
48
+ const { flags } = await this.parse(TicketList);
49
+ const ticketService = new TicketService(this.configService);
50
+ if (!ticketService.isConfigured()) {
51
+ this.error('API not configured. Set GUT_API_ENDPOINT and GUT_TENANT_ID environment variables.');
52
+ }
53
+ try {
54
+ const response = await ticketService.listTickets({
55
+ status: flags.status,
56
+ limit: flags.limit
57
+ });
58
+ if (flags.json) {
59
+ this.log(JSON.stringify(response.tickets, null, 2));
60
+ return;
61
+ }
62
+ if (response.tickets.length === 0) {
63
+ this.log('No tickets found');
64
+ return;
65
+ }
66
+ // Create table
67
+ const table = new Table({
68
+ head: [
69
+ chalk.bold('ID'),
70
+ chalk.bold('Status'),
71
+ chalk.bold('Priority'),
72
+ chalk.bold('Summary'),
73
+ chalk.bold('Confidence'),
74
+ chalk.bold('Branch')
75
+ ],
76
+ colWidths: [15, 18, 10, 40, 12, 30]
77
+ });
78
+ for (const ticket of response.tickets) {
79
+ const statusText = `${getStatusEmoji(ticket.status)} ${ticket.status}`;
80
+ const priorityText = `${getPriorityEmoji(ticket.priority)} ${ticket.priority}`;
81
+ const confidenceText = this.formatConfidence(ticket.confidence);
82
+ const summary = ticket.summary.length > 37
83
+ ? ticket.summary.slice(0, 34) + '...'
84
+ : ticket.summary;
85
+ const branch = ticket.branch.length > 27
86
+ ? '...' + ticket.branch.slice(-24)
87
+ : ticket.branch;
88
+ table.push([
89
+ ticket.ticketId,
90
+ statusText,
91
+ priorityText,
92
+ summary,
93
+ confidenceText,
94
+ branch
95
+ ]);
96
+ }
97
+ this.log('\n' + table.toString());
98
+ this.log(`\nShowing ${response.tickets.length} ticket(s)`);
99
+ if (response.nextToken) {
100
+ this.log(chalk.dim('More tickets available. Use --limit to fetch more.'));
101
+ }
102
+ }
103
+ catch (error) {
104
+ const message = error instanceof Error ? error.message : String(error);
105
+ this.error(`Failed to list tickets: ${message}`);
106
+ }
107
+ }
108
+ formatConfidence(confidence) {
109
+ if (confidence >= 90) {
110
+ return chalk.green(`${confidence}%`);
111
+ }
112
+ if (confidence >= 70) {
113
+ return chalk.yellow(`${confidence}%`);
114
+ }
115
+ if (confidence >= 50) {
116
+ return chalk.hex('#FFA500')(`${confidence}%`); // Orange
117
+ }
118
+ return chalk.red(`${confidence}%`);
119
+ }
120
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class TicketSync extends BaseCommand {
3
+ static args: {
4
+ ticketId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ direction: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ protected get requiresInit(): boolean;
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,85 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { BaseCommand } from '../../base-command.js';
5
+ import { TicketService } from '../../services/ticket.service.js';
6
+ export default class TicketSync extends BaseCommand {
7
+ static args = {
8
+ ticketId: Args.string({
9
+ description: 'ticket ID to sync',
10
+ name: 'ticketId',
11
+ required: true
12
+ })
13
+ };
14
+ static description = 'Sync ticket state with external source (JIRA, GitHub, etc.)';
15
+ static examples = [
16
+ '<%= config.bin %> <%= command.id %> PROJ-1234',
17
+ '<%= config.bin %> <%= command.id %> PROJ-1234 --direction push',
18
+ '<%= config.bin %> <%= command.id %> PROJ-1234 --direction pull'
19
+ ];
20
+ static flags = {
21
+ direction: Flags.string({
22
+ char: 'd',
23
+ default: 'push',
24
+ description: 'sync direction (push: gut -> source, pull: source -> gut)',
25
+ options: ['push', 'pull']
26
+ }),
27
+ json: Flags.boolean({
28
+ char: 'j',
29
+ description: 'output as JSON'
30
+ })
31
+ };
32
+ get requiresInit() {
33
+ return false;
34
+ }
35
+ async run() {
36
+ const { args, flags } = await this.parse(TicketSync);
37
+ const ticketService = new TicketService(this.configService);
38
+ if (!ticketService.isConfigured()) {
39
+ this.error('API not configured. Set GUT_API_ENDPOINT and GUT_TENANT_ID environment variables.');
40
+ }
41
+ const direction = flags.direction;
42
+ const directionEmoji = direction === 'push' ? '⬆️' : '⬇️';
43
+ const directionText = direction === 'push'
44
+ ? 'gut → source'
45
+ : 'source → gut';
46
+ const spinner = ora(`${directionEmoji} Syncing ticket (${directionText})...`).start();
47
+ try {
48
+ const response = await ticketService.syncTicket(args.ticketId, direction);
49
+ if (flags.json) {
50
+ spinner.stop();
51
+ this.log(JSON.stringify(response, null, 2));
52
+ return;
53
+ }
54
+ if (response.synced) {
55
+ spinner.succeed('Sync completed');
56
+ }
57
+ else {
58
+ spinner.warn('Sync completed with warnings');
59
+ }
60
+ this.log('');
61
+ this.log(chalk.bold(`📋 Ticket: ${response.ticketId}`));
62
+ this.log(` 🔗 Source: ${response.source.type}`);
63
+ this.log(` 🌐 URL: ${response.source.externalUrl}`);
64
+ if (response.actions.length > 0) {
65
+ this.log('');
66
+ this.log(chalk.bold('📝 Actions performed:'));
67
+ for (const action of response.actions) {
68
+ const isError = action.toLowerCase().includes('error');
69
+ const icon = isError ? chalk.red('✗') : chalk.green('✓');
70
+ this.log(` ${icon} ${action}`);
71
+ }
72
+ }
73
+ else {
74
+ this.log('');
75
+ this.log(chalk.dim(' No actions performed'));
76
+ }
77
+ this.log('');
78
+ }
79
+ catch (error) {
80
+ spinner.fail('Sync failed');
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ this.error(`Failed to sync ticket: ${message}`);
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class TicketUpdate extends BaseCommand {
3
+ static args: {
4
+ ticketId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ confidence: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ phase: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ protected get requiresInit(): boolean;
15
+ run(): Promise<void>;
16
+ private formatConfidence;
17
+ }