@mrc2204/openclaw-jira-tools 1.0.1
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.en.md +127 -0
- package/README.md +129 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +21 -0
- package/dist/lib/defaults-store.d.ts +8 -0
- package/dist/lib/defaults-store.js +27 -0
- package/dist/lib/jira-client.d.ts +36 -0
- package/dist/lib/jira-client.js +112 -0
- package/dist/lib/runtime.d.ts +17 -0
- package/dist/lib/runtime.js +21 -0
- package/dist/shared/types.d.ts +41 -0
- package/dist/shared/types.js +1 -0
- package/dist/tools/create-tools.d.ts +3 -0
- package/dist/tools/create-tools.js +134 -0
- package/dist/tools/defaults-tools.d.ts +2 -0
- package/dist/tools/defaults-tools.js +34 -0
- package/dist/tools/inspect-tool.d.ts +3 -0
- package/dist/tools/inspect-tool.js +53 -0
- package/dist/tools/management-tools.d.ts +3 -0
- package/dist/tools/management-tools.js +574 -0
- package/openclaw.plugin.json +45 -0
- package/package.json +39 -0
- package/skill/jira/SKILL.md +435 -0
- package/skill/jira/_meta.json +6 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# openclaw-jira-tools
|
|
2
|
+
|
|
3
|
+
Native OpenClaw plugin that registers Jira tools for agents, helping them use Jira consistently, avoid forgetting configuration, and prefer tool-native workflows instead of shelling out to host-level `jira-cli`.
|
|
4
|
+
|
|
5
|
+
## Goals
|
|
6
|
+
- Standardize Jira tools for OpenClaw agents
|
|
7
|
+
- Avoid dependency on `brew install jira-cli` on the host machine
|
|
8
|
+
- Read Jira configuration from plugin config (`plugins.entries.jira-tools.config`)
|
|
9
|
+
- Support per-agent / per-user defaults to reduce config drift
|
|
10
|
+
- Sanitize fields against Jira `createmeta` / create screen constraints
|
|
11
|
+
- Support Vietnamese-first operational Jira workflows
|
|
12
|
+
|
|
13
|
+
## Available tools
|
|
14
|
+
|
|
15
|
+
### Create / defaults
|
|
16
|
+
- `jira_create_task`
|
|
17
|
+
- `jira_create_epic`
|
|
18
|
+
- `jira_create_subtask`
|
|
19
|
+
- `jira_set_defaults`
|
|
20
|
+
- `jira_inspect_project`
|
|
21
|
+
|
|
22
|
+
### Issue management
|
|
23
|
+
- `jira_issue_list`
|
|
24
|
+
- `jira_issue_view`
|
|
25
|
+
- `jira_open`
|
|
26
|
+
- `jira_issue_edit`
|
|
27
|
+
- `jira_issue_assign`
|
|
28
|
+
- `jira_issue_comment_add`
|
|
29
|
+
- `jira_issue_clone`
|
|
30
|
+
- `jira_issue_delete`
|
|
31
|
+
- `jira_issue_watch`
|
|
32
|
+
- `jira_issue_move`
|
|
33
|
+
- `jira_issue_worklog_add`
|
|
34
|
+
- `jira_issue_link`
|
|
35
|
+
- `jira_issue_unlink`
|
|
36
|
+
|
|
37
|
+
### Epic / project / board / sprint / release
|
|
38
|
+
- `jira_epic_list`
|
|
39
|
+
- `jira_epic_view`
|
|
40
|
+
- `jira_epic_add`
|
|
41
|
+
- `jira_epic_remove`
|
|
42
|
+
- `jira_project_list`
|
|
43
|
+
- `jira_project_view`
|
|
44
|
+
- `jira_board_list`
|
|
45
|
+
- `jira_sprint_list`
|
|
46
|
+
- `jira_sprint_add`
|
|
47
|
+
- `jira_sprint_close`
|
|
48
|
+
- `jira_release_list`
|
|
49
|
+
- `jira_me`
|
|
50
|
+
- `jira_serverinfo`
|
|
51
|
+
|
|
52
|
+
## Repository structure
|
|
53
|
+
```text
|
|
54
|
+
openclaw-jira-tools/
|
|
55
|
+
├── openclaw.plugin.json
|
|
56
|
+
├── package.json
|
|
57
|
+
├── tsconfig.json
|
|
58
|
+
├── src/
|
|
59
|
+
│ ├── index.ts
|
|
60
|
+
│ ├── lib/
|
|
61
|
+
│ ├── shared/
|
|
62
|
+
│ └── tools/
|
|
63
|
+
├── skill/
|
|
64
|
+
│ └── jira/
|
|
65
|
+
│ ├── SKILL.md
|
|
66
|
+
│ └── _meta.json
|
|
67
|
+
└── dist/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Bundled skill
|
|
71
|
+
This repository also ships a Jira skill to guide agents on how to use the toolset correctly:
|
|
72
|
+
- `skill/jira/SKILL.md`
|
|
73
|
+
- `skill/jira/_meta.json`
|
|
74
|
+
|
|
75
|
+
The bundled skill documents:
|
|
76
|
+
- issue classification rules
|
|
77
|
+
- Vietnamese issue/comment templates
|
|
78
|
+
- user intent → Jira tool mapping
|
|
79
|
+
- rules for preferring `jira-tools` over host `jira-cli`
|
|
80
|
+
- defaults strategy so agents do not forget configuration
|
|
81
|
+
|
|
82
|
+
## Correct install from npm
|
|
83
|
+
|
|
84
|
+
> npm package: https://www.npmjs.com/package/@mrc2204/openclaw-jira-tools
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
openclaw plugins install @mrc2204/openclaw-jira-tools
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If you need a local/development workflow, use path-based install separately instead of locking a specific version here.
|
|
91
|
+
|
|
92
|
+
## Local install for development
|
|
93
|
+
```bash
|
|
94
|
+
cd ~/Work/projects/jira-tools
|
|
95
|
+
npm install
|
|
96
|
+
npm run build
|
|
97
|
+
openclaw plugins install -l ~/Work/projects/jira-tools
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Example config
|
|
101
|
+
```json5
|
|
102
|
+
{
|
|
103
|
+
plugins: {
|
|
104
|
+
entries: {
|
|
105
|
+
"jira-tools": {
|
|
106
|
+
enabled: true,
|
|
107
|
+
config: {
|
|
108
|
+
server: "https://your-domain.atlassian.net",
|
|
109
|
+
email: "you@example.com",
|
|
110
|
+
token: "<jira-token>",
|
|
111
|
+
defaultProject: "TAA",
|
|
112
|
+
language: "vi",
|
|
113
|
+
requireClickableLinks: true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Notes
|
|
122
|
+
- If an agent allowlist includes `jira-tools`, OpenClaw enables all tools registered by this plugin.
|
|
123
|
+
- This is a plugin-native Jira surface; it does not automatically import every command from a host-installed `jira-cli` binary.
|
|
124
|
+
- Some Epic-related flows may still depend on Jira project screen configuration (for example Epic Name / Epic Link fields).
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# openclaw-jira-tools
|
|
2
|
+
|
|
3
|
+
> English README: [README.en.md](./README.en.md)
|
|
4
|
+
|
|
5
|
+
Plugin OpenClaw chuẩn để đăng ký native Jira tools cho agent, giúp agent dùng Jira nhất quán, không quên config và ưu tiên tool-native thay vì shell-out `jira-cli` trên host.
|
|
6
|
+
|
|
7
|
+
## Mục tiêu
|
|
8
|
+
- Chuẩn hóa Jira tools cho agent trong OpenClaw
|
|
9
|
+
- Không phụ thuộc `brew install jira-cli` trên máy host
|
|
10
|
+
- Đọc config Jira từ plugin config (`plugins.entries.jira-tools.config`)
|
|
11
|
+
- Hỗ trợ defaults theo agent/user để giảm quên config
|
|
12
|
+
- Sanitize field theo Jira `createmeta` / create screen
|
|
13
|
+
- Ưu tiên nội dung tiếng Việt trong flow vận hành Jira
|
|
14
|
+
|
|
15
|
+
## Bộ tools hiện có
|
|
16
|
+
|
|
17
|
+
### Tạo issue / defaults
|
|
18
|
+
- `jira_create_task`
|
|
19
|
+
- `jira_create_epic`
|
|
20
|
+
- `jira_create_subtask`
|
|
21
|
+
- `jira_set_defaults`
|
|
22
|
+
- `jira_inspect_project`
|
|
23
|
+
|
|
24
|
+
### Issue management
|
|
25
|
+
- `jira_issue_list`
|
|
26
|
+
- `jira_issue_view`
|
|
27
|
+
- `jira_open`
|
|
28
|
+
- `jira_issue_edit`
|
|
29
|
+
- `jira_issue_assign`
|
|
30
|
+
- `jira_issue_comment_add`
|
|
31
|
+
- `jira_issue_clone`
|
|
32
|
+
- `jira_issue_delete`
|
|
33
|
+
- `jira_issue_watch`
|
|
34
|
+
- `jira_issue_move`
|
|
35
|
+
- `jira_issue_worklog_add`
|
|
36
|
+
- `jira_issue_link`
|
|
37
|
+
- `jira_issue_unlink`
|
|
38
|
+
|
|
39
|
+
### Epic / project / board / sprint / release
|
|
40
|
+
- `jira_epic_list`
|
|
41
|
+
- `jira_epic_view`
|
|
42
|
+
- `jira_epic_add`
|
|
43
|
+
- `jira_epic_remove`
|
|
44
|
+
- `jira_project_list`
|
|
45
|
+
- `jira_project_view`
|
|
46
|
+
- `jira_board_list`
|
|
47
|
+
- `jira_sprint_list`
|
|
48
|
+
- `jira_sprint_add`
|
|
49
|
+
- `jira_sprint_close`
|
|
50
|
+
- `jira_release_list`
|
|
51
|
+
- `jira_me`
|
|
52
|
+
- `jira_serverinfo`
|
|
53
|
+
|
|
54
|
+
## Cấu trúc repo
|
|
55
|
+
```text
|
|
56
|
+
openclaw-jira-tools/
|
|
57
|
+
├── openclaw.plugin.json
|
|
58
|
+
├── package.json
|
|
59
|
+
├── tsconfig.json
|
|
60
|
+
├── src/
|
|
61
|
+
│ ├── index.ts
|
|
62
|
+
│ ├── lib/
|
|
63
|
+
│ ├── shared/
|
|
64
|
+
│ └── tools/
|
|
65
|
+
├── skill/
|
|
66
|
+
│ └── jira/
|
|
67
|
+
│ ├── SKILL.md
|
|
68
|
+
│ └── _meta.json
|
|
69
|
+
└── dist/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Skill đi kèm
|
|
73
|
+
Repo này mang kèm skill Jira để hướng dẫn agent dùng bộ tools đúng flow:
|
|
74
|
+
- `skill/jira/SKILL.md`
|
|
75
|
+
- `skill/jira/_meta.json`
|
|
76
|
+
|
|
77
|
+
Skill này mô tả:
|
|
78
|
+
- chuẩn phân loại issue
|
|
79
|
+
- template tiếng Việt
|
|
80
|
+
- mapping ý định user → tool Jira tương ứng
|
|
81
|
+
- rule dùng `jira-tools` thay cho shell-out `jira-cli`
|
|
82
|
+
- rule defaults để agent không quên config
|
|
83
|
+
|
|
84
|
+
## Cài đặt đúng từ npm
|
|
85
|
+
|
|
86
|
+
> Package npm: https://www.npmjs.com/package/@mrc2204/openclaw-jira-tools
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
openclaw plugins install @mrc2204/openclaw-jira-tools
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Nếu cần môi trường development/local thì dùng path install riêng, không dùng flow này để khóa version.
|
|
93
|
+
|
|
94
|
+
## Cài local cho phát triển
|
|
95
|
+
```bash
|
|
96
|
+
cd ~/Work/projects/jira-tools
|
|
97
|
+
npm install
|
|
98
|
+
npm run build
|
|
99
|
+
openclaw plugins install -l ~/Work/projects/jira-tools
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Config mẫu
|
|
103
|
+
```json5
|
|
104
|
+
{
|
|
105
|
+
plugins: {
|
|
106
|
+
entries: {
|
|
107
|
+
"jira-tools": {
|
|
108
|
+
enabled: true,
|
|
109
|
+
config: {
|
|
110
|
+
server: "https://your-domain.atlassian.net",
|
|
111
|
+
email: "you@example.com",
|
|
112
|
+
token: "<jira-token>",
|
|
113
|
+
defaultProject: "TAA",
|
|
114
|
+
language: "vi",
|
|
115
|
+
requireClickableLinks: true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Ghi chú
|
|
124
|
+
- Nếu allowlist của agent chứa `jira-tools`, OpenClaw sẽ bật toàn bộ tools của plugin này.
|
|
125
|
+
- Đây là plugin-native surface; không tự động import toàn bộ command tree của binary `jira-cli` host.
|
|
126
|
+
- Một số flow Epic có thể vẫn phụ thuộc Jira screen config của project (ví dụ Epic Name / Epic Link field).
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { registerCreateTools } from "./tools/create-tools.js";
|
|
2
|
+
import { registerDefaultsTools } from "./tools/defaults-tools.js";
|
|
3
|
+
import { registerInspectTool } from "./tools/inspect-tool.js";
|
|
4
|
+
import { registerManagementTools } from "./tools/management-tools.js";
|
|
5
|
+
const plugin = {
|
|
6
|
+
id: "jira-tools",
|
|
7
|
+
name: "Jira Tools",
|
|
8
|
+
version: "0.1.0",
|
|
9
|
+
register(api) {
|
|
10
|
+
const rawConfig = (api.pluginConfig || api?.config?.plugins?.entries?.["jira-tools"]?.config || {});
|
|
11
|
+
if (!rawConfig.server || !rawConfig.email || !rawConfig.token) {
|
|
12
|
+
throw new Error("jira-tools requires plugins.entries['jira-tools'].config.server/email/token");
|
|
13
|
+
}
|
|
14
|
+
registerCreateTools(api, rawConfig);
|
|
15
|
+
registerDefaultsTools(api);
|
|
16
|
+
registerInspectTool(api, rawConfig);
|
|
17
|
+
registerManagementTools(api, rawConfig);
|
|
18
|
+
console.log("[jira-tools] Registered tools: jira_create_task, jira_create_epic, jira_create_subtask, jira_set_defaults, jira_inspect_project, jira_me, jira_serverinfo, jira_project_list, jira_project_view, jira_issue_list, jira_issue_view, jira_open, jira_issue_edit, jira_issue_assign, jira_issue_comment_add, jira_issue_clone, jira_issue_delete, jira_issue_watch, jira_issue_move, jira_issue_worklog_add, jira_issue_link, jira_issue_unlink, jira_epic_list, jira_epic_view, jira_epic_add, jira_epic_remove, jira_board_list, jira_sprint_list, jira_sprint_add, jira_sprint_close, jira_release_list");
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export default plugin;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JiraDefaultsRecord } from "../shared/types.js";
|
|
2
|
+
export declare class JiraDefaultsStore {
|
|
3
|
+
private baseDir;
|
|
4
|
+
constructor(baseDir?: string);
|
|
5
|
+
private filePath;
|
|
6
|
+
get(agentId: string, userId: string): JiraDefaultsRecord;
|
|
7
|
+
set(agentId: string, userId: string, patch: JiraDefaultsRecord): JiraDefaultsRecord;
|
|
8
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function safeKey(input) {
|
|
4
|
+
return input.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
5
|
+
}
|
|
6
|
+
export class JiraDefaultsStore {
|
|
7
|
+
baseDir;
|
|
8
|
+
constructor(baseDir) {
|
|
9
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || `${process.env.HOME}/.openclaw`;
|
|
10
|
+
this.baseDir = baseDir || join(stateDir, "plugin-data", "jira-tools", "defaults");
|
|
11
|
+
mkdirSync(this.baseDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
filePath(agentId, userId) {
|
|
14
|
+
return join(this.baseDir, `${safeKey(agentId)}__${safeKey(userId)}.json`);
|
|
15
|
+
}
|
|
16
|
+
get(agentId, userId) {
|
|
17
|
+
const fp = this.filePath(agentId, userId);
|
|
18
|
+
if (!existsSync(fp))
|
|
19
|
+
return {};
|
|
20
|
+
return JSON.parse(readFileSync(fp, "utf8"));
|
|
21
|
+
}
|
|
22
|
+
set(agentId, userId, patch) {
|
|
23
|
+
const next = { ...this.get(agentId, userId), ...patch };
|
|
24
|
+
writeFileSync(this.filePath(agentId, userId), JSON.stringify(next, null, 2), "utf8");
|
|
25
|
+
return next;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { JiraCreateInput, JiraDefaultsRecord, JiraToolsConfig } from "../shared/types.js";
|
|
2
|
+
export interface JiraResolvedConfig {
|
|
3
|
+
server: string;
|
|
4
|
+
email: string;
|
|
5
|
+
token: string;
|
|
6
|
+
defaultProject: string;
|
|
7
|
+
language: string;
|
|
8
|
+
requireClickableLinks: boolean;
|
|
9
|
+
issueTypeMap: {
|
|
10
|
+
task: string;
|
|
11
|
+
epic: string;
|
|
12
|
+
subtask: string;
|
|
13
|
+
};
|
|
14
|
+
fieldMappings: {
|
|
15
|
+
epicName: string;
|
|
16
|
+
epicLink: string;
|
|
17
|
+
parentLink: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare class JiraClient {
|
|
21
|
+
private readonly config;
|
|
22
|
+
constructor(config: JiraResolvedConfig);
|
|
23
|
+
private headers;
|
|
24
|
+
request(path: string, init?: RequestInit): Promise<any>;
|
|
25
|
+
issueUrl(issueKey: string): string;
|
|
26
|
+
getProject(projectKey: string): Promise<any>;
|
|
27
|
+
getCreateMeta(projectKey: string, issueTypeName: string): Promise<any>;
|
|
28
|
+
searchUsers(query: string): Promise<any>;
|
|
29
|
+
getIssue(issueKey: string): Promise<any>;
|
|
30
|
+
resolveAssigneeAccountId(email?: string, displayName?: string): Promise<string | undefined>;
|
|
31
|
+
buildDescription(kind: string, summary: string, objective: string, technicalDetails: string, dod: string, links?: string[], parentKey?: string): any;
|
|
32
|
+
sanitizeFields(fields: Record<string, any>, createmeta: any): Record<string, any>;
|
|
33
|
+
createIssue(fields: Record<string, any>): Promise<any>;
|
|
34
|
+
}
|
|
35
|
+
export declare function resolveConfig(raw: JiraToolsConfig): JiraResolvedConfig;
|
|
36
|
+
export declare function mergeDefaults(input: JiraCreateInput, defaults: JiraDefaultsRecord): JiraCreateInput;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export class JiraClient {
|
|
2
|
+
config;
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
headers() {
|
|
7
|
+
return {
|
|
8
|
+
"Accept": "application/json",
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
"Authorization": `Basic ${Buffer.from(`${this.config.email}:${this.config.token}`).toString("base64")}`,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
async request(path, init) {
|
|
14
|
+
const res = await fetch(`${this.config.server}${path}`, {
|
|
15
|
+
...init,
|
|
16
|
+
headers: { ...this.headers(), ...(init?.headers || {}) },
|
|
17
|
+
});
|
|
18
|
+
const text = await res.text();
|
|
19
|
+
const payload = text ? JSON.parse(text) : {};
|
|
20
|
+
if (!res.ok)
|
|
21
|
+
throw new Error(`${res.status}: ${JSON.stringify(payload)}`);
|
|
22
|
+
return payload;
|
|
23
|
+
}
|
|
24
|
+
issueUrl(issueKey) {
|
|
25
|
+
return `${this.config.server}/browse/${issueKey}`;
|
|
26
|
+
}
|
|
27
|
+
async getProject(projectKey) {
|
|
28
|
+
return this.request(`/rest/api/3/project/${projectKey}`);
|
|
29
|
+
}
|
|
30
|
+
async getCreateMeta(projectKey, issueTypeName) {
|
|
31
|
+
const q = new URLSearchParams({
|
|
32
|
+
projectKeys: projectKey,
|
|
33
|
+
issuetypeNames: issueTypeName,
|
|
34
|
+
expand: "projects.issuetypes.fields",
|
|
35
|
+
});
|
|
36
|
+
return this.request(`/rest/api/3/issue/createmeta?${q.toString()}`);
|
|
37
|
+
}
|
|
38
|
+
async searchUsers(query) {
|
|
39
|
+
const q = new URLSearchParams({ query, maxResults: "20" });
|
|
40
|
+
return this.request(`/rest/api/3/user/search?${q.toString()}`);
|
|
41
|
+
}
|
|
42
|
+
async getIssue(issueKey) {
|
|
43
|
+
return this.request(`/rest/api/3/issue/${issueKey}`);
|
|
44
|
+
}
|
|
45
|
+
async resolveAssigneeAccountId(email, displayName) {
|
|
46
|
+
const query = email || displayName;
|
|
47
|
+
if (!query)
|
|
48
|
+
return undefined;
|
|
49
|
+
const users = await this.searchUsers(query);
|
|
50
|
+
if (!Array.isArray(users) || users.length === 0)
|
|
51
|
+
throw new Error(`Không tìm thấy assignee: ${query}`);
|
|
52
|
+
const exact = users.find((u) => (email && u.emailAddress === email) || (displayName && u.displayName === displayName));
|
|
53
|
+
return (exact || users[0]).accountId;
|
|
54
|
+
}
|
|
55
|
+
buildDescription(kind, summary, objective, technicalDetails, dod, links, parentKey) {
|
|
56
|
+
const lines = [
|
|
57
|
+
`## Loại issue`, `- ${kind}`,
|
|
58
|
+
``, `## Mục tiêu`, objective,
|
|
59
|
+
``, `## Chi tiết kỹ thuật`, technicalDetails,
|
|
60
|
+
``, `## Tiêu chuẩn hoàn thành (DoD)`, dod,
|
|
61
|
+
];
|
|
62
|
+
if (parentKey)
|
|
63
|
+
lines.push("", "## Issue cha", `- ${parentKey}`, `- Link: ${this.issueUrl(parentKey)}`);
|
|
64
|
+
if (links && links.length)
|
|
65
|
+
lines.push("", "## Links liên quan", ...links.map((x) => `- ${x}`));
|
|
66
|
+
lines.push("", "## Ghi chú", "- Nội dung và comment Jira dùng tiếng Việt.");
|
|
67
|
+
const content = lines.map((line) => line ? ({ type: "paragraph", content: [{ type: "text", text: line }] }) : ({ type: "paragraph", content: [] }));
|
|
68
|
+
return { type: "doc", version: 1, content };
|
|
69
|
+
}
|
|
70
|
+
sanitizeFields(fields, createmeta) {
|
|
71
|
+
const allowed = new Set();
|
|
72
|
+
const projects = createmeta?.projects || [];
|
|
73
|
+
const issueTypes = projects[0]?.issuetypes || [];
|
|
74
|
+
const metaFields = issueTypes[0]?.fields || {};
|
|
75
|
+
for (const k of Object.keys(metaFields))
|
|
76
|
+
allowed.add(k);
|
|
77
|
+
const keep = new Set(["project", "summary", "description", "issuetype", "labels", "assignee", "parent"]);
|
|
78
|
+
return Object.fromEntries(Object.entries(fields).filter(([k]) => keep.has(k) || allowed.has(k)));
|
|
79
|
+
}
|
|
80
|
+
async createIssue(fields) {
|
|
81
|
+
return this.request(`/rest/api/3/issue`, { method: "POST", body: JSON.stringify({ fields }) });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function resolveConfig(raw) {
|
|
85
|
+
return {
|
|
86
|
+
server: raw.server.replace(/\/$/, ""),
|
|
87
|
+
email: raw.email,
|
|
88
|
+
token: raw.token,
|
|
89
|
+
defaultProject: raw.defaultProject || "TAA",
|
|
90
|
+
language: raw.language || "vi",
|
|
91
|
+
requireClickableLinks: raw.requireClickableLinks !== false,
|
|
92
|
+
issueTypeMap: {
|
|
93
|
+
task: raw.issueTypeMap?.task || "Task",
|
|
94
|
+
epic: raw.issueTypeMap?.epic || "Epic",
|
|
95
|
+
subtask: raw.issueTypeMap?.subtask || "Subtask",
|
|
96
|
+
},
|
|
97
|
+
fieldMappings: {
|
|
98
|
+
epicName: raw.fieldMappings?.epicName || "customfield_10011",
|
|
99
|
+
epicLink: raw.fieldMappings?.epicLink || "customfield_10014",
|
|
100
|
+
parentLink: raw.fieldMappings?.parentLink || "customfield_10018",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function mergeDefaults(input, defaults) {
|
|
105
|
+
return {
|
|
106
|
+
...input,
|
|
107
|
+
project: input.project || defaults.defaultProject,
|
|
108
|
+
assigneeEmail: input.assigneeEmail || defaults.assigneeEmail,
|
|
109
|
+
assigneeName: input.assigneeName || defaults.assigneeName,
|
|
110
|
+
labels: input.labels?.length ? input.labels : defaults.labels,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function getSessionKey(ctx: any): string;
|
|
2
|
+
export declare function parseSessionIdentity(sessionKey: string): {
|
|
3
|
+
agentId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function createToolResult(text: string, isError?: boolean): {
|
|
7
|
+
content: {
|
|
8
|
+
type: "text";
|
|
9
|
+
text: string;
|
|
10
|
+
}[];
|
|
11
|
+
details: {
|
|
12
|
+
toolResult: {
|
|
13
|
+
text: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
isError: boolean;
|
|
17
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function getSessionKey(ctx) {
|
|
2
|
+
return String(ctx?.sessionKey ||
|
|
3
|
+
ctx?.session?.key ||
|
|
4
|
+
ctx?.session?.sessionKey ||
|
|
5
|
+
ctx?.meta?.sessionKey ||
|
|
6
|
+
"main");
|
|
7
|
+
}
|
|
8
|
+
export function parseSessionIdentity(sessionKey) {
|
|
9
|
+
const parts = sessionKey.split(":");
|
|
10
|
+
if (parts.length >= 5 && parts[0] === "agent") {
|
|
11
|
+
return { agentId: parts[1] || "main", userId: parts[4] || "main" };
|
|
12
|
+
}
|
|
13
|
+
return { agentId: "main", userId: sessionKey || "main" };
|
|
14
|
+
}
|
|
15
|
+
export function createToolResult(text, isError = false) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text }],
|
|
18
|
+
details: { toolResult: { text } },
|
|
19
|
+
isError,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface JiraToolsConfig {
|
|
2
|
+
server: string;
|
|
3
|
+
email: string;
|
|
4
|
+
token: string;
|
|
5
|
+
defaultProject?: string;
|
|
6
|
+
language?: string;
|
|
7
|
+
requireClickableLinks?: boolean;
|
|
8
|
+
issueTypeMap?: {
|
|
9
|
+
task?: string;
|
|
10
|
+
epic?: string;
|
|
11
|
+
subtask?: string;
|
|
12
|
+
};
|
|
13
|
+
fieldMappings?: {
|
|
14
|
+
epicName?: string;
|
|
15
|
+
epicLink?: string;
|
|
16
|
+
parentLink?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface JiraDefaultsRecord {
|
|
20
|
+
defaultProject?: string;
|
|
21
|
+
assigneeEmail?: string;
|
|
22
|
+
assigneeName?: string;
|
|
23
|
+
labels?: string[];
|
|
24
|
+
epicKey?: string;
|
|
25
|
+
parentLink?: string;
|
|
26
|
+
templateTask?: string;
|
|
27
|
+
templateEpic?: string;
|
|
28
|
+
templateSubtask?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface JiraCreateInput {
|
|
31
|
+
project?: string;
|
|
32
|
+
summary: string;
|
|
33
|
+
body?: string;
|
|
34
|
+
objective?: string;
|
|
35
|
+
technicalDetails?: string;
|
|
36
|
+
dod?: string;
|
|
37
|
+
labels?: string[];
|
|
38
|
+
links?: string[];
|
|
39
|
+
assigneeEmail?: string;
|
|
40
|
+
assigneeName?: string;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|