swarm-tickets 2.0.0 → 2.0.3
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/lib/storage/json-adapter.js +3 -0
- package/lib/storage/sqlite-adapter.js +4 -0
- package/lib/storage/supabase-adapter.js +3 -0
- package/package.json +1 -1
- package/setup.js +7 -8
- package/ticket-server.js +7 -1
- package/ticket-tracker.html +110 -5
|
@@ -86,6 +86,9 @@ class JsonAdapter extends BaseAdapter {
|
|
|
86
86
|
if (filters.status) {
|
|
87
87
|
tickets = tickets.filter(t => t.status === filters.status);
|
|
88
88
|
}
|
|
89
|
+
if (filters.excludeStatus) {
|
|
90
|
+
tickets = tickets.filter(t => t.status !== filters.excludeStatus);
|
|
91
|
+
}
|
|
89
92
|
if (filters.priority) {
|
|
90
93
|
tickets = tickets.filter(t => t.priority === filters.priority);
|
|
91
94
|
}
|
|
@@ -223,6 +223,10 @@ class SqliteAdapter extends BaseAdapter {
|
|
|
223
223
|
query += ' AND status = ?';
|
|
224
224
|
params.push(filters.status);
|
|
225
225
|
}
|
|
226
|
+
if (filters.excludeStatus) {
|
|
227
|
+
query += ' AND status != ?';
|
|
228
|
+
params.push(filters.excludeStatus);
|
|
229
|
+
}
|
|
226
230
|
if (filters.priority) {
|
|
227
231
|
query += ' AND priority = ?';
|
|
228
232
|
params.push(filters.priority);
|
|
@@ -218,6 +218,9 @@ class SupabaseAdapter extends BaseAdapter {
|
|
|
218
218
|
if (filters.status) {
|
|
219
219
|
query = query.eq('status', filters.status);
|
|
220
220
|
}
|
|
221
|
+
if (filters.excludeStatus) {
|
|
222
|
+
query = query.neq('status', filters.excludeStatus);
|
|
223
|
+
}
|
|
221
224
|
if (filters.priority) {
|
|
222
225
|
query = query.eq('priority', filters.priority);
|
|
223
226
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarm-tickets",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Lightweight ticket tracking system for AI-powered bug fixing with Claude-flow/Claude Code. Supports JSON, SQLite, and Supabase storage.",
|
|
5
5
|
"main": "ticket-server.js",
|
|
6
6
|
"bin": {
|
package/setup.js
CHANGED
|
@@ -24,16 +24,15 @@ try {
|
|
|
24
24
|
console.log('✅ Installed swarm skill');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// Copy ticket-tracker.html to project root
|
|
27
|
+
// Copy ticket-tracker.html to project root (always update to get latest features)
|
|
28
28
|
const htmlSource = path.join(__dirname, 'ticket-tracker.html');
|
|
29
29
|
const htmlDest = path.join(projectRoot, 'ticket-tracker.html');
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
30
|
+
|
|
31
|
+
const htmlExists = fs.existsSync(htmlDest);
|
|
32
|
+
fs.copyFileSync(htmlSource, htmlDest);
|
|
33
|
+
console.log(htmlExists
|
|
34
|
+
? '✅ Updated ticket-tracker.html to latest version'
|
|
35
|
+
: '✅ Copied ticket-tracker.html to project root');
|
|
37
36
|
|
|
38
37
|
// Create tickets.json if it doesn't exist
|
|
39
38
|
const ticketsFile = path.join(projectRoot, 'tickets.json');
|
package/ticket-server.js
CHANGED
|
@@ -47,7 +47,7 @@ async function findAvailablePort(startPort) {
|
|
|
47
47
|
|
|
48
48
|
// ==================== TICKET ENDPOINTS ====================
|
|
49
49
|
|
|
50
|
-
// GET all tickets
|
|
50
|
+
// GET all tickets (excludes closed by default for performance)
|
|
51
51
|
app.get('/api/tickets', async (req, res) => {
|
|
52
52
|
try {
|
|
53
53
|
const filters = {};
|
|
@@ -55,6 +55,12 @@ app.get('/api/tickets', async (req, res) => {
|
|
|
55
55
|
if (req.query.priority) filters.priority = req.query.priority;
|
|
56
56
|
if (req.query.route) filters.route = req.query.route;
|
|
57
57
|
|
|
58
|
+
// Exclude closed tickets by default (use ?include_closed=true to include them)
|
|
59
|
+
const includeClosed = req.query.include_closed === 'true';
|
|
60
|
+
if (!includeClosed && !filters.status) {
|
|
61
|
+
filters.excludeStatus = 'closed';
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
const tickets = await storage.getAllTickets(filters);
|
|
59
65
|
res.json(tickets);
|
|
60
66
|
} catch (error) {
|
package/ticket-tracker.html
CHANGED
|
@@ -445,7 +445,6 @@
|
|
|
445
445
|
<option value="open">Open</option>
|
|
446
446
|
<option value="in-progress">In Progress</option>
|
|
447
447
|
<option value="fixed">Fixed</option>
|
|
448
|
-
<option value="closed">Closed</option>
|
|
449
448
|
</select>
|
|
450
449
|
</div>
|
|
451
450
|
|
|
@@ -464,7 +463,6 @@
|
|
|
464
463
|
<option value="open">Open</option>
|
|
465
464
|
<option value="in-progress">In Progress</option>
|
|
466
465
|
<option value="fixed">Fixed</option>
|
|
467
|
-
<option value="closed">Closed</option>
|
|
468
466
|
</select>
|
|
469
467
|
</div>
|
|
470
468
|
|
|
@@ -560,6 +558,46 @@
|
|
|
560
558
|
</div>
|
|
561
559
|
</div>
|
|
562
560
|
|
|
561
|
+
<!-- Edit Ticket Modal -->
|
|
562
|
+
<div id="edit-modal" class="modal">
|
|
563
|
+
<div class="modal-content" style="max-width: 600px;">
|
|
564
|
+
<div class="modal-header">
|
|
565
|
+
<h3>✏️ Edit Ticket</h3>
|
|
566
|
+
<button class="modal-close" onclick="closeEditModal()">×</button>
|
|
567
|
+
</div>
|
|
568
|
+
<form id="edit-form">
|
|
569
|
+
<input type="hidden" id="edit-ticket-id">
|
|
570
|
+
<div class="form-group">
|
|
571
|
+
<label for="edit-status">Status</label>
|
|
572
|
+
<select id="edit-status">
|
|
573
|
+
<option value="open">Open</option>
|
|
574
|
+
<option value="in-progress">In Progress</option>
|
|
575
|
+
<option value="fixed">Fixed</option>
|
|
576
|
+
</select>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="form-group">
|
|
579
|
+
<label for="edit-priority">Priority</label>
|
|
580
|
+
<select id="edit-priority">
|
|
581
|
+
<option value="">Not Set</option>
|
|
582
|
+
<option value="critical">Critical</option>
|
|
583
|
+
<option value="high">High</option>
|
|
584
|
+
<option value="medium">Medium</option>
|
|
585
|
+
<option value="low">Low</option>
|
|
586
|
+
</select>
|
|
587
|
+
</div>
|
|
588
|
+
<div class="form-group">
|
|
589
|
+
<label for="edit-description">Description</label>
|
|
590
|
+
<textarea id="edit-description" rows="3" placeholder="Description..."></textarea>
|
|
591
|
+
</div>
|
|
592
|
+
<div class="form-group">
|
|
593
|
+
<label for="edit-namespace">Namespace (where fix applied)</label>
|
|
594
|
+
<input type="text" id="edit-namespace" placeholder="e.g., auth/login, database/connection">
|
|
595
|
+
</div>
|
|
596
|
+
<button type="submit">Save Changes</button>
|
|
597
|
+
</form>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
563
601
|
<script>
|
|
564
602
|
let tickets = [];
|
|
565
603
|
let useServer = false;
|
|
@@ -798,6 +836,63 @@
|
|
|
798
836
|
document.getElementById('comment-modal').classList.remove('open');
|
|
799
837
|
}
|
|
800
838
|
|
|
839
|
+
// Open edit modal
|
|
840
|
+
function openEditModal(ticketId) {
|
|
841
|
+
const ticket = tickets.find(t => t.id === ticketId);
|
|
842
|
+
if (!ticket) return;
|
|
843
|
+
|
|
844
|
+
document.getElementById('edit-ticket-id').value = ticketId;
|
|
845
|
+
document.getElementById('edit-status').value = ticket.status || 'open';
|
|
846
|
+
document.getElementById('edit-priority').value = ticket.priority || '';
|
|
847
|
+
document.getElementById('edit-description').value = ticket.description || '';
|
|
848
|
+
document.getElementById('edit-namespace').value = ticket.namespace || '';
|
|
849
|
+
document.getElementById('edit-modal').classList.add('open');
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Close edit modal
|
|
853
|
+
function closeEditModal() {
|
|
854
|
+
document.getElementById('edit-modal').classList.remove('open');
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Handle edit form
|
|
858
|
+
document.getElementById('edit-form').addEventListener('submit', async function(e) {
|
|
859
|
+
e.preventDefault();
|
|
860
|
+
const ticketId = document.getElementById('edit-ticket-id').value;
|
|
861
|
+
const updates = {
|
|
862
|
+
status: document.getElementById('edit-status').value,
|
|
863
|
+
priority: document.getElementById('edit-priority').value || null,
|
|
864
|
+
description: document.getElementById('edit-description').value,
|
|
865
|
+
namespace: document.getElementById('edit-namespace').value || null
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
if (useServer) {
|
|
869
|
+
try {
|
|
870
|
+
await fetch(`${API_BASE}/tickets/${ticketId}`, {
|
|
871
|
+
method: 'PATCH',
|
|
872
|
+
headers: { 'Content-Type': 'application/json' },
|
|
873
|
+
body: JSON.stringify(updates)
|
|
874
|
+
});
|
|
875
|
+
await loadTickets();
|
|
876
|
+
renderTickets();
|
|
877
|
+
renderStats();
|
|
878
|
+
closeEditModal();
|
|
879
|
+
} catch (error) {
|
|
880
|
+
alert('Failed to update ticket');
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
// Update in localStorage
|
|
884
|
+
const ticket = tickets.find(t => t.id === ticketId);
|
|
885
|
+
if (ticket) {
|
|
886
|
+
Object.assign(ticket, updates);
|
|
887
|
+
ticket.updatedAt = new Date().toISOString();
|
|
888
|
+
localStorage.setItem('claudeflow-tickets', JSON.stringify(tickets));
|
|
889
|
+
renderTickets();
|
|
890
|
+
renderStats();
|
|
891
|
+
closeEditModal();
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
|
|
801
896
|
// Add comment
|
|
802
897
|
async function addComment(ticketId, author, content) {
|
|
803
898
|
if (useServer) {
|
|
@@ -1004,8 +1099,11 @@
|
|
|
1004
1099
|
<button class="quick-prompt-btn btn-small" onclick="copyQuickPrompt('${ticket.id}')">
|
|
1005
1100
|
📋 Quick Prompt
|
|
1006
1101
|
</button>
|
|
1102
|
+
<button class="btn-small" onclick="openEditModal('${ticket.id}')" style="background: #3498db;">
|
|
1103
|
+
✏️ Edit
|
|
1104
|
+
</button>
|
|
1007
1105
|
<button class="btn-secondary btn-small" onclick="openCommentModal('${ticket.id}')">
|
|
1008
|
-
💬
|
|
1106
|
+
💬 Comment
|
|
1009
1107
|
</button>
|
|
1010
1108
|
${ticket.status !== 'closed' ? `
|
|
1011
1109
|
<button class="btn-danger btn-small" onclick="closeTicket('${ticket.id}')">
|
|
@@ -1058,20 +1156,27 @@
|
|
|
1058
1156
|
renderTickets(filtered);
|
|
1059
1157
|
}
|
|
1060
1158
|
|
|
1061
|
-
// Close
|
|
1159
|
+
// Close modals on escape
|
|
1062
1160
|
document.addEventListener('keydown', (e) => {
|
|
1063
1161
|
if (e.key === 'Escape') {
|
|
1064
1162
|
closeCommentModal();
|
|
1163
|
+
closeEditModal();
|
|
1065
1164
|
}
|
|
1066
1165
|
});
|
|
1067
1166
|
|
|
1068
|
-
// Close
|
|
1167
|
+
// Close modals on overlay click
|
|
1069
1168
|
document.getElementById('comment-modal').addEventListener('click', (e) => {
|
|
1070
1169
|
if (e.target.classList.contains('modal')) {
|
|
1071
1170
|
closeCommentModal();
|
|
1072
1171
|
}
|
|
1073
1172
|
});
|
|
1074
1173
|
|
|
1174
|
+
document.getElementById('edit-modal').addEventListener('click', (e) => {
|
|
1175
|
+
if (e.target.classList.contains('modal')) {
|
|
1176
|
+
closeEditModal();
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1075
1180
|
// Initialize
|
|
1076
1181
|
updateProjectName();
|
|
1077
1182
|
updateFormLabels();
|