@mgsoftwarebv/mcp-server-bridge 2.20.0 → 2.22.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/dist/index.js +151 -113
- package/dist/index.js.map +1 -1
- package/package.json +48 -48
package/dist/index.js
CHANGED
|
@@ -207,7 +207,7 @@ var TOOLS = [
|
|
|
207
207
|
priority: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
208
208
|
projectId: { type: "string" },
|
|
209
209
|
customerId: { type: "string" },
|
|
210
|
-
q: { type: "string", description: "Search query for title or description" },
|
|
210
|
+
q: { type: "string", description: "Search query for ticket number, title, or description" },
|
|
211
211
|
pageSize: { type: "number", default: 20, maximum: 100 }
|
|
212
212
|
},
|
|
213
213
|
required: []
|
|
@@ -296,28 +296,16 @@ var TOOLS = [
|
|
|
296
296
|
// === NEW AI SESSION TOOLS ===
|
|
297
297
|
{
|
|
298
298
|
name: "start-ai-session-smart",
|
|
299
|
-
description: "Start a new AI development session with automatic tracking
|
|
299
|
+
description: "Start a new AI development session with automatic tracking",
|
|
300
300
|
inputSchema: {
|
|
301
301
|
type: "object",
|
|
302
302
|
properties: {
|
|
303
303
|
ticketId: { type: "string" },
|
|
304
304
|
ticketUrl: { type: "string", description: "URL to the ticket" },
|
|
305
305
|
cursorSessionId: { type: "string", description: "Cursor session identifier" },
|
|
306
|
-
|
|
307
|
-
type: "
|
|
308
|
-
|
|
309
|
-
description: "Relevant files for complexity analysis"
|
|
310
|
-
},
|
|
311
|
-
timeBreakdown: {
|
|
312
|
-
type: "object",
|
|
313
|
-
description: "Time estimate breakdown by development phase (REQUIRED)",
|
|
314
|
-
properties: {
|
|
315
|
-
analysisMinutes: { type: "number", description: "Ticket/context analysis time" },
|
|
316
|
-
investigationMinutes: { type: "number", description: "Bug investigation/reproduction (0 if not a bug)" },
|
|
317
|
-
developmentMinutes: { type: "number", description: "Actual coding/fixing time" },
|
|
318
|
-
communicationMinutes: { type: "number", description: "Customer response writing time" }
|
|
319
|
-
},
|
|
320
|
-
required: ["analysisMinutes", "investigationMinutes", "developmentMinutes", "communicationMinutes"]
|
|
306
|
+
totalEstimatedMinutes: {
|
|
307
|
+
type: "number",
|
|
308
|
+
description: "Total estimated time in minutes (senior dev WITHOUT AI, rounded to 15 min)"
|
|
321
309
|
},
|
|
322
310
|
complexityScore: {
|
|
323
311
|
type: "number",
|
|
@@ -326,7 +314,7 @@ var TOOLS = [
|
|
|
326
314
|
description: "Estimated complexity from 1-10"
|
|
327
315
|
}
|
|
328
316
|
},
|
|
329
|
-
required: ["ticketId", "
|
|
317
|
+
required: ["ticketId", "totalEstimatedMinutes"]
|
|
330
318
|
}
|
|
331
319
|
},
|
|
332
320
|
{
|
|
@@ -647,7 +635,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
647
635
|
if (priority) query = query.eq("priority", priority);
|
|
648
636
|
if (projectId) query = query.eq("project_id", projectId);
|
|
649
637
|
if (customerId) query = query.eq("customer_id", customerId);
|
|
650
|
-
if (q) query = query.or(`title.ilike.%${q}%,description.ilike.%${q}%`);
|
|
638
|
+
if (q) query = query.or(`ticket_number.ilike.%${q}%,title.ilike.%${q}%,description.ilike.%${q}%`);
|
|
651
639
|
const { data, error } = await query.order("created_at", { ascending: false });
|
|
652
640
|
if (error) throw error;
|
|
653
641
|
return {
|
|
@@ -807,10 +795,48 @@ ${attachments && attachments.length > 0 ? `
|
|
|
807
795
|
case "create-ticket": {
|
|
808
796
|
const { title, description, status = "open", priority = "medium", type = "task", projectId, customerId } = args2;
|
|
809
797
|
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
810
|
-
|
|
811
|
-
|
|
798
|
+
let resolvedTeamId = authContext.teamId;
|
|
799
|
+
let resolvedCustomerId = customerId;
|
|
800
|
+
let projectAbbreviation = "";
|
|
801
|
+
if (projectId) {
|
|
802
|
+
const { data: project } = await supabase.from("projects").select("name, team_id, customer_id").eq("id", projectId).single();
|
|
803
|
+
if (project) {
|
|
804
|
+
if (project.team_id) {
|
|
805
|
+
resolvedTeamId = project.team_id;
|
|
806
|
+
}
|
|
807
|
+
if (!resolvedCustomerId && project.customer_id) {
|
|
808
|
+
resolvedCustomerId = project.customer_id;
|
|
809
|
+
}
|
|
810
|
+
if (project.name) {
|
|
811
|
+
const name2 = project.name.toUpperCase().replace(/[^A-Z0-9\s]/g, "");
|
|
812
|
+
const words = name2.split(/\s+/).filter(Boolean);
|
|
813
|
+
if (words.length >= 2) {
|
|
814
|
+
projectAbbreviation = words.slice(0, 2).map((w) => w.substring(0, 3)).join("").substring(0, 5);
|
|
815
|
+
} else if (words.length === 1 && words[0]) {
|
|
816
|
+
projectAbbreviation = words[0].substring(0, 5);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
let ticketNumber;
|
|
822
|
+
if (projectId && projectAbbreviation) {
|
|
823
|
+
const pattern = `${year}-${projectAbbreviation}-%`;
|
|
824
|
+
const { data: existingTickets } = await supabase.from("tickets").select("ticket_number").eq("project_id", projectId).like("ticket_number", pattern).order("ticket_number", { ascending: false }).limit(1);
|
|
825
|
+
let nextSequence = 1;
|
|
826
|
+
if (existingTickets && existingTickets.length > 0 && existingTickets[0]?.ticket_number) {
|
|
827
|
+
const parts = existingTickets[0].ticket_number.split("-");
|
|
828
|
+
if (parts.length === 3) {
|
|
829
|
+
const lastSeq = parseInt(parts[2], 10);
|
|
830
|
+
if (!isNaN(lastSeq)) nextSequence = lastSeq + 1;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
ticketNumber = `${year}-${projectAbbreviation}-${String(nextSequence).padStart(3, "0")}`;
|
|
834
|
+
} else {
|
|
835
|
+
const { count } = await supabase.from("tickets").select("*", { count: "exact", head: true }).eq("team_id", resolvedTeamId);
|
|
836
|
+
ticketNumber = `${year}-${String((count || 0) + 1).padStart(3, "0")}`;
|
|
837
|
+
}
|
|
812
838
|
const { data, error } = await supabase.from("tickets").insert({
|
|
813
|
-
team_id:
|
|
839
|
+
team_id: resolvedTeamId,
|
|
814
840
|
ticket_number: ticketNumber,
|
|
815
841
|
title,
|
|
816
842
|
description,
|
|
@@ -818,7 +844,7 @@ ${attachments && attachments.length > 0 ? `
|
|
|
818
844
|
priority,
|
|
819
845
|
type,
|
|
820
846
|
project_id: projectId || null,
|
|
821
|
-
customer_id:
|
|
847
|
+
customer_id: resolvedCustomerId || null,
|
|
822
848
|
requester_id: authContext.userId
|
|
823
849
|
}).select().single();
|
|
824
850
|
if (error) throw error;
|
|
@@ -949,91 +975,34 @@ ${description ? `Description: ${description}
|
|
|
949
975
|
}
|
|
950
976
|
// === AI SESSION TOOLS ===
|
|
951
977
|
case "start-ai-session-smart": {
|
|
952
|
-
const { ticketId, ticketUrl, cursorSessionId,
|
|
953
|
-
if (!
|
|
954
|
-
throw new Error("
|
|
955
|
-
}
|
|
956
|
-
const
|
|
957
|
-
analysisMinutes: roundToNearest15Minutes(timeBreakdown.analysisMinutes || 0),
|
|
958
|
-
investigationMinutes: roundToNearest15Minutes(timeBreakdown.investigationMinutes || 0),
|
|
959
|
-
developmentMinutes: roundToNearest15Minutes(timeBreakdown.developmentMinutes || 0),
|
|
960
|
-
communicationMinutes: roundToNearest15Minutes(timeBreakdown.communicationMinutes || 0)
|
|
961
|
-
};
|
|
962
|
-
const totalEstimateMinutes = roundedTimeBreakdown.analysisMinutes + roundedTimeBreakdown.investigationMinutes + roundedTimeBreakdown.developmentMinutes + roundedTimeBreakdown.communicationMinutes;
|
|
978
|
+
const { ticketId, ticketUrl, cursorSessionId, totalEstimatedMinutes, complexityScore } = args2;
|
|
979
|
+
if (!totalEstimatedMinutes) {
|
|
980
|
+
throw new Error("totalEstimatedMinutes is required");
|
|
981
|
+
}
|
|
982
|
+
const roundedMinutes = roundToNearest15Minutes(totalEstimatedMinutes);
|
|
963
983
|
const sessionStartTime = /* @__PURE__ */ new Date();
|
|
964
984
|
const { data: sessionData, error } = await supabase.from("ai_sessions").insert({
|
|
965
985
|
ticket_id: ticketId,
|
|
966
986
|
provider_user_id: authContext.userId,
|
|
967
987
|
team_id: authContext.teamId,
|
|
968
988
|
cursor_session_id: cursorSessionId || null,
|
|
969
|
-
ai_time_estimate_minutes:
|
|
989
|
+
ai_time_estimate_minutes: roundedMinutes,
|
|
970
990
|
complexity_score: complexityScore || null,
|
|
971
991
|
status: "in_progress"
|
|
972
992
|
}).select("id, ticket_id, cursor_session_id, created_at").single();
|
|
973
993
|
if (error) throw error;
|
|
974
|
-
const phaseActivities = [
|
|
975
|
-
{
|
|
976
|
-
ai_session_id: sessionData.id,
|
|
977
|
-
activity_type: "analysis",
|
|
978
|
-
description: "Ticket analysis and context understanding",
|
|
979
|
-
duration_seconds: 0,
|
|
980
|
-
estimated_duration_seconds: roundedTimeBreakdown.analysisMinutes * 60,
|
|
981
|
-
status: "in_progress",
|
|
982
|
-
// Analysis starts immediately
|
|
983
|
-
productivity_score: 8,
|
|
984
|
-
started_at: sessionStartTime.toISOString()
|
|
985
|
-
},
|
|
986
|
-
{
|
|
987
|
-
ai_session_id: sessionData.id,
|
|
988
|
-
activity_type: "bug_investigation",
|
|
989
|
-
description: "Bug investigation and root cause analysis",
|
|
990
|
-
duration_seconds: 0,
|
|
991
|
-
estimated_duration_seconds: roundedTimeBreakdown.investigationMinutes * 60,
|
|
992
|
-
status: "pending",
|
|
993
|
-
productivity_score: null
|
|
994
|
-
},
|
|
995
|
-
{
|
|
996
|
-
ai_session_id: sessionData.id,
|
|
997
|
-
activity_type: "development",
|
|
998
|
-
description: "Implementation and coding",
|
|
999
|
-
duration_seconds: 0,
|
|
1000
|
-
estimated_duration_seconds: roundedTimeBreakdown.developmentMinutes * 60,
|
|
1001
|
-
status: "pending",
|
|
1002
|
-
productivity_score: null
|
|
1003
|
-
},
|
|
1004
|
-
{
|
|
1005
|
-
ai_session_id: sessionData.id,
|
|
1006
|
-
activity_type: "communication",
|
|
1007
|
-
description: "Customer response and documentation",
|
|
1008
|
-
duration_seconds: 0,
|
|
1009
|
-
estimated_duration_seconds: roundedTimeBreakdown.communicationMinutes * 60,
|
|
1010
|
-
status: "pending",
|
|
1011
|
-
productivity_score: null
|
|
1012
|
-
}
|
|
1013
|
-
];
|
|
1014
|
-
await supabase.from("ai_time_logs").insert(phaseActivities);
|
|
1015
994
|
const sessionId = `ai-sess-${sessionData.id.substring(0, 8)}`;
|
|
1016
995
|
return {
|
|
1017
996
|
content: [{
|
|
1018
997
|
type: "text",
|
|
1019
|
-
text: `\u{1F680} **AI Session Started
|
|
998
|
+
text: `\u{1F680} **AI Session Started!**
|
|
1020
999
|
|
|
1021
1000
|
\u{1F194} Session ID: **${sessionId}**
|
|
1022
1001
|
\u{1F3AB} Ticket: ${ticketId}
|
|
1023
|
-
|
|
1024
|
-
\u{1F4CA} **Time Breakdown:**
|
|
1025
|
-
\u2022 Analysis: ${roundedTimeBreakdown.analysisMinutes} min
|
|
1026
|
-
\u2022 Investigation: ${roundedTimeBreakdown.investigationMinutes} min
|
|
1027
|
-
\u2022 Development: ${roundedTimeBreakdown.developmentMinutes} min
|
|
1028
|
-
\u2022 Communication: ${roundedTimeBreakdown.communicationMinutes} min
|
|
1029
|
-
\u2022 **Total: ${totalEstimateMinutes} min**
|
|
1030
|
-
|
|
1002
|
+
\u23F1\uFE0F Estimated: ${roundedMinutes} min
|
|
1031
1003
|
${complexityScore ? `\u{1F3AF} Complexity: ${complexityScore}/10
|
|
1032
|
-
` : ""}\u23F1\uFE0F **Phase Tracking Started** (Analysis in progress)
|
|
1033
|
-
${cursorSessionId ? `\u{1F517} Cursor Session: ${cursorSessionId}
|
|
1034
1004
|
` : ""}\u{1F4C5} Started: ${sessionStartTime.toLocaleString()}
|
|
1035
1005
|
|
|
1036
|
-
\u2705 Session initialized with phase breakdown!
|
|
1037
1006
|
\u{1F4DD} Timetrack entry will be created when you complete the session.`
|
|
1038
1007
|
}]
|
|
1039
1008
|
};
|
|
@@ -1092,10 +1061,21 @@ ${cursorSessionId ? `\u{1F517} Cursor Session: ${cursorSessionId}
|
|
|
1092
1061
|
efficiency_score: currentEfficiency.toFixed(2),
|
|
1093
1062
|
actual_time_minutes: totalMinutesElapsed
|
|
1094
1063
|
}).eq("id", session.id);
|
|
1095
|
-
const { data:
|
|
1064
|
+
const { data: existingEntries, error: entryError } = await supabase.from("agenda_events").select("id, tracked_duration, title, description, start_time").eq("ai_session_id", session.id).eq("status", "draft").order("created_at", { ascending: false });
|
|
1096
1065
|
let trackerAction = "";
|
|
1097
1066
|
let trackerDetails = "";
|
|
1098
|
-
|
|
1067
|
+
const existingEntry = existingEntries && existingEntries.length > 0 ? existingEntries[0] : null;
|
|
1068
|
+
if (existingEntries && existingEntries.length > 1) {
|
|
1069
|
+
const totalExistingDuration = existingEntries.reduce((sum, entry) => sum + (entry.tracked_duration || 0), 0);
|
|
1070
|
+
const duplicateIds = existingEntries.slice(1).map((e) => e.id);
|
|
1071
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1072
|
+
if (totalExistingDuration > (existingEntry?.tracked_duration || 0)) {
|
|
1073
|
+
await supabase.from("agenda_events").update({ tracked_duration: totalExistingDuration }).eq("id", existingEntry.id);
|
|
1074
|
+
existingEntry.tracked_duration = totalExistingDuration;
|
|
1075
|
+
}
|
|
1076
|
+
trackerAction = `Consolidated ${existingEntries.length} duplicate entries`;
|
|
1077
|
+
}
|
|
1078
|
+
if (existingEntry) {
|
|
1099
1079
|
const newDuration = (existingEntry.tracked_duration || 0) + roundedFollowUpMinutes * 60;
|
|
1100
1080
|
await supabase.from("agenda_events").update({
|
|
1101
1081
|
tracked_duration: newDuration,
|
|
@@ -1103,7 +1083,7 @@ ${cursorSessionId ? `\u{1F517} Cursor Session: ${cursorSessionId}
|
|
|
1103
1083
|
title: workDescription,
|
|
1104
1084
|
description: workDescription
|
|
1105
1085
|
}).eq("id", existingEntry.id);
|
|
1106
|
-
trackerAction = "Updated existing tracker";
|
|
1086
|
+
trackerAction = trackerAction || "Updated existing tracker";
|
|
1107
1087
|
trackerDetails = ` \u2022 Total tracked time: ${Math.round(newDuration / 60)} minutes (+${roundedFollowUpMinutes} min)
|
|
1108
1088
|
\u2022 Description: ${workDescription}
|
|
1109
1089
|
`;
|
|
@@ -1526,10 +1506,17 @@ ${workDescription}`;
|
|
|
1526
1506
|
const estimatedMinutes = session.ai_time_estimate_minutes || timeSpentMinutes;
|
|
1527
1507
|
const sessionStart = new Date(session.created_at);
|
|
1528
1508
|
const estimatedEnd = new Date(sessionStart.getTime() + estimatedMinutes * 6e4);
|
|
1529
|
-
const { data:
|
|
1509
|
+
const { data: existingAgendaEntries } = await supabase.from("agenda_events").select("id, tracked_duration").eq("ai_session_id", session.id).eq("status", "draft").order("created_at", { ascending: false });
|
|
1530
1510
|
let agendaEvent = null;
|
|
1531
1511
|
let agendaError = null;
|
|
1532
1512
|
let wasUpdated = false;
|
|
1513
|
+
let consolidatedCount = 0;
|
|
1514
|
+
const existingAgendaEntry = existingAgendaEntries && existingAgendaEntries.length > 0 ? existingAgendaEntries[0] : null;
|
|
1515
|
+
if (existingAgendaEntries && existingAgendaEntries.length > 1) {
|
|
1516
|
+
const duplicateIds = existingAgendaEntries.slice(1).map((e) => e.id);
|
|
1517
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1518
|
+
consolidatedCount = existingAgendaEntries.length - 1;
|
|
1519
|
+
}
|
|
1533
1520
|
if (existingAgendaEntry) {
|
|
1534
1521
|
const { data: updated, error: updateError } = await supabase.from("agenda_events").update({
|
|
1535
1522
|
title: ticketInfo?.title || "Development Work",
|
|
@@ -1569,6 +1556,9 @@ ${workDescription}`;
|
|
|
1569
1556
|
if (agendaError) {
|
|
1570
1557
|
console.error(`\u26A0\uFE0F Failed to ${wasUpdated ? "update" : "create"} agenda event:`, agendaError);
|
|
1571
1558
|
}
|
|
1559
|
+
if (consolidatedCount > 0) {
|
|
1560
|
+
console.log(`\u{1F9F9} Cleaned up ${consolidatedCount} duplicate agenda entries for session ${aiSessionId}`);
|
|
1561
|
+
}
|
|
1572
1562
|
let responseText = `\u{1F389} **AI Session Completed Successfully!**
|
|
1573
1563
|
|
|
1574
1564
|
`;
|
|
@@ -1689,29 +1679,77 @@ ${efficiencyNotes}
|
|
|
1689
1679
|
}
|
|
1690
1680
|
const durationSeconds = Math.round(estimatedHours * 3600);
|
|
1691
1681
|
const now = /* @__PURE__ */ new Date();
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1682
|
+
let agendaEntry = null;
|
|
1683
|
+
let agendaError = null;
|
|
1684
|
+
let wasUpdated = false;
|
|
1685
|
+
let consolidatedCount = 0;
|
|
1686
|
+
if (aiSession?.id || ticket?.id) {
|
|
1687
|
+
let query = supabase.from("agenda_events").select("id, tracked_duration, project_id, ticket_id, ai_session_id").eq("status", "draft").eq("user_id", authContext.userId).order("created_at", { ascending: false });
|
|
1688
|
+
if (aiSession?.id) {
|
|
1689
|
+
query = query.eq("ai_session_id", aiSession.id);
|
|
1690
|
+
} else if (ticket?.id) {
|
|
1691
|
+
query = query.eq("ticket_id", ticket.id);
|
|
1692
|
+
}
|
|
1693
|
+
const { data: existingEntries } = await query;
|
|
1694
|
+
if (existingEntries && existingEntries.length > 0) {
|
|
1695
|
+
const existingEntry = existingEntries[0];
|
|
1696
|
+
if (existingEntries.length > 1) {
|
|
1697
|
+
const duplicateIds = existingEntries.slice(1).map((e) => e.id);
|
|
1698
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1699
|
+
consolidatedCount = existingEntries.length - 1;
|
|
1700
|
+
}
|
|
1701
|
+
const newDuration = (existingEntry.tracked_duration || 0) + durationSeconds;
|
|
1702
|
+
const { data: updated, error: updateError } = await supabase.from("agenda_events").update({
|
|
1703
|
+
tracked_duration: newDuration,
|
|
1704
|
+
end_time: now.toISOString(),
|
|
1705
|
+
title: workDescription,
|
|
1706
|
+
description: chatContextSummary || workDescription,
|
|
1707
|
+
project_id: project?.id || existingEntry.project_id
|
|
1708
|
+
}).eq("id", existingEntry.id).select("id, tracked_duration, project_id, ticket_id, ai_session_id").single();
|
|
1709
|
+
agendaEntry = updated;
|
|
1710
|
+
agendaError = updateError;
|
|
1711
|
+
wasUpdated = true;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (!agendaEntry) {
|
|
1715
|
+
const startTime = new Date(now.getTime() - durationSeconds * 1e3);
|
|
1716
|
+
const { data: created, error: createError } = await supabase.from("agenda_events").insert({
|
|
1717
|
+
team_id: authContext.teamId,
|
|
1718
|
+
user_id: authContext.userId,
|
|
1719
|
+
project_id: project?.id || null,
|
|
1720
|
+
ticket_id: ticket?.id || null,
|
|
1721
|
+
ai_session_id: aiSession?.id || null,
|
|
1722
|
+
title: workDescription,
|
|
1723
|
+
description: chatContextSummary || workDescription,
|
|
1724
|
+
start_time: startTime.toISOString(),
|
|
1725
|
+
end_time: now.toISOString(),
|
|
1726
|
+
type: "work",
|
|
1727
|
+
status: "draft",
|
|
1728
|
+
all_day: false,
|
|
1729
|
+
is_tracked: true,
|
|
1730
|
+
tracked_duration: durationSeconds
|
|
1731
|
+
}).select("id, tracked_duration, project_id, ticket_id, ai_session_id").single();
|
|
1732
|
+
agendaEntry = created;
|
|
1733
|
+
agendaError = createError;
|
|
1734
|
+
}
|
|
1709
1735
|
if (agendaError || !agendaEntry) {
|
|
1710
|
-
throw new Error(`Failed to create time entry: ${agendaError?.message || "Unknown error"}`);
|
|
1736
|
+
throw new Error(`Failed to ${wasUpdated ? "update" : "create"} time entry: ${agendaError?.message || "Unknown error"}`);
|
|
1711
1737
|
}
|
|
1712
|
-
let responseText = `\u23F1\uFE0F **Hours Logged Successfully!**
|
|
1738
|
+
let responseText = `\u23F1\uFE0F **Hours ${wasUpdated ? "Added to Existing Entry" : "Logged Successfully"}!**
|
|
1713
1739
|
|
|
1714
1740
|
`;
|
|
1741
|
+
if (wasUpdated) {
|
|
1742
|
+
responseText += `\u{1F504} **Updated existing draft entry** (avoiding duplicates)
|
|
1743
|
+
`;
|
|
1744
|
+
responseText += ` \u2022 New total: ${Math.round((agendaEntry.tracked_duration || 0) / 3600 * 10) / 10}h
|
|
1745
|
+
|
|
1746
|
+
`;
|
|
1747
|
+
}
|
|
1748
|
+
if (consolidatedCount > 0) {
|
|
1749
|
+
responseText += `\u{1F9F9} **Cleaned up ${consolidatedCount} duplicate entries**
|
|
1750
|
+
|
|
1751
|
+
`;
|
|
1752
|
+
}
|
|
1715
1753
|
responseText += `\u{1F4CB} **Entry Details:**
|
|
1716
1754
|
`;
|
|
1717
1755
|
if (project) {
|
|
@@ -1731,7 +1769,7 @@ ${efficiencyNotes}
|
|
|
1731
1769
|
}
|
|
1732
1770
|
responseText += ` \u2022 Description: ${workDescription}
|
|
1733
1771
|
`;
|
|
1734
|
-
responseText += ` \u2022 Estimated Hours: ${estimatedHours}h (${Math.floor(estimatedHours)}h ${Math.round(estimatedHours % 1 * 60)}m)
|
|
1772
|
+
responseText += ` \u2022 ${wasUpdated ? "Added" : "Estimated"} Hours: ${estimatedHours}h (${Math.floor(estimatedHours)}h ${Math.round(estimatedHours % 1 * 60)}m)
|
|
1735
1773
|
`;
|
|
1736
1774
|
responseText += ` \u2022 Status: DRAFT (not billed yet)
|
|
1737
1775
|
`;
|
|
@@ -1745,7 +1783,7 @@ ${efficiencyNotes}
|
|
|
1745
1783
|
|
|
1746
1784
|
`;
|
|
1747
1785
|
}
|
|
1748
|
-
responseText += `\u2705 Time entry created and ready for review in the agenda!`;
|
|
1786
|
+
responseText += `\u2705 Time entry ${wasUpdated ? "updated" : "created"} and ready for review in the agenda!`;
|
|
1749
1787
|
return {
|
|
1750
1788
|
content: [{
|
|
1751
1789
|
type: "text",
|