@proletariat/cli 0.3.83 → 0.3.84

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.
@@ -19,4 +19,13 @@ export default class PRMerge extends PMOCommand {
19
19
  private hasPMO;
20
20
  init(): Promise<void>;
21
21
  execute(): Promise<void>;
22
+ /**
23
+ * Resolve the linked ticket for a merged PR.
24
+ *
25
+ * Lookup order:
26
+ * 1. PR metadata (pr_number / pr_url) on tickets
27
+ * 2. agent_work table (branch → ticket_id)
28
+ * 3. PR branch name parsing (extract ticket ID from conventional branch format)
29
+ */
30
+ private resolveLinkedTicket;
22
31
  }
@@ -1,14 +1,13 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { PMOCommand, pmoBaseFlags, } from '../../lib/pmo/index.js';
3
3
  import { getWorkColumnSetting, findColumnByName } from '../../lib/pmo/utils.js';
4
- import Database from 'better-sqlite3';
5
- import * as path from 'node:path';
6
- import { getWorkspaceInfo } from '../../lib/agents/commands.js';
7
4
  import { styles } from '../../lib/styles.js';
8
5
  import { isGHInstalled, isGHAuthenticated, getPRByNumber, listOpenPRs, mergePR, getGitHubRepo, } from '../../lib/pr/index.js';
9
6
  import { shouldOutputJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
10
7
  import { FlagResolver } from '../../lib/flags/index.js';
11
8
  import { getEventBus } from '../../lib/events/event-bus.js';
9
+ import { validateBranchName } from '../../lib/branch/index.js';
10
+ import { PMO_TABLES } from '../../lib/pmo/schema.js';
12
11
  export default class PRMerge extends PMOCommand {
13
12
  static description = 'Merge a GitHub pull request by number';
14
13
  static examples = [
@@ -110,69 +109,52 @@ export default class PRMerge extends PMOCommand {
110
109
  if (!result.success) {
111
110
  return handleError('MERGE_FAILED', `Failed to merge PR #${prNumber}: ${result.error}`);
112
111
  }
113
- // Update ticket metadata if PR was linked and emit merge event for outbound sync
112
+ // After successful merge, resolve the linked ticket and move it to Done
114
113
  let linkedTicketId;
114
+ let ticketMovedToDone = false;
115
+ let ticketTransitionProvider;
115
116
  if (this.hasPMO) {
116
117
  try {
117
- const allTickets = await this.storage.listTickets(flags.project);
118
- // Find linked ticket by pr_number or by extracting number from pr_url
119
- const linkedTicket = allTickets.find(t => t.metadata?.pr_number === String(prNumber) ||
120
- t.metadata?.pr_url?.endsWith(`/pull/${prNumber}`) ||
121
- t.metadata?.pr_url?.endsWith(`/${prNumber}`));
118
+ const linkedTicket = await this.resolveLinkedTicket(prNumber, prInfo.headBranch, flags.project);
122
119
  if (linkedTicket) {
123
120
  linkedTicketId = linkedTicket.id;
121
+ // Update PR state metadata
124
122
  await this.storage.updateTicket(linkedTicket.id, {
125
123
  metadata: {
126
124
  ...linkedTicket.metadata,
127
125
  pr_state: 'MERGED',
128
126
  },
129
127
  });
130
- // Move ticket to Done column in local PMO and external provider
128
+ // Move ticket to Done column
131
129
  try {
132
- let workspaceInfo;
133
- try {
134
- workspaceInfo = getWorkspaceInfo();
135
- }
136
- catch {
137
- workspaceInfo = null;
138
- }
139
- const dbPath = workspaceInfo
140
- ? path.join(workspaceInfo.path, '.proletariat', 'workspace.db')
130
+ const db = this.storage.getDatabase();
131
+ const targetColumnName = getWorkColumnSetting(db, 'done');
132
+ const board = linkedTicket.projectId
133
+ ? await this.storage.getProjectBoard(linkedTicket.projectId)
141
134
  : null;
142
- if (dbPath) {
143
- const db = new Database(dbPath);
144
- try {
145
- const targetColumnName = getWorkColumnSetting(db, 'done');
146
- const board = linkedTicket.projectId
147
- ? await this.storage.getProjectBoard(linkedTicket.projectId)
148
- : null;
149
- const columnNames = board ? board.columns.map(col => col.name) : [];
150
- const doneColumn = findColumnByName(columnNames, targetColumnName);
151
- if (doneColumn && linkedTicket.projectId) {
152
- // Move in local PMO
153
- await this.storage.moveTicket(linkedTicket.projectId, linkedTicket.id, doneColumn);
154
- // Sync to external provider (e.g., Linear)
155
- const provider = await this.resolveTicketProvider(linkedTicket.id, linkedTicket.projectId);
156
- if (provider.name !== 'pmo') {
157
- const moveResult = await provider.moveTicket(linkedTicket.id, doneColumn);
158
- if (moveResult.success) {
159
- this.log(styles.muted(` Synced to ${moveResult.provider}: ${doneColumn}`));
160
- }
161
- }
135
+ const columnNames = board ? board.columns.map(col => col.name) : [];
136
+ const doneColumn = findColumnByName(columnNames, targetColumnName);
137
+ if (doneColumn && linkedTicket.projectId) {
138
+ // Move in local PMO
139
+ await this.storage.moveTicket(linkedTicket.projectId, linkedTicket.id, doneColumn);
140
+ ticketMovedToDone = true;
141
+ // Sync to external provider (e.g., Linear)
142
+ const provider = await this.resolveTicketProvider(linkedTicket.id, linkedTicket.projectId);
143
+ if (provider.name !== 'pmo') {
144
+ const moveResult = await provider.moveTicket(linkedTicket.id, doneColumn);
145
+ if (moveResult.success) {
146
+ ticketTransitionProvider = moveResult.provider;
162
147
  }
163
148
  }
164
- finally {
165
- db.close();
166
- }
167
149
  }
168
150
  }
169
- catch {
170
- // Non-critical - don't fail the merge if ticket transition fails
151
+ catch (err) {
152
+ this.warn(`Failed to move ticket ${linkedTicket.id} to Done: ${err instanceof Error ? err.message : err}`);
171
153
  }
172
154
  }
173
155
  }
174
- catch {
175
- // Non-critical - don't fail the merge if PMO update fails
156
+ catch (err) {
157
+ this.warn(`Failed to resolve linked ticket: ${err instanceof Error ? err.message : err}`);
176
158
  }
177
159
  }
178
160
  // Emit work:pr_merged event for outbound sync (e.g., Linear auto-transition)
@@ -202,7 +184,8 @@ export default class PRMerge extends PMOCommand {
202
184
  method,
203
185
  branchDeleted: flags['delete-branch'],
204
186
  linkedTicket: linkedTicketId ?? null,
205
- linearSyncEmitted: !!linkedTicketId,
187
+ ticketMovedToDone,
188
+ ticketTransitionProvider: ticketTransitionProvider ?? null,
206
189
  }, createMetadata('pr merge', flags));
207
190
  return;
208
191
  }
@@ -214,8 +197,55 @@ export default class PRMerge extends PMOCommand {
214
197
  this.log(styles.muted(` Branch ${prInfo.headBranch} deleted`));
215
198
  }
216
199
  if (linkedTicketId) {
217
- this.log(styles.muted(` Linear sync triggered for ${linkedTicketId}`));
200
+ this.log(styles.muted(` Ticket ${linkedTicketId} → Done`));
201
+ if (ticketTransitionProvider) {
202
+ this.log(styles.muted(` Synced to ${ticketTransitionProvider}`));
203
+ }
204
+ }
205
+ }
206
+ /**
207
+ * Resolve the linked ticket for a merged PR.
208
+ *
209
+ * Lookup order:
210
+ * 1. PR metadata (pr_number / pr_url) on tickets
211
+ * 2. agent_work table (branch → ticket_id)
212
+ * 3. PR branch name parsing (extract ticket ID from conventional branch format)
213
+ */
214
+ async resolveLinkedTicket(prNumber, headBranch, projectId) {
215
+ // 1. Find by PR metadata on tickets
216
+ const allTickets = await this.storage.listTickets(projectId);
217
+ const byMetadata = allTickets.find(t => t.metadata?.pr_number === String(prNumber) ||
218
+ t.metadata?.pr_url?.endsWith(`/pull/${prNumber}`) ||
219
+ t.metadata?.pr_url?.endsWith(`/${prNumber}`));
220
+ if (byMetadata)
221
+ return byMetadata;
222
+ // 2. Look up from agent_work table by branch name
223
+ try {
224
+ const db = this.storage.getDatabase();
225
+ const row = db.prepare(`SELECT ticket_id FROM ${PMO_TABLES.agent_work} WHERE branch = ? LIMIT 1`).get(headBranch);
226
+ if (row?.ticket_id) {
227
+ const ticket = await this.storage.getTicket(row.ticket_id);
228
+ if (ticket)
229
+ return ticket;
230
+ }
231
+ }
232
+ catch {
233
+ // agent_work lookup failed, continue to branch name parsing
234
+ }
235
+ // 3. Parse ticket ID from branch name (e.g., PRLT-1043/feat/owner/agent/desc)
236
+ const branchResult = validateBranchName(headBranch);
237
+ if (branchResult.valid && branchResult.parts?.ticketId) {
238
+ const ticketId = branchResult.parts.ticketId;
239
+ // Try direct lookup (works for TKT-xxx internal IDs)
240
+ const directTicket = await this.storage.getTicket(ticketId);
241
+ if (directTicket)
242
+ return directTicket;
243
+ // Search by external_key metadata (works for PRLT-xxx, LINEAR-xxx, etc.)
244
+ const byExternalKey = allTickets.find(t => t.metadata?.external_key === ticketId);
245
+ if (byExternalKey)
246
+ return byExternalKey;
218
247
  }
248
+ return null;
219
249
  }
220
250
  }
221
251
  //# sourceMappingURL=merge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../src/commands/pr/merge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../src/commands/pr/merge.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,13 @@ export default class TicketUpdate extends PMOCommand {
6
6
  ticketId: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'description-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
12
  priority: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
13
  category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ labels: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
16
  bulk: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
17
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
18
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -15,5 +20,9 @@ export default class TicketUpdate extends PMOCommand {
15
20
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
21
  };
17
22
  execute(): Promise<void>;
23
+ /**
24
+ * Interactive mode when no flags are provided - original priority/category selection flow
25
+ */
26
+ private executeInteractive;
18
27
  private executeBulk;
19
28
  }
@@ -1,16 +1,22 @@
1
+ import { readFileSync } from 'node:fs';
1
2
  import { Args, Flags } from '@oclif/core';
2
3
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
3
4
  import { getWorkspacePriorities } from '../../lib/pmo/utils.js';
4
5
  import { styles } from '../../lib/styles.js';
5
6
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
+ import { formatTicket } from '../../lib/mcp/helpers.js';
6
8
  export default class TicketUpdate extends PMOCommand {
7
- static description = 'Update priority/category for ticket(s)';
9
+ static description = 'Update ticket fields (title, description, priority, category, labels, status)';
8
10
  static examples = [
11
+ '<%= config.bin %> <%= command.id %> TKT-001 --title "New title"',
12
+ '<%= config.bin %> <%= command.id %> TKT-001 --description "Updated description"',
13
+ '<%= config.bin %> <%= command.id %> TKT-001 --description-file ./spec.md',
9
14
  '<%= config.bin %> <%= command.id %> TKT-001 --priority P1',
10
15
  '<%= config.bin %> <%= command.id %> TKT-001 --category bug',
11
- '<%= config.bin %> <%= command.id %> --bulk',
16
+ '<%= config.bin %> <%= command.id %> TKT-001 --labels frontend,urgent',
17
+ '<%= config.bin %> <%= command.id %> TKT-001 --status "In Progress"',
18
+ '<%= config.bin %> <%= command.id %> TKT-001 --title "Fix auth" --priority P0 --status "In Progress" --json',
12
19
  '<%= config.bin %> <%= command.id %> --bulk --priority P1',
13
- '<%= config.bin %> <%= command.id %> --json # Output choices as JSON',
14
20
  ];
15
21
  static args = {
16
22
  ticketId: Args.string({
@@ -20,13 +26,32 @@ export default class TicketUpdate extends PMOCommand {
20
26
  };
21
27
  static flags = {
22
28
  ...pmoBaseFlags,
29
+ title: Flags.string({
30
+ char: 't',
31
+ description: 'Set ticket title',
32
+ }),
33
+ description: Flags.string({
34
+ char: 'd',
35
+ description: 'Set ticket description',
36
+ }),
37
+ 'description-file': Flags.string({
38
+ description: 'Read ticket description from a file',
39
+ }),
23
40
  priority: Flags.string({
24
41
  char: 'p',
25
- description: 'Set priority (uses workspace priority scale)',
42
+ description: 'Set priority (uses workspace priority scale, "none" to clear)',
26
43
  }),
27
44
  category: Flags.string({
28
45
  char: 'c',
29
- description: 'Set category (e.g., feature, bug, refactor)',
46
+ description: 'Set category (e.g., feature, bug, refactor, "none" to clear)',
47
+ }),
48
+ labels: Flags.string({
49
+ char: 'l',
50
+ description: 'Set labels (comma-separated, replaces existing labels; use "" to clear)',
51
+ }),
52
+ status: Flags.string({
53
+ char: 's',
54
+ description: 'Set status/column (e.g., "In Progress", "Done")',
30
55
  }),
31
56
  bulk: Flags.boolean({
32
57
  char: 'b',
@@ -44,15 +69,22 @@ export default class TicketUpdate extends PMOCommand {
44
69
  const projectId = flags.project;
45
70
  // Check if JSON output mode is active
46
71
  const jsonMode = shouldOutputJson(flags);
47
- // Get all tickets
48
- const allTickets = await this.storage.listTickets(projectId);
49
- if (allTickets.length === 0) {
72
+ // Helper to handle errors in JSON mode
73
+ const handleError = (code, message) => {
50
74
  if (jsonMode) {
51
- outputErrorAsJson('NO_TICKETS', 'No tickets found.', createMetadata('ticket update', flags));
75
+ outputErrorAsJson(code, message, createMetadata('ticket update', flags));
52
76
  return;
53
77
  }
54
- this.log(styles.warning('No tickets found.'));
55
- return;
78
+ this.error(message);
79
+ };
80
+ // Validate --description and --description-file are not both provided
81
+ if (flags.description !== undefined && flags['description-file'] !== undefined) {
82
+ return handleError('INVALID_FLAGS', 'Cannot use both --description and --description-file.');
83
+ }
84
+ // Get all tickets
85
+ const allTickets = await this.storage.listTickets(projectId);
86
+ if (allTickets.length === 0) {
87
+ return handleError('NO_TICKETS', 'No tickets found.');
56
88
  }
57
89
  // Bulk mode
58
90
  if (flags.bulk) {
@@ -78,74 +110,145 @@ export default class TicketUpdate extends PMOCommand {
78
110
  // Get ticket
79
111
  const ticket = await this.storage.getTicket(ticketId);
80
112
  if (!ticket) {
81
- if (jsonMode) {
82
- outputErrorAsJson('TICKET_NOT_FOUND', `Ticket "${ticketId}" not found.`, createMetadata('ticket update', flags));
83
- return;
113
+ return handleError('TICKET_NOT_FOUND', `Ticket "${ticketId}" not found.`);
114
+ }
115
+ // Check if any flags were provided
116
+ const hasFlags = flags.title !== undefined || flags.description !== undefined ||
117
+ flags['description-file'] !== undefined || flags.priority !== undefined ||
118
+ flags.category !== undefined || flags.labels !== undefined ||
119
+ flags.status !== undefined;
120
+ if (!hasFlags) {
121
+ // No flags provided - fall back to interactive priority/category selection
122
+ await this.executeInteractive(ticketId, ticket, projectId, jsonMode, flags);
123
+ return;
124
+ }
125
+ // Build changes from flags
126
+ const changes = {};
127
+ const changedFields = [];
128
+ if (flags.title !== undefined) {
129
+ changes.title = flags.title;
130
+ changedFields.push(`Title: ${flags.title}`);
131
+ }
132
+ // Handle description (--description or --description-file)
133
+ if (flags['description-file'] !== undefined) {
134
+ try {
135
+ const content = readFileSync(flags['description-file'], 'utf-8');
136
+ changes.description = content;
137
+ changedFields.push(`Description: (from file ${flags['description-file']})`);
138
+ }
139
+ catch (error) {
140
+ return handleError('FILE_READ_ERROR', `Failed to read description file "${flags['description-file']}": ${error instanceof Error ? error.message : String(error)}`);
84
141
  }
85
- this.error(`Ticket "${ticketId}" not found.`);
86
142
  }
87
- // Determine what to update
88
- let updatePriority = flags.priority;
89
- let updateCategory = flags.category;
90
- if (!updatePriority && !updateCategory) {
91
- // Ask what to update
92
- const jsonModeConfig = jsonMode ? { flags, commandName: 'ticket update' } : null;
93
- const { updateType } = await this.prompt([{
143
+ else if (flags.description !== undefined) {
144
+ changes.description = flags.description;
145
+ changedFields.push(`Description: ${flags.description.length > 60 ? flags.description.slice(0, 60) + '...' : flags.description}`);
146
+ }
147
+ if (flags.priority !== undefined) {
148
+ changes.priority = flags.priority === 'none' ? null : flags.priority;
149
+ changedFields.push(`Priority: ${flags.priority === 'none' ? 'none' : flags.priority}`);
150
+ }
151
+ if (flags.category !== undefined) {
152
+ changes.category = flags.category === 'none' ? undefined : flags.category;
153
+ changedFields.push(`Category: ${flags.category === 'none' ? 'none' : flags.category}`);
154
+ }
155
+ if (flags.labels !== undefined) {
156
+ const labelList = flags.labels === '' ? [] : flags.labels.split(',').map(l => l.trim()).filter(Boolean);
157
+ changes.labels = labelList;
158
+ changedFields.push(`Labels: ${labelList.length > 0 ? labelList.join(', ') : 'none'}`);
159
+ }
160
+ // Apply field changes (everything except status)
161
+ if (Object.keys(changes).length > 0) {
162
+ await this.storage.updateTicket(ticketId, changes);
163
+ }
164
+ // Handle status change separately via provider
165
+ if (flags.status !== undefined) {
166
+ const provider = await this.resolveTicketProvider(ticketId, ticket.projectId || '');
167
+ const moveResult = await provider.moveTicket(ticketId, flags.status);
168
+ if (!moveResult.success) {
169
+ return handleError('STATUS_CHANGE_FAILED', `Failed to change status to "${flags.status}": ${moveResult.error}`);
170
+ }
171
+ changedFields.push(`Status: ${flags.status}`);
172
+ }
173
+ // Auto-export
174
+ await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
175
+ // Refresh ticket for output
176
+ const updatedTicket = await this.storage.getTicket(ticketId) || ticket;
177
+ // JSON output mode
178
+ if (jsonMode) {
179
+ this.log(JSON.stringify({
180
+ success: true,
181
+ ticket: formatTicket(updatedTicket),
182
+ }, null, 2));
183
+ return;
184
+ }
185
+ this.log(styles.success(`\n✅ Updated ticket ${styles.emphasis(ticketId)}`));
186
+ for (const field of changedFields) {
187
+ this.log(styles.muted(` ${field}`));
188
+ }
189
+ }
190
+ /**
191
+ * Interactive mode when no flags are provided - original priority/category selection flow
192
+ */
193
+ async executeInteractive(ticketId, ticket, projectId, jsonMode, flags) {
194
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'ticket update' } : null;
195
+ const { updateType } = await this.prompt([{
196
+ type: 'list',
197
+ name: 'updateType',
198
+ message: 'What would you like to update?',
199
+ choices: [
200
+ { name: 'Priority', value: 'priority', command: `prlt ticket update ${ticketId} --priority <P0|P1|P2|P3>${projectId ? ` -P ${projectId}` : ''} --json` },
201
+ { name: 'Category', value: 'category', command: `prlt ticket update ${ticketId} --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
202
+ { name: 'Both', value: 'both', command: `prlt ticket update ${ticketId} --priority <P0|P1|P2|P3> --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
203
+ ],
204
+ }], jsonModeConfig);
205
+ let updatePriority;
206
+ let updateCategory;
207
+ if (updateType === 'priority' || updateType === 'both') {
208
+ const db = this.storage.getDatabase();
209
+ const workspacePriorities = getWorkspacePriorities(db);
210
+ const { priority } = await this.prompt([{
94
211
  type: 'list',
95
- name: 'updateType',
96
- message: 'What would you like to update?',
212
+ name: 'priority',
213
+ message: 'Set priority to:',
97
214
  choices: [
98
- { name: 'Priority', value: 'priority', command: `prlt ticket update ${ticketId} --priority <P0|P1|P2|P3>${projectId ? ` -P ${projectId}` : ''} --json` },
99
- { name: 'Category', value: 'category', command: `prlt ticket update ${ticketId} --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
100
- { name: 'Both', value: 'both', command: `prlt ticket update ${ticketId} --priority <P0|P1|P2|P3> --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
215
+ { name: `(Keep existing: ${ticket.priority || 'none'})`, value: null, command: '' },
216
+ ...workspacePriorities.map(p => ({ name: p, value: p, command: `prlt ticket update ${ticketId} --priority ${p}${projectId ? ` -P ${projectId}` : ''} --json` })),
217
+ { name: 'None (clear priority)', value: '', command: `prlt ticket update ${ticketId} --priority none${projectId ? ` -P ${projectId}` : ''} --json` },
101
218
  ],
102
219
  }], jsonModeConfig);
103
- if (updateType === 'priority' || updateType === 'both') {
104
- const db = this.storage.getDatabase();
105
- const workspacePriorities = getWorkspacePriorities(db);
106
- const { priority } = await this.prompt([{
107
- type: 'list',
108
- name: 'priority',
109
- message: 'Set priority to:',
110
- choices: [
111
- { name: `(Keep existing: ${ticket.priority || 'none'})`, value: null, command: '' },
112
- ...workspacePriorities.map(p => ({ name: p, value: p, command: `prlt ticket update ${ticketId} --priority ${p}${projectId ? ` -P ${projectId}` : ''} --json` })),
113
- { name: 'None (clear priority)', value: '', command: `prlt ticket update ${ticketId} --priority none${projectId ? ` -P ${projectId}` : ''} --json` },
114
- ],
115
- }], jsonModeConfig);
116
- if (priority !== null) {
117
- updatePriority = priority;
118
- }
220
+ if (priority !== null) {
221
+ updatePriority = priority;
119
222
  }
120
- if (updateType === 'category' || updateType === 'both') {
121
- const { categoryChoice } = await this.prompt([{
122
- type: 'list',
123
- name: 'categoryChoice',
124
- message: 'Set category to:',
125
- choices: [
126
- { name: `(Keep existing: ${ticket.category || 'none'})`, value: null, command: '' },
127
- { name: 'feature', value: 'feature', command: `prlt ticket update ${ticketId} --category feature${projectId ? ` -P ${projectId}` : ''} --json` },
128
- { name: 'bug', value: 'bug', command: `prlt ticket update ${ticketId} --category bug${projectId ? ` -P ${projectId}` : ''} --json` },
129
- { name: 'refactor', value: 'refactor', command: `prlt ticket update ${ticketId} --category refactor${projectId ? ` -P ${projectId}` : ''} --json` },
130
- { name: 'docs', value: 'docs', command: `prlt ticket update ${ticketId} --category docs${projectId ? ` -P ${projectId}` : ''} --json` },
131
- { name: 'test', value: 'test', command: `prlt ticket update ${ticketId} --category test${projectId ? ` -P ${projectId}` : ''} --json` },
132
- { name: 'chore', value: 'chore', command: `prlt ticket update ${ticketId} --category chore${projectId ? ` -P ${projectId}` : ''} --json` },
133
- { name: 'None (clear category)', value: '', command: `prlt ticket update ${ticketId} --category none${projectId ? ` -P ${projectId}` : ''} --json` },
134
- { name: 'Custom...', value: '__custom__', command: `prlt ticket update ${ticketId} --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
135
- ],
223
+ }
224
+ if (updateType === 'category' || updateType === 'both') {
225
+ const { categoryChoice } = await this.prompt([{
226
+ type: 'list',
227
+ name: 'categoryChoice',
228
+ message: 'Set category to:',
229
+ choices: [
230
+ { name: `(Keep existing: ${ticket.category || 'none'})`, value: null, command: '' },
231
+ { name: 'feature', value: 'feature', command: `prlt ticket update ${ticketId} --category feature${projectId ? ` -P ${projectId}` : ''} --json` },
232
+ { name: 'bug', value: 'bug', command: `prlt ticket update ${ticketId} --category bug${projectId ? ` -P ${projectId}` : ''} --json` },
233
+ { name: 'refactor', value: 'refactor', command: `prlt ticket update ${ticketId} --category refactor${projectId ? ` -P ${projectId}` : ''} --json` },
234
+ { name: 'docs', value: 'docs', command: `prlt ticket update ${ticketId} --category docs${projectId ? ` -P ${projectId}` : ''} --json` },
235
+ { name: 'test', value: 'test', command: `prlt ticket update ${ticketId} --category test${projectId ? ` -P ${projectId}` : ''} --json` },
236
+ { name: 'chore', value: 'chore', command: `prlt ticket update ${ticketId} --category chore${projectId ? ` -P ${projectId}` : ''} --json` },
237
+ { name: 'None (clear category)', value: '', command: `prlt ticket update ${ticketId} --category none${projectId ? ` -P ${projectId}` : ''} --json` },
238
+ { name: 'Custom...', value: '__custom__', command: `prlt ticket update ${ticketId} --category <category>${projectId ? ` -P ${projectId}` : ''} --json` },
239
+ ],
240
+ }], jsonModeConfig);
241
+ if (categoryChoice === '__custom__') {
242
+ const { customCategory } = await this.prompt([{
243
+ type: 'input',
244
+ name: 'customCategory',
245
+ message: 'Enter custom category:',
246
+ validate: (input) => input.length > 0 || 'Category is required',
136
247
  }], jsonModeConfig);
137
- if (categoryChoice === '__custom__') {
138
- const { customCategory } = await this.prompt([{
139
- type: 'input',
140
- name: 'customCategory',
141
- message: 'Enter custom category:',
142
- validate: (input) => input.length > 0 || 'Category is required',
143
- }], jsonModeConfig);
144
- updateCategory = customCategory;
145
- }
146
- else if (categoryChoice !== null) {
147
- updateCategory = categoryChoice;
148
- }
248
+ updateCategory = customCategory;
249
+ }
250
+ else if (categoryChoice !== null) {
251
+ updateCategory = categoryChoice;
149
252
  }
150
253
  }
151
254
  // Check if anything to update