jira-pilot 2.1.2 → 2.2.0
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 +54 -0
- package/bin/jira.ts +2 -0
- package/dist/bin/jira.js +2 -0
- package/dist/bin/jira.js.map +1 -1
- package/dist/src/commands/ai-actions/plan.js +1 -1
- package/dist/src/commands/ai-actions/plan.js.map +1 -1
- package/dist/src/commands/ai-actions/review.js +5 -4
- package/dist/src/commands/ai-actions/review.js.map +1 -1
- package/dist/src/commands/ai-actions/standup.js +1 -1
- package/dist/src/commands/ai-actions/standup.js.map +1 -1
- package/dist/src/commands/ai.js +1 -1
- package/dist/src/commands/ai.js.map +1 -1
- package/dist/src/commands/board.js +10 -5
- package/dist/src/commands/board.js.map +1 -1
- package/dist/src/commands/bulk.js +11 -10
- package/dist/src/commands/bulk.js.map +1 -1
- package/dist/src/commands/config.js +1 -1
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/dashboard.js +19 -12
- package/dist/src/commands/dashboard.js.map +1 -1
- package/dist/src/commands/filter.js +7 -4
- package/dist/src/commands/filter.js.map +1 -1
- package/dist/src/commands/git.js +1 -1
- package/dist/src/commands/git.js.map +1 -1
- package/dist/src/commands/issue-attach.js +1 -1
- package/dist/src/commands/issue-attach.js.map +1 -1
- package/dist/src/commands/issue-pr.js +1 -1
- package/dist/src/commands/issue-pr.js.map +1 -1
- package/dist/src/commands/issue-worklog.js +10 -5
- package/dist/src/commands/issue-worklog.js.map +1 -1
- package/dist/src/commands/issue.js +173 -122
- package/dist/src/commands/issue.js.map +1 -1
- package/dist/src/commands/project.js +10 -5
- package/dist/src/commands/project.js.map +1 -1
- package/dist/src/commands/sprint.js +19 -8
- package/dist/src/commands/sprint.js.map +1 -1
- package/dist/src/commands/tui.d.ts +2 -0
- package/dist/src/commands/tui.js +10 -0
- package/dist/src/commands/tui.js.map +1 -0
- package/dist/src/server/mcp-server.js +209 -27
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/services/ai-service.js +7 -4
- package/dist/src/services/ai-service.js.map +1 -1
- package/dist/src/services/api-service.d.ts +2 -0
- package/dist/src/services/api-service.js +32 -20
- package/dist/src/services/api-service.js.map +1 -1
- package/dist/src/tui/App.d.ts +1 -0
- package/dist/src/tui/App.js +26 -0
- package/dist/src/tui/App.js.map +1 -0
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.js +8 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/screens/BoardList.d.ts +1 -0
- package/dist/src/tui/screens/BoardList.js +71 -0
- package/dist/src/tui/screens/BoardList.js.map +1 -0
- package/dist/src/tui/screens/Dashboard.d.ts +1 -0
- package/dist/src/tui/screens/Dashboard.js +41 -0
- package/dist/src/tui/screens/Dashboard.js.map +1 -0
- package/dist/src/tui/screens/IssueDetail.d.ts +6 -0
- package/dist/src/tui/screens/IssueDetail.js +40 -0
- package/dist/src/tui/screens/IssueDetail.js.map +1 -0
- package/dist/src/tui/screens/IssueList.d.ts +1 -0
- package/dist/src/tui/screens/IssueList.js +72 -0
- package/dist/src/tui/screens/IssueList.js.map +1 -0
- package/dist/src/tui/screens/KanbanBoard.d.ts +6 -0
- package/dist/src/tui/screens/KanbanBoard.js +86 -0
- package/dist/src/tui/screens/KanbanBoard.js.map +1 -0
- package/dist/src/tui/utils/adf-render.d.ts +1 -0
- package/dist/src/tui/utils/adf-render.js +29 -0
- package/dist/src/tui/utils/adf-render.js.map +1 -0
- package/dist/src/utils/api-paths.d.ts +31 -0
- package/dist/src/utils/api-paths.js +32 -0
- package/dist/src/utils/api-paths.js.map +1 -0
- package/dist/src/utils/error-handler.d.ts +2 -2
- package/dist/src/utils/error-handler.js.map +1 -1
- package/dist/src/utils/http.d.ts +27 -0
- package/dist/src/utils/http.js +95 -0
- package/dist/src/utils/http.js.map +1 -0
- package/dist/src/utils/spinner.d.ts +21 -0
- package/dist/src/utils/spinner.js +79 -0
- package/dist/src/utils/spinner.js.map +1 -0
- package/package.json +10 -5
- package/src/commands/ai-actions/plan.ts +1 -1
- package/src/commands/ai-actions/review.ts +5 -4
- package/src/commands/ai-actions/standup.ts +1 -1
- package/src/commands/ai.ts +1 -1
- package/src/commands/board.ts +10 -5
- package/src/commands/bulk.ts +11 -10
- package/src/commands/config.ts +1 -1
- package/src/commands/dashboard.ts +20 -12
- package/src/commands/filter.ts +8 -5
- package/src/commands/git.ts +1 -1
- package/src/commands/issue-attach.ts +1 -1
- package/src/commands/issue-pr.ts +1 -1
- package/src/commands/issue-worklog.ts +10 -5
- package/src/commands/issue.ts +181 -124
- package/src/commands/project.ts +10 -5
- package/src/commands/sprint.ts +19 -8
- package/src/commands/tui.ts +11 -0
- package/src/server/mcp-server.ts +234 -27
- package/src/services/ai-service.ts +7 -4
- package/src/services/api-service.ts +34 -21
- package/src/tui/App.tsx +61 -0
- package/src/tui/index.tsx +8 -0
- package/src/tui/screens/BoardList.tsx +102 -0
- package/src/tui/screens/Dashboard.tsx +75 -0
- package/src/tui/screens/IssueDetail.tsx +93 -0
- package/src/tui/screens/IssueList.tsx +116 -0
- package/src/tui/screens/KanbanBoard.tsx +133 -0
- package/src/tui/utils/adf-render.ts +30 -0
- package/src/utils/api-paths.ts +32 -0
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/http.ts +128 -0
- package/src/utils/spinner.ts +87 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
|
|
2
|
+
import https from 'https';
|
|
3
|
+
|
|
4
|
+
interface HelperConfig {
|
|
5
|
+
baseURL?: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
validateStatus?: (status: number) => boolean;
|
|
8
|
+
httpsAgent?: https.Agent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface RequestConfig extends HelperConfig {
|
|
12
|
+
params?: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Response<T = any> {
|
|
16
|
+
data: T;
|
|
17
|
+
status: number;
|
|
18
|
+
statusText: string;
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class HttpClient {
|
|
23
|
+
private defaults: HelperConfig;
|
|
24
|
+
|
|
25
|
+
constructor(defaults: HelperConfig = {}) {
|
|
26
|
+
this.defaults = defaults;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static create(config: HelperConfig = {}) {
|
|
30
|
+
return new HttpClient(config);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async request<T = any>(url: string, options: RequestInit & RequestConfig = {}): Promise<Response<T>> {
|
|
34
|
+
const baseURL = options.baseURL || this.defaults.baseURL || '';
|
|
35
|
+
const fullUrl = new URL(url.startsWith('http') ? url : `${baseURL}${url}`);
|
|
36
|
+
|
|
37
|
+
if (options.params) {
|
|
38
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
39
|
+
if (value !== undefined) fullUrl.searchParams.append(key, String(value));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const headers = {
|
|
44
|
+
...this.defaults.headers,
|
|
45
|
+
...options.headers
|
|
46
|
+
} as Record<string, string>;
|
|
47
|
+
|
|
48
|
+
// Handle body for JSON
|
|
49
|
+
let body = options.body;
|
|
50
|
+
if (body && typeof body === 'object') {
|
|
51
|
+
const explicitContentType = headers['Content-Type'];
|
|
52
|
+
if (!explicitContentType || explicitContentType.includes('application/json')) {
|
|
53
|
+
if (!explicitContentType) {
|
|
54
|
+
headers['Content-Type'] = 'application/json';
|
|
55
|
+
}
|
|
56
|
+
body = JSON.stringify(body);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const fetchOptions: RequestInit = {
|
|
61
|
+
method: options.method || 'GET',
|
|
62
|
+
headers,
|
|
63
|
+
body: body as BodyInit,
|
|
64
|
+
// Node native fetch doesn't support 'agent' directly in standard RequestInit
|
|
65
|
+
// but we can pass it via 'dispatcher' in undici, or ignore if using global fetch.
|
|
66
|
+
// For simple usage we usually ignore httpsAgent unless strictly needed.
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const response = await fetch(fullUrl.toString(), fetchOptions);
|
|
70
|
+
|
|
71
|
+
let data: any;
|
|
72
|
+
const contentType = response.headers.get('content-type');
|
|
73
|
+
const contentLength = response.headers.get('content-length');
|
|
74
|
+
|
|
75
|
+
if (response.status === 204 || (contentLength && parseInt(contentLength) === 0)) {
|
|
76
|
+
data = null;
|
|
77
|
+
} else if (contentType && contentType.includes('application/json')) {
|
|
78
|
+
try {
|
|
79
|
+
data = await response.json();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// If JSON parsing fails (e.g. empty body despite content-type), fallback to text or null
|
|
82
|
+
const text = await response.text();
|
|
83
|
+
try {
|
|
84
|
+
data = JSON.parse(text);
|
|
85
|
+
} catch {
|
|
86
|
+
data = text || null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
data = await response.text();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const res: Response<T> = {
|
|
94
|
+
data,
|
|
95
|
+
status: response.status,
|
|
96
|
+
statusText: response.statusText,
|
|
97
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const validateStatus = options.validateStatus || this.defaults.validateStatus || ((s) => s >= 200 && s < 300);
|
|
101
|
+
if (!validateStatus(res.status)) {
|
|
102
|
+
const error: any = new Error(`Request failed with status ${res.status}`);
|
|
103
|
+
error.response = res;
|
|
104
|
+
error.status = res.status;
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return res;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async get<T = any>(url: string, config: RequestConfig = {}) {
|
|
112
|
+
return this.request<T>(url, { ...config, method: 'GET' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async post<T = any>(url: string, data?: any, config: RequestConfig = {}) {
|
|
116
|
+
return this.request<T>(url, { ...config, method: 'POST', body: data });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async put<T = any>(url: string, data?: any, config: RequestConfig = {}) {
|
|
120
|
+
return this.request<T>(url, { ...config, method: 'PUT', body: data });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async delete<T = any>(url: string, config: RequestConfig = {}) {
|
|
124
|
+
return this.request<T>(url, { ...config, method: 'DELETE' });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default HttpClient;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export class Spinner {
|
|
2
|
+
private timer: NodeJS.Timeout | null = null;
|
|
3
|
+
private frameIndex = 0;
|
|
4
|
+
private frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
5
|
+
public text: string;
|
|
6
|
+
|
|
7
|
+
constructor(text: string | { text: string }) {
|
|
8
|
+
this.text = typeof text === 'string' ? text : (text?.text || '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
start(text?: string): Spinner {
|
|
12
|
+
if (text) this.text = text;
|
|
13
|
+
this.frameIndex = 0;
|
|
14
|
+
// Only start animation if TTY and not CI (basic check)
|
|
15
|
+
if (process.stdout.isTTY) {
|
|
16
|
+
this.timer = setInterval(() => {
|
|
17
|
+
const frame = this.frames[this.frameIndex];
|
|
18
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
19
|
+
this.write(`\r${frame} ${this.text}`);
|
|
20
|
+
}, 80);
|
|
21
|
+
} else {
|
|
22
|
+
console.log(this.text);
|
|
23
|
+
}
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
stop(): Spinner {
|
|
28
|
+
if (this.timer) {
|
|
29
|
+
clearInterval(this.timer);
|
|
30
|
+
this.timer = null;
|
|
31
|
+
this.clearLine();
|
|
32
|
+
}
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
succeed(text?: string): Spinner {
|
|
37
|
+
this.stop();
|
|
38
|
+
const msg = text || this.text;
|
|
39
|
+
console.log(`${this.color('green', '✔')} ${msg}`);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fail(text?: string): Spinner {
|
|
44
|
+
this.stop();
|
|
45
|
+
const msg = text || this.text;
|
|
46
|
+
console.log(`${this.color('red', '✖')} ${msg}`);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
info(text?: string): Spinner {
|
|
51
|
+
this.stop();
|
|
52
|
+
console.log(`${this.color('blue', 'ℹ')} ${text || this.text}`);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
warn(text?: string): Spinner {
|
|
57
|
+
this.stop();
|
|
58
|
+
console.log(`${this.color('yellow', '⚠')} ${text || this.text}`);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private clearLine() {
|
|
63
|
+
if (process.stdout.isTTY) {
|
|
64
|
+
process.stdout.clearLine(0);
|
|
65
|
+
process.stdout.cursorTo(0);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private write(str: string) {
|
|
70
|
+
process.stdout.write(str);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private color(color: string, str: string): string {
|
|
74
|
+
const colors: any = {
|
|
75
|
+
green: '\x1b[32m',
|
|
76
|
+
red: '\x1b[31m',
|
|
77
|
+
blue: '\x1b[34m',
|
|
78
|
+
yellow: '\x1b[33m',
|
|
79
|
+
reset: '\x1b[0m'
|
|
80
|
+
};
|
|
81
|
+
return `${colors[color] || ''}${str}${colors.reset}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default function ora(options: string | { text: string } = '') {
|
|
86
|
+
return new Spinner(options);
|
|
87
|
+
}
|