feat: what is new since my last visit, and turning a comment into a ticket
Opening a ticket notes the visit and answers the one before (issue_visits), so the page marks others' comments and changes since. A comment can become a ticket of its own, as a subtask or linked; it remembers where it went. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -632,6 +632,26 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/issues/{issue}/visit": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
/**
|
||||
* I opened this ticket now
|
||||
* @description Answers when I had opened it before (null: never), to show what others did since. Private to me.
|
||||
*/
|
||||
put: operations["IssuesController_visit"];
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/issues/{issue}/follow-tree": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -1241,6 +1261,26 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/comments/{commentId}/issue": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* Make a ticket of a comment
|
||||
* @description Its text becomes the description, as a subtask of its ticket or linked to it (RELATES_TO); the comment shows where it went. Answers the new ticket. Whoever can change tickets.
|
||||
*/
|
||||
post: operations["CommentsController_turnIntoIssue"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/comments/{commentId}/resolution": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -3181,6 +3221,13 @@ export interface components {
|
||||
verdict: components["schemas"]["ReviewVerdict"];
|
||||
answer?: string;
|
||||
};
|
||||
IssueVisitDto: {
|
||||
/**
|
||||
* Format: date-time
|
||||
* @description My visit before this one (null: first time)
|
||||
*/
|
||||
previousVisit: string | null;
|
||||
};
|
||||
IssuePageDto: {
|
||||
items: components["schemas"]["IssueDto"][];
|
||||
/** @description Total matches (for pagination) */
|
||||
@@ -3377,6 +3424,11 @@ export interface components {
|
||||
/** @description Index of the winning option once closed (null on a tie) */
|
||||
winner: number | null;
|
||||
};
|
||||
CommentSpawnDto: {
|
||||
id: string;
|
||||
/** @example WEB-45 */
|
||||
key: string;
|
||||
};
|
||||
CommentDto: {
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
@@ -3407,6 +3459,8 @@ export interface components {
|
||||
resolvedBy: components["schemas"]["UserRefDto"] | null;
|
||||
/** Format: date-time */
|
||||
resolvedAt: string | null;
|
||||
/** @description Tickets made from it («Convertir en ticket»), oldest first */
|
||||
spawned: components["schemas"]["CommentSpawnDto"][];
|
||||
/** Format: date-time */
|
||||
createdAt: string;
|
||||
/** Format: date-time */
|
||||
@@ -3490,6 +3544,17 @@ export interface components {
|
||||
*/
|
||||
decide: boolean;
|
||||
};
|
||||
TurnCommentIntoIssueDto: {
|
||||
/** @example También falla en Android */
|
||||
title: string;
|
||||
/** @description One of the project's ticket type ids */
|
||||
type: string;
|
||||
/**
|
||||
* @description Hang it from the ticket of the comment; otherwise it is linked to it (RELATES_TO)
|
||||
* @default false
|
||||
*/
|
||||
asSubtask: boolean;
|
||||
};
|
||||
ResolveThreadDto: {
|
||||
/** @description What was settled: added as a reply marked as a decision before it closes */
|
||||
conclusion?: string;
|
||||
@@ -6732,6 +6797,82 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
IssuesController_visit: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Ticket id or key */
|
||||
issue: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["IssueVisitDto"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Someone changed it at the same time: retry */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Business rule (e.g. a transition the workflow forbids) */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
IssuesController_followTree: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -9935,6 +10076,85 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
CommentsController_turnIntoIssue: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
commentId: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["TurnCommentIntoIssueDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["CommandResultDto"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Someone changed it at the same time: retry */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Business rule (e.g. a transition the workflow forbids) */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
CommentsController_resolveThread: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
@@ -599,6 +599,37 @@ export class IssuesResolver {
|
||||
);
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'turn_comment_into_issue',
|
||||
description:
|
||||
"Make a ticket of a comment that turned out to be work of its own: its text becomes the description, as a subtask of the comment's ticket (asSubtask) or linked to it. Read type ids with get_project. Answers the new ticket's key.",
|
||||
paramsSchema: z.object({
|
||||
commentId: z.string().min(1),
|
||||
title: z.string().min(1).max(200),
|
||||
type: z.string().min(1).describe("One of the project's ticket type ids"),
|
||||
asSubtask: z.boolean().default(false),
|
||||
}),
|
||||
annotations: WRITE,
|
||||
})
|
||||
turnCommentIntoIssue(
|
||||
{
|
||||
commentId,
|
||||
title,
|
||||
type,
|
||||
asSubtask,
|
||||
}: { commentId: string; title: string; type: string; asSubtask: boolean },
|
||||
ctx: McpContext,
|
||||
) {
|
||||
return toolResult(() =>
|
||||
this.api.data(
|
||||
this.api.as(ctx).POST('/api/comments/{commentId}/issue', {
|
||||
params: { path: { commentId } },
|
||||
body: { title, type, asSubtask },
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'link_issues',
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user