@relipa/ai-flow-kit 0.2.0-beta.1 → 0.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/bin/aiflow.js +8 -4
- package/custom/skills/ba-skills/skill-ba-initial-analysis-template-v1.md +7 -5
- package/custom/skills/ba-skills/skill-ba-initial-analysis-v1.md +6 -1
- package/custom/skills/ba-skills/skill-ba-qna-template-v1.md +6 -5
- package/custom/skills/ba-skills/skill-ba-qna-v1.md +17 -5
- package/custom/skills/create-system-requirement/SKILL.md +104 -26
- package/custom/skills/ingest-data/SKILL.md +56 -8
- package/custom/templates/shared/create-spec-workflow.md +93 -31
- package/custom/templates/shared/create-testcase-workflow.md +30 -2
- package/custom/templates/shared/gate-workflow.md +5 -4
- package/docs/common/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/scripts/link-resolver.js +68 -28
- package/scripts/task.js +25 -8
- package/scripts/ticket-writer.js +72 -3
package/scripts/ticket-writer.js
CHANGED
|
@@ -24,7 +24,9 @@ const {
|
|
|
24
24
|
fetchBacklogIssueTypes,
|
|
25
25
|
fetchBacklogPriorities,
|
|
26
26
|
createBacklogIssue,
|
|
27
|
+
getBacklogIssue,
|
|
27
28
|
createJiraIssue,
|
|
29
|
+
getJiraIssue,
|
|
28
30
|
} = linkResolver;
|
|
29
31
|
|
|
30
32
|
// Only these keys can be written via `ak credentials set` — dedicated adapter credentials
|
|
@@ -104,13 +106,23 @@ async function pickBacklogDefaults(domain, apiKeyRead, projectId) {
|
|
|
104
106
|
* "target": "backlog" | "jira",
|
|
105
107
|
* "projectId": "12345", // optional — Backlog only, overrides BACKLOG_DEFAULT_PROJECT_ID
|
|
106
108
|
* "projectKey": "PROJ", // optional — Jira only, overrides JIRA_DEFAULT_PROJECT_KEY
|
|
109
|
+
* "parentTicket": { // optional — ingest-data SKILL.md § 5.6b "Ticket cha"
|
|
110
|
+
* "mode": "create" | "existing" | "none",
|
|
111
|
+
* "title": "...", // required when mode === "create"
|
|
112
|
+
* "description": "...", // optional when mode === "create"
|
|
113
|
+
* "existingId": "..." // required when mode === "existing" (Backlog key/id or Jira key)
|
|
114
|
+
* },
|
|
107
115
|
* "tasks": [
|
|
108
|
-
* { "type": "spec|coding|test|other", "title": "...", "description": "..." }
|
|
116
|
+
* { "type": "spec|system-requirement|impact-analysis|coding|test|execute-test|other", "title": "...", "description": "..." }
|
|
109
117
|
* ]
|
|
110
118
|
* }
|
|
111
119
|
*
|
|
112
120
|
* `description` is expected to already be fully composed by the AI per the ticket template in
|
|
113
121
|
* PM Workflow_v1.0.md §6.1 (Nội dung task + Nguồn tham chiếu) — this command posts it as-is.
|
|
122
|
+
*
|
|
123
|
+
* When `parentTicket.mode` is "create" or "existing", every task in `tasks` is created as a
|
|
124
|
+
* child of that parent (Backlog `parentIssueId` / Jira `parent`) — see link-resolver.js for the
|
|
125
|
+
* caveat that this depends on the target project supporting issue hierarchy/subtasks.
|
|
114
126
|
*/
|
|
115
127
|
async function createTicketsCommand(inputFile, options = {}) {
|
|
116
128
|
const emit = (obj) => {
|
|
@@ -119,6 +131,7 @@ async function createTicketsCommand(inputFile, options = {}) {
|
|
|
119
131
|
console.log(chalk.red(`✗ ${obj.message || obj.error}`));
|
|
120
132
|
} else {
|
|
121
133
|
console.log(chalk.green(`✓ ${obj.results.filter(r => r.ok).length}/${obj.results.length} ticket(s) created on ${obj.target}.`));
|
|
134
|
+
if (obj.parent) console.log(` ${chalk.cyan('↳ parent:')} ${obj.parent.ticketId} ${chalk.gray(obj.parent.url)}`);
|
|
122
135
|
for (const r of obj.results) {
|
|
123
136
|
console.log(r.ok ? ` ${chalk.green('✓')} ${r.ticketId} ${chalk.gray(r.title)}` : ` ${chalk.red('✗')} ${chalk.gray(r.title)} — ${r.error}`);
|
|
124
137
|
}
|
|
@@ -173,6 +186,34 @@ async function createTicketsCommand(inputFile, options = {}) {
|
|
|
173
186
|
return;
|
|
174
187
|
}
|
|
175
188
|
|
|
189
|
+
const parentTicket = input.parentTicket || { mode: 'none' };
|
|
190
|
+
let parentRef = null;
|
|
191
|
+
if (parentTicket.mode === 'create') {
|
|
192
|
+
try {
|
|
193
|
+
const createdParent = await createBacklogIssue(write.domain, write.apiKeyWrite, {
|
|
194
|
+
projectId,
|
|
195
|
+
summary: parentTicket.title,
|
|
196
|
+
description: parentTicket.description || '',
|
|
197
|
+
issueTypeId: defaults.defaultIssueTypeId,
|
|
198
|
+
priorityId: defaults.defaultPriorityId,
|
|
199
|
+
});
|
|
200
|
+
parentRef = { ticketId: createdParent.ticketId, internalId: createdParent.internalId, url: createdParent.url };
|
|
201
|
+
} catch (err) {
|
|
202
|
+
emit({ ok: false, error: 'parent-create-failed', message: `Không tạo được ticket cha trên Backlog: ${err.message}` });
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
} else if (parentTicket.mode === 'existing') {
|
|
207
|
+
try {
|
|
208
|
+
const existing = await getBacklogIssue(write.domain, write.apiKeyRead, parentTicket.existingId);
|
|
209
|
+
parentRef = { ticketId: existing.issueKey, internalId: existing.id, url: `https://${write.domain}/view/${existing.issueKey}` };
|
|
210
|
+
} catch (err) {
|
|
211
|
+
emit({ ok: false, error: 'parent-not-found', message: `Không tìm thấy ticket cha "${parentTicket.existingId}" trên Backlog: ${err.message}` });
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
176
217
|
const results = [];
|
|
177
218
|
for (const task of tasks) {
|
|
178
219
|
try {
|
|
@@ -182,13 +223,14 @@ async function createTicketsCommand(inputFile, options = {}) {
|
|
|
182
223
|
description: task.description,
|
|
183
224
|
issueTypeId: task.issueTypeId || defaults.defaultIssueTypeId,
|
|
184
225
|
priorityId: task.priorityId || defaults.defaultPriorityId,
|
|
226
|
+
parentIssueId: parentRef ? parentRef.internalId : undefined,
|
|
185
227
|
});
|
|
186
228
|
results.push({ type: task.type, title: task.title, ok: true, ticketId: created.ticketId, url: created.url });
|
|
187
229
|
} catch (err) {
|
|
188
230
|
results.push({ type: task.type, title: task.title, ok: false, error: err.message });
|
|
189
231
|
}
|
|
190
232
|
}
|
|
191
|
-
emit({ ok: true, target, projectId, results });
|
|
233
|
+
emit({ ok: true, target, projectId, parent: parentRef, results });
|
|
192
234
|
return;
|
|
193
235
|
}
|
|
194
236
|
|
|
@@ -206,6 +248,32 @@ async function createTicketsCommand(inputFile, options = {}) {
|
|
|
206
248
|
return;
|
|
207
249
|
}
|
|
208
250
|
|
|
251
|
+
const parentTicket = input.parentTicket || { mode: 'none' };
|
|
252
|
+
let parentRef = null;
|
|
253
|
+
if (parentTicket.mode === 'create') {
|
|
254
|
+
try {
|
|
255
|
+
const createdParent = await createJiraIssue(write.domain, write.emailWrite, write.apiTokenWrite, {
|
|
256
|
+
projectKey,
|
|
257
|
+
summary: parentTicket.title,
|
|
258
|
+
description: parentTicket.description || '',
|
|
259
|
+
});
|
|
260
|
+
parentRef = { ticketId: createdParent.ticketId, url: createdParent.url };
|
|
261
|
+
} catch (err) {
|
|
262
|
+
emit({ ok: false, error: 'parent-create-failed', message: `Không tạo được ticket cha trên Jira: ${err.message}` });
|
|
263
|
+
process.exitCode = 1;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
} else if (parentTicket.mode === 'existing') {
|
|
267
|
+
try {
|
|
268
|
+
const existing = await getJiraIssue(write.domain, write.emailWrite, write.apiTokenWrite, parentTicket.existingId);
|
|
269
|
+
parentRef = { ticketId: existing.key, url: `https://${write.domain}/browse/${existing.key}` };
|
|
270
|
+
} catch (err) {
|
|
271
|
+
emit({ ok: false, error: 'parent-not-found', message: `Không tìm thấy ticket cha "${parentTicket.existingId}" trên Jira: ${err.message}` });
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
209
277
|
const results = [];
|
|
210
278
|
for (const task of tasks) {
|
|
211
279
|
try {
|
|
@@ -214,13 +282,14 @@ async function createTicketsCommand(inputFile, options = {}) {
|
|
|
214
282
|
summary: task.title,
|
|
215
283
|
description: task.description,
|
|
216
284
|
issueTypeName: task.issueType || 'Task',
|
|
285
|
+
parentKey: parentRef ? parentRef.ticketId : undefined,
|
|
217
286
|
});
|
|
218
287
|
results.push({ type: task.type, title: task.title, ok: true, ticketId: created.ticketId, url: created.url });
|
|
219
288
|
} catch (err) {
|
|
220
289
|
results.push({ type: task.type, title: task.title, ok: false, error: err.message });
|
|
221
290
|
}
|
|
222
291
|
}
|
|
223
|
-
emit({ ok: true, target, projectKey, results });
|
|
292
|
+
emit({ ok: true, target, projectKey, parent: parentRef, results });
|
|
224
293
|
}
|
|
225
294
|
|
|
226
295
|
/**
|