feat: «Mi día», the few tickets I pick for today

A home panel with the tickets picked for today (10 at most), crossed out
as they get done and counted («1 de 3 terminados»), my open tickets to
add with one click, and what was left unfinished on my last day to carry
over. «☀ Mi día» on a ticket adds it or takes it out. The day is the
person's own (YYYY-MM-DD from the browser); private, no history (Mongo
`my_days`).

API: myDay(day), addToMyDay, removeFromMyDay; GET /api/me/day,
PUT/DELETE /api/me/day/{issue}. MCP: my_day.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 20:18:10 +02:00
parent b88296c22b
commit dfb5f973f4
31 changed files with 2183 additions and 50 deletions

View File

@@ -502,6 +502,44 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/me/day": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* «Mi día»: the tickets I picked for today
* @description And what was left unfinished on my latest earlier day.
*/
get: operations["IssuesController_myDay"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/me/day/{issue}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
/** Add a ticket to «Mi día» (10 at most) */
put: operations["IssuesController_addToMyDay"];
post?: never;
/** Take a ticket out of «Mi día» */
delete: operations["IssuesController_removeFromMyDay"];
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/me/reminders": {
parameters: {
query?: never;
@@ -2951,6 +2989,16 @@ export interface components {
*/
at: string;
};
MyDayLeftoverDto: {
day: string;
issues: components["schemas"]["IssueDto"][];
};
MyDayDto: {
/** @description YYYY-MM-DD, in my time zone */
day: string;
issues: components["schemas"]["IssueDto"][];
leftover: components["schemas"]["MyDayLeftoverDto"] | null;
};
IssueReminderDto: {
/** Format: date-time */
at: string;
@@ -5809,6 +5857,218 @@ export interface operations {
};
};
};
IssuesController_myDay: {
parameters: {
query: {
/** @description Today, in my time zone */
day: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["MyDayDto"];
};
};
/** @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"];
};
};
};
};
IssuesController_addToMyDay: {
parameters: {
query: {
/** @description Today, in my time zone */
day: string;
};
header?: never;
path: {
/** @description Ticket id or key */
issue: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @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_removeFromMyDay: {
parameters: {
query: {
/** @description Today, in my time zone */
day: string;
};
header?: never;
path: {
/** @description Ticket id or key */
issue: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @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_myReminders: {
parameters: {
query?: never;

View File

@@ -783,6 +783,50 @@ export class IssuesResolver {
);
}
@Tool({
name: 'my_day',
description:
'«Mi día»: the tickets I picked for today (done ones marked), and what was left unfinished on my last day. Optionally add or remove one first. day is today in my time zone, YYYY-MM-DD.',
paramsSchema: z.object({
day: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
add: issueRef.optional(),
remove: issueRef.optional(),
}),
annotations: WRITE,
})
myDay(
params: { day: string; add?: string; remove?: string },
ctx: McpContext,
) {
const client = this.api.as(ctx);
const query = { day: params.day };
return toolResult(async () => {
if (params.add)
await this.api.data(
client.PUT('/api/me/day/{issue}', {
params: { path: { issue: params.add }, query },
}),
);
if (params.remove)
await this.api.data(
client.DELETE('/api/me/day/{issue}', {
params: { path: { issue: params.remove }, query },
}),
);
const myDay = await this.api.data(
client.GET('/api/me/day', { params: { query } }),
);
return {
day: myDay.day,
issues: myDay.issues.map(issueLine),
leftover: myDay.leftover && {
day: myDay.leftover.day,
issues: myDay.leftover.issues.map(issueLine),
},
};
});
}
@Tool({
name: 'remind_me',
description: