profclaw 0.0.1-beta.2 → 2.0.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/CHANGELOG.md +21 -38
- package/CONTRIBUTING.md +3 -5
- package/README.md +128 -63
- package/dist/chat/execution/tools/index.d.ts +1 -1
- package/dist/chat/execution/tools/index.js +41 -2
- package/dist/chat/providers/slack/index.d.ts +2 -2
- package/dist/cli/commands/models.js +39 -26
- package/dist/cli/commands/onboard.js +64 -79
- package/dist/cli/commands/setup.js +293 -90
- package/dist/cli/index.js +0 -6
- package/dist/cli/providers.d.ts +15 -0
- package/dist/cli/providers.js +41 -0
- package/dist/core/deployment.js +2 -0
- package/dist/providers/ai-sdk.js +26 -6
- package/dist/routes/channels.d.ts +12 -0
- package/dist/routes/channels.js +166 -0
- package/dist/server/route-loader.js +1 -0
- package/dist/server.js +115 -109
- package/package.json +1 -1
- package/skills/git-workflow/SKILL.md +3 -3
- package/ui/dist/assets/{ActivityView-DIPQSJ_d.js → ActivityView-BeshZXmE.js} +1 -1
- package/ui/dist/assets/{AgentList-CqyaYrcq.js → AgentList-QVHeldPi.js} +1 -1
- package/ui/dist/assets/{AnalyticsDashboard-C-mjuGHT.js → AnalyticsDashboard-C9R97dXE.js} +1 -1
- package/ui/dist/assets/{AreaChart-CfYWmbtr.js → AreaChart-B73gaiVQ.js} +1 -1
- package/ui/dist/assets/{CartesianChart-BaQ3It0c.js → CartesianChart-CXYL2R-A.js} +1 -1
- package/ui/dist/assets/{ChatView-DxxQy6pl.js → ChatView-qjZErlrp.js} +1 -1
- package/ui/dist/assets/{CostsDashboard-CDBBcGkg.js → CostsDashboard-O4DCLy1i.js} +1 -1
- package/ui/dist/assets/{EstimateSelect-CDjf5qKl.js → EstimateSelect-1hQf4Osk.js} +1 -1
- package/ui/dist/assets/{FloatingChatbot-CZHjvJIe.js → FloatingChatbot-BZBzrJxd.js} +1 -1
- package/ui/dist/assets/{InviteCodeManagement-DTm-IPTm.js → InviteCodeManagement-LEoGqYeA.js} +1 -1
- package/ui/dist/assets/{ProjectDetail-DlYUcOss.js → ProjectDetail--gPfvATB.js} +1 -1
- package/ui/dist/assets/{ProjectIcon-BMRpnM04.js → ProjectIcon-Chd_ofsw.js} +1 -1
- package/ui/dist/assets/{ProjectList-D85TN0i0.js → ProjectList-BvUfVB6c.js} +1 -1
- package/ui/dist/assets/{Settings-CgPqzUvj.js → Settings-1kaf7VHE.js} +1 -1
- package/ui/dist/assets/{StatusIndicator-yig0uZEA.js → StatusIndicator-pRfkNcdV.js} +1 -1
- package/ui/dist/assets/{SummaryDetail-CwLAfHgJ.js → SummaryDetail-DV3AmONk.js} +1 -1
- package/ui/dist/assets/{SummaryList-BvS5Idnu.js → SummaryList-B6kXiTxL.js} +1 -1
- package/ui/dist/assets/{TaskDetail-zarC1fOt.js → TaskDetail-CV6CzbOS.js} +1 -1
- package/ui/dist/assets/{TaskList-CyJq3ANE.js → TaskList-DL7brQKd.js} +1 -1
- package/ui/dist/assets/{TicketBoard-BlWr3-iH.js → TicketBoard-CrkkFj7C.js} +1 -1
- package/ui/dist/assets/{TicketDetail-DmqWKj2W.js → TicketDetail-hnnYclpD.js} +1 -1
- package/ui/dist/assets/{TicketList-Scj4pMWa.js → TicketList-CsDAyQCL.js} +1 -1
- package/ui/dist/assets/{UserManagement-BH7uG7dw.js → UserManagement-BGFQyTSr.js} +1 -1
- package/ui/dist/assets/{ViewSwitcher-3HzZUdWn.js → ViewSwitcher-CnL0OqA8.js} +1 -1
- package/ui/dist/assets/{alert-dialog-DWPb2IfQ.js → alert-dialog-SKM6Gjz4.js} +1 -1
- package/ui/dist/assets/{archive-CGck7gxR.js → archive-BsLa7brF.js} +1 -1
- package/ui/dist/assets/{badge-DEmc3Z_l.js → badge-CVHH1a-v.js} +1 -1
- package/ui/dist/assets/{brain-CfFdLVqb.js → brain-6o_RHVpe.js} +1 -1
- package/ui/dist/assets/{bug-DqSGPUUE.js → bug-CmZI7jqM.js} +1 -1
- package/ui/dist/assets/{calendar-BuZ4zbPW.js → calendar-Bf0t2B82.js} +1 -1
- package/ui/dist/assets/{chevron-up-DcJ4LL_N.js → chevron-up-CoUsOvZj.js} +1 -1
- package/ui/dist/assets/{circle-x-C78PNZjF.js → circle-x-Dgw11LgT.js} +1 -1
- package/ui/dist/assets/{constants-DhDjZe4P.js → constants-Q5Zivl-a.js} +1 -1
- package/ui/dist/assets/{cpu-Ck6AoG8c.js → cpu-DiPMQICV.js} +1 -1
- package/ui/dist/assets/{database-CA2w8Lvt.js → database-BwTfupDi.js} +1 -1
- package/ui/dist/assets/{dialog-BOdGWhgE.js → dialog-BUqN9Psj.js} +1 -1
- package/ui/dist/assets/{ellipsis-FhHKRbfl.js → ellipsis-UJx4Xnf6.js} +1 -1
- package/ui/dist/assets/{ellipsis-vertical-CO0UqE4-.js → ellipsis-vertical-DoULYEKn.js} +1 -1
- package/ui/dist/assets/{file-code-ChnhYYj1.js → file-code-B2UrJLwi.js} +1 -1
- package/ui/dist/assets/{folder-open-CHwA1-ve.js → folder-open-BJi577JH.js} +1 -1
- package/ui/dist/assets/{funnel-ByEjRFNB.js → funnel-PkaD_HSR.js} +1 -1
- package/ui/dist/assets/{git-branch-GTOPQgU4.js → git-branch-C_K30am0.js} +1 -1
- package/ui/dist/assets/{git-commit-horizontal-F5M84Kut.js → git-commit-horizontal-IT6ecxWQ.js} +1 -1
- package/ui/dist/assets/{globe-CQWfr7hT.js → globe-f-jjCa_g.js} +1 -1
- package/ui/dist/assets/{hash-nKC9gtLg.js → hash-DNEv8Elo.js} +1 -1
- package/ui/dist/assets/{history-DfFSA9xJ.js → history-BQSBo_1Q.js} +1 -1
- package/ui/dist/assets/{index-DHGvP9mp.js → index-BUmnwPQr.js} +1 -1
- package/ui/dist/assets/{index-Z4-RtZcs.js → index-BfF5G4Cw.js} +1 -1
- package/ui/dist/assets/{index-DeI8lKRR.js → index-C8N375c2.js} +1 -1
- package/ui/dist/assets/index-CuxnqVVz.css +1 -0
- package/ui/dist/assets/{index-Ca3vk7Gq.js → index-CzMM3QOk.js} +1 -1
- package/ui/dist/assets/{index--JkIxVXb.js → index-lJlNPFVJ.js} +3 -3
- package/ui/dist/assets/{index-a4Fxq0Hb.js → index-nS3EErDg.js} +1 -1
- package/ui/dist/assets/{layers-DFeLExSa.js → layers-jFqVrWMD.js} +1 -1
- package/ui/dist/assets/{layout-dashboard-WmgjMhWF.js → layout-dashboard-CcKt1hXw.js} +1 -1
- package/ui/dist/assets/{lightbulb-Bo8hEr7B.js → lightbulb-Jhp7FvbU.js} +1 -1
- package/ui/dist/assets/{link-2-YqrpQXBH.js → link-2-DdO7ostF.js} +1 -1
- package/ui/dist/assets/{markdown-renderer-Croje6Xe.js → markdown-renderer-n1ToC4XQ.js} +1 -1
- package/ui/dist/assets/{message-square-CD0H5hot.js → message-square-CBgH65X7.js} +1 -1
- package/ui/dist/assets/{pencil-DpFnj5dn.js → pencil-CYXCEPQh.js} +1 -1
- package/ui/dist/assets/{play-DVgea_jT.js → play-d1ONaH0-.js} +1 -1
- package/ui/dist/assets/{popover-JXecfWG5.js → popover-CrmZgoWu.js} +1 -1
- package/ui/dist/assets/{progress-C1ghf1Fu.js → progress-uv4SPkL5.js} +1 -1
- package/ui/dist/assets/{rocket-BFxPYGx9.js → rocket-DhvUetsk.js} +1 -1
- package/ui/dist/assets/{select-MADP8m6Q.js → select-CeQ8AHGy.js} +1 -1
- package/ui/dist/assets/{send-eYnW_7n-.js → send-P0r5D7td.js} +1 -1
- package/ui/dist/assets/{server-CPMaysH8.js → server-CVHTZB72.js} +1 -1
- package/ui/dist/assets/{settings-2-scXUOuE_.js → settings-2-ocj5uHGx.js} +1 -1
- package/ui/dist/assets/{sheet-r6NSOxi-.js → sheet-DuQExSho.js} +1 -1
- package/ui/dist/assets/{shield-check-PEr7nmVw.js → shield-check-DfVdE34l.js} +1 -1
- package/ui/dist/assets/{shield-off-CLtXio62.js → shield-off-8l0J6R-8.js} +1 -1
- package/ui/dist/assets/{skeleton-C9oEzNlU.js → skeleton-TFiFJ3nB.js} +1 -1
- package/ui/dist/assets/{smartphone-DusFfY0U.js → smartphone-77_g3SQn.js} +1 -1
- package/ui/dist/assets/{square-e1yxB2eR.js → square-Ckzsr3Rn.js} +1 -1
- package/ui/dist/assets/{switch-tNs0n-kz.js → switch-CAePkd6k.js} +1 -1
- package/ui/dist/assets/{table-CXxtLicg.js → table-ZjAZ7hkt.js} +1 -1
- package/ui/dist/assets/{tag-tAwMmQ9G.js → tag-C35mgoEv.js} +1 -1
- package/ui/dist/assets/{textarea-BjiEeIWh.js → textarea-DsVPnSgR.js} +1 -1
- package/ui/dist/assets/{timer-Vel1N3RP.js → timer-DHJBS2w1.js} +1 -1
- package/ui/dist/assets/{tooltip-ZMwtfr1J.js → tooltip-BYmQ331W.js} +1 -1
- package/ui/dist/assets/{trash-2-eM-CNwsZ.js → trash-2-keO5tKPE.js} +1 -1
- package/ui/dist/assets/{trending-up-BGafmjRo.js → trending-up-BBv8KbrY.js} +1 -1
- package/ui/dist/assets/{wand-sparkles-DPvcd9Pi.js → wand-sparkles-BusQ52ZK.js} +1 -1
- package/ui/dist/assets/{wrench-B8s3zYVV.js → wrench-02YaOSiK.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/dist/chat/auto-reply.d.ts +0 -90
- package/dist/chat/auto-reply.js +0 -272
- package/dist/chat/execution/pty.d.ts +0 -92
- package/dist/chat/execution/pty.js +0 -285
- package/dist/chat/execution/tools/glinr-ops.d.ts +0 -202
- package/dist/chat/execution/tools/glinr-ops.js +0 -694
- package/dist/cli/commands/browser.d.ts +0 -3
- package/dist/cli/commands/browser.js +0 -138
- package/dist/cli/commands/canvas.d.ts +0 -3
- package/dist/cli/commands/canvas.js +0 -155
- package/dist/cli/commands/nodes.d.ts +0 -3
- package/dist/cli/commands/nodes.js +0 -193
- package/dist/diagnostics/exporters.d.ts +0 -21
- package/dist/diagnostics/exporters.js +0 -138
- package/dist/diagnostics/index.d.ts +0 -10
- package/dist/diagnostics/index.js +0 -9
- package/dist/diagnostics/middleware.d.ts +0 -12
- package/dist/diagnostics/middleware.js +0 -58
- package/dist/diagnostics/tracer.d.ts +0 -133
- package/dist/diagnostics/tracer.js +0 -257
- package/dist/discovery/index.d.ts +0 -8
- package/dist/discovery/index.js +0 -7
- package/dist/discovery/mdns.d.ts +0 -40
- package/dist/discovery/mdns.js +0 -249
- package/dist/plugins/registry.d.ts +0 -122
- package/dist/plugins/registry.js +0 -352
- package/dist/plugins/sandbox.d.ts +0 -77
- package/dist/plugins/sandbox.js +0 -287
- package/dist/plugins/search/brave.d.ts +0 -9
- package/dist/plugins/search/brave.js +0 -157
- package/dist/plugins/search/duckduckgo.d.ts +0 -9
- package/dist/plugins/search/duckduckgo.js +0 -171
- package/dist/plugins/search/index.d.ts +0 -13
- package/dist/plugins/search/index.js +0 -23
- package/dist/plugins/search/searxng.d.ts +0 -9
- package/dist/plugins/search/searxng.js +0 -170
- package/dist/plugins/search/serper.d.ts +0 -9
- package/dist/plugins/search/serper.js +0 -135
- package/dist/plugins/search/tavily.d.ts +0 -9
- package/dist/plugins/search/tavily.js +0 -146
- package/dist/providers/adapters/anthropic.d.ts +0 -26
- package/dist/providers/adapters/anthropic.js +0 -306
- package/dist/providers/adapters/base.d.ts +0 -53
- package/dist/providers/adapters/base.js +0 -99
- package/dist/providers/adapters/ollama.d.ts +0 -30
- package/dist/providers/adapters/ollama.js +0 -292
- package/dist/utils/ai-cache.d.ts +0 -72
- package/dist/utils/ai-cache.js +0 -204
- package/dist/utils/file-lock.d.ts +0 -85
- package/dist/utils/file-lock.js +0 -204
- package/ui/dist/assets/index-WclMPci1.css +0 -1
|
@@ -1,694 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GLINR Operations Tools
|
|
3
|
-
*
|
|
4
|
-
* Ticket, project, and sprint management through AI chat.
|
|
5
|
-
* These tools allow the AI to create and manage GLINR's own data.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { randomUUID } from 'node:crypto';
|
|
9
|
-
import { getDb } from '../../../storage/index.js';
|
|
10
|
-
import { projects, tickets } from '../../../storage/schema.js';
|
|
11
|
-
import { eq, sql, desc, and, like, or } from 'drizzle-orm';
|
|
12
|
-
import { logger } from '../../../utils/logger.js';
|
|
13
|
-
// =============================================================================
|
|
14
|
-
// Availability Check
|
|
15
|
-
// =============================================================================
|
|
16
|
-
/** Cache database availability to avoid repeated checks */
|
|
17
|
-
let dbAvailabilityCache = null;
|
|
18
|
-
let lastDbCheck = 0;
|
|
19
|
-
const DB_CHECK_INTERVAL_MS = 30000; // Recheck every 30 seconds
|
|
20
|
-
/**
|
|
21
|
-
* Check if database is available
|
|
22
|
-
* Rechecks periodically in case database becomes available after startup
|
|
23
|
-
*/
|
|
24
|
-
function checkDatabaseAvailability() {
|
|
25
|
-
const now = Date.now();
|
|
26
|
-
// Use cache if recent and was available
|
|
27
|
-
if (dbAvailabilityCache && dbAvailabilityCache.available && now - lastDbCheck < DB_CHECK_INTERVAL_MS) {
|
|
28
|
-
return dbAvailabilityCache;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
// This will throw if storage is not initialized
|
|
32
|
-
getDb();
|
|
33
|
-
dbAvailabilityCache = { available: true };
|
|
34
|
-
lastDbCheck = now;
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
dbAvailabilityCache = {
|
|
38
|
-
available: false,
|
|
39
|
-
reason: 'Database not initialized',
|
|
40
|
-
};
|
|
41
|
-
lastDbCheck = now;
|
|
42
|
-
}
|
|
43
|
-
return dbAvailabilityCache;
|
|
44
|
-
}
|
|
45
|
-
// =============================================================================
|
|
46
|
-
// Schemas
|
|
47
|
-
// =============================================================================
|
|
48
|
-
const CreateTicketParamsSchema = z.object({
|
|
49
|
-
projectKey: z.string().describe('Project key (e.g., "GLINR")'),
|
|
50
|
-
title: z.string().min(1).describe('Ticket title'),
|
|
51
|
-
description: z.string().optional().describe('Detailed description'),
|
|
52
|
-
type: z.enum(['task', 'bug', 'story', 'epic', 'subtask', 'feature', 'improvement']).default('task'),
|
|
53
|
-
priority: z.enum(['critical', 'high', 'medium', 'low', 'none']).default('medium'),
|
|
54
|
-
labels: z.array(z.string()).optional().describe('Labels/tags'),
|
|
55
|
-
storyPoints: z.number().optional().describe('Story points estimate'),
|
|
56
|
-
});
|
|
57
|
-
const CreateProjectParamsSchema = z.object({
|
|
58
|
-
name: z.string().min(1).describe('Project name'),
|
|
59
|
-
key: z.string().min(2).max(10).describe('Project key prefix (e.g., "GLINR")'),
|
|
60
|
-
description: z.string().optional().describe('Project description'),
|
|
61
|
-
icon: z.string().optional().describe('Emoji icon (e.g., "🚀")'),
|
|
62
|
-
color: z.string().optional().describe('Hex color (e.g., "#6366f1")'),
|
|
63
|
-
});
|
|
64
|
-
const ListTicketsParamsSchema = z.object({
|
|
65
|
-
projectKey: z.string().optional().describe('Filter by project'),
|
|
66
|
-
status: z.string().optional().describe('Filter by status'),
|
|
67
|
-
type: z.string().optional().describe('Filter by type'),
|
|
68
|
-
priority: z.string().optional().describe('Filter by priority'),
|
|
69
|
-
search: z.string().optional().describe('Search in title/description'),
|
|
70
|
-
limit: z.number().default(20).describe('Max results'),
|
|
71
|
-
});
|
|
72
|
-
const ListProjectsParamsSchema = z.object({
|
|
73
|
-
status: z.enum(['active', 'archived', 'all']).default('active'),
|
|
74
|
-
});
|
|
75
|
-
const UpdateTicketParamsSchema = z.object({
|
|
76
|
-
ticketKey: z.string().describe('Ticket key (e.g., "GLINR-123")'),
|
|
77
|
-
title: z.string().optional().describe('New title'),
|
|
78
|
-
description: z.string().optional().describe('New description'),
|
|
79
|
-
status: z.enum(['backlog', 'todo', 'in_progress', 'in_review', 'done', 'cancelled']).optional(),
|
|
80
|
-
priority: z.enum(['critical', 'high', 'medium', 'low', 'none']).optional(),
|
|
81
|
-
type: z.enum(['task', 'bug', 'story', 'epic', 'subtask', 'feature', 'improvement']).optional(),
|
|
82
|
-
labels: z.array(z.string()).optional(),
|
|
83
|
-
storyPoints: z.number().optional(),
|
|
84
|
-
});
|
|
85
|
-
const GetTicketParamsSchema = z.object({
|
|
86
|
-
ticketKey: z.string().describe('Ticket key (e.g., "GLINR-123")'),
|
|
87
|
-
});
|
|
88
|
-
const AddCommentParamsSchema = z.object({
|
|
89
|
-
ticketKey: z.string().describe('Ticket key (e.g., "GLINR-123")'),
|
|
90
|
-
content: z.string().min(1).describe('Comment content (supports markdown)'),
|
|
91
|
-
});
|
|
92
|
-
const MoveTicketParamsSchema = z.object({
|
|
93
|
-
ticketKey: z.string().describe('Ticket key (e.g., "GLINR-123")'),
|
|
94
|
-
targetProjectKey: z.string().describe('Target project key (e.g., "MOBILE")'),
|
|
95
|
-
});
|
|
96
|
-
const AssignTicketParamsSchema = z.object({
|
|
97
|
-
ticketKey: z.string().describe('Ticket key (e.g., "GLINR-123")'),
|
|
98
|
-
assignee: z.string().optional().describe('Human assignee name or email'),
|
|
99
|
-
assigneeAgent: z.enum(['claude', 'openclaw', 'ollama', 'gemini']).optional().describe('AI agent to assign'),
|
|
100
|
-
unassign: z.boolean().optional().describe('Set to true to remove assignment'),
|
|
101
|
-
});
|
|
102
|
-
// =============================================================================
|
|
103
|
-
// Helper Functions
|
|
104
|
-
// =============================================================================
|
|
105
|
-
async function getProjectByKey(key) {
|
|
106
|
-
const client = getDb();
|
|
107
|
-
const result = await client.select().from(projects).where(eq(projects.key, key.toUpperCase())).limit(1);
|
|
108
|
-
return result[0] || null;
|
|
109
|
-
}
|
|
110
|
-
async function getNextTicketSequence(projectId) {
|
|
111
|
-
const client = getDb();
|
|
112
|
-
const result = await client
|
|
113
|
-
.select({ maxNum: sql `COALESCE(MAX(${tickets.sequence}), 0)` })
|
|
114
|
-
.from(tickets)
|
|
115
|
-
.where(eq(tickets.projectId, projectId));
|
|
116
|
-
return (result[0]?.maxNum || 0) + 1;
|
|
117
|
-
}
|
|
118
|
-
async function getTicketByKey(ticketKey) {
|
|
119
|
-
const client = getDb();
|
|
120
|
-
const [projectKey, numStr] = ticketKey.split('-');
|
|
121
|
-
const sequence = parseInt(numStr, 10);
|
|
122
|
-
if (!projectKey || isNaN(sequence)) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
const project = await getProjectByKey(projectKey);
|
|
126
|
-
if (!project)
|
|
127
|
-
return null;
|
|
128
|
-
const result = await client
|
|
129
|
-
.select()
|
|
130
|
-
.from(tickets)
|
|
131
|
-
.where(and(eq(tickets.projectId, project.id), eq(tickets.sequence, sequence)))
|
|
132
|
-
.limit(1);
|
|
133
|
-
return result[0] ? { ...result[0], projectKey: project.key } : null;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Safely format a date value to ISO string
|
|
137
|
-
* Handles Date objects, timestamps, and invalid values
|
|
138
|
-
*/
|
|
139
|
-
function formatDateSafe(value) {
|
|
140
|
-
if (!value)
|
|
141
|
-
return new Date().toISOString();
|
|
142
|
-
// If it's already a valid Date
|
|
143
|
-
if (value instanceof Date && !isNaN(value.getTime())) {
|
|
144
|
-
return value.toISOString();
|
|
145
|
-
}
|
|
146
|
-
// If it's a number (Unix timestamp in seconds or ms)
|
|
147
|
-
if (typeof value === 'number') {
|
|
148
|
-
// Detect if it's seconds or milliseconds
|
|
149
|
-
const ms = value > 1e12 ? value : value * 1000;
|
|
150
|
-
const date = new Date(ms);
|
|
151
|
-
if (!isNaN(date.getTime())) {
|
|
152
|
-
return date.toISOString();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// If it's a string, try to parse it
|
|
156
|
-
if (typeof value === 'string') {
|
|
157
|
-
const date = new Date(value);
|
|
158
|
-
if (!isNaN(date.getTime())) {
|
|
159
|
-
return date.toISOString();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Fallback to now
|
|
163
|
-
return new Date().toISOString();
|
|
164
|
-
}
|
|
165
|
-
// =============================================================================
|
|
166
|
-
// Create Ticket Tool
|
|
167
|
-
// =============================================================================
|
|
168
|
-
export const createTicketTool = {
|
|
169
|
-
name: 'create_ticket',
|
|
170
|
-
description: `Create a new ticket/task/bug in GLINR. Use AFTER list_projects to get a valid projectKey.
|
|
171
|
-
|
|
172
|
-
IMPORTANT: You MUST call this tool after list_projects when the user asks to create a ticket/task/bug.
|
|
173
|
-
Required params: projectKey (from list_projects), title (from user request)
|
|
174
|
-
Optional: type (bug/feature/task), priority, description
|
|
175
|
-
|
|
176
|
-
The response includes a 'url' field - use this to create a clickable markdown link in your summary:
|
|
177
|
-
Example: "Created ticket [GLINR-42](/tickets/abc123)"
|
|
178
|
-
|
|
179
|
-
DO NOT just list projects and stop - you must CREATE the ticket to complete the user's request.`,
|
|
180
|
-
category: 'glinr',
|
|
181
|
-
securityLevel: 'safe',
|
|
182
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
183
|
-
parameters: CreateTicketParamsSchema,
|
|
184
|
-
isAvailable: checkDatabaseAvailability,
|
|
185
|
-
examples: [
|
|
186
|
-
{
|
|
187
|
-
description: 'Create a bug ticket',
|
|
188
|
-
params: { projectKey: 'GLINR', title: 'Fix login button', type: 'bug', priority: 'high' },
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
description: 'Create a feature with description',
|
|
192
|
-
params: {
|
|
193
|
-
projectKey: 'GLINR',
|
|
194
|
-
title: 'Add dark mode',
|
|
195
|
-
description: 'Implement dark mode toggle in settings',
|
|
196
|
-
type: 'feature',
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
async execute(_context, params) {
|
|
201
|
-
try {
|
|
202
|
-
const client = getDb();
|
|
203
|
-
// Get project
|
|
204
|
-
const project = await getProjectByKey(params.projectKey);
|
|
205
|
-
if (!project) {
|
|
206
|
-
return {
|
|
207
|
-
success: false,
|
|
208
|
-
error: {
|
|
209
|
-
code: 'PROJECT_NOT_FOUND',
|
|
210
|
-
message: `Project "${params.projectKey}" not found. Use list_projects to see available projects.`,
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
// Get next ticket sequence
|
|
215
|
-
const sequence = await getNextTicketSequence(project.id);
|
|
216
|
-
const ticketKey = `${project.key}-${sequence}`;
|
|
217
|
-
const id = randomUUID();
|
|
218
|
-
const now = new Date();
|
|
219
|
-
// Insert ticket - mark as AI-created
|
|
220
|
-
await client.insert(tickets).values({
|
|
221
|
-
id,
|
|
222
|
-
projectId: project.id,
|
|
223
|
-
sequence,
|
|
224
|
-
title: params.title,
|
|
225
|
-
description: params.description || '',
|
|
226
|
-
status: 'backlog',
|
|
227
|
-
type: params.type,
|
|
228
|
-
priority: params.priority,
|
|
229
|
-
labels: params.labels || [],
|
|
230
|
-
estimate: params.storyPoints || null,
|
|
231
|
-
createdBy: 'ai', // Flag as AI-created
|
|
232
|
-
aiAgent: 'glinr-chat', // Track which AI created it
|
|
233
|
-
createdAt: now,
|
|
234
|
-
updatedAt: now,
|
|
235
|
-
});
|
|
236
|
-
logger.info(`[GLINR] Created ticket ${ticketKey}: ${params.title}`);
|
|
237
|
-
const data = {
|
|
238
|
-
id,
|
|
239
|
-
key: ticketKey,
|
|
240
|
-
title: params.title,
|
|
241
|
-
description: params.description,
|
|
242
|
-
status: 'backlog',
|
|
243
|
-
type: params.type,
|
|
244
|
-
priority: params.priority,
|
|
245
|
-
labels: params.labels,
|
|
246
|
-
storyPoints: params.storyPoints,
|
|
247
|
-
projectKey: project.key,
|
|
248
|
-
createdAt: now.toISOString(),
|
|
249
|
-
url: `/tickets/${id}`,
|
|
250
|
-
};
|
|
251
|
-
const labelsLine = params.labels?.length ? `\n- Labels: ${params.labels.join(', ')}` : '';
|
|
252
|
-
const output = `## Ticket Created\n**${ticketKey}** — ${params.title}\n- Type: ${params.type}\n- Priority: ${params.priority}\n- Status: backlog${labelsLine}`;
|
|
253
|
-
return { success: true, data, output };
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
logger.error('[GLINR] Create ticket error:', error instanceof Error ? error : undefined);
|
|
257
|
-
return {
|
|
258
|
-
success: false,
|
|
259
|
-
error: {
|
|
260
|
-
code: 'CREATE_FAILED',
|
|
261
|
-
message: error instanceof Error ? error.message : 'Failed to create ticket',
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
// =============================================================================
|
|
268
|
-
// Create Project Tool
|
|
269
|
-
// =============================================================================
|
|
270
|
-
export const createProjectTool = {
|
|
271
|
-
name: 'create_project',
|
|
272
|
-
description: `Create a new project in GLINR. Projects organize tickets under a unique key.`,
|
|
273
|
-
category: 'glinr',
|
|
274
|
-
securityLevel: 'safe',
|
|
275
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
276
|
-
parameters: CreateProjectParamsSchema,
|
|
277
|
-
isAvailable: checkDatabaseAvailability,
|
|
278
|
-
examples: [
|
|
279
|
-
{
|
|
280
|
-
description: 'Create a project',
|
|
281
|
-
params: { name: 'Mobile App', key: 'MOBILE', icon: '📱' },
|
|
282
|
-
},
|
|
283
|
-
],
|
|
284
|
-
async execute(_context, params) {
|
|
285
|
-
try {
|
|
286
|
-
const client = getDb();
|
|
287
|
-
const key = params.key.toUpperCase();
|
|
288
|
-
// Check if key exists
|
|
289
|
-
const existing = await getProjectByKey(key);
|
|
290
|
-
if (existing) {
|
|
291
|
-
return {
|
|
292
|
-
success: false,
|
|
293
|
-
error: {
|
|
294
|
-
code: 'KEY_EXISTS',
|
|
295
|
-
message: `Project key "${key}" already exists`,
|
|
296
|
-
},
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
const id = randomUUID();
|
|
300
|
-
const now = new Date();
|
|
301
|
-
// Get next project sequence
|
|
302
|
-
const maxSeq = await client.select({ max: sql `COALESCE(MAX(sequence), 0)` }).from(projects);
|
|
303
|
-
const sequence = (maxSeq[0]?.max || 0) + 1;
|
|
304
|
-
await client.insert(projects).values({
|
|
305
|
-
id,
|
|
306
|
-
sequence,
|
|
307
|
-
name: params.name,
|
|
308
|
-
key,
|
|
309
|
-
description: params.description || null,
|
|
310
|
-
icon: params.icon || '📁',
|
|
311
|
-
color: params.color || '#6366f1',
|
|
312
|
-
status: 'active',
|
|
313
|
-
createdAt: now,
|
|
314
|
-
updatedAt: now,
|
|
315
|
-
});
|
|
316
|
-
logger.info(`[GLINR] Created project ${key}: ${params.name}`);
|
|
317
|
-
const data = {
|
|
318
|
-
id,
|
|
319
|
-
name: params.name,
|
|
320
|
-
key,
|
|
321
|
-
description: params.description,
|
|
322
|
-
icon: params.icon || '📁',
|
|
323
|
-
color: params.color || '#6366f1',
|
|
324
|
-
status: 'active',
|
|
325
|
-
};
|
|
326
|
-
const output = `## Project Created\n**${params.name}** (${key})${params.description ? `\n${params.description}` : ''}`;
|
|
327
|
-
return { success: true, data, output };
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
logger.error('[GLINR] Create project error:', error instanceof Error ? error : undefined);
|
|
331
|
-
return {
|
|
332
|
-
success: false,
|
|
333
|
-
error: {
|
|
334
|
-
code: 'CREATE_FAILED',
|
|
335
|
-
message: error instanceof Error ? error.message : 'Failed to create project',
|
|
336
|
-
},
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
};
|
|
341
|
-
// =============================================================================
|
|
342
|
-
// List Tickets Tool
|
|
343
|
-
// =============================================================================
|
|
344
|
-
export const listTicketsTool = {
|
|
345
|
-
name: 'list_tickets',
|
|
346
|
-
description: `List and search tickets in GLINR. Can filter by project, status, type, priority, or search text.`,
|
|
347
|
-
category: 'glinr',
|
|
348
|
-
securityLevel: 'safe',
|
|
349
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
350
|
-
parameters: ListTicketsParamsSchema,
|
|
351
|
-
isAvailable: checkDatabaseAvailability,
|
|
352
|
-
examples: [
|
|
353
|
-
{ description: 'List all tickets', params: {} },
|
|
354
|
-
{ description: 'List bugs in a project', params: { projectKey: 'GLINR', type: 'bug' } },
|
|
355
|
-
{ description: 'Search tickets', params: { search: 'login' } },
|
|
356
|
-
],
|
|
357
|
-
async execute(_context, params) {
|
|
358
|
-
try {
|
|
359
|
-
const client = getDb();
|
|
360
|
-
// Build conditions
|
|
361
|
-
const conditions = [];
|
|
362
|
-
if (params.projectKey) {
|
|
363
|
-
const project = await getProjectByKey(params.projectKey);
|
|
364
|
-
if (project) {
|
|
365
|
-
conditions.push(eq(tickets.projectId, project.id));
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (params.status) {
|
|
369
|
-
conditions.push(eq(tickets.status, params.status));
|
|
370
|
-
}
|
|
371
|
-
if (params.type) {
|
|
372
|
-
conditions.push(eq(tickets.type, params.type));
|
|
373
|
-
}
|
|
374
|
-
if (params.priority) {
|
|
375
|
-
conditions.push(eq(tickets.priority, params.priority));
|
|
376
|
-
}
|
|
377
|
-
if (params.search) {
|
|
378
|
-
conditions.push(or(like(tickets.title, `%${params.search}%`), like(tickets.description, `%${params.search}%`)));
|
|
379
|
-
}
|
|
380
|
-
// Execute query
|
|
381
|
-
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
382
|
-
const results = await client
|
|
383
|
-
.select({
|
|
384
|
-
id: tickets.id,
|
|
385
|
-
projectId: tickets.projectId,
|
|
386
|
-
sequence: tickets.sequence,
|
|
387
|
-
title: tickets.title,
|
|
388
|
-
description: tickets.description,
|
|
389
|
-
status: tickets.status,
|
|
390
|
-
type: tickets.type,
|
|
391
|
-
priority: tickets.priority,
|
|
392
|
-
labels: tickets.labels,
|
|
393
|
-
estimate: tickets.estimate,
|
|
394
|
-
createdAt: tickets.createdAt,
|
|
395
|
-
})
|
|
396
|
-
.from(tickets)
|
|
397
|
-
.where(whereClause)
|
|
398
|
-
.orderBy(desc(tickets.createdAt))
|
|
399
|
-
.limit(params.limit);
|
|
400
|
-
// Get project keys for each ticket
|
|
401
|
-
const projectIdSet = new Set();
|
|
402
|
-
for (const t of results) {
|
|
403
|
-
if (t.projectId)
|
|
404
|
-
projectIdSet.add(t.projectId);
|
|
405
|
-
}
|
|
406
|
-
const projectMap = new Map();
|
|
407
|
-
for (const projectId of projectIdSet) {
|
|
408
|
-
const project = await client.select().from(projects).where(eq(projects.id, projectId)).limit(1);
|
|
409
|
-
if (project[0]) {
|
|
410
|
-
projectMap.set(projectId, project[0].key);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
const ticketResults = results.map((t) => ({
|
|
414
|
-
id: t.id,
|
|
415
|
-
key: `${projectMap.get(t.projectId || '') || 'UNK'}-${t.sequence}`,
|
|
416
|
-
title: t.title,
|
|
417
|
-
description: t.description || undefined,
|
|
418
|
-
status: t.status,
|
|
419
|
-
type: t.type,
|
|
420
|
-
priority: t.priority,
|
|
421
|
-
labels: t.labels || undefined,
|
|
422
|
-
storyPoints: t.estimate || undefined,
|
|
423
|
-
projectKey: projectMap.get(t.projectId || '') || 'UNK',
|
|
424
|
-
createdAt: formatDateSafe(t.createdAt),
|
|
425
|
-
url: `/tickets/${t.id}`,
|
|
426
|
-
}));
|
|
427
|
-
// Build markdown output
|
|
428
|
-
let output;
|
|
429
|
-
if (ticketResults.length === 0) {
|
|
430
|
-
output = '## Tickets\n\nNo tickets found matching your criteria.';
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
const rows = ticketResults.map(t => `| ${t.key} | ${t.title} | ${t.status} | ${t.priority} |`);
|
|
434
|
-
output = `## Tickets (${ticketResults.length})\n\n| Key | Title | Status | Priority |\n|-----|-------|--------|----------|\n${rows.join('\n')}`;
|
|
435
|
-
}
|
|
436
|
-
return {
|
|
437
|
-
success: true,
|
|
438
|
-
data: { tickets: ticketResults, total: ticketResults.length },
|
|
439
|
-
output,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
logger.error('[GLINR] List tickets error:', error instanceof Error ? error : undefined);
|
|
444
|
-
return {
|
|
445
|
-
success: false,
|
|
446
|
-
error: {
|
|
447
|
-
code: 'LIST_FAILED',
|
|
448
|
-
message: error instanceof Error ? error.message : 'Failed to list tickets',
|
|
449
|
-
},
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
};
|
|
454
|
-
// =============================================================================
|
|
455
|
-
// List Projects Tool
|
|
456
|
-
// =============================================================================
|
|
457
|
-
export const listProjectsTool = {
|
|
458
|
-
name: 'list_projects',
|
|
459
|
-
description: `List all projects in GLINR. **REQUIRED FIRST STEP** before creating tickets - returns project keys needed for create_ticket.
|
|
460
|
-
|
|
461
|
-
WORKFLOW: To create a ticket, you MUST:
|
|
462
|
-
1. FIRST call list_projects → get the projectKey (e.g., "GLINR")
|
|
463
|
-
2. THEN call create_ticket with that projectKey
|
|
464
|
-
|
|
465
|
-
Example: User says "create a bug ticket" →
|
|
466
|
-
- Call list_projects → returns { projects: [{key: "GLINR", ...}] }
|
|
467
|
-
- Call create_ticket with projectKey="GLINR", title="...", type="bug"
|
|
468
|
-
- Return summary to user`,
|
|
469
|
-
category: 'glinr',
|
|
470
|
-
securityLevel: 'safe',
|
|
471
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
472
|
-
parameters: ListProjectsParamsSchema,
|
|
473
|
-
isAvailable: checkDatabaseAvailability,
|
|
474
|
-
examples: [{ description: 'List active projects', params: { status: 'active' } }],
|
|
475
|
-
async execute(_context, params) {
|
|
476
|
-
try {
|
|
477
|
-
const client = getDb();
|
|
478
|
-
const conditions = params.status !== 'all' ? [eq(projects.status, params.status === 'archived' ? 'archived' : 'active')] : [];
|
|
479
|
-
const results = await client
|
|
480
|
-
.select()
|
|
481
|
-
.from(projects)
|
|
482
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
483
|
-
.orderBy(projects.name);
|
|
484
|
-
// Get ticket counts
|
|
485
|
-
const projectResults = [];
|
|
486
|
-
for (const p of results) {
|
|
487
|
-
const countResult = await client
|
|
488
|
-
.select({ count: sql `COUNT(*)` })
|
|
489
|
-
.from(tickets)
|
|
490
|
-
.where(eq(tickets.projectId, p.id));
|
|
491
|
-
projectResults.push({
|
|
492
|
-
id: p.id,
|
|
493
|
-
name: p.name,
|
|
494
|
-
key: p.key,
|
|
495
|
-
description: p.description || undefined,
|
|
496
|
-
icon: p.icon || undefined,
|
|
497
|
-
color: p.color || undefined,
|
|
498
|
-
status: p.status,
|
|
499
|
-
ticketCount: countResult[0]?.count || 0,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
// Build hint for AI about next actions
|
|
503
|
-
const projectKeys = projectResults.map(p => p.key).join(', ');
|
|
504
|
-
const hint = projectResults.length > 0
|
|
505
|
-
? `NEXT STEP: To create a ticket, call create_ticket with one of these projectKeys: ${projectKeys}`
|
|
506
|
-
: `No projects found. Create a project first with create_project, then create tickets in it.`;
|
|
507
|
-
// Build markdown output
|
|
508
|
-
let output;
|
|
509
|
-
if (projectResults.length === 0) {
|
|
510
|
-
output = '## Projects\n\nNo projects found. Use `create_project` to create one.';
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
const lines = projectResults.map(p => `- **${p.name}** (${p.key}) — ${p.ticketCount ?? 0} tickets`);
|
|
514
|
-
output = `## Projects\n\n${lines.join('\n')}`;
|
|
515
|
-
}
|
|
516
|
-
return {
|
|
517
|
-
success: true,
|
|
518
|
-
data: { projects: projectResults, hint },
|
|
519
|
-
output,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
catch (error) {
|
|
523
|
-
logger.error('[GLINR] List projects error:', error instanceof Error ? error : undefined);
|
|
524
|
-
return {
|
|
525
|
-
success: false,
|
|
526
|
-
error: {
|
|
527
|
-
code: 'LIST_FAILED',
|
|
528
|
-
message: error instanceof Error ? error.message : 'Failed to list projects',
|
|
529
|
-
},
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
},
|
|
533
|
-
};
|
|
534
|
-
// =============================================================================
|
|
535
|
-
// Update Ticket Tool
|
|
536
|
-
// =============================================================================
|
|
537
|
-
export const updateTicketTool = {
|
|
538
|
-
name: 'update_ticket',
|
|
539
|
-
description: `Update an existing ticket's status, priority, description, or other fields.`,
|
|
540
|
-
category: 'glinr',
|
|
541
|
-
securityLevel: 'safe',
|
|
542
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
543
|
-
parameters: UpdateTicketParamsSchema,
|
|
544
|
-
isAvailable: checkDatabaseAvailability,
|
|
545
|
-
examples: [
|
|
546
|
-
{ description: 'Mark ticket done', params: { ticketKey: 'GLINR-123', status: 'done' } },
|
|
547
|
-
{ description: 'Update priority', params: { ticketKey: 'GLINR-123', priority: 'critical' } },
|
|
548
|
-
],
|
|
549
|
-
async execute(_context, params) {
|
|
550
|
-
try {
|
|
551
|
-
const client = getDb();
|
|
552
|
-
// Get existing ticket
|
|
553
|
-
const ticket = await getTicketByKey(params.ticketKey);
|
|
554
|
-
if (!ticket) {
|
|
555
|
-
return {
|
|
556
|
-
success: false,
|
|
557
|
-
error: {
|
|
558
|
-
code: 'TICKET_NOT_FOUND',
|
|
559
|
-
message: `Ticket "${params.ticketKey}" not found`,
|
|
560
|
-
},
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
// Build update object — updatedAt must be Date (schema uses mode: "timestamp")
|
|
564
|
-
const updates = {
|
|
565
|
-
updatedAt: new Date(),
|
|
566
|
-
};
|
|
567
|
-
if (params.title !== undefined)
|
|
568
|
-
updates.title = params.title;
|
|
569
|
-
if (params.description !== undefined)
|
|
570
|
-
updates.description = params.description;
|
|
571
|
-
if (params.status !== undefined)
|
|
572
|
-
updates.status = params.status;
|
|
573
|
-
if (params.priority !== undefined)
|
|
574
|
-
updates.priority = params.priority;
|
|
575
|
-
if (params.type !== undefined)
|
|
576
|
-
updates.type = params.type;
|
|
577
|
-
if (params.labels !== undefined)
|
|
578
|
-
updates.labels = params.labels;
|
|
579
|
-
if (params.storyPoints !== undefined)
|
|
580
|
-
updates.estimate = params.storyPoints;
|
|
581
|
-
await client.update(tickets).set(updates).where(eq(tickets.id, ticket.id));
|
|
582
|
-
// Get updated ticket
|
|
583
|
-
const updated = await getTicketByKey(params.ticketKey);
|
|
584
|
-
logger.info(`[GLINR] Updated ticket ${params.ticketKey}`);
|
|
585
|
-
const data = {
|
|
586
|
-
id: updated.id,
|
|
587
|
-
key: params.ticketKey,
|
|
588
|
-
title: updated.title,
|
|
589
|
-
description: updated.description || undefined,
|
|
590
|
-
status: updated.status,
|
|
591
|
-
type: updated.type,
|
|
592
|
-
priority: updated.priority,
|
|
593
|
-
labels: updated.labels || undefined,
|
|
594
|
-
storyPoints: updated.estimate || undefined,
|
|
595
|
-
projectKey: updated.projectKey,
|
|
596
|
-
createdAt: formatDateSafe(updated.createdAt),
|
|
597
|
-
url: `/tickets/${updated.id}`,
|
|
598
|
-
};
|
|
599
|
-
// Build change summary
|
|
600
|
-
const changes = [];
|
|
601
|
-
if (params.status !== undefined)
|
|
602
|
-
changes.push(`- Status: ${ticket.status} → ${params.status}`);
|
|
603
|
-
if (params.priority !== undefined)
|
|
604
|
-
changes.push(`- Priority: ${ticket.priority} → ${params.priority}`);
|
|
605
|
-
if (params.type !== undefined)
|
|
606
|
-
changes.push(`- Type: ${ticket.type} → ${params.type}`);
|
|
607
|
-
if (params.title !== undefined)
|
|
608
|
-
changes.push(`- Title: ${params.title}`);
|
|
609
|
-
if (params.labels !== undefined)
|
|
610
|
-
changes.push(`- Labels: ${params.labels.join(', ')}`);
|
|
611
|
-
if (params.storyPoints !== undefined)
|
|
612
|
-
changes.push(`- Story Points: ${params.storyPoints}`);
|
|
613
|
-
const output = `## Ticket Updated\n**${params.ticketKey}** — ${updated.title}\n${changes.join('\n')}`;
|
|
614
|
-
return { success: true, data, output };
|
|
615
|
-
}
|
|
616
|
-
catch (error) {
|
|
617
|
-
logger.error('[GLINR] Update ticket error:', error instanceof Error ? error : undefined);
|
|
618
|
-
return {
|
|
619
|
-
success: false,
|
|
620
|
-
error: {
|
|
621
|
-
code: 'UPDATE_FAILED',
|
|
622
|
-
message: error instanceof Error ? error.message : 'Failed to update ticket',
|
|
623
|
-
},
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
},
|
|
627
|
-
};
|
|
628
|
-
// =============================================================================
|
|
629
|
-
// Get Ticket Tool
|
|
630
|
-
// =============================================================================
|
|
631
|
-
export const getTicketTool = {
|
|
632
|
-
name: 'get_ticket',
|
|
633
|
-
description: `Get details of a specific ticket by its key.`,
|
|
634
|
-
category: 'glinr',
|
|
635
|
-
securityLevel: 'safe',
|
|
636
|
-
allowedHosts: ['sandbox', 'gateway', 'local'],
|
|
637
|
-
parameters: GetTicketParamsSchema,
|
|
638
|
-
isAvailable: checkDatabaseAvailability,
|
|
639
|
-
examples: [{ description: 'Get ticket details', params: { ticketKey: 'GLINR-123' } }],
|
|
640
|
-
async execute(_context, params) {
|
|
641
|
-
try {
|
|
642
|
-
const ticket = await getTicketByKey(params.ticketKey);
|
|
643
|
-
if (!ticket) {
|
|
644
|
-
return {
|
|
645
|
-
success: false,
|
|
646
|
-
error: {
|
|
647
|
-
code: 'TICKET_NOT_FOUND',
|
|
648
|
-
message: `Ticket "${params.ticketKey}" not found`,
|
|
649
|
-
},
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
const data = {
|
|
653
|
-
id: ticket.id,
|
|
654
|
-
key: params.ticketKey,
|
|
655
|
-
title: ticket.title,
|
|
656
|
-
description: ticket.description || undefined,
|
|
657
|
-
status: ticket.status,
|
|
658
|
-
type: ticket.type,
|
|
659
|
-
priority: ticket.priority,
|
|
660
|
-
labels: ticket.labels || undefined,
|
|
661
|
-
storyPoints: ticket.estimate || undefined,
|
|
662
|
-
projectKey: ticket.projectKey,
|
|
663
|
-
createdAt: formatDateSafe(ticket.createdAt),
|
|
664
|
-
url: `/tickets/${ticket.id}`,
|
|
665
|
-
};
|
|
666
|
-
const labelsLine = ticket.labels?.length ? `\n- Labels: ${ticket.labels.join(', ')}` : '';
|
|
667
|
-
const descLine = ticket.description ? `\n\n${ticket.description}` : '';
|
|
668
|
-
const output = `## ${params.ticketKey} — ${ticket.title}\n- Status: ${ticket.status}\n- Type: ${ticket.type}\n- Priority: ${ticket.priority}${labelsLine}${descLine}`;
|
|
669
|
-
return { success: true, data, output };
|
|
670
|
-
}
|
|
671
|
-
catch (error) {
|
|
672
|
-
logger.error('[GLINR] Get ticket error:', error instanceof Error ? error : undefined);
|
|
673
|
-
return {
|
|
674
|
-
success: false,
|
|
675
|
-
error: {
|
|
676
|
-
code: 'GET_FAILED',
|
|
677
|
-
message: error instanceof Error ? error.message : 'Failed to get ticket',
|
|
678
|
-
},
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
};
|
|
683
|
-
// =============================================================================
|
|
684
|
-
// Exports
|
|
685
|
-
// =============================================================================
|
|
686
|
-
export const glinrTools = [
|
|
687
|
-
createTicketTool,
|
|
688
|
-
createProjectTool,
|
|
689
|
-
listTicketsTool,
|
|
690
|
-
listProjectsTool,
|
|
691
|
-
updateTicketTool,
|
|
692
|
-
getTicketTool,
|
|
693
|
-
];
|
|
694
|
-
//# sourceMappingURL=glinr-ops.js.map
|