jira-ai 0.3.18 → 0.3.19

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/dist/cli.js CHANGED
@@ -32,7 +32,7 @@ const program = new Command();
32
32
  program
33
33
  .name('jira-ai')
34
34
  .description('CLI tool for interacting with Atlassian Jira')
35
- .version('0.3.18')
35
+ .version('0.3.19')
36
36
  .option('-o, --organization <alias>', 'Override the active Jira organization');
37
37
  // Hook to handle the global option before any command runs
38
38
  program.on('option:organization', (alias) => {
@@ -113,6 +113,9 @@ program
113
113
  program
114
114
  .command('task-with-details <task-id>')
115
115
  .description('Show task title, body, and comments')
116
+ .option('--include-detailed-history', 'Include the full history of task actions')
117
+ .option('--history-limit <number>', 'Number of history entries to show (default: 50)', '50')
118
+ .option('--history-offset <number>', 'Number of history entries to skip (default: 0)', '0')
116
119
  .action(withPermission('task-with-details', taskWithDetailsCommand, {
117
120
  validateArgs: (args) => validateOptions(IssueKeySchema, args[0])
118
121
  }));
@@ -3,10 +3,14 @@ import { getTaskWithDetails } from '../lib/jira-client.js';
3
3
  import { formatTaskDetails } from '../lib/formatters.js';
4
4
  import { CommandError } from '../lib/errors.js';
5
5
  import { ui } from '../lib/ui.js';
6
- export async function taskWithDetailsCommand(taskId) {
6
+ export async function taskWithDetailsCommand(taskId, options = {}) {
7
7
  ui.startSpinner(`Fetching details for ${taskId}...`);
8
8
  try {
9
- const task = await getTaskWithDetails(taskId);
9
+ const task = await getTaskWithDetails(taskId, {
10
+ includeHistory: options.includeDetailedHistory,
11
+ historyLimit: options.historyLimit ? parseInt(options.historyLimit, 10) : undefined,
12
+ historyOffset: options.historyOffset ? parseInt(options.historyOffset, 10) : undefined,
13
+ });
10
14
  ui.succeedSpinner(chalk.green('Task details retrieved'));
11
15
  console.log(formatTaskDetails(task));
12
16
  }
@@ -122,6 +122,33 @@ export function formatTaskDetails(task) {
122
122
  else {
123
123
  output += chalk.gray('No comments yet.\n\n');
124
124
  }
125
+ // History
126
+ if (task.history && task.history.length > 0) {
127
+ output += formatTaskHistory(task.history);
128
+ }
129
+ return output;
130
+ }
131
+ /**
132
+ * Format task history
133
+ */
134
+ export function formatTaskHistory(history) {
135
+ if (history.length === 0) {
136
+ return chalk.gray('No history entries found.\n\n');
137
+ }
138
+ const table = createTable(['Date', 'Author', 'Field', 'From', 'To'], [22, 18, 15, 25, 25]);
139
+ history.forEach((entry) => {
140
+ entry.items.forEach((item, index) => {
141
+ table.push([
142
+ index === 0 ? formatTimestamp(entry.created) : '',
143
+ index === 0 ? truncate(entry.author, 18) : '',
144
+ chalk.yellow(item.field),
145
+ truncate(item.from || chalk.gray('None'), 25),
146
+ truncate(item.to || chalk.gray('None'), 25),
147
+ ]);
148
+ });
149
+ });
150
+ let output = '\n' + chalk.bold(`Task History (${history.length} entries)`) + '\n';
151
+ output += table.toString() + '\n';
125
152
  return output;
126
153
  }
127
154
  /**
@@ -105,11 +105,13 @@ export async function getProjects() {
105
105
  /**
106
106
  * Get task details with comments
107
107
  */
108
- export async function getTaskWithDetails(taskId) {
108
+ export async function getTaskWithDetails(taskId, options = {}) {
109
109
  const client = getJiraClient();
110
+ const { includeHistory, historyLimit = 50, historyOffset = 0 } = options;
110
111
  // Get issue details
111
112
  const issue = await client.issues.getIssue({
112
113
  issueIdOrKey: taskId,
114
+ expand: includeHistory ? 'changelog' : undefined,
113
115
  fields: [
114
116
  'summary',
115
117
  'description',
@@ -157,6 +159,33 @@ export async function getTaskWithDetails(taskId) {
157
159
  name: subtask.fields?.status?.name || 'Unknown',
158
160
  },
159
161
  })) || [];
162
+ // Extract history if requested
163
+ let history = undefined;
164
+ if (includeHistory && issue.changelog) {
165
+ let allHistories = issue.changelog.histories || [];
166
+ // If there are more histories than returned in the initial expand, we might need to fetch more
167
+ // Jira usually returns 100 histories in the expand.
168
+ if (issue.changelog.total && issue.changelog.total > allHistories.length && (historyOffset + historyLimit) > allHistories.length) {
169
+ const moreHistories = await client.issues.getChangeLogs({
170
+ issueIdOrKey: taskId,
171
+ });
172
+ allHistories = moreHistories.values || allHistories;
173
+ }
174
+ history = allHistories.map((h) => ({
175
+ id: h.id,
176
+ author: h.author?.displayName || 'Unknown',
177
+ created: h.created,
178
+ items: h.items?.map((item) => ({
179
+ field: item.field || '',
180
+ from: item.fromString,
181
+ to: item.toString
182
+ }))
183
+ }));
184
+ // Sort by date descending (most recent first)
185
+ history.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
186
+ // Apply offset and limit
187
+ history = history.slice(historyOffset, historyOffset + historyLimit);
188
+ }
160
189
  return {
161
190
  id: issue.id || '',
162
191
  key: issue.key || '',
@@ -179,6 +208,7 @@ export async function getTaskWithDetails(taskId) {
179
208
  comments,
180
209
  parent,
181
210
  subtasks,
211
+ history,
182
212
  };
183
213
  }
184
214
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jira-ai",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "AI friendly Jira CLI to save context",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",