@mgsoftwarebv/mcp-server-bridge 3.3.7 → 3.4.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 +2 -13
- package/dist/index.js +147 -1243
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -92216,13 +92216,13 @@ function construct(client, config3 = {}) {
|
|
|
92216
92216
|
};
|
|
92217
92217
|
}
|
|
92218
92218
|
const session = new PostgresJsSession(client, dialect, schema, { logger: logger2, cache: config3.cache });
|
|
92219
|
-
const
|
|
92220
|
-
|
|
92221
|
-
|
|
92222
|
-
if (
|
|
92223
|
-
|
|
92219
|
+
const db3 = new PostgresJsDatabase(dialect, session, schema);
|
|
92220
|
+
db3.$client = client;
|
|
92221
|
+
db3.$cache = config3.cache;
|
|
92222
|
+
if (db3.$cache) {
|
|
92223
|
+
db3.$cache["invalidate"] = config3.cache?.onMutate;
|
|
92224
92224
|
}
|
|
92225
|
-
return
|
|
92225
|
+
return db3;
|
|
92226
92226
|
}
|
|
92227
92227
|
function drizzle(...params) {
|
|
92228
92228
|
if (typeof params[0] === "string") {
|
|
@@ -103378,12 +103378,12 @@ var createJobDb = () => {
|
|
|
103378
103378
|
connect_timeout: 10
|
|
103379
103379
|
// 10 second connection timeout
|
|
103380
103380
|
});
|
|
103381
|
-
const
|
|
103381
|
+
const db3 = drizzle(jobPool, {
|
|
103382
103382
|
schema: schema_exports,
|
|
103383
103383
|
casing: "snake_case"
|
|
103384
103384
|
});
|
|
103385
103385
|
return {
|
|
103386
|
-
db:
|
|
103386
|
+
db: db3,
|
|
103387
103387
|
disconnect: () => jobPool.end()
|
|
103388
103388
|
};
|
|
103389
103389
|
};
|
|
@@ -103488,16 +103488,6 @@ async function getUserAccessibleCustomerIds(userId) {
|
|
|
103488
103488
|
}
|
|
103489
103489
|
return [...all];
|
|
103490
103490
|
}
|
|
103491
|
-
async function resolveAiSessionId(prefix, teamIds) {
|
|
103492
|
-
if (teamIds.length === 0) return null;
|
|
103493
|
-
const rows = await db.select({ id: aiSessions.id }).from(aiSessions).where(
|
|
103494
|
-
and(
|
|
103495
|
-
teamIds.length === 1 ? eq(aiSessions.teamId, teamIds[0]) : sql`${aiSessions.teamId} = ANY(${teamIds}::uuid[])`,
|
|
103496
|
-
sql`${aiSessions.id}::text LIKE ${`${prefix}%`}`
|
|
103497
|
-
)
|
|
103498
|
-
).limit(1);
|
|
103499
|
-
return rows[0]?.id ?? null;
|
|
103500
|
-
}
|
|
103501
103491
|
|
|
103502
103492
|
// src/auth.ts
|
|
103503
103493
|
var authStorage = new AsyncLocalStorage();
|
|
@@ -105378,10 +105368,6 @@ var Server = class extends Protocol {
|
|
|
105378
105368
|
function asToolArgs(input) {
|
|
105379
105369
|
return input ?? {};
|
|
105380
105370
|
}
|
|
105381
|
-
function roundToNearest15Minutes(minutes) {
|
|
105382
|
-
if (minutes <= 0) return 0;
|
|
105383
|
-
return Math.round(minutes / 15) * 15;
|
|
105384
|
-
}
|
|
105385
105371
|
function buildTicketAccessPredicate(teamIds, projectIds, customerIds) {
|
|
105386
105372
|
const branches = [];
|
|
105387
105373
|
if (teamIds.length > 0) branches.push(inArray(schema_exports.tickets.teamId, teamIds));
|
|
@@ -106291,242 +106277,6 @@ var TOOLS = [
|
|
|
106291
106277
|
required: ["documentId", "invoiceId"]
|
|
106292
106278
|
}
|
|
106293
106279
|
},
|
|
106294
|
-
{
|
|
106295
|
-
name: "start-ai-session-smart",
|
|
106296
|
-
description: "Start a new AI development session with automatic tracking",
|
|
106297
|
-
inputSchema: {
|
|
106298
|
-
type: "object",
|
|
106299
|
-
properties: {
|
|
106300
|
-
teamId: teamIdProp,
|
|
106301
|
-
ticketId: { type: "string" },
|
|
106302
|
-
ticketUrl: { type: "string", description: "URL to the ticket" },
|
|
106303
|
-
cursorSessionId: {
|
|
106304
|
-
type: "string",
|
|
106305
|
-
description: "Cursor session identifier"
|
|
106306
|
-
},
|
|
106307
|
-
totalEstimatedMinutes: {
|
|
106308
|
-
type: "number",
|
|
106309
|
-
description: "Total estimated time in minutes (senior dev WITHOUT AI, rounded to 15 min)"
|
|
106310
|
-
},
|
|
106311
|
-
complexityScore: {
|
|
106312
|
-
type: "number",
|
|
106313
|
-
minimum: 1,
|
|
106314
|
-
maximum: 10,
|
|
106315
|
-
description: "Estimated complexity from 1-10"
|
|
106316
|
-
}
|
|
106317
|
-
},
|
|
106318
|
-
required: ["ticketId", "totalEstimatedMinutes"]
|
|
106319
|
-
}
|
|
106320
|
-
},
|
|
106321
|
-
{
|
|
106322
|
-
name: "track-manual-follow-up",
|
|
106323
|
-
description: "Track manual follow-up prompt by developer",
|
|
106324
|
-
inputSchema: {
|
|
106325
|
-
type: "object",
|
|
106326
|
-
properties: {
|
|
106327
|
-
teamId: teamIdProp,
|
|
106328
|
-
aiSessionId: { type: "string" },
|
|
106329
|
-
originalPrompt: { type: "string" },
|
|
106330
|
-
aiResponse: { type: "string" },
|
|
106331
|
-
developerFollowUp: { type: "string" },
|
|
106332
|
-
followUpReason: {
|
|
106333
|
-
type: "string",
|
|
106334
|
-
enum: [
|
|
106335
|
-
"incomplete_result",
|
|
106336
|
-
"wrong_approach",
|
|
106337
|
-
"needs_clarification",
|
|
106338
|
-
"error_in_code"
|
|
106339
|
-
]
|
|
106340
|
-
},
|
|
106341
|
-
outcome: {
|
|
106342
|
-
type: "string",
|
|
106343
|
-
enum: ["success", "partial_success", "still_failed"],
|
|
106344
|
-
default: "success"
|
|
106345
|
-
},
|
|
106346
|
-
estimatedMinutes: {
|
|
106347
|
-
type: "number",
|
|
106348
|
-
description: "Estimated time needed for this follow-up work (think as senior dev WITHOUT AI, in minutes)"
|
|
106349
|
-
},
|
|
106350
|
-
workDescription: {
|
|
106351
|
-
type: "string",
|
|
106352
|
-
description: "Detailed work description generated by AI (2-3 sentences, summarizing all work done in session including follow-ups)"
|
|
106353
|
-
}
|
|
106354
|
-
},
|
|
106355
|
-
required: [
|
|
106356
|
-
"aiSessionId",
|
|
106357
|
-
"originalPrompt",
|
|
106358
|
-
"aiResponse",
|
|
106359
|
-
"developerFollowUp",
|
|
106360
|
-
"followUpReason",
|
|
106361
|
-
"estimatedMinutes",
|
|
106362
|
-
"workDescription"
|
|
106363
|
-
]
|
|
106364
|
-
}
|
|
106365
|
-
},
|
|
106366
|
-
{
|
|
106367
|
-
name: "get-session-context",
|
|
106368
|
-
description: "Get current session context for follow-up continuity",
|
|
106369
|
-
inputSchema: {
|
|
106370
|
-
type: "object",
|
|
106371
|
-
properties: {
|
|
106372
|
-
teamId: teamIdProp,
|
|
106373
|
-
aiSessionId: { type: "string" },
|
|
106374
|
-
includeTicketData: { type: "boolean", default: true },
|
|
106375
|
-
includeTodoProgress: { type: "boolean", default: true },
|
|
106376
|
-
includeFollowUpHistory: { type: "boolean", default: false }
|
|
106377
|
-
},
|
|
106378
|
-
required: ["aiSessionId"]
|
|
106379
|
-
}
|
|
106380
|
-
},
|
|
106381
|
-
{
|
|
106382
|
-
name: "sync-session-todos",
|
|
106383
|
-
description: "Synchronize todo list with AI session (replace existing) or add new todos",
|
|
106384
|
-
inputSchema: {
|
|
106385
|
-
type: "object",
|
|
106386
|
-
properties: {
|
|
106387
|
-
teamId: teamIdProp,
|
|
106388
|
-
aiSessionId: { type: "string" },
|
|
106389
|
-
todos: {
|
|
106390
|
-
type: "array",
|
|
106391
|
-
items: {
|
|
106392
|
-
type: "object",
|
|
106393
|
-
properties: {
|
|
106394
|
-
todoId: {
|
|
106395
|
-
type: "string",
|
|
106396
|
-
description: "Optional external todo ID for tracking"
|
|
106397
|
-
},
|
|
106398
|
-
content: { type: "string" },
|
|
106399
|
-
status: {
|
|
106400
|
-
type: "string",
|
|
106401
|
-
enum: ["pending", "in_progress", "completed", "cancelled"]
|
|
106402
|
-
},
|
|
106403
|
-
estimatedMinutes: { type: "number" }
|
|
106404
|
-
},
|
|
106405
|
-
required: ["content", "status"]
|
|
106406
|
-
}
|
|
106407
|
-
},
|
|
106408
|
-
replaceAll: {
|
|
106409
|
-
type: "boolean",
|
|
106410
|
-
default: true,
|
|
106411
|
-
description: "If true, replace all existing todos. If false, add new todos to existing ones"
|
|
106412
|
-
}
|
|
106413
|
-
},
|
|
106414
|
-
required: ["aiSessionId", "todos"]
|
|
106415
|
-
}
|
|
106416
|
-
},
|
|
106417
|
-
{
|
|
106418
|
-
name: "add-follow-up-todos",
|
|
106419
|
-
description: "Add new todos from follow-up (without replacing existing ones)",
|
|
106420
|
-
inputSchema: {
|
|
106421
|
-
type: "object",
|
|
106422
|
-
properties: {
|
|
106423
|
-
teamId: teamIdProp,
|
|
106424
|
-
aiSessionId: { type: "string" },
|
|
106425
|
-
newTodos: {
|
|
106426
|
-
type: "array",
|
|
106427
|
-
items: {
|
|
106428
|
-
type: "object",
|
|
106429
|
-
properties: {
|
|
106430
|
-
content: { type: "string" },
|
|
106431
|
-
status: {
|
|
106432
|
-
type: "string",
|
|
106433
|
-
enum: ["pending", "in_progress"],
|
|
106434
|
-
default: "pending"
|
|
106435
|
-
},
|
|
106436
|
-
estimatedMinutes: { type: "number" },
|
|
106437
|
-
addedInFollowUp: { type: "boolean", default: true }
|
|
106438
|
-
},
|
|
106439
|
-
required: ["content"]
|
|
106440
|
-
}
|
|
106441
|
-
},
|
|
106442
|
-
followUpReason: {
|
|
106443
|
-
type: "string",
|
|
106444
|
-
description: "Why were these todos added in follow-up"
|
|
106445
|
-
}
|
|
106446
|
-
},
|
|
106447
|
-
required: ["aiSessionId", "newTodos"]
|
|
106448
|
-
}
|
|
106449
|
-
},
|
|
106450
|
-
{
|
|
106451
|
-
name: "update-session-status",
|
|
106452
|
-
description: "Update AI session status and completion info",
|
|
106453
|
-
inputSchema: {
|
|
106454
|
-
type: "object",
|
|
106455
|
-
properties: {
|
|
106456
|
-
teamId: teamIdProp,
|
|
106457
|
-
aiSessionId: { type: "string" },
|
|
106458
|
-
status: {
|
|
106459
|
-
type: "string",
|
|
106460
|
-
enum: ["started", "in_progress", "paused", "completed", "failed"]
|
|
106461
|
-
},
|
|
106462
|
-
actualTimeMinutes: { type: "number" },
|
|
106463
|
-
completionNotes: { type: "string" }
|
|
106464
|
-
},
|
|
106465
|
-
required: ["aiSessionId", "status"]
|
|
106466
|
-
}
|
|
106467
|
-
},
|
|
106468
|
-
{
|
|
106469
|
-
name: "get-completion-context",
|
|
106470
|
-
description: "Get all context needed for Cursor AI to generate customer response",
|
|
106471
|
-
inputSchema: {
|
|
106472
|
-
type: "object",
|
|
106473
|
-
properties: {
|
|
106474
|
-
teamId: teamIdProp,
|
|
106475
|
-
aiSessionId: { type: "string" },
|
|
106476
|
-
includeFollowUps: { type: "boolean", default: true },
|
|
106477
|
-
includeTimeMetrics: { type: "boolean", default: true },
|
|
106478
|
-
includeTodos: { type: "boolean", default: true }
|
|
106479
|
-
},
|
|
106480
|
-
required: ["aiSessionId"]
|
|
106481
|
-
}
|
|
106482
|
-
},
|
|
106483
|
-
{
|
|
106484
|
-
name: "save-customer-response",
|
|
106485
|
-
description: "Save customer response generated by Cursor AI",
|
|
106486
|
-
inputSchema: {
|
|
106487
|
-
type: "object",
|
|
106488
|
-
properties: {
|
|
106489
|
-
teamId: teamIdProp,
|
|
106490
|
-
aiSessionId: { type: "string" },
|
|
106491
|
-
customerResponse: {
|
|
106492
|
-
type: "string",
|
|
106493
|
-
description: "Customer response generated by Cursor AI"
|
|
106494
|
-
},
|
|
106495
|
-
responseType: {
|
|
106496
|
-
type: "string",
|
|
106497
|
-
enum: ["completion", "progress_update", "needs_clarification"],
|
|
106498
|
-
default: "completion"
|
|
106499
|
-
}
|
|
106500
|
-
},
|
|
106501
|
-
required: ["aiSessionId", "customerResponse"]
|
|
106502
|
-
}
|
|
106503
|
-
},
|
|
106504
|
-
{
|
|
106505
|
-
name: "complete-ai-session",
|
|
106506
|
-
description: "Complete AI session with work summary - time calculated automatically",
|
|
106507
|
-
inputSchema: {
|
|
106508
|
-
type: "object",
|
|
106509
|
-
properties: {
|
|
106510
|
-
teamId: teamIdProp,
|
|
106511
|
-
aiSessionId: { type: "string" },
|
|
106512
|
-
workCompleted: {
|
|
106513
|
-
type: "array",
|
|
106514
|
-
items: { type: "string" },
|
|
106515
|
-
description: "List of completed tasks/todos in English"
|
|
106516
|
-
},
|
|
106517
|
-
technicalSummary: {
|
|
106518
|
-
type: "string",
|
|
106519
|
-
description: "Technical summary of work done in English"
|
|
106520
|
-
},
|
|
106521
|
-
invoiceDescription: {
|
|
106522
|
-
type: "string",
|
|
106523
|
-
description: "Short invoice-friendly description in the language of the ticket (2-3 sentences max, suitable for billing)"
|
|
106524
|
-
},
|
|
106525
|
-
efficiencyNotes: { type: "string" }
|
|
106526
|
-
},
|
|
106527
|
-
required: ["aiSessionId", "workCompleted"]
|
|
106528
|
-
}
|
|
106529
|
-
},
|
|
106530
106280
|
{
|
|
106531
106281
|
name: "log-hours",
|
|
106532
106282
|
description: "Analyze current chat conversation and log hours as draft tracker entry. AI analyzes chat context to estimate hours as a senior developer would (without AI assistance). Cursor AI matches workspace name to correct project from list (optional).",
|
|
@@ -106542,10 +106292,6 @@ var TOOLS = [
|
|
|
106542
106292
|
type: "string",
|
|
106543
106293
|
description: "Ticket ID (UUID) - Optional. Cursor AI should call get-tickets (filtered by project) to try matching chat context to an open ticket. Only include if a clear match is found."
|
|
106544
106294
|
},
|
|
106545
|
-
aiSessionId: {
|
|
106546
|
-
type: "string",
|
|
106547
|
-
description: "AI Session ID - Optional. If a ticket has an active AI dev session, include this ID to link the hours to that session."
|
|
106548
|
-
},
|
|
106549
106295
|
workDescription: {
|
|
106550
106296
|
type: "string",
|
|
106551
106297
|
description: "Short description of the work done (for the tracker entry)"
|
|
@@ -112400,7 +112146,6 @@ async function handleLogHours(input) {
|
|
|
112400
112146
|
const {
|
|
112401
112147
|
projectId,
|
|
112402
112148
|
ticketId,
|
|
112403
|
-
aiSessionId,
|
|
112404
112149
|
workDescription,
|
|
112405
112150
|
estimatedHours,
|
|
112406
112151
|
chatContextSummary
|
|
@@ -112409,7 +112154,6 @@ async function handleLogHours(input) {
|
|
|
112409
112154
|
if (!scope.ok) return scope.response;
|
|
112410
112155
|
let project = null;
|
|
112411
112156
|
let ticket = null;
|
|
112412
|
-
let aiSession = null;
|
|
112413
112157
|
if (projectId) {
|
|
112414
112158
|
if (!scope.projectIds.includes(projectId)) {
|
|
112415
112159
|
throw new Error(
|
|
@@ -112451,15 +112195,6 @@ async function handleLogHours(input) {
|
|
|
112451
112195
|
}
|
|
112452
112196
|
ticket = ticketData;
|
|
112453
112197
|
}
|
|
112454
|
-
if (aiSessionId) {
|
|
112455
|
-
const [sessionData] = await db.select({
|
|
112456
|
-
id: schema_exports.aiSessions.id,
|
|
112457
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
112458
|
-
status: schema_exports.aiSessions.status
|
|
112459
|
-
}).from(schema_exports.aiSessions).where(eq(schema_exports.aiSessions.id, aiSessionId)).limit(1);
|
|
112460
|
-
if (!sessionData) throw new Error(`AI Session not found: ${aiSessionId}.`);
|
|
112461
|
-
aiSession = sessionData;
|
|
112462
|
-
}
|
|
112463
112198
|
let insertTeamId = ticket?.teamId ?? project?.teamId ?? null;
|
|
112464
112199
|
if (!insertTeamId) {
|
|
112465
112200
|
const resolved = await resolveTeamId(input.teamId);
|
|
@@ -112471,40 +112206,24 @@ async function handleLogHours(input) {
|
|
|
112471
112206
|
let agendaEntry = null;
|
|
112472
112207
|
let wasUpdated = false;
|
|
112473
112208
|
let consolidatedCount = 0;
|
|
112474
|
-
if (
|
|
112209
|
+
if (ticket?.id) {
|
|
112475
112210
|
let existingEntries = [];
|
|
112476
|
-
|
|
112211
|
+
const linkedEvents = await db.select({
|
|
112212
|
+
timesheetEventId: schema_exports.timesheetEventTickets.timesheetEventId
|
|
112213
|
+
}).from(schema_exports.timesheetEventTickets).where(eq(schema_exports.timesheetEventTickets.ticketId, ticket.id));
|
|
112214
|
+
const eventIds = linkedEvents.map((e6) => e6.timesheetEventId);
|
|
112215
|
+
if (eventIds.length > 0) {
|
|
112477
112216
|
existingEntries = await db.select({
|
|
112478
112217
|
id: schema_exports.timesheetEvents.id,
|
|
112479
112218
|
trackedDuration: schema_exports.timesheetEvents.trackedDuration,
|
|
112480
|
-
projectId: schema_exports.timesheetEvents.projectId
|
|
112481
|
-
aiSessionId: schema_exports.timesheetEvents.aiSessionId
|
|
112219
|
+
projectId: schema_exports.timesheetEvents.projectId
|
|
112482
112220
|
}).from(schema_exports.timesheetEvents).where(
|
|
112483
112221
|
and(
|
|
112222
|
+
inArray(schema_exports.timesheetEvents.id, eventIds),
|
|
112484
112223
|
eq(schema_exports.timesheetEvents.status, "draft"),
|
|
112485
|
-
eq(schema_exports.timesheetEvents.userId, ctx.userId)
|
|
112486
|
-
eq(schema_exports.timesheetEvents.aiSessionId, aiSession.id)
|
|
112224
|
+
eq(schema_exports.timesheetEvents.userId, ctx.userId)
|
|
112487
112225
|
)
|
|
112488
112226
|
).orderBy(desc(schema_exports.timesheetEvents.createdAt));
|
|
112489
|
-
} else if (ticket?.id) {
|
|
112490
|
-
const linkedEvents = await db.select({
|
|
112491
|
-
timesheetEventId: schema_exports.timesheetEventTickets.timesheetEventId
|
|
112492
|
-
}).from(schema_exports.timesheetEventTickets).where(eq(schema_exports.timesheetEventTickets.ticketId, ticket.id));
|
|
112493
|
-
const eventIds = linkedEvents.map((e6) => e6.timesheetEventId);
|
|
112494
|
-
if (eventIds.length > 0) {
|
|
112495
|
-
existingEntries = await db.select({
|
|
112496
|
-
id: schema_exports.timesheetEvents.id,
|
|
112497
|
-
trackedDuration: schema_exports.timesheetEvents.trackedDuration,
|
|
112498
|
-
projectId: schema_exports.timesheetEvents.projectId,
|
|
112499
|
-
aiSessionId: schema_exports.timesheetEvents.aiSessionId
|
|
112500
|
-
}).from(schema_exports.timesheetEvents).where(
|
|
112501
|
-
and(
|
|
112502
|
-
inArray(schema_exports.timesheetEvents.id, eventIds),
|
|
112503
|
-
eq(schema_exports.timesheetEvents.status, "draft"),
|
|
112504
|
-
eq(schema_exports.timesheetEvents.userId, ctx.userId)
|
|
112505
|
-
)
|
|
112506
|
-
).orderBy(desc(schema_exports.timesheetEvents.createdAt));
|
|
112507
|
-
}
|
|
112508
112227
|
}
|
|
112509
112228
|
if (existingEntries.length > 0) {
|
|
112510
112229
|
const existingEntry = existingEntries[0];
|
|
@@ -112523,8 +112242,7 @@ async function handleLogHours(input) {
|
|
|
112523
112242
|
}).where(eq(schema_exports.timesheetEvents.id, existingEntry.id)).returning({
|
|
112524
112243
|
id: schema_exports.timesheetEvents.id,
|
|
112525
112244
|
trackedDuration: schema_exports.timesheetEvents.trackedDuration,
|
|
112526
|
-
projectId: schema_exports.timesheetEvents.projectId
|
|
112527
|
-
aiSessionId: schema_exports.timesheetEvents.aiSessionId
|
|
112245
|
+
projectId: schema_exports.timesheetEvents.projectId
|
|
112528
112246
|
});
|
|
112529
112247
|
agendaEntry = updated ?? null;
|
|
112530
112248
|
wasUpdated = true;
|
|
@@ -112536,7 +112254,6 @@ async function handleLogHours(input) {
|
|
|
112536
112254
|
teamId: insertTeamId,
|
|
112537
112255
|
userId: ctx.userId,
|
|
112538
112256
|
projectId: project?.id ?? null,
|
|
112539
|
-
aiSessionId: aiSession?.id ?? null,
|
|
112540
112257
|
title: workDescription,
|
|
112541
112258
|
description: chatContextSummary ?? workDescription,
|
|
112542
112259
|
startTime: startTime.toISOString(),
|
|
@@ -112549,8 +112266,7 @@ async function handleLogHours(input) {
|
|
|
112549
112266
|
}).returning({
|
|
112550
112267
|
id: schema_exports.timesheetEvents.id,
|
|
112551
112268
|
trackedDuration: schema_exports.timesheetEvents.trackedDuration,
|
|
112552
|
-
projectId: schema_exports.timesheetEvents.projectId
|
|
112553
|
-
aiSessionId: schema_exports.timesheetEvents.aiSessionId
|
|
112269
|
+
projectId: schema_exports.timesheetEvents.projectId
|
|
112554
112270
|
});
|
|
112555
112271
|
agendaEntry = created ?? null;
|
|
112556
112272
|
if (agendaEntry && ticket?.id) {
|
|
@@ -112581,9 +112297,6 @@ async function handleLogHours(input) {
|
|
|
112581
112297
|
`;
|
|
112582
112298
|
if (ticket)
|
|
112583
112299
|
responseText += ` \u2022 Ticket: ${ticket.title} (${ticket.status})
|
|
112584
|
-
`;
|
|
112585
|
-
if (aiSession)
|
|
112586
|
-
responseText += ` \u2022 AI Session: ${aiSession.id} (${aiSession.status})
|
|
112587
112300
|
`;
|
|
112588
112301
|
responseText += ` \u2022 Description: ${workDescription}
|
|
112589
112302
|
`;
|
|
@@ -113292,870 +113005,6 @@ async function handleRemoveProjectMember(input) {
|
|
|
113292
113005
|
return textResponse(text3);
|
|
113293
113006
|
}
|
|
113294
113007
|
|
|
113295
|
-
// src/tools/session-completion.ts
|
|
113296
|
-
async function handleGetCompletionContext(input) {
|
|
113297
|
-
const {
|
|
113298
|
-
aiSessionId,
|
|
113299
|
-
includeFollowUps = true,
|
|
113300
|
-
includeTimeMetrics = true,
|
|
113301
|
-
includeTodos = true
|
|
113302
|
-
} = input;
|
|
113303
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113304
|
-
if (!scope.ok) return scope.response;
|
|
113305
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
113306
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
113307
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113308
|
-
const [session] = await db.select({
|
|
113309
|
-
id: schema_exports.aiSessions.id,
|
|
113310
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
113311
|
-
aiTimeEstimateMinutes: schema_exports.aiSessions.aiTimeEstimateMinutes,
|
|
113312
|
-
actualTimeMinutes: schema_exports.aiSessions.actualTimeMinutes,
|
|
113313
|
-
efficiencyScore: schema_exports.aiSessions.efficiencyScore,
|
|
113314
|
-
createdAt: schema_exports.aiSessions.createdAt,
|
|
113315
|
-
completedAt: schema_exports.aiSessions.completedAt,
|
|
113316
|
-
status: schema_exports.aiSessions.status,
|
|
113317
|
-
complexityScore: schema_exports.aiSessions.complexityScore
|
|
113318
|
-
}).from(schema_exports.aiSessions).where(eq(schema_exports.aiSessions.id, fullSessionId)).limit(1);
|
|
113319
|
-
if (!session) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113320
|
-
const [ticket] = await db.select({
|
|
113321
|
-
ticketNumber: schema_exports.tickets.ticketNumber,
|
|
113322
|
-
title: schema_exports.tickets.title,
|
|
113323
|
-
description: schema_exports.tickets.description,
|
|
113324
|
-
type: schema_exports.tickets.type,
|
|
113325
|
-
priority: schema_exports.tickets.priority
|
|
113326
|
-
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id, session.ticketId)).limit(1);
|
|
113327
|
-
if (!ticket) throw new Error("Ticket not found for session");
|
|
113328
|
-
const contextData = {
|
|
113329
|
-
session: {
|
|
113330
|
-
id: aiSessionId,
|
|
113331
|
-
status: session.status,
|
|
113332
|
-
complexity: session.complexityScore,
|
|
113333
|
-
createdAt: session.createdAt,
|
|
113334
|
-
completedAt: session.completedAt
|
|
113335
|
-
},
|
|
113336
|
-
ticket: {
|
|
113337
|
-
number: ticket.ticketNumber,
|
|
113338
|
-
title: ticket.title,
|
|
113339
|
-
description: ticket.description,
|
|
113340
|
-
type: ticket.type,
|
|
113341
|
-
priority: ticket.priority
|
|
113342
|
-
}
|
|
113343
|
-
};
|
|
113344
|
-
if (includeTimeMetrics) {
|
|
113345
|
-
const timeSaved = session.aiTimeEstimateMinutes && session.actualTimeMinutes ? Math.max(0, session.aiTimeEstimateMinutes - session.actualTimeMinutes) : null;
|
|
113346
|
-
contextData.timeMetrics = {
|
|
113347
|
-
estimatedMinutes: session.aiTimeEstimateMinutes,
|
|
113348
|
-
actualMinutes: session.actualTimeMinutes,
|
|
113349
|
-
timeSaved,
|
|
113350
|
-
efficiency: session.efficiencyScore,
|
|
113351
|
-
sessionDuration: session.completedAt && session.createdAt ? Math.round(
|
|
113352
|
-
(new Date(session.completedAt).getTime() - new Date(session.createdAt).getTime()) / 6e4
|
|
113353
|
-
) : null
|
|
113354
|
-
};
|
|
113355
|
-
}
|
|
113356
|
-
if (includeTodos) {
|
|
113357
|
-
const todos3 = await db.select({
|
|
113358
|
-
content: schema_exports.aiTodos.content,
|
|
113359
|
-
status: schema_exports.aiTodos.status,
|
|
113360
|
-
estimatedMinutes: schema_exports.aiTodos.estimatedMinutes,
|
|
113361
|
-
actualMinutes: schema_exports.aiTodos.actualMinutes,
|
|
113362
|
-
completedAt: schema_exports.aiTodos.completedAt
|
|
113363
|
-
}).from(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, session.id)).orderBy(asc(schema_exports.aiTodos.createdAt));
|
|
113364
|
-
contextData.todos = todos3;
|
|
113365
|
-
}
|
|
113366
|
-
if (includeFollowUps) {
|
|
113367
|
-
const followUps = await db.select({
|
|
113368
|
-
followUpReason: schema_exports.manualFollowUps.followUpReason,
|
|
113369
|
-
outcome: schema_exports.manualFollowUps.outcome,
|
|
113370
|
-
timeSpentMinutes: schema_exports.manualFollowUps.timeSpentMinutes,
|
|
113371
|
-
createdAt: schema_exports.manualFollowUps.createdAt
|
|
113372
|
-
}).from(schema_exports.manualFollowUps).where(eq(schema_exports.manualFollowUps.aiSessionId, session.id)).orderBy(asc(schema_exports.manualFollowUps.createdAt));
|
|
113373
|
-
contextData.followUps = followUps;
|
|
113374
|
-
}
|
|
113375
|
-
const todosLen = contextData.todos ?? [];
|
|
113376
|
-
const completedTodos = todosLen.filter(
|
|
113377
|
-
(t8) => t8.status === "completed"
|
|
113378
|
-
).length;
|
|
113379
|
-
const followUpsLen = contextData.followUps?.length ?? 0;
|
|
113380
|
-
return {
|
|
113381
|
-
content: [
|
|
113382
|
-
{
|
|
113383
|
-
type: "text",
|
|
113384
|
-
text: `\u{1F4CB} **Completion Context Retrieved!**
|
|
113385
|
-
|
|
113386
|
-
\u{1F3AB} **Ticket:** ${ticket.ticketNumber} - ${ticket.title}
|
|
113387
|
-
\u{1F194} **Session:** ${aiSessionId} (${session.status})
|
|
113388
|
-
\u23F1\uFE0F **Time:** ${session.actualTimeMinutes || "N/A"}/${session.aiTimeEstimateMinutes || "N/A"} minutes
|
|
113389
|
-
\u{1F4CB} **Todos:** ${completedTodos}/${todosLen.length} completed
|
|
113390
|
-
\u{1F504} **Follow-ups:** ${followUpsLen}
|
|
113391
|
-
|
|
113392
|
-
\u2705 **Full context ready for Cursor AI to generate customer response!**
|
|
113393
|
-
|
|
113394
|
-
**Context Data:**
|
|
113395
|
-
\`\`\`json
|
|
113396
|
-
${JSON.stringify(contextData, null, 2)}\`\`\``
|
|
113397
|
-
}
|
|
113398
|
-
]
|
|
113399
|
-
};
|
|
113400
|
-
}
|
|
113401
|
-
async function handleSaveCustomerResponse(input) {
|
|
113402
|
-
const { aiSessionId, customerResponse, responseType = "completion" } = input;
|
|
113403
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113404
|
-
if (!scope.ok) return scope.response;
|
|
113405
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
113406
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
113407
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113408
|
-
await db.insert(schema_exports.aiResponses).values({
|
|
113409
|
-
aiSessionId: fullSessionId,
|
|
113410
|
-
responseType,
|
|
113411
|
-
content: customerResponse,
|
|
113412
|
-
isReadyForCustomer: true,
|
|
113413
|
-
providerApproved: false
|
|
113414
|
-
});
|
|
113415
|
-
return {
|
|
113416
|
-
content: [
|
|
113417
|
-
{
|
|
113418
|
-
type: "text",
|
|
113419
|
-
text: `\u{1F4BE} **Customer Response Saved!**
|
|
113420
|
-
|
|
113421
|
-
\u{1F194} Session: ${aiSessionId}
|
|
113422
|
-
\u{1F4DD} Response Type: ${responseType}
|
|
113423
|
-
\u{1F4C4} Length: ${customerResponse.length} characters
|
|
113424
|
-
|
|
113425
|
-
\u2705 **Response ready for provider approval**
|
|
113426
|
-
\u{1F50D} Provider can review in AI tab before sending to customer
|
|
113427
|
-
|
|
113428
|
-
**Preview:**
|
|
113429
|
-
\`\`\`
|
|
113430
|
-
${customerResponse.substring(0, 200)}${customerResponse.length > 200 ? "..." : ""}\`\`\``
|
|
113431
|
-
}
|
|
113432
|
-
]
|
|
113433
|
-
};
|
|
113434
|
-
}
|
|
113435
|
-
async function handleCompleteAiSession(input) {
|
|
113436
|
-
const ctx = getAuthContext();
|
|
113437
|
-
const {
|
|
113438
|
-
aiSessionId,
|
|
113439
|
-
workCompleted,
|
|
113440
|
-
technicalSummary,
|
|
113441
|
-
invoiceDescription,
|
|
113442
|
-
efficiencyNotes
|
|
113443
|
-
} = input;
|
|
113444
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113445
|
-
if (!scope.ok) return scope.response;
|
|
113446
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
113447
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
113448
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113449
|
-
const [existingSession] = await db.select({
|
|
113450
|
-
id: schema_exports.aiSessions.id,
|
|
113451
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
113452
|
-
aiTimeEstimateMinutes: schema_exports.aiSessions.aiTimeEstimateMinutes,
|
|
113453
|
-
createdAt: schema_exports.aiSessions.createdAt,
|
|
113454
|
-
teamId: schema_exports.aiSessions.teamId
|
|
113455
|
-
}).from(schema_exports.aiSessions).where(eq(schema_exports.aiSessions.id, fullSessionId)).limit(1);
|
|
113456
|
-
if (!existingSession) {
|
|
113457
|
-
throw new Error(`Session not found: ${aiSessionId}`);
|
|
113458
|
-
}
|
|
113459
|
-
const completionTime = /* @__PURE__ */ new Date();
|
|
113460
|
-
const sessionStartTime = new Date(existingSession.createdAt);
|
|
113461
|
-
const timeSpentMinutes = Math.round(
|
|
113462
|
-
(completionTime.getTime() - sessionStartTime.getTime()) / 6e4
|
|
113463
|
-
);
|
|
113464
|
-
const [session] = await db.update(schema_exports.aiSessions).set({
|
|
113465
|
-
status: "completed",
|
|
113466
|
-
actualTimeMinutes: timeSpentMinutes,
|
|
113467
|
-
completedAt: completionTime.toISOString(),
|
|
113468
|
-
efficiencyScore: null
|
|
113469
|
-
}).where(eq(schema_exports.aiSessions.id, existingSession.id)).returning({
|
|
113470
|
-
id: schema_exports.aiSessions.id,
|
|
113471
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
113472
|
-
aiTimeEstimateMinutes: schema_exports.aiSessions.aiTimeEstimateMinutes,
|
|
113473
|
-
createdAt: schema_exports.aiSessions.createdAt
|
|
113474
|
-
});
|
|
113475
|
-
if (!session) throw new Error(`Failed to update session: ${aiSessionId}`);
|
|
113476
|
-
const efficiencyScore = session.aiTimeEstimateMinutes ? timeSpentMinutes / session.aiTimeEstimateMinutes : 1;
|
|
113477
|
-
await db.update(schema_exports.aiSessions).set({ efficiencyScore: efficiencyScore.toFixed(2) }).where(eq(schema_exports.aiSessions.id, session.id));
|
|
113478
|
-
const activePhases = await db.select().from(schema_exports.aiTimeLogs).where(
|
|
113479
|
-
and(
|
|
113480
|
-
eq(schema_exports.aiTimeLogs.aiSessionId, existingSession.id),
|
|
113481
|
-
eq(schema_exports.aiTimeLogs.status, "in_progress")
|
|
113482
|
-
)
|
|
113483
|
-
);
|
|
113484
|
-
for (const phase of activePhases) {
|
|
113485
|
-
const duration5 = Math.round(
|
|
113486
|
-
(completionTime.getTime() - new Date(phase.startedAt).getTime()) / 1e3
|
|
113487
|
-
);
|
|
113488
|
-
await db.update(schema_exports.aiTimeLogs).set({
|
|
113489
|
-
endedAt: completionTime.toISOString(),
|
|
113490
|
-
durationSeconds: duration5,
|
|
113491
|
-
status: "completed"
|
|
113492
|
-
}).where(eq(schema_exports.aiTimeLogs.id, phase.id));
|
|
113493
|
-
}
|
|
113494
|
-
await db.update(schema_exports.aiTimeLogs).set({ status: "skipped" }).where(
|
|
113495
|
-
and(
|
|
113496
|
-
eq(schema_exports.aiTimeLogs.aiSessionId, existingSession.id),
|
|
113497
|
-
eq(schema_exports.aiTimeLogs.status, "pending"),
|
|
113498
|
-
eq(schema_exports.aiTimeLogs.estimatedDurationSeconds, 0)
|
|
113499
|
-
)
|
|
113500
|
-
);
|
|
113501
|
-
const sessionDuration = Math.round(
|
|
113502
|
-
(completionTime.getTime() - new Date(session.createdAt).getTime()) / 6e4
|
|
113503
|
-
);
|
|
113504
|
-
const workSummary = `Completed ${workCompleted.length} tasks including: ${workCompleted.slice(0, 3).join(", ")}${workCompleted.length > 3 ? " and more" : ""}.`;
|
|
113505
|
-
const [ticketInfo] = await db.select({
|
|
113506
|
-
ticketNumber: schema_exports.tickets.ticketNumber,
|
|
113507
|
-
title: schema_exports.tickets.title,
|
|
113508
|
-
projectId: schema_exports.tickets.projectId
|
|
113509
|
-
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id, session.ticketId)).limit(1);
|
|
113510
|
-
let completionDescription;
|
|
113511
|
-
if (invoiceDescription) {
|
|
113512
|
-
completionDescription = `${ticketInfo?.ticketNumber || "Ticket"}: ${invoiceDescription}`;
|
|
113513
|
-
} else {
|
|
113514
|
-
const workDescription = workCompleted.map((task, index2) => `${index2 + 1}. ${task}`).join("\n");
|
|
113515
|
-
completionDescription = `${ticketInfo?.ticketNumber || "Ticket"}: ${technicalSummary || workSummary}
|
|
113516
|
-
|
|
113517
|
-
Completed work:
|
|
113518
|
-
${workDescription}`;
|
|
113519
|
-
}
|
|
113520
|
-
const estimatedMinutes = session.aiTimeEstimateMinutes ?? timeSpentMinutes;
|
|
113521
|
-
const sessionStart = new Date(session.createdAt);
|
|
113522
|
-
const estimatedEnd = new Date(
|
|
113523
|
-
sessionStart.getTime() + estimatedMinutes * 6e4
|
|
113524
|
-
);
|
|
113525
|
-
const existingAgendaEntries = await db.select({
|
|
113526
|
-
id: schema_exports.timesheetEvents.id,
|
|
113527
|
-
trackedDuration: schema_exports.timesheetEvents.trackedDuration
|
|
113528
|
-
}).from(schema_exports.timesheetEvents).where(
|
|
113529
|
-
and(
|
|
113530
|
-
eq(schema_exports.timesheetEvents.aiSessionId, session.id),
|
|
113531
|
-
eq(schema_exports.timesheetEvents.status, "draft")
|
|
113532
|
-
)
|
|
113533
|
-
).orderBy(desc(schema_exports.timesheetEvents.createdAt));
|
|
113534
|
-
let timesheetEventId = null;
|
|
113535
|
-
let wasUpdated = false;
|
|
113536
|
-
let consolidatedCount = 0;
|
|
113537
|
-
const existingAgendaEntry = existingAgendaEntries[0] ?? null;
|
|
113538
|
-
if (existingAgendaEntries.length > 1) {
|
|
113539
|
-
const duplicateIds = existingAgendaEntries.slice(1).map((e6) => e6.id);
|
|
113540
|
-
await db.delete(schema_exports.timesheetEvents).where(inArray(schema_exports.timesheetEvents.id, duplicateIds));
|
|
113541
|
-
consolidatedCount = existingAgendaEntries.length - 1;
|
|
113542
|
-
}
|
|
113543
|
-
try {
|
|
113544
|
-
if (existingAgendaEntry) {
|
|
113545
|
-
const [updated] = await db.update(schema_exports.timesheetEvents).set({
|
|
113546
|
-
title: ticketInfo?.title || "Development Work",
|
|
113547
|
-
description: completionDescription,
|
|
113548
|
-
endTime: estimatedEnd.toISOString(),
|
|
113549
|
-
projectId: ticketInfo?.projectId ?? null,
|
|
113550
|
-
trackedDuration: estimatedMinutes * 60
|
|
113551
|
-
}).where(eq(schema_exports.timesheetEvents.id, existingAgendaEntry.id)).returning({ id: schema_exports.timesheetEvents.id });
|
|
113552
|
-
timesheetEventId = updated?.id ?? null;
|
|
113553
|
-
wasUpdated = true;
|
|
113554
|
-
} else {
|
|
113555
|
-
const [created] = await db.insert(schema_exports.timesheetEvents).values({
|
|
113556
|
-
teamId: existingSession.teamId,
|
|
113557
|
-
userId: ctx.userId,
|
|
113558
|
-
title: ticketInfo?.title || "Development Work",
|
|
113559
|
-
description: completionDescription,
|
|
113560
|
-
startTime: sessionStart.toISOString(),
|
|
113561
|
-
endTime: estimatedEnd.toISOString(),
|
|
113562
|
-
projectId: ticketInfo?.projectId ?? null,
|
|
113563
|
-
aiSessionId: session.id,
|
|
113564
|
-
type: "work",
|
|
113565
|
-
status: "draft",
|
|
113566
|
-
allDay: false,
|
|
113567
|
-
isTracked: true,
|
|
113568
|
-
trackedDuration: estimatedMinutes * 60
|
|
113569
|
-
}).returning({ id: schema_exports.timesheetEvents.id });
|
|
113570
|
-
timesheetEventId = created?.id ?? null;
|
|
113571
|
-
}
|
|
113572
|
-
if (timesheetEventId && session.ticketId) {
|
|
113573
|
-
await db.insert(schema_exports.timesheetEventTickets).values({
|
|
113574
|
-
timesheetEventId,
|
|
113575
|
-
ticketId: session.ticketId
|
|
113576
|
-
}).onConflictDoNothing();
|
|
113577
|
-
}
|
|
113578
|
-
} catch (err) {
|
|
113579
|
-
console.error(
|
|
113580
|
-
`\u26A0\uFE0F Failed to ${wasUpdated ? "update" : "create"} agenda event:`,
|
|
113581
|
-
err
|
|
113582
|
-
);
|
|
113583
|
-
}
|
|
113584
|
-
if (consolidatedCount > 0) {
|
|
113585
|
-
console.log(
|
|
113586
|
-
`\u{1F9F9} Cleaned up ${consolidatedCount} duplicate agenda entries for session ${aiSessionId}`
|
|
113587
|
-
);
|
|
113588
|
-
}
|
|
113589
|
-
let responseText = `\u{1F389} **AI Session Completed Successfully!**
|
|
113590
|
-
|
|
113591
|
-
`;
|
|
113592
|
-
responseText += `\u{1F194} Session: ${aiSessionId}
|
|
113593
|
-
`;
|
|
113594
|
-
responseText += `\u{1F4CA} **Performance Summary:**
|
|
113595
|
-
`;
|
|
113596
|
-
responseText += ` \u2022 Tasks Completed: ${workCompleted.length}
|
|
113597
|
-
`;
|
|
113598
|
-
responseText += ` \u2022 Time Spent: ${timeSpentMinutes} minutes
|
|
113599
|
-
`;
|
|
113600
|
-
responseText += ` \u2022 Estimated Time: ${session.aiTimeEstimateMinutes || "N/A"} minutes
|
|
113601
|
-
`;
|
|
113602
|
-
responseText += ` \u2022 Efficiency: ${efficiencyScore < 1 ? "\u{1F680}" : efficiencyScore > 1.5 ? "\u26A0\uFE0F" : "\u23F1\uFE0F"} ${(efficiencyScore * 100).toFixed(0)}%
|
|
113603
|
-
`;
|
|
113604
|
-
responseText += ` \u2022 Session Duration: ${sessionDuration} minutes
|
|
113605
|
-
|
|
113606
|
-
`;
|
|
113607
|
-
responseText += `\u2705 **Work Completed:**
|
|
113608
|
-
`;
|
|
113609
|
-
workCompleted.forEach((task, index2) => {
|
|
113610
|
-
responseText += `${index2 + 1}. ${task}
|
|
113611
|
-
`;
|
|
113612
|
-
});
|
|
113613
|
-
responseText += `
|
|
113614
|
-
`;
|
|
113615
|
-
if (technicalSummary) {
|
|
113616
|
-
responseText += `\u{1F527} **Technical Summary:**
|
|
113617
|
-
${technicalSummary}
|
|
113618
|
-
|
|
113619
|
-
`;
|
|
113620
|
-
}
|
|
113621
|
-
if (efficiencyNotes) {
|
|
113622
|
-
responseText += `\u{1F4C8} **Efficiency Notes:**
|
|
113623
|
-
${efficiencyNotes}
|
|
113624
|
-
|
|
113625
|
-
`;
|
|
113626
|
-
}
|
|
113627
|
-
if (timesheetEventId) {
|
|
113628
|
-
responseText += `\u{1F4C5} **Timetrack Entry ${wasUpdated ? "Updated" : "Created"}:**
|
|
113629
|
-
`;
|
|
113630
|
-
responseText += ` \u2022 Agenda event ${wasUpdated ? "updated with final" : "created with"} work summary
|
|
113631
|
-
`;
|
|
113632
|
-
responseText += ` \u2022 Status: DRAFT (requires approval in agenda)
|
|
113633
|
-
`;
|
|
113634
|
-
responseText += ` \u2022 Duration: ${estimatedMinutes} minutes
|
|
113635
|
-
`;
|
|
113636
|
-
responseText += ` \u2022 Period: ${sessionStart.toLocaleString()} - ${completionTime.toLocaleString()}
|
|
113637
|
-
|
|
113638
|
-
`;
|
|
113639
|
-
}
|
|
113640
|
-
responseText += `\u{1F4CB} **Context for Customer Response:**
|
|
113641
|
-
`;
|
|
113642
|
-
responseText += ` \u2022 Use "get-completion-context" to retrieve full context
|
|
113643
|
-
`;
|
|
113644
|
-
responseText += ` \u2022 Generate customer-friendly response based on completed work
|
|
113645
|
-
`;
|
|
113646
|
-
responseText += ` \u2022 Focus on business value and customer benefits
|
|
113647
|
-
|
|
113648
|
-
`;
|
|
113649
|
-
responseText += `\u{1F3AF} **Session archived successfully!**`;
|
|
113650
|
-
return { content: [{ type: "text", text: responseText }] };
|
|
113651
|
-
}
|
|
113652
|
-
|
|
113653
|
-
// src/tools/sessions.ts
|
|
113654
|
-
async function transitionToNextPhase(sessionId, currentPhase) {
|
|
113655
|
-
try {
|
|
113656
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
113657
|
-
const phaseOrder = [
|
|
113658
|
-
"analysis",
|
|
113659
|
-
"bug_investigation",
|
|
113660
|
-
"development",
|
|
113661
|
-
"communication"
|
|
113662
|
-
];
|
|
113663
|
-
const allPhases = await db.select().from(schema_exports.aiTimeLogs).where(eq(schema_exports.aiTimeLogs.aiSessionId, sessionId)).orderBy(asc(schema_exports.aiTimeLogs.activityType));
|
|
113664
|
-
let currentPhaseType = currentPhase;
|
|
113665
|
-
if (!currentPhaseType) {
|
|
113666
|
-
const activePhase = allPhases.find((p3) => p3.status === "in_progress");
|
|
113667
|
-
currentPhaseType = activePhase?.activityType ?? void 0;
|
|
113668
|
-
}
|
|
113669
|
-
if (!currentPhaseType) {
|
|
113670
|
-
const analysisPhase = allPhases.find(
|
|
113671
|
-
(p3) => p3.activityType === "analysis"
|
|
113672
|
-
);
|
|
113673
|
-
if (analysisPhase && analysisPhase.status === "pending" && (analysisPhase.estimatedDurationSeconds ?? 0) > 0) {
|
|
113674
|
-
await db.update(schema_exports.aiTimeLogs).set({ status: "in_progress", startedAt: now2.toISOString() }).where(eq(schema_exports.aiTimeLogs.id, analysisPhase.id));
|
|
113675
|
-
console.error("\u2705 Started analysis phase");
|
|
113676
|
-
}
|
|
113677
|
-
return;
|
|
113678
|
-
}
|
|
113679
|
-
const currentPhaseRecord = allPhases.find(
|
|
113680
|
-
(p3) => p3.activityType === currentPhaseType && p3.status === "in_progress"
|
|
113681
|
-
);
|
|
113682
|
-
if (currentPhaseRecord) {
|
|
113683
|
-
const duration5 = Math.round(
|
|
113684
|
-
(now2.getTime() - new Date(currentPhaseRecord.startedAt).getTime()) / 1e3
|
|
113685
|
-
);
|
|
113686
|
-
await db.update(schema_exports.aiTimeLogs).set({
|
|
113687
|
-
status: "completed",
|
|
113688
|
-
endedAt: now2.toISOString(),
|
|
113689
|
-
durationSeconds: duration5
|
|
113690
|
-
}).where(eq(schema_exports.aiTimeLogs.id, currentPhaseRecord.id));
|
|
113691
|
-
console.error(`\u2705 Completed phase: ${currentPhaseType} (${duration5}s)`);
|
|
113692
|
-
}
|
|
113693
|
-
const currentIndex = phaseOrder.indexOf(currentPhaseType);
|
|
113694
|
-
if (currentIndex === -1 || currentIndex === phaseOrder.length - 1) {
|
|
113695
|
-
console.error("No next phase to transition to");
|
|
113696
|
-
return;
|
|
113697
|
-
}
|
|
113698
|
-
for (let i6 = currentIndex + 1; i6 < phaseOrder.length; i6++) {
|
|
113699
|
-
const nextPhaseType = phaseOrder[i6];
|
|
113700
|
-
const nextPhase = allPhases.find((p3) => p3.activityType === nextPhaseType);
|
|
113701
|
-
if (!nextPhase) continue;
|
|
113702
|
-
if ((nextPhase.estimatedDurationSeconds ?? 0) === 0) {
|
|
113703
|
-
await db.update(schema_exports.aiTimeLogs).set({ status: "skipped" }).where(eq(schema_exports.aiTimeLogs.id, nextPhase.id));
|
|
113704
|
-
console.error(
|
|
113705
|
-
`\u23ED\uFE0F Skipped phase: ${nextPhaseType} (0 minutes estimated)`
|
|
113706
|
-
);
|
|
113707
|
-
continue;
|
|
113708
|
-
}
|
|
113709
|
-
if (nextPhase.status === "pending") {
|
|
113710
|
-
await db.update(schema_exports.aiTimeLogs).set({ status: "in_progress", startedAt: now2.toISOString() }).where(eq(schema_exports.aiTimeLogs.id, nextPhase.id));
|
|
113711
|
-
console.error(`\u2705 Started next phase: ${nextPhaseType}`);
|
|
113712
|
-
return;
|
|
113713
|
-
}
|
|
113714
|
-
}
|
|
113715
|
-
console.error("All remaining phases skipped or completed");
|
|
113716
|
-
} catch (error49) {
|
|
113717
|
-
console.error("Error transitioning to next phase:", error49);
|
|
113718
|
-
}
|
|
113719
|
-
}
|
|
113720
|
-
async function handleStartAiSession(input) {
|
|
113721
|
-
const ctx = getAuthContext();
|
|
113722
|
-
const { ticketId, cursorSessionId, totalEstimatedMinutes, complexityScore } = input;
|
|
113723
|
-
if (!totalEstimatedMinutes) {
|
|
113724
|
-
throw new Error("totalEstimatedMinutes is required");
|
|
113725
|
-
}
|
|
113726
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113727
|
-
if (!scope.ok) return scope.response;
|
|
113728
|
-
const [ticketRow] = await db.select({
|
|
113729
|
-
teamId: schema_exports.tickets.teamId,
|
|
113730
|
-
projectId: schema_exports.tickets.projectId,
|
|
113731
|
-
customerId: schema_exports.tickets.customerId
|
|
113732
|
-
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id, ticketId)).limit(1);
|
|
113733
|
-
if (!ticketRow) {
|
|
113734
|
-
throw new Error(
|
|
113735
|
-
`Ticket not found: ${ticketId}. Call get-tickets to find the correct ticket.`
|
|
113736
|
-
);
|
|
113737
|
-
}
|
|
113738
|
-
const hasTicketAccess = scope.teamIds.includes(ticketRow.teamId) || !!ticketRow.projectId && scope.projectIds.includes(ticketRow.projectId) || !!ticketRow.customerId && scope.customerIds.includes(ticketRow.customerId);
|
|
113739
|
-
if (!hasTicketAccess) {
|
|
113740
|
-
throw new Error(`No access to ticket: ${ticketId}.`);
|
|
113741
|
-
}
|
|
113742
|
-
const insertTeamId = ticketRow.teamId;
|
|
113743
|
-
const roundedMinutes = roundToNearest15Minutes(totalEstimatedMinutes);
|
|
113744
|
-
const sessionStartTime = /* @__PURE__ */ new Date();
|
|
113745
|
-
const [sessionData] = await db.insert(schema_exports.aiSessions).values({
|
|
113746
|
-
ticketId,
|
|
113747
|
-
providerUserId: ctx.userId,
|
|
113748
|
-
teamId: insertTeamId,
|
|
113749
|
-
cursorSessionId: cursorSessionId ?? null,
|
|
113750
|
-
aiTimeEstimateMinutes: roundedMinutes,
|
|
113751
|
-
complexityScore: complexityScore ?? null,
|
|
113752
|
-
status: "in_progress"
|
|
113753
|
-
}).returning({
|
|
113754
|
-
id: schema_exports.aiSessions.id,
|
|
113755
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
113756
|
-
cursorSessionId: schema_exports.aiSessions.cursorSessionId,
|
|
113757
|
-
createdAt: schema_exports.aiSessions.createdAt
|
|
113758
|
-
});
|
|
113759
|
-
if (!sessionData) {
|
|
113760
|
-
throw new Error("Failed to create AI session");
|
|
113761
|
-
}
|
|
113762
|
-
const sessionId = `ai-sess-${sessionData.id.substring(0, 8)}`;
|
|
113763
|
-
return {
|
|
113764
|
-
content: [
|
|
113765
|
-
{
|
|
113766
|
-
type: "text",
|
|
113767
|
-
text: `\u{1F680} **AI Session Started!**
|
|
113768
|
-
|
|
113769
|
-
\u{1F194} Session ID: **${sessionId}**
|
|
113770
|
-
\u{1F3AB} Ticket: ${ticketId}
|
|
113771
|
-
\u23F1\uFE0F Estimated: ${roundedMinutes} min
|
|
113772
|
-
${complexityScore ? `\u{1F3AF} Complexity: ${complexityScore}/10
|
|
113773
|
-
` : ""}\u{1F4C5} Started: ${sessionStartTime.toLocaleString()}
|
|
113774
|
-
|
|
113775
|
-
\u{1F4DD} Timetrack entry will be created when you complete the session.`
|
|
113776
|
-
}
|
|
113777
|
-
]
|
|
113778
|
-
};
|
|
113779
|
-
}
|
|
113780
|
-
async function handleTrackManualFollowUp(input) {
|
|
113781
|
-
const ctx = getAuthContext();
|
|
113782
|
-
const {
|
|
113783
|
-
aiSessionId,
|
|
113784
|
-
originalPrompt,
|
|
113785
|
-
aiResponse,
|
|
113786
|
-
developerFollowUp,
|
|
113787
|
-
followUpReason,
|
|
113788
|
-
outcome = "success",
|
|
113789
|
-
estimatedMinutes,
|
|
113790
|
-
workDescription
|
|
113791
|
-
} = input;
|
|
113792
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113793
|
-
if (!scope.ok) return scope.response;
|
|
113794
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
113795
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
113796
|
-
if (!fullSessionId) {
|
|
113797
|
-
throw new Error(`Session not found: ${aiSessionId}`);
|
|
113798
|
-
}
|
|
113799
|
-
const [session] = await db.select({
|
|
113800
|
-
id: schema_exports.aiSessions.id,
|
|
113801
|
-
status: schema_exports.aiSessions.status,
|
|
113802
|
-
createdAt: schema_exports.aiSessions.createdAt,
|
|
113803
|
-
aiTimeEstimateMinutes: schema_exports.aiSessions.aiTimeEstimateMinutes,
|
|
113804
|
-
teamId: schema_exports.aiSessions.teamId
|
|
113805
|
-
}).from(schema_exports.aiSessions).where(eq(schema_exports.aiSessions.id, fullSessionId)).limit(1);
|
|
113806
|
-
if (!session) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113807
|
-
const followUpTime = /* @__PURE__ */ new Date();
|
|
113808
|
-
const oldEstimate = session.aiTimeEstimateMinutes ?? 60;
|
|
113809
|
-
const roundedFollowUpMinutes = roundToNearest15Minutes(estimatedMinutes || 0);
|
|
113810
|
-
const newEstimate = oldEstimate + roundedFollowUpMinutes;
|
|
113811
|
-
await db.update(schema_exports.aiSessions).set({
|
|
113812
|
-
status: "in_progress",
|
|
113813
|
-
aiTimeEstimateMinutes: newEstimate
|
|
113814
|
-
}).where(eq(schema_exports.aiSessions.id, session.id));
|
|
113815
|
-
await db.insert(schema_exports.manualFollowUps).values({
|
|
113816
|
-
aiSessionId: session.id,
|
|
113817
|
-
developerId: ctx.userId,
|
|
113818
|
-
teamId: session.teamId,
|
|
113819
|
-
originalPrompt,
|
|
113820
|
-
aiResponse,
|
|
113821
|
-
followUpPrompt: developerFollowUp,
|
|
113822
|
-
followUpReason,
|
|
113823
|
-
outcome,
|
|
113824
|
-
timeSpentMinutes: null,
|
|
113825
|
-
resolvedAt: outcome === "success" ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
113826
|
-
});
|
|
113827
|
-
await db.insert(schema_exports.aiTimeLogs).values({
|
|
113828
|
-
aiSessionId: session.id,
|
|
113829
|
-
activityType: "debugging",
|
|
113830
|
-
description: `Follow-up: ${followUpReason.replace("_", " ")} - ${outcome}`,
|
|
113831
|
-
durationSeconds: 0,
|
|
113832
|
-
productivityScore: outcome === "success" ? 9 : outcome === "partial_success" ? 6 : 4,
|
|
113833
|
-
startedAt: followUpTime.toISOString()
|
|
113834
|
-
});
|
|
113835
|
-
const sessionStartTime = new Date(session.createdAt);
|
|
113836
|
-
const totalMinutesElapsed = Math.round(
|
|
113837
|
-
(followUpTime.getTime() - sessionStartTime.getTime()) / 6e4
|
|
113838
|
-
);
|
|
113839
|
-
const currentEfficiency = totalMinutesElapsed > 0 ? totalMinutesElapsed / newEstimate : 1;
|
|
113840
|
-
await db.update(schema_exports.aiSessions).set({
|
|
113841
|
-
efficiencyScore: currentEfficiency.toFixed(2),
|
|
113842
|
-
actualTimeMinutes: totalMinutesElapsed
|
|
113843
|
-
}).where(eq(schema_exports.aiSessions.id, session.id));
|
|
113844
|
-
const existingEntries = await db.select({
|
|
113845
|
-
id: schema_exports.timesheetEvents.id,
|
|
113846
|
-
trackedDuration: schema_exports.timesheetEvents.trackedDuration,
|
|
113847
|
-
title: schema_exports.timesheetEvents.title,
|
|
113848
|
-
description: schema_exports.timesheetEvents.description,
|
|
113849
|
-
startTime: schema_exports.timesheetEvents.startTime
|
|
113850
|
-
}).from(schema_exports.timesheetEvents).where(
|
|
113851
|
-
and(
|
|
113852
|
-
eq(schema_exports.timesheetEvents.aiSessionId, session.id),
|
|
113853
|
-
eq(schema_exports.timesheetEvents.status, "draft")
|
|
113854
|
-
)
|
|
113855
|
-
).orderBy(desc(schema_exports.timesheetEvents.createdAt));
|
|
113856
|
-
let trackerAction = "";
|
|
113857
|
-
let trackerDetails = "";
|
|
113858
|
-
let existingEntry = existingEntries[0] ?? null;
|
|
113859
|
-
if (existingEntries.length > 1) {
|
|
113860
|
-
const totalExistingDuration = existingEntries.reduce(
|
|
113861
|
-
(sum, entry) => sum + (entry.trackedDuration ?? 0),
|
|
113862
|
-
0
|
|
113863
|
-
);
|
|
113864
|
-
const duplicateIds = existingEntries.slice(1).map((e6) => e6.id);
|
|
113865
|
-
await db.delete(schema_exports.timesheetEvents).where(inArray(schema_exports.timesheetEvents.id, duplicateIds));
|
|
113866
|
-
if (existingEntry && totalExistingDuration > (existingEntry.trackedDuration ?? 0)) {
|
|
113867
|
-
await db.update(schema_exports.timesheetEvents).set({ trackedDuration: totalExistingDuration }).where(eq(schema_exports.timesheetEvents.id, existingEntry.id));
|
|
113868
|
-
existingEntry = {
|
|
113869
|
-
...existingEntry,
|
|
113870
|
-
trackedDuration: totalExistingDuration
|
|
113871
|
-
};
|
|
113872
|
-
}
|
|
113873
|
-
trackerAction = `Consolidated ${existingEntries.length} duplicate entries`;
|
|
113874
|
-
}
|
|
113875
|
-
if (existingEntry) {
|
|
113876
|
-
const newDuration = (existingEntry.trackedDuration ?? 0) + roundedFollowUpMinutes * 60;
|
|
113877
|
-
await db.update(schema_exports.timesheetEvents).set({
|
|
113878
|
-
trackedDuration: newDuration,
|
|
113879
|
-
endTime: followUpTime.toISOString(),
|
|
113880
|
-
title: workDescription,
|
|
113881
|
-
description: workDescription
|
|
113882
|
-
}).where(eq(schema_exports.timesheetEvents.id, existingEntry.id));
|
|
113883
|
-
trackerAction = trackerAction || "Updated existing tracker";
|
|
113884
|
-
trackerDetails = ` \u2022 Total tracked time: ${Math.round(newDuration / 60)} minutes (+${roundedFollowUpMinutes} min)
|
|
113885
|
-
\u2022 Description: ${workDescription}
|
|
113886
|
-
`;
|
|
113887
|
-
} else {
|
|
113888
|
-
const durationSeconds = roundedFollowUpMinutes * 60;
|
|
113889
|
-
const startTime = new Date(followUpTime.getTime() - durationSeconds * 1e3);
|
|
113890
|
-
await db.insert(schema_exports.timesheetEvents).values({
|
|
113891
|
-
teamId: session.teamId,
|
|
113892
|
-
userId: ctx.userId,
|
|
113893
|
-
aiSessionId: session.id,
|
|
113894
|
-
title: workDescription,
|
|
113895
|
-
description: workDescription,
|
|
113896
|
-
startTime: startTime.toISOString(),
|
|
113897
|
-
endTime: followUpTime.toISOString(),
|
|
113898
|
-
type: "work",
|
|
113899
|
-
status: "draft",
|
|
113900
|
-
allDay: false,
|
|
113901
|
-
isTracked: true,
|
|
113902
|
-
trackedDuration: durationSeconds
|
|
113903
|
-
});
|
|
113904
|
-
trackerAction = "Created new tracker";
|
|
113905
|
-
trackerDetails = ` \u2022 Tracked time: ${roundedFollowUpMinutes} minutes
|
|
113906
|
-
\u2022 Description: ${workDescription}
|
|
113907
|
-
`;
|
|
113908
|
-
}
|
|
113909
|
-
return {
|
|
113910
|
-
content: [
|
|
113911
|
-
{
|
|
113912
|
-
type: "text",
|
|
113913
|
-
text: `\u{1F504} **Follow-up Tracked & Session Restarted!**
|
|
113914
|
-
|
|
113915
|
-
\u{1F194} Session: ${aiSessionId} (back to active)
|
|
113916
|
-
\u{1F50D} Reason: ${followUpReason.replace("_", " ")}
|
|
113917
|
-
\u2705 Outcome: ${outcome}
|
|
113918
|
-
|
|
113919
|
-
\u{1F4CA} **Time Estimate Updated:**
|
|
113920
|
-
\u2022 Old estimate: ${oldEstimate} minutes
|
|
113921
|
-
\u2022 Follow-up estimate: +${roundedFollowUpMinutes} minutes (rounded to 15min)
|
|
113922
|
-
\u2022 New estimate: ${newEstimate} minutes
|
|
113923
|
-
|
|
113924
|
-
\u{1F4C8} **Current Progress:**
|
|
113925
|
-
\u2022 Total time elapsed: ${totalMinutesElapsed} minutes
|
|
113926
|
-
\u2022 Efficiency: ${currentEfficiency < 1 ? "\u{1F680} " : currentEfficiency > 1.5 ? "\u26A0\uFE0F " : "\u23F1\uFE0F "}${(currentEfficiency * 100).toFixed(0)}%
|
|
113927
|
-
|
|
113928
|
-
\u23F1\uFE0F **Tracker Entry: ${trackerAction}**
|
|
113929
|
-
` + trackerDetails + `
|
|
113930
|
-
\u26A1 **Time tracking resumed** - continue with confidence!`
|
|
113931
|
-
}
|
|
113932
|
-
]
|
|
113933
|
-
};
|
|
113934
|
-
}
|
|
113935
|
-
async function handleGetSessionContext(input) {
|
|
113936
|
-
const {
|
|
113937
|
-
aiSessionId,
|
|
113938
|
-
includeTicketData = true,
|
|
113939
|
-
includeTodoProgress = true,
|
|
113940
|
-
includeFollowUpHistory = false
|
|
113941
|
-
} = input;
|
|
113942
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
113943
|
-
if (!scope.ok) return scope.response;
|
|
113944
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
113945
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
113946
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113947
|
-
const [session] = await db.select({
|
|
113948
|
-
id: schema_exports.aiSessions.id,
|
|
113949
|
-
ticketId: schema_exports.aiSessions.ticketId,
|
|
113950
|
-
status: schema_exports.aiSessions.status,
|
|
113951
|
-
aiTimeEstimateMinutes: schema_exports.aiSessions.aiTimeEstimateMinutes,
|
|
113952
|
-
actualTimeMinutes: schema_exports.aiSessions.actualTimeMinutes,
|
|
113953
|
-
complexityScore: schema_exports.aiSessions.complexityScore,
|
|
113954
|
-
createdAt: schema_exports.aiSessions.createdAt,
|
|
113955
|
-
cursorSessionId: schema_exports.aiSessions.cursorSessionId
|
|
113956
|
-
}).from(schema_exports.aiSessions).where(eq(schema_exports.aiSessions.id, fullSessionId)).limit(1);
|
|
113957
|
-
if (!session) throw new Error(`Session not found: ${aiSessionId}`);
|
|
113958
|
-
const context2 = {
|
|
113959
|
-
status: session.status,
|
|
113960
|
-
timeEstimate: session.aiTimeEstimateMinutes,
|
|
113961
|
-
actualTime: session.actualTimeMinutes,
|
|
113962
|
-
complexity: session.complexityScore,
|
|
113963
|
-
createdAt: session.createdAt
|
|
113964
|
-
};
|
|
113965
|
-
if (includeTicketData) {
|
|
113966
|
-
const [ticket] = await db.select({
|
|
113967
|
-
id: schema_exports.tickets.id,
|
|
113968
|
-
ticketNumber: schema_exports.tickets.ticketNumber,
|
|
113969
|
-
title: schema_exports.tickets.title,
|
|
113970
|
-
description: schema_exports.tickets.description,
|
|
113971
|
-
status: schema_exports.tickets.status,
|
|
113972
|
-
priority: schema_exports.tickets.priority,
|
|
113973
|
-
type: schema_exports.tickets.type
|
|
113974
|
-
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id, session.ticketId)).limit(1);
|
|
113975
|
-
context2.ticketData = ticket ?? null;
|
|
113976
|
-
}
|
|
113977
|
-
if (includeTodoProgress) {
|
|
113978
|
-
const todos3 = await db.select({
|
|
113979
|
-
id: schema_exports.aiTodos.id,
|
|
113980
|
-
content: schema_exports.aiTodos.content,
|
|
113981
|
-
status: schema_exports.aiTodos.status,
|
|
113982
|
-
estimatedMinutes: schema_exports.aiTodos.estimatedMinutes,
|
|
113983
|
-
actualMinutes: schema_exports.aiTodos.actualMinutes
|
|
113984
|
-
}).from(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, session.id)).orderBy(asc(schema_exports.aiTodos.sequenceOrder));
|
|
113985
|
-
context2.todos = todos3;
|
|
113986
|
-
context2.todoProgress = {
|
|
113987
|
-
total: todos3.length,
|
|
113988
|
-
completed: todos3.filter((t8) => t8.status === "completed").length,
|
|
113989
|
-
inProgress: todos3.filter((t8) => t8.status === "in_progress").length
|
|
113990
|
-
};
|
|
113991
|
-
}
|
|
113992
|
-
if (includeFollowUpHistory) {
|
|
113993
|
-
const followUps = await db.select({
|
|
113994
|
-
followUpReason: schema_exports.manualFollowUps.followUpReason,
|
|
113995
|
-
outcome: schema_exports.manualFollowUps.outcome,
|
|
113996
|
-
timeSpentMinutes: schema_exports.manualFollowUps.timeSpentMinutes,
|
|
113997
|
-
createdAt: schema_exports.manualFollowUps.createdAt
|
|
113998
|
-
}).from(schema_exports.manualFollowUps).where(eq(schema_exports.manualFollowUps.aiSessionId, session.id)).orderBy(asc(schema_exports.manualFollowUps.createdAt));
|
|
113999
|
-
context2.followUpHistory = followUps;
|
|
114000
|
-
}
|
|
114001
|
-
const ticketData = context2.ticketData;
|
|
114002
|
-
const todoProgress = context2.todoProgress;
|
|
114003
|
-
const followUpHistory = context2.followUpHistory;
|
|
114004
|
-
return {
|
|
114005
|
-
content: [
|
|
114006
|
-
{
|
|
114007
|
-
type: "text",
|
|
114008
|
-
text: `\u{1F3AF} **Session Context Retrieved**
|
|
114009
|
-
|
|
114010
|
-
Session: ${aiSessionId}
|
|
114011
|
-
Status: ${session.status}
|
|
114012
|
-
${ticketData ? `Ticket: ${ticketData.ticketNumber} - ${ticketData.title}
|
|
114013
|
-
` : ""}${todoProgress ? `Todo Progress: ${todoProgress.completed}/${todoProgress.total} completed
|
|
114014
|
-
` : ""}${followUpHistory ? `Follow-ups: ${followUpHistory.length}
|
|
114015
|
-
` : ""}
|
|
114016
|
-
\u{1F4CB} Full context preserved for seamless continuation!`
|
|
114017
|
-
}
|
|
114018
|
-
]
|
|
114019
|
-
};
|
|
114020
|
-
}
|
|
114021
|
-
async function handleSyncSessionTodos(input) {
|
|
114022
|
-
const { aiSessionId, todos: todos3, replaceAll = true } = input;
|
|
114023
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
114024
|
-
if (!scope.ok) return scope.response;
|
|
114025
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
114026
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
114027
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
114028
|
-
if (replaceAll) {
|
|
114029
|
-
await db.delete(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, fullSessionId));
|
|
114030
|
-
}
|
|
114031
|
-
if (todos3 && todos3.length > 0) {
|
|
114032
|
-
let startSequence = 0;
|
|
114033
|
-
if (!replaceAll) {
|
|
114034
|
-
const [maxTodo] = await db.select({ sequenceOrder: schema_exports.aiTodos.sequenceOrder }).from(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, fullSessionId)).orderBy(desc(schema_exports.aiTodos.sequenceOrder)).limit(1);
|
|
114035
|
-
startSequence = (maxTodo?.sequenceOrder ?? 0) + 1;
|
|
114036
|
-
}
|
|
114037
|
-
await db.insert(schema_exports.aiTodos).values(
|
|
114038
|
-
todos3.map((todo, index2) => ({
|
|
114039
|
-
aiSessionId: fullSessionId,
|
|
114040
|
-
content: todo.content,
|
|
114041
|
-
status: todo.status,
|
|
114042
|
-
cursorTodoId: todo.todoId ?? null,
|
|
114043
|
-
estimatedMinutes: todo.estimatedMinutes ?? null,
|
|
114044
|
-
sequenceOrder: startSequence + index2
|
|
114045
|
-
}))
|
|
114046
|
-
);
|
|
114047
|
-
}
|
|
114048
|
-
let phaseTransition = null;
|
|
114049
|
-
const currentTodos = await db.select({ status: schema_exports.aiTodos.status }).from(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, fullSessionId));
|
|
114050
|
-
if (currentTodos.length > 0) {
|
|
114051
|
-
const hasInProgress = currentTodos.some((t8) => t8.status === "in_progress");
|
|
114052
|
-
const allCompleted = currentTodos.every((t8) => t8.status === "completed");
|
|
114053
|
-
const [currentPhase] = await db.select({
|
|
114054
|
-
activityType: schema_exports.aiTimeLogs.activityType,
|
|
114055
|
-
status: schema_exports.aiTimeLogs.status
|
|
114056
|
-
}).from(schema_exports.aiTimeLogs).where(
|
|
114057
|
-
and(
|
|
114058
|
-
eq(schema_exports.aiTimeLogs.aiSessionId, fullSessionId),
|
|
114059
|
-
eq(schema_exports.aiTimeLogs.status, "in_progress")
|
|
114060
|
-
)
|
|
114061
|
-
).limit(1);
|
|
114062
|
-
if (hasInProgress && currentPhase?.activityType === "analysis") {
|
|
114063
|
-
await transitionToNextPhase(fullSessionId, "analysis");
|
|
114064
|
-
phaseTransition = "Analysis completed \u2192 Next phase started (Investigation/Development)";
|
|
114065
|
-
}
|
|
114066
|
-
if (hasInProgress && currentPhase?.activityType === "bug_investigation") {
|
|
114067
|
-
const completedCount = currentTodos.filter(
|
|
114068
|
-
(t8) => t8.status === "completed"
|
|
114069
|
-
).length;
|
|
114070
|
-
if (completedCount > 0) {
|
|
114071
|
-
await transitionToNextPhase(fullSessionId, "bug_investigation");
|
|
114072
|
-
phaseTransition = "Investigation completed \u2192 Development phase started";
|
|
114073
|
-
}
|
|
114074
|
-
}
|
|
114075
|
-
if (allCompleted && currentPhase?.activityType === "development") {
|
|
114076
|
-
await transitionToNextPhase(fullSessionId, "development");
|
|
114077
|
-
phaseTransition = "Development completed \u2192 Communication phase started";
|
|
114078
|
-
}
|
|
114079
|
-
}
|
|
114080
|
-
return {
|
|
114081
|
-
content: [
|
|
114082
|
-
{
|
|
114083
|
-
type: "text",
|
|
114084
|
-
text: `\u2705 **Todos ${replaceAll ? "Synced" : "Added"} Successfully!**
|
|
114085
|
-
|
|
114086
|
-
Session: ${aiSessionId}
|
|
114087
|
-
${replaceAll ? "Synced" : "Added"} ${todos3?.length || 0} todos
|
|
114088
|
-
${replaceAll ? "" : "\u2795 Added to existing todo list\n"}${phaseTransition ? `\u{1F504} Phase Transition: ${phaseTransition}
|
|
114089
|
-
` : ""}
|
|
114090
|
-
\u{1F4DD} Todo list updated and tracked for progress monitoring!`
|
|
114091
|
-
}
|
|
114092
|
-
]
|
|
114093
|
-
};
|
|
114094
|
-
}
|
|
114095
|
-
async function handleAddFollowUpTodos(input) {
|
|
114096
|
-
const { aiSessionId, newTodos, followUpReason } = input;
|
|
114097
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
114098
|
-
if (!scope.ok) return scope.response;
|
|
114099
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
114100
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
114101
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
114102
|
-
if (newTodos && newTodos.length > 0) {
|
|
114103
|
-
const [maxTodo] = await db.select({ sequenceOrder: schema_exports.aiTodos.sequenceOrder }).from(schema_exports.aiTodos).where(eq(schema_exports.aiTodos.aiSessionId, fullSessionId)).orderBy(desc(schema_exports.aiTodos.sequenceOrder)).limit(1);
|
|
114104
|
-
const startSequence = (maxTodo?.sequenceOrder ?? 0) + 1;
|
|
114105
|
-
await db.insert(schema_exports.aiTodos).values(
|
|
114106
|
-
newTodos.map((todo, index2) => ({
|
|
114107
|
-
aiSessionId: fullSessionId,
|
|
114108
|
-
content: `[Follow-up] ${todo.content}`,
|
|
114109
|
-
status: todo.status ?? "pending",
|
|
114110
|
-
estimatedMinutes: todo.estimatedMinutes ?? null,
|
|
114111
|
-
sequenceOrder: startSequence + index2
|
|
114112
|
-
}))
|
|
114113
|
-
);
|
|
114114
|
-
}
|
|
114115
|
-
return {
|
|
114116
|
-
content: [
|
|
114117
|
-
{
|
|
114118
|
-
type: "text",
|
|
114119
|
-
text: `\u2705 **Follow-up Todos Added Successfully!**
|
|
114120
|
-
|
|
114121
|
-
Session: ${aiSessionId}
|
|
114122
|
-
Added ${newTodos?.length || 0} new todos from follow-up
|
|
114123
|
-
${followUpReason ? `Reason: ${followUpReason}
|
|
114124
|
-
` : ""}
|
|
114125
|
-
\u{1F4DD} New tasks identified and added to existing workflow!`
|
|
114126
|
-
}
|
|
114127
|
-
]
|
|
114128
|
-
};
|
|
114129
|
-
}
|
|
114130
|
-
async function handleUpdateSessionStatus(input) {
|
|
114131
|
-
const { aiSessionId, status, actualTimeMinutes, completionNotes } = input;
|
|
114132
|
-
const scope = await resolveTeamScope(input.teamId);
|
|
114133
|
-
if (!scope.ok) return scope.response;
|
|
114134
|
-
const prefix = aiSessionId.replace("ai-sess-", "");
|
|
114135
|
-
const fullSessionId = await resolveAiSessionId(prefix, scope.teamIds);
|
|
114136
|
-
if (!fullSessionId) throw new Error(`Session not found: ${aiSessionId}`);
|
|
114137
|
-
await db.update(schema_exports.aiSessions).set({
|
|
114138
|
-
status,
|
|
114139
|
-
actualTimeMinutes: actualTimeMinutes ?? null,
|
|
114140
|
-
completedAt: status === "completed" ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
114141
|
-
}).where(eq(schema_exports.aiSessions.id, fullSessionId));
|
|
114142
|
-
return {
|
|
114143
|
-
content: [
|
|
114144
|
-
{
|
|
114145
|
-
type: "text",
|
|
114146
|
-
text: `\u{1F3AF} **Session Status Updated!**
|
|
114147
|
-
|
|
114148
|
-
Session: ${aiSessionId}
|
|
114149
|
-
Status: ${status}
|
|
114150
|
-
${actualTimeMinutes ? `Actual Time: ${actualTimeMinutes} minutes
|
|
114151
|
-
` : ""}${status === "completed" ? `\u2705 Session completed successfully!
|
|
114152
|
-
` : ""}${completionNotes ? `Notes: ${completionNotes}
|
|
114153
|
-
` : ""}`
|
|
114154
|
-
}
|
|
114155
|
-
]
|
|
114156
|
-
};
|
|
114157
|
-
}
|
|
114158
|
-
|
|
114159
113008
|
// src/tools/teams.ts
|
|
114160
113009
|
async function handleGetTeams() {
|
|
114161
113010
|
const ctx = getAuthContext();
|
|
@@ -120218,6 +119067,118 @@ ${created.projectId ? `Project ID: ${created.projectId}
|
|
|
120218
119067
|
};
|
|
120219
119068
|
}
|
|
120220
119069
|
|
|
119070
|
+
// src/utils/ticket-number.ts
|
|
119071
|
+
async function isTicketNumberTaken(ticketDb, teamId, ticketNumber, excludeTicketId) {
|
|
119072
|
+
const conditions = [
|
|
119073
|
+
eq(schema_exports.tickets.teamId, teamId),
|
|
119074
|
+
eq(schema_exports.tickets.ticketNumber, ticketNumber),
|
|
119075
|
+
eq(schema_exports.tickets.isDeleted, false)
|
|
119076
|
+
];
|
|
119077
|
+
if (excludeTicketId) {
|
|
119078
|
+
conditions.push(ne(schema_exports.tickets.id, excludeTicketId));
|
|
119079
|
+
}
|
|
119080
|
+
const [existing] = await ticketDb.select({ id: schema_exports.tickets.id }).from(schema_exports.tickets).where(and(...conditions)).limit(1);
|
|
119081
|
+
return Boolean(existing);
|
|
119082
|
+
}
|
|
119083
|
+
async function getMaxProjectSequence(ticketDb, teamId, pattern, excludeTicketId) {
|
|
119084
|
+
const conditions = [
|
|
119085
|
+
eq(schema_exports.tickets.teamId, teamId),
|
|
119086
|
+
like(schema_exports.tickets.ticketNumber, pattern),
|
|
119087
|
+
eq(schema_exports.tickets.isDeleted, false)
|
|
119088
|
+
];
|
|
119089
|
+
if (excludeTicketId) {
|
|
119090
|
+
conditions.push(ne(schema_exports.tickets.id, excludeTicketId));
|
|
119091
|
+
}
|
|
119092
|
+
const [row] = await ticketDb.select({
|
|
119093
|
+
maxSeq: sql`coalesce(max(
|
|
119094
|
+
case
|
|
119095
|
+
when split_part(${schema_exports.tickets.ticketNumber}, '-', 3) ~ '^[0-9]+$'
|
|
119096
|
+
then split_part(${schema_exports.tickets.ticketNumber}, '-', 3)::integer
|
|
119097
|
+
else null
|
|
119098
|
+
end
|
|
119099
|
+
), 0)`
|
|
119100
|
+
}).from(schema_exports.tickets).where(and(...conditions));
|
|
119101
|
+
return Number(row?.maxSeq ?? 0);
|
|
119102
|
+
}
|
|
119103
|
+
async function getMaxSimpleSequence(ticketDb, teamId, currentYear, excludeTicketId) {
|
|
119104
|
+
const conditions = [
|
|
119105
|
+
eq(schema_exports.tickets.teamId, teamId),
|
|
119106
|
+
like(schema_exports.tickets.ticketNumber, `${currentYear}-%`),
|
|
119107
|
+
eq(schema_exports.tickets.isDeleted, false)
|
|
119108
|
+
];
|
|
119109
|
+
if (excludeTicketId) {
|
|
119110
|
+
conditions.push(ne(schema_exports.tickets.id, excludeTicketId));
|
|
119111
|
+
}
|
|
119112
|
+
const [row] = await ticketDb.select({
|
|
119113
|
+
maxSeq: sql`coalesce(max(
|
|
119114
|
+
case
|
|
119115
|
+
when ${schema_exports.tickets.ticketNumber} ~ ${`^${currentYear}-[0-9]+$`}
|
|
119116
|
+
then split_part(${schema_exports.tickets.ticketNumber}, '-', 2)::integer
|
|
119117
|
+
else null
|
|
119118
|
+
end
|
|
119119
|
+
), 0)`
|
|
119120
|
+
}).from(schema_exports.tickets).where(and(...conditions));
|
|
119121
|
+
return Number(row?.maxSeq ?? 0);
|
|
119122
|
+
}
|
|
119123
|
+
async function resolveUniqueTicketNumber(ticketDb, teamId, buildNumber, startSequence, excludeTicketId) {
|
|
119124
|
+
let sequence = startSequence;
|
|
119125
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
119126
|
+
const ticketNumber = buildNumber(sequence);
|
|
119127
|
+
const taken = await isTicketNumberTaken(
|
|
119128
|
+
ticketDb,
|
|
119129
|
+
teamId,
|
|
119130
|
+
ticketNumber,
|
|
119131
|
+
excludeTicketId
|
|
119132
|
+
);
|
|
119133
|
+
if (!taken) {
|
|
119134
|
+
return ticketNumber;
|
|
119135
|
+
}
|
|
119136
|
+
sequence += 1;
|
|
119137
|
+
}
|
|
119138
|
+
throw new Error("Failed to generate a unique ticket number");
|
|
119139
|
+
}
|
|
119140
|
+
async function generateTicketNumber(ticketDb, teamId, projectId, options) {
|
|
119141
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
119142
|
+
const excludeTicketId = options?.excludeTicketId ?? null;
|
|
119143
|
+
if (projectId) {
|
|
119144
|
+
const [project] = await ticketDb.select({ name: schema_exports.projects.name }).from(schema_exports.projects).where(eq(schema_exports.projects.id, projectId)).limit(1);
|
|
119145
|
+
if (project?.name) {
|
|
119146
|
+
const projectAbbreviation = generateProjectAbbreviation(project.name);
|
|
119147
|
+
const pattern = `${currentYear}-${projectAbbreviation}-%`;
|
|
119148
|
+
const maxSequence2 = await getMaxProjectSequence(
|
|
119149
|
+
ticketDb,
|
|
119150
|
+
teamId,
|
|
119151
|
+
pattern,
|
|
119152
|
+
excludeTicketId
|
|
119153
|
+
);
|
|
119154
|
+
let nextSequence = maxSequence2 + 1;
|
|
119155
|
+
if (projectAbbreviation === "MBC" && nextSequence < 100001) {
|
|
119156
|
+
nextSequence = 100001;
|
|
119157
|
+
}
|
|
119158
|
+
return resolveUniqueTicketNumber(
|
|
119159
|
+
ticketDb,
|
|
119160
|
+
teamId,
|
|
119161
|
+
(sequence) => `${currentYear}-${projectAbbreviation}-${String(sequence).padStart(3, "0")}`,
|
|
119162
|
+
nextSequence,
|
|
119163
|
+
excludeTicketId
|
|
119164
|
+
);
|
|
119165
|
+
}
|
|
119166
|
+
}
|
|
119167
|
+
const maxSequence = await getMaxSimpleSequence(
|
|
119168
|
+
ticketDb,
|
|
119169
|
+
teamId,
|
|
119170
|
+
currentYear,
|
|
119171
|
+
excludeTicketId
|
|
119172
|
+
);
|
|
119173
|
+
return resolveUniqueTicketNumber(
|
|
119174
|
+
ticketDb,
|
|
119175
|
+
teamId,
|
|
119176
|
+
(sequence) => `${currentYear}-${String(sequence).padStart(3, "0")}`,
|
|
119177
|
+
maxSequence + 1,
|
|
119178
|
+
excludeTicketId
|
|
119179
|
+
);
|
|
119180
|
+
}
|
|
119181
|
+
|
|
120221
119182
|
// src/tools/ticket-update.ts
|
|
120222
119183
|
async function handleUpdateTicket(input) {
|
|
120223
119184
|
const ctx = getAuthContext();
|
|
@@ -120253,6 +119214,7 @@ async function handleUpdateTicket(input) {
|
|
|
120253
119214
|
updateValues.priority = input.priority;
|
|
120254
119215
|
}
|
|
120255
119216
|
if (input.type !== void 0) updateValues.type = input.type;
|
|
119217
|
+
const projectExplicitlyChanged = input.projectId !== void 0 && input.projectId !== ticket.projectId;
|
|
120256
119218
|
if (input.projectId !== void 0) updateValues.projectId = input.projectId;
|
|
120257
119219
|
if (input.customerId !== void 0) {
|
|
120258
119220
|
updateValues.customerId = input.customerId;
|
|
@@ -120267,6 +119229,14 @@ async function handleUpdateTicket(input) {
|
|
|
120267
119229
|
updateValues.dueDate = input.dueDate;
|
|
120268
119230
|
updateValues.dueDateAllDay = input.dueDate ? true : false;
|
|
120269
119231
|
}
|
|
119232
|
+
if (projectExplicitlyChanged) {
|
|
119233
|
+
updateValues.ticketNumber = await generateTicketNumber(
|
|
119234
|
+
db,
|
|
119235
|
+
ticket.teamId,
|
|
119236
|
+
input.projectId,
|
|
119237
|
+
{ excludeTicketId: ticket.id }
|
|
119238
|
+
);
|
|
119239
|
+
}
|
|
120270
119240
|
let deadlineChange = null;
|
|
120271
119241
|
if (input.dueDate !== void 0) {
|
|
120272
119242
|
deadlineChange = await syncTicketDeadline(
|
|
@@ -120336,7 +119306,11 @@ async function handleUpdateTicket(input) {
|
|
|
120336
119306
|
changes.push("title updated");
|
|
120337
119307
|
}
|
|
120338
119308
|
if (input.description !== void 0) changes.push("description updated");
|
|
120339
|
-
if (input.projectId !== void 0)
|
|
119309
|
+
if (input.projectId !== void 0) {
|
|
119310
|
+
changes.push(
|
|
119311
|
+
projectExplicitlyChanged ? `project updated (renumbered to ${String(updateValues.ticketNumber ?? ticket.ticketNumber)})` : "project updated"
|
|
119312
|
+
);
|
|
119313
|
+
}
|
|
120340
119314
|
if (input.customerId !== void 0) changes.push("customer updated");
|
|
120341
119315
|
if (input.estimatedHours !== void 0) changes.push("estimated hours updated");
|
|
120342
119316
|
if (deadlineChange) changes.push(deadlineChange);
|
|
@@ -120768,13 +119742,10 @@ async function handleCreateTicket(input) {
|
|
|
120768
119742
|
} = input;
|
|
120769
119743
|
const resolved = await resolveTeamId(input.teamId);
|
|
120770
119744
|
if (!resolved.ok) return resolved.response;
|
|
120771
|
-
const year2 = (/* @__PURE__ */ new Date()).getFullYear();
|
|
120772
119745
|
let resolvedTeamId = resolved.teamId;
|
|
120773
119746
|
let resolvedCustomerId = customerId;
|
|
120774
|
-
let projectAbbreviation = "";
|
|
120775
119747
|
if (projectId) {
|
|
120776
119748
|
const [project] = await db.select({
|
|
120777
|
-
name: schema_exports.projects.name,
|
|
120778
119749
|
teamId: schema_exports.projects.teamId,
|
|
120779
119750
|
customerId: schema_exports.projects.customerId
|
|
120780
119751
|
}).from(schema_exports.projects).where(eq(schema_exports.projects.id, projectId)).limit(1);
|
|
@@ -120787,40 +119758,9 @@ async function handleCreateTicket(input) {
|
|
|
120787
119758
|
if (!resolvedCustomerId && project.customerId) {
|
|
120788
119759
|
resolvedCustomerId = project.customerId;
|
|
120789
119760
|
}
|
|
120790
|
-
if (project.name) {
|
|
120791
|
-
const upper = project.name.toUpperCase().replace(/[^A-Z0-9\s]/g, "");
|
|
120792
|
-
const words = upper.split(/\s+/).filter(Boolean);
|
|
120793
|
-
if (words.length >= 2) {
|
|
120794
|
-
projectAbbreviation = words.slice(0, 2).map((w2) => w2.substring(0, 3)).join("").substring(0, 5);
|
|
120795
|
-
} else if (words.length === 1 && words[0]) {
|
|
120796
|
-
projectAbbreviation = words[0].substring(0, 5);
|
|
120797
|
-
}
|
|
120798
|
-
}
|
|
120799
119761
|
}
|
|
120800
119762
|
}
|
|
120801
|
-
|
|
120802
|
-
if (projectId && projectAbbreviation) {
|
|
120803
|
-
const pattern = `${year2}-${projectAbbreviation}-%`;
|
|
120804
|
-
const [highest] = await db.select({ ticketNumber: schema_exports.tickets.ticketNumber }).from(schema_exports.tickets).where(
|
|
120805
|
-
and(
|
|
120806
|
-
eq(schema_exports.tickets.projectId, projectId),
|
|
120807
|
-
ilike(schema_exports.tickets.ticketNumber, pattern)
|
|
120808
|
-
)
|
|
120809
|
-
).orderBy(desc(schema_exports.tickets.ticketNumber)).limit(1);
|
|
120810
|
-
let nextSequence = 1;
|
|
120811
|
-
if (highest?.ticketNumber) {
|
|
120812
|
-
const parts = highest.ticketNumber.split("-");
|
|
120813
|
-
if (parts.length === 3 && parts[2]) {
|
|
120814
|
-
const lastSeq = Number.parseInt(parts[2], 10);
|
|
120815
|
-
if (!Number.isNaN(lastSeq)) nextSequence = lastSeq + 1;
|
|
120816
|
-
}
|
|
120817
|
-
}
|
|
120818
|
-
ticketNumber = `${year2}-${projectAbbreviation}-${String(nextSequence).padStart(3, "0")}`;
|
|
120819
|
-
} else {
|
|
120820
|
-
const [countRow] = await db.select({ n: sql`count(*)::int` }).from(schema_exports.tickets).where(eq(schema_exports.tickets.teamId, resolvedTeamId));
|
|
120821
|
-
const count = Number(countRow?.n ?? 0);
|
|
120822
|
-
ticketNumber = `${year2}-${String(count + 1).padStart(3, "0")}`;
|
|
120823
|
-
}
|
|
119763
|
+
const ticketNumber = await generateTicketNumber(db, resolvedTeamId, projectId);
|
|
120824
119764
|
const [created] = await db.insert(schema_exports.tickets).values({
|
|
120825
119765
|
teamId: resolvedTeamId,
|
|
120826
119766
|
ticketNumber,
|
|
@@ -120885,7 +119825,7 @@ ${tagErrors.map((e6) => ` \u2022 ${e6}`).join("\n")}
|
|
|
120885
119825
|
}
|
|
120886
119826
|
|
|
120887
119827
|
// src/server.ts
|
|
120888
|
-
var SERVER_VERSION = "3.
|
|
119828
|
+
var SERVER_VERSION = "3.4.0";
|
|
120889
119829
|
function createMcpServer() {
|
|
120890
119830
|
const server = new Server(
|
|
120891
119831
|
{
|
|
@@ -121009,42 +119949,6 @@ function createMcpServer() {
|
|
|
121009
119949
|
return await handleLinkDocumentToInvoice(
|
|
121010
119950
|
asToolArgs(toolArgs)
|
|
121011
119951
|
);
|
|
121012
|
-
case "start-ai-session-smart":
|
|
121013
|
-
return await handleStartAiSession(
|
|
121014
|
-
asToolArgs(toolArgs)
|
|
121015
|
-
);
|
|
121016
|
-
case "track-manual-follow-up":
|
|
121017
|
-
return await handleTrackManualFollowUp(
|
|
121018
|
-
asToolArgs(toolArgs)
|
|
121019
|
-
);
|
|
121020
|
-
case "get-session-context":
|
|
121021
|
-
return await handleGetSessionContext(
|
|
121022
|
-
asToolArgs(toolArgs)
|
|
121023
|
-
);
|
|
121024
|
-
case "sync-session-todos":
|
|
121025
|
-
return await handleSyncSessionTodos(
|
|
121026
|
-
asToolArgs(toolArgs)
|
|
121027
|
-
);
|
|
121028
|
-
case "add-follow-up-todos":
|
|
121029
|
-
return await handleAddFollowUpTodos(
|
|
121030
|
-
asToolArgs(toolArgs)
|
|
121031
|
-
);
|
|
121032
|
-
case "update-session-status":
|
|
121033
|
-
return await handleUpdateSessionStatus(
|
|
121034
|
-
asToolArgs(toolArgs)
|
|
121035
|
-
);
|
|
121036
|
-
case "get-completion-context":
|
|
121037
|
-
return await handleGetCompletionContext(
|
|
121038
|
-
asToolArgs(toolArgs)
|
|
121039
|
-
);
|
|
121040
|
-
case "save-customer-response":
|
|
121041
|
-
return await handleSaveCustomerResponse(
|
|
121042
|
-
asToolArgs(toolArgs)
|
|
121043
|
-
);
|
|
121044
|
-
case "complete-ai-session":
|
|
121045
|
-
return await handleCompleteAiSession(
|
|
121046
|
-
asToolArgs(toolArgs)
|
|
121047
|
-
);
|
|
121048
119952
|
case "log-hours":
|
|
121049
119953
|
return await handleLogHours(asToolArgs(toolArgs));
|
|
121050
119954
|
case "get-github-file":
|