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.
@@ -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.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
- if (!fs.existsSync(htmlDest)) {
32
- fs.copyFileSync(htmlSource, htmlDest);
33
- console.log('✅ Copied ticket-tracker.html to project root');
34
- } else {
35
- console.log('⚠️ ticket-tracker.html already exists, skipping');
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) {
@@ -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()">&times;</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
- 💬 Add Comment
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 modal on escape
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 modal on overlay click
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();