@hyperdrive.bot/gut 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ $ npm install -g @hyperdrive.bot/gut
18
18
  $ gut COMMAND
19
19
  running command...
20
20
  $ gut (--version)
21
- @hyperdrive.bot/gut/0.1.10 linux-x64 node-v22.22.0
21
+ @hyperdrive.bot/gut/0.1.12 linux-x64 node-v22.22.1
22
22
  $ gut --help [COMMAND]
23
23
  USAGE
24
24
  $ gut COMMAND
@@ -30,6 +30,9 @@ USAGE
30
30
  * [`gut add [PATH]`](#gut-add-path)
31
31
  * [`gut affected [ENTITY]`](#gut-affected-entity)
32
32
  * [`gut audit`](#gut-audit)
33
+ * [`gut auth login`](#gut-auth-login)
34
+ * [`gut auth logout`](#gut-auth-logout)
35
+ * [`gut auth status`](#gut-auth-status)
33
36
  * [`gut back`](#gut-back)
34
37
  * [`gut checkout BRANCH`](#gut-checkout-branch)
35
38
  * [`gut commit`](#gut-commit)
@@ -58,6 +61,7 @@ USAGE
58
61
  * [`gut status`](#gut-status)
59
62
  * [`gut sync`](#gut-sync)
60
63
  * [`gut ticket`](#gut-ticket)
64
+ * [`gut ticket config`](#gut-ticket-config)
61
65
  * [`gut ticket focus TICKETID`](#gut-ticket-focus-ticketid)
62
66
  * [`gut ticket get TICKETID`](#gut-ticket-get-ticketid)
63
67
  * [`gut ticket hint [HINT] TICKETID`](#gut-ticket-hint-hint-ticketid)
@@ -67,6 +71,7 @@ USAGE
67
71
  * [`gut unfocus`](#gut-unfocus)
68
72
  * [`gut used-by [ENTITY]`](#gut-used-by-entity)
69
73
  * [`gut workspace ACTION`](#gut-workspace-action)
74
+ * [`gut worktree create NAME`](#gut-worktree-create-name)
70
75
 
71
76
  ## `gut add [PATH]`
72
77
 
@@ -155,6 +160,64 @@ EXAMPLES
155
160
  $ gut audit --compliance
156
161
  ```
157
162
 
163
+ ## `gut auth login`
164
+
165
+ Authenticate with Gut using OAuth 2.0 PKCE flow
166
+
167
+ ```
168
+ USAGE
169
+ $ gut auth login [-d <value>] [-p <value>]
170
+
171
+ FLAGS
172
+ -d, --domain=<value> Tenant domain (e.g., acme.hyperdrive.bot)
173
+ -p, --port=<value> [default: 8766] Local callback server port
174
+
175
+ DESCRIPTION
176
+ Authenticate with Gut using OAuth 2.0 PKCE flow
177
+
178
+ EXAMPLES
179
+ $ gut auth login
180
+
181
+ $ gut auth login --domain acme.hyperdrive.bot
182
+
183
+ $ gut auth login --port 9876
184
+ ```
185
+
186
+ ## `gut auth logout`
187
+
188
+ Remove stored credentials and logout
189
+
190
+ ```
191
+ USAGE
192
+ $ gut auth logout
193
+
194
+ DESCRIPTION
195
+ Remove stored credentials and logout
196
+
197
+ EXAMPLES
198
+ $ gut auth logout
199
+ ```
200
+
201
+ ## `gut auth status`
202
+
203
+ Show current authentication status
204
+
205
+ ```
206
+ USAGE
207
+ $ gut auth status [-v]
208
+
209
+ FLAGS
210
+ -v, --verbose Show detailed credential information
211
+
212
+ DESCRIPTION
213
+ Show current authentication status
214
+
215
+ EXAMPLES
216
+ $ gut auth status
217
+
218
+ $ gut auth status --verbose
219
+ ```
220
+
158
221
  ## `gut back`
159
222
 
160
223
  Navigate back to the previous focus
@@ -310,7 +373,7 @@ ARGUMENTS
310
373
  NAME Name of the entity to clone
311
374
 
312
375
  FLAGS
313
- -b, --branch=<value> [default: main] Branch to clone
376
+ -b, --branch=<value> Branch to clone (auto-detects workspace branch, falls back to master)
314
377
  -d, --depth=<value> Create a shallow clone with specified depth
315
378
  -f, --force Force clone even if directory exists
316
379
  -p, --path=<value> Custom path to clone to (relative to workspace)
@@ -335,7 +398,7 @@ USAGE
335
398
  $ gut entity clone-all [-b <value>] [-d <value>] [-f] [-p] [--skip-existing]
336
399
 
337
400
  FLAGS
338
- -b, --branch=<value> [default: main] Branch to clone for all entities
401
+ -b, --branch=<value> Branch to clone for all entities (auto-detects workspace branch, falls back to master)
339
402
  -d, --depth=<value> Create shallow clones with specified depth
340
403
  -f, --force Force clone even if directories exist
341
404
  -p, --parallel Clone entities in parallel
@@ -842,6 +905,31 @@ EXAMPLES
842
905
  $ gut ticket update PROJ-1234 --status in_progress
843
906
  ```
844
907
 
908
+ ## `gut ticket config`
909
+
910
+ Configure ticket source (JIRA, GitHub, etc.) for this tenant
911
+
912
+ ```
913
+ USAGE
914
+ $ gut ticket config [--secret-arn <value>] [-j] [--show] [--type jira|github|linear] [--url <value>]
915
+
916
+ FLAGS
917
+ -j, --json output as JSON
918
+ --secret-arn=<value> AWS Secrets Manager ARN for credentials
919
+ --show show current configuration
920
+ --type=<option> source type
921
+ <options: jira|github|linear>
922
+ --url=<value> base URL (e.g., https://company.atlassian.net)
923
+
924
+ DESCRIPTION
925
+ Configure ticket source (JIRA, GitHub, etc.) for this tenant
926
+
927
+ EXAMPLES
928
+ $ gut ticket config --show
929
+
930
+ $ gut ticket config --type jira --url https://company.atlassian.net --secret-arn arn:aws:secretsmanager:...
931
+ ```
932
+
845
933
  ## `gut ticket focus TICKETID`
846
934
 
847
935
  Focus on a ticket - downloads manifest and clones required entities
@@ -959,25 +1047,35 @@ Sync ticket state with external source (JIRA, GitHub, etc.)
959
1047
 
960
1048
  ```
961
1049
  USAGE
962
- $ gut ticket sync TICKETID [-d push|pull] [-j]
1050
+ $ gut ticket sync TICKETID [-d push|pull] [-j] [--no-enrich]
963
1051
 
964
1052
  ARGUMENTS
965
- TICKETID ticket ID to sync
1053
+ TICKETID ticket ID to sync (e.g., PROJ-1234)
966
1054
 
967
1055
  FLAGS
968
1056
  -d, --direction=<option> [default: push] sync direction (push: gut -> source, pull: source -> gut)
969
1057
  <options: push|pull>
970
1058
  -j, --json output as JSON
1059
+ --no-enrich skip enrichment queue when pulling new tickets
971
1060
 
972
1061
  DESCRIPTION
973
1062
  Sync ticket state with external source (JIRA, GitHub, etc.)
974
1063
 
1064
+ For pull direction:
1065
+ - If ticket exists in gut: updates from source
1066
+ - If ticket doesn't exist: creates it and queues enrichment
1067
+
1068
+ For push direction:
1069
+ - Updates source system with gut ticket state
1070
+
975
1071
  EXAMPLES
976
1072
  $ gut ticket sync PROJ-1234
977
1073
 
978
1074
  $ gut ticket sync PROJ-1234 --direction push
979
1075
 
980
1076
  $ gut ticket sync PROJ-1234 --direction pull
1077
+
1078
+ $ gut ticket sync PROJ-1234 --direction pull --no-enrich
981
1079
  ```
982
1080
 
983
1081
  ## `gut ticket update TICKETID`
@@ -1075,4 +1173,31 @@ EXAMPLES
1075
1173
 
1076
1174
  $ gut workspace generate-metadata
1077
1175
  ```
1176
+
1177
+ ## `gut worktree create NAME`
1178
+
1179
+ Create mirrored worktrees for super-repo and focused entities
1180
+
1181
+ ```
1182
+ USAGE
1183
+ $ gut worktree create NAME [--base-dir <value>] [--from <value>] [--install]
1184
+
1185
+ ARGUMENTS
1186
+ NAME Branch name for the worktree
1187
+
1188
+ FLAGS
1189
+ --base-dir=<value> [default: /tmp/gut-worktrees] Root directory for worktrees
1190
+ --from=<value> Base branch to create from (defaults to current branch)
1191
+ --install Run pnpm install after creation
1192
+
1193
+ DESCRIPTION
1194
+ Create mirrored worktrees for super-repo and focused entities
1195
+
1196
+ EXAMPLES
1197
+ $ gut worktree create workflow/deploy-batch
1198
+
1199
+ $ gut worktree create workflow/deploy-batch --from master --install
1200
+
1201
+ $ gut worktree create feature/story-42 --base-dir /home/user/worktrees
1202
+ ```
1078
1203
  <!-- commandsstop -->
@@ -5,12 +5,14 @@ import { EntityService } from './services/entity.service.js';
5
5
  import { FocusService } from './services/focus.service.js';
6
6
  import { GitService } from './services/git.service.js';
7
7
  import { TicketService } from './services/ticket.service.js';
8
+ import { WorktreeService } from './services/worktree.service.js';
8
9
  export declare abstract class BaseCommand extends Command {
9
10
  protected configService: ConfigService;
10
11
  protected entityService: EntityService;
11
12
  protected focusService: FocusService;
12
13
  protected gitService: GitService;
13
14
  protected ticketService: TicketService;
15
+ protected worktreeService: WorktreeService;
14
16
  protected get requiresInit(): boolean;
15
17
  protected formatPath(path: string): string;
16
18
  protected getStatusIcon(hasChanges: boolean): string;
@@ -4,6 +4,7 @@ import { EntityService } from './services/entity.service.js';
4
4
  import { FocusService } from './services/focus.service.js';
5
5
  import { GitService } from './services/git.service.js';
6
6
  import { TicketService } from './services/ticket.service.js';
7
+ import { WorktreeService } from './services/worktree.service.js';
7
8
  const ENTITY_TYPE_EMOJI = {
8
9
  client: '🏢',
9
10
  company: '🏛️',
@@ -22,6 +23,7 @@ export class BaseCommand extends Command {
22
23
  focusService;
23
24
  gitService;
24
25
  ticketService;
26
+ worktreeService;
25
27
  get requiresInit() {
26
28
  // Override in commands that don't require initialization
27
29
  return true;
@@ -47,6 +49,7 @@ export class BaseCommand extends Command {
47
49
  this.focusService = new FocusService(this.configService);
48
50
  this.gitService = new GitService();
49
51
  this.ticketService = new TicketService(this.configService);
52
+ this.worktreeService = new WorktreeService(this.configService, this.gitService, this.focusService);
50
53
  // Check workspace initialization for commands that require it
51
54
  if (this.requiresInit && !this.configService.isInitialized()) {
52
55
  this.error('Workspace not initialized. Run "gut init" first.');
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Login extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ port: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,103 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import open from 'open';
5
+ import ora from 'ora';
6
+ import { buildAuthUrl, buildStoredCredentials, exchangeCodeForTokens, generateCodeChallenge, generateCodeVerifier, getAWSCredentialsFromIdentityPool, getCredentialsPath, saveCredentials, startCallbackServer, } from '@hyperdrive.bot/cli-auth';
7
+ import { createTenantService } from '../../services/tenant.service.js';
8
+ export default class Login extends Command {
9
+ static description = 'Authenticate with Gut using OAuth 2.0 PKCE flow';
10
+ static examples = [
11
+ '<%= config.bin %> <%= command.id %>',
12
+ '<%= config.bin %> <%= command.id %> --domain acme.hyperdrive.bot',
13
+ '<%= config.bin %> <%= command.id %> --port 9876',
14
+ ];
15
+ static flags = {
16
+ domain: Flags.string({
17
+ char: 'd',
18
+ description: 'Tenant domain (e.g., acme.hyperdrive.bot)',
19
+ }),
20
+ port: Flags.integer({
21
+ char: 'p',
22
+ default: 8766, // Different from hyperdrive's 8765 to avoid conflicts
23
+ description: 'Local callback server port',
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(Login);
28
+ const tenantService = createTenantService(flags.domain);
29
+ this.log(chalk.blue('🚀 Starting Gut authentication...'));
30
+ this.log('');
31
+ // Step 1: Resolve tenant configuration from bootstrap
32
+ let tenantConfig;
33
+ const spinner = ora('Fetching tenant configuration...').start();
34
+ try {
35
+ let tenantDomain = flags.domain;
36
+ if (!tenantDomain) {
37
+ spinner.stop();
38
+ const savedTenantDomain = tenantService.getTenantDomain();
39
+ const answers = await inquirer.prompt([
40
+ {
41
+ default: savedTenantDomain || undefined,
42
+ message: 'Enter your tenant domain:',
43
+ name: 'tenant',
44
+ type: 'input',
45
+ validate: (input) => input.trim().length > 0 || 'Tenant domain is required',
46
+ },
47
+ ]);
48
+ tenantDomain = answers.tenant;
49
+ spinner.start('Fetching tenant configuration...');
50
+ }
51
+ tenantConfig = await tenantService.fetchTenantConfig(tenantDomain);
52
+ spinner.succeed(chalk.green(`Tenant found: ${tenantConfig.displayName}`));
53
+ }
54
+ catch (error) {
55
+ spinner.fail(chalk.red('Failed to fetch tenant configuration'));
56
+ this.log('');
57
+ const errorMessage = error instanceof Error ? error.message : String(error);
58
+ this.error(errorMessage + '\n\n' +
59
+ chalk.yellow('💡 Tips:\n') +
60
+ chalk.gray(' - Check your tenant domain spelling\n') +
61
+ chalk.gray(' - Set GUT_TENANT_DOMAIN environment variable\n'));
62
+ }
63
+ try {
64
+ // Generate PKCE parameters
65
+ const codeVerifier = generateCodeVerifier();
66
+ const codeChallenge = generateCodeChallenge(codeVerifier);
67
+ // Start local callback server
68
+ const authCodePromise = startCallbackServer(flags.port, 300000, 'Gut');
69
+ // Open browser for authentication
70
+ const authUrl = buildAuthUrl(tenantConfig, codeChallenge, flags.port);
71
+ this.log(chalk.yellow('📱 Opening browser for authentication...'));
72
+ this.log(chalk.gray(`If browser doesn't open, visit: ${authUrl}`));
73
+ this.log('');
74
+ await open(authUrl);
75
+ spinner.start('Waiting for authentication...');
76
+ // Wait for callback with auth code
77
+ const code = await authCodePromise;
78
+ spinner.succeed(chalk.green('Authentication callback received!'));
79
+ // Exchange code for tokens
80
+ spinner.start('Exchanging authorization code for tokens...');
81
+ const tokens = await exchangeCodeForTokens(tenantConfig, code, codeVerifier, flags.port);
82
+ spinner.succeed(chalk.green('Tokens obtained successfully!'));
83
+ // Get AWS credentials from Cognito Identity Pool
84
+ spinner.start('Obtaining AWS credentials...');
85
+ const awsCredentials = await getAWSCredentialsFromIdentityPool(tenantConfig, tokens.id_token);
86
+ spinner.succeed(chalk.green('AWS credentials obtained!'));
87
+ // Save credentials
88
+ spinner.start('Saving credentials...');
89
+ const storedCredentials = buildStoredCredentials(tokens, awsCredentials, tenantConfig);
90
+ saveCredentials(storedCredentials, tenantConfig.tenantDomain, 'gut');
91
+ spinner.succeed(chalk.green('Credentials saved!'));
92
+ this.log('');
93
+ this.log(chalk.green('✅ Successfully authenticated!'));
94
+ this.log(chalk.gray('You can now use all Gut CLI commands.'));
95
+ this.log('');
96
+ this.log(chalk.gray(`✓ Credentials saved to ${getCredentialsPath(tenantConfig.tenantDomain, 'gut')}`));
97
+ this.log(chalk.gray('💡 Credentials refresh automatically when needed.'));
98
+ }
99
+ catch (error) {
100
+ this.error(chalk.red(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`));
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Logout extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,39 @@
1
+ import { Command } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import { AuthService } from '../../services/auth.service.js';
5
+ export default class Logout extends Command {
6
+ static description = 'Remove stored credentials and logout';
7
+ static examples = ['<%= config.bin %> <%= command.id %>'];
8
+ async run() {
9
+ const authService = new AuthService();
10
+ // Check if credentials exist
11
+ const credentials = authService.loadCredentials();
12
+ if (!credentials) {
13
+ this.log(chalk.yellow('⚠️ No active session found'));
14
+ return;
15
+ }
16
+ // Confirm logout
17
+ const { confirm } = await inquirer.prompt([
18
+ {
19
+ default: false,
20
+ message: 'Are you sure you want to logout?',
21
+ name: 'confirm',
22
+ type: 'confirm',
23
+ },
24
+ ]);
25
+ if (!confirm) {
26
+ this.log(chalk.gray('Logout cancelled'));
27
+ return;
28
+ }
29
+ try {
30
+ authService.clearCredentials();
31
+ this.log('');
32
+ this.log(chalk.green('✅ Successfully logged out!'));
33
+ this.log(chalk.gray('Run `gut auth login` to authenticate again.'));
34
+ }
35
+ catch (error) {
36
+ this.error(chalk.red(`Logout failed: ${error instanceof Error ? error.message : String(error)}`));
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Status extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,87 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { AuthService } from '../../services/auth.service.js';
4
+ export default class Status extends Command {
5
+ static description = 'Show current authentication status';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ '<%= config.bin %> <%= command.id %> --verbose',
9
+ ];
10
+ static flags = {
11
+ verbose: Flags.boolean({
12
+ char: 'v',
13
+ default: false,
14
+ description: 'Show detailed credential information',
15
+ }),
16
+ };
17
+ async run() {
18
+ const { flags } = await this.parse(Status);
19
+ const authService = new AuthService();
20
+ const credentials = authService.loadCredentials();
21
+ if (!credentials) {
22
+ this.log(chalk.yellow('⚠️ Not authenticated'));
23
+ this.log('');
24
+ this.log(chalk.gray('Run `gut auth login` to authenticate.'));
25
+ return;
26
+ }
27
+ const isExpired = authService.isExpired(credentials);
28
+ const needsRefresh = authService.needsRefresh(credentials);
29
+ this.log(chalk.blue('🔐 Authentication Status'));
30
+ this.log('');
31
+ // Status
32
+ if (isExpired) {
33
+ this.log(chalk.red('Status: ✗ Expired'));
34
+ }
35
+ else if (needsRefresh) {
36
+ this.log(chalk.yellow('Status: ⚠ Expiring soon (will auto-refresh)'));
37
+ }
38
+ else {
39
+ this.log(chalk.green('Status: ✓ Authenticated'));
40
+ }
41
+ this.log('');
42
+ // Basic info
43
+ this.log(chalk.white('Tenant:'), chalk.cyan(credentials.tenantDomain));
44
+ this.log(chalk.white('Tenant ID:'), chalk.gray(credentials.tenantId));
45
+ this.log(chalk.white('Region:'), chalk.gray(credentials.region));
46
+ // Expiration
47
+ const expiration = new Date(credentials.awsCredentials.expiration);
48
+ const now = new Date();
49
+ const timeRemaining = expiration.getTime() - now.getTime();
50
+ if (timeRemaining > 0) {
51
+ const minutes = Math.floor(timeRemaining / (1000 * 60));
52
+ const hours = Math.floor(minutes / 60);
53
+ const remainingMinutes = minutes % 60;
54
+ if (hours > 0) {
55
+ this.log(chalk.white('Expires in:'), chalk.gray(`${hours}h ${remainingMinutes}m`));
56
+ }
57
+ else {
58
+ this.log(chalk.white('Expires in:'), chalk.gray(`${remainingMinutes}m`));
59
+ }
60
+ }
61
+ else {
62
+ this.log(chalk.white('Expired:'), chalk.red('Yes'));
63
+ }
64
+ if (flags.verbose) {
65
+ this.log('');
66
+ this.log(chalk.blue('📋 Detailed Information'));
67
+ this.log('');
68
+ this.log(chalk.white('API URL:'), chalk.gray(credentials.apiUrl));
69
+ this.log(chalk.white('Obtained at:'), chalk.gray(credentials.obtainedAt));
70
+ this.log(chalk.white('Expiration:'), chalk.gray(expiration.toISOString()));
71
+ if (credentials.cognitoConfig) {
72
+ this.log('');
73
+ this.log(chalk.white('Cognito Client ID:'), chalk.gray(credentials.cognitoConfig.clientId));
74
+ this.log(chalk.white('User Pool ID:'), chalk.gray(credentials.cognitoConfig.userPoolId));
75
+ }
76
+ this.log('');
77
+ this.log(chalk.white('Credentials file:'), chalk.gray(authService.getCredentialsDir()));
78
+ }
79
+ this.log('');
80
+ if (isExpired) {
81
+ this.log(chalk.yellow('💡 Run `gut auth login` to re-authenticate.'));
82
+ }
83
+ else {
84
+ this.log(chalk.gray('💡 Credentials refresh automatically when needed.'));
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class TicketConfig extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'secret-arn': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ show: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,22 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base-command.js';
3
+ export default class TicketConfig extends BaseCommand {
4
+ static description = 'Configure ticket source (JIRA, GitHub, etc.) for this tenant';
5
+ static examples = [
6
+ '<%= config.bin %> ticket config --show',
7
+ '<%= config.bin %> ticket config --type jira --url https://company.atlassian.net --secret-arn arn:aws:secretsmanager:...',
8
+ ];
9
+ static flags = {
10
+ 'secret-arn': Flags.string({ description: 'AWS Secrets Manager ARN for credentials' }),
11
+ json: Flags.boolean({ char: 'j', description: 'output as JSON' }),
12
+ show: Flags.boolean({ description: 'show current configuration' }),
13
+ type: Flags.string({
14
+ description: 'source type',
15
+ options: ['jira', 'github', 'linear'],
16
+ }),
17
+ url: Flags.string({ description: 'base URL (e.g., https://company.atlassian.net)' }),
18
+ };
19
+ async run() {
20
+ this.error('ticket config is not yet implemented — API service pending');
21
+ }
22
+ }
@@ -8,6 +8,7 @@ export default class TicketSync extends BaseCommand {
8
8
  static flags: {
9
9
  direction: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'no-enrich': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  protected get requiresInit(): boolean;
13
14
  run(): Promise<void>;