jira-ai 0.6.5 → 0.6.6

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
@@ -18,7 +18,7 @@ import { createTaskCommand } from './commands/create-task.js';
18
18
  import { transitionCommand } from './commands/transition.js';
19
19
  import { getIssueStatisticsCommand } from './commands/get-issue-statistics.js';
20
20
  import { getPersonWorklogCommand } from './commands/get-person-worklog.js';
21
- import { confluenceGetPageCommand, confluenceListSpacesCommand, confluenceGetSpacePagesHierarchyCommand } from './commands/confluence.js';
21
+ import { confluenceGetPageCommand, confluenceListSpacesCommand, confluenceGetSpacePagesHierarchyCommand, confluenceAddCommentCommand } from './commands/confluence.js';
22
22
  import { aboutCommand } from './commands/about.js';
23
23
  import { authCommand } from './commands/auth.js';
24
24
  import { settingsCommand } from './commands/settings.js';
@@ -30,7 +30,7 @@ import { checkForUpdate, formatUpdateMessage, checkForUpdateSync } from './lib/u
30
30
  import { CliError } from './types/errors.js';
31
31
  import { CommandError } from './lib/errors.js';
32
32
  import { ui } from './lib/ui.js';
33
- import { CreateTaskSchema, AddCommentSchema, UpdateDescriptionSchema, RunJqlSchema, GetPersonWorklogSchema, GetIssueStatisticsSchema, validateOptions, IssueKeySchema, ProjectKeySchema, TimeframeSchema } from './lib/validation.js';
33
+ import { CreateTaskSchema, AddCommentSchema, UpdateDescriptionSchema, ConfluenceAddCommentSchema, RunJqlSchema, GetPersonWorklogSchema, GetIssueStatisticsSchema, validateOptions, IssueKeySchema, ProjectKeySchema, TimeframeSchema } from './lib/validation.js';
34
34
  import { realpathSync } from 'fs';
35
35
  // Load environment variables
36
36
  // @ts-ignore - quiet option exists but is not in types
@@ -263,6 +263,11 @@ confl
263
263
  .command('pages <space-key>')
264
264
  .description('Display a hierarchical tree view of pages within a specific space.')
265
265
  .action(withPermission('confl', confluenceGetSpacePagesHierarchyCommand, { skipValidation: false }));
266
+ confl
267
+ .command('add-comment <url>')
268
+ .description('Add a new comment to a Confluence page using content from a local Markdown file.')
269
+ .requiredOption('--from-file <path>', 'Path to Markdown file')
270
+ .action(withPermission('confl', confluenceAddCommentCommand, { schema: ConfluenceAddCommentSchema }));
266
271
  // About command (always allowed)
267
272
  program
268
273
  .command('about')
@@ -1,9 +1,72 @@
1
1
  import chalk from 'chalk';
2
- import { getPage, getPageComments, parseConfluenceUrl, listSpaces, getSpacePagesHierarchy } from '../lib/confluence-client.js';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { markdownToAdf } from 'marklassian';
5
+ import { getPage, getPageComments, parseConfluenceUrl, listSpaces, getSpacePagesHierarchy, addPageComment } from '../lib/confluence-client.js';
3
6
  import { formatConfluencePage, formatConfluenceSpaces, formatConfluencePageHierarchy } from '../lib/formatters.js';
4
7
  import { ui } from '../lib/ui.js';
5
8
  import { CommandError } from '../lib/errors.js';
6
9
  import { isConfluenceSpaceAllowed } from '../lib/settings.js';
10
+ export async function confluenceAddCommentCommand(url, options) {
11
+ const { fromFile } = options;
12
+ // Validate space key before proceeding
13
+ try {
14
+ const { spaceKey } = parseConfluenceUrl(url);
15
+ if (spaceKey && !isConfluenceSpaceAllowed(spaceKey)) {
16
+ throw new CommandError(`Access to Confluence space '${spaceKey}' is restricted by your settings.`);
17
+ }
18
+ }
19
+ catch (e) {
20
+ if (e instanceof CommandError)
21
+ throw e;
22
+ // URL parsing errors will be caught during addPageComment
23
+ }
24
+ // Resolve and read file
25
+ const absolutePath = path.resolve(fromFile);
26
+ let markdownContent;
27
+ try {
28
+ markdownContent = fs.readFileSync(absolutePath, 'utf-8');
29
+ }
30
+ catch (error) {
31
+ throw new CommandError(`Error reading file: ${error.message}`, {
32
+ hints: ['Make sure the file exists and you have permission to read it.']
33
+ });
34
+ }
35
+ if (markdownContent.trim() === '') {
36
+ throw new CommandError('Markdown file is empty');
37
+ }
38
+ // Convert Markdown to ADF
39
+ let adfContent;
40
+ try {
41
+ adfContent = markdownToAdf(markdownContent);
42
+ }
43
+ catch (error) {
44
+ throw new CommandError(`Error converting Markdown to ADF: ${error.message}`, {
45
+ hints: ['Ensure the Markdown content is valid.']
46
+ });
47
+ }
48
+ ui.startSpinner('Adding comment to Confluence page...');
49
+ try {
50
+ await addPageComment(url, adfContent);
51
+ ui.succeedSpinner(chalk.green('Comment added successfully to Confluence page'));
52
+ console.log(chalk.gray(`\nPage: ${url}`));
53
+ console.log(chalk.gray(`File: ${absolutePath}`));
54
+ }
55
+ catch (error) {
56
+ ui.failSpinner();
57
+ if (error instanceof CommandError)
58
+ throw error;
59
+ const errorMsg = error.message?.toLowerCase() || '';
60
+ const hints = [];
61
+ if (errorMsg.includes('404') || errorMsg.includes('not found')) {
62
+ hints.push('The Confluence page was not found. Check if the URL is correct.');
63
+ }
64
+ else if (errorMsg.includes('401') || errorMsg.includes('403') || errorMsg.includes('unauthorized')) {
65
+ hints.push('Authentication failed or you do not have permission to comment on this page.');
66
+ }
67
+ throw new CommandError(`Failed to add comment: ${error.message}`, { hints });
68
+ }
69
+ }
7
70
  export async function confluenceGetPageCommand(url) {
8
71
  // Check permission before fetching if space key can be extracted from URL
9
72
  try {
@@ -182,3 +182,21 @@ export async function getSpacePagesHierarchy(spaceKey, maxDepth = 5) {
182
182
  }
183
183
  return hierarchy;
184
184
  }
185
+ /**
186
+ * Add a comment to a Confluence page
187
+ */
188
+ export async function addPageComment(url, adfContent) {
189
+ const client = getConfluenceClient();
190
+ const { pageId } = parseConfluenceUrl(url);
191
+ // @ts-ignore - CreateContent type requires title and space which are not needed for comments
192
+ await client.content.createContent({
193
+ type: 'comment',
194
+ container: { id: pageId, type: 'page' },
195
+ body: {
196
+ atlas_doc_format: {
197
+ value: JSON.stringify(adfContent),
198
+ representation: 'atlas_doc_format'
199
+ }
200
+ }
201
+ });
202
+ }
@@ -62,6 +62,9 @@ export const AddCommentSchema = z.object({
62
62
  filePath: z.string().trim().min(1, 'File path is required').pipe(FilePathSchema),
63
63
  issueKey: z.string().trim().min(1, 'Issue key is required').pipe(IssueKeySchema),
64
64
  });
65
+ export const ConfluenceAddCommentSchema = z.object({
66
+ fromFile: z.string().trim().min(1, 'File path is required').pipe(FilePathSchema),
67
+ });
65
68
  export const UpdateDescriptionSchema = z.object({
66
69
  fromFile: z.string().trim().min(1, 'File path is required').pipe(FilePathSchema),
67
70
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jira-ai",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "AI friendly Jira CLI to save context",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",