@cluesmith/codev 1.2.3 → 1.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.
Files changed (80) hide show
  1. package/bin/generate-image.js +7 -0
  2. package/dist/agent-farm/cli.d.ts.map +1 -1
  3. package/dist/agent-farm/cli.js +2 -37
  4. package/dist/agent-farm/cli.js.map +1 -1
  5. package/dist/agent-farm/commands/cleanup.js +12 -51
  6. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  7. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  8. package/dist/agent-farm/commands/spawn.js +13 -1
  9. package/dist/agent-farm/commands/spawn.js.map +1 -1
  10. package/dist/agent-farm/commands/start.js +32 -0
  11. package/dist/agent-farm/commands/start.js.map +1 -1
  12. package/dist/agent-farm/servers/dashboard-server.d.ts +0 -2
  13. package/dist/agent-farm/servers/dashboard-server.d.ts.map +1 -1
  14. package/dist/agent-farm/servers/dashboard-server.js +196 -9
  15. package/dist/agent-farm/servers/dashboard-server.js.map +1 -1
  16. package/dist/agent-farm/servers/open-server.d.ts +0 -2
  17. package/dist/agent-farm/servers/open-server.d.ts.map +1 -1
  18. package/dist/agent-farm/servers/open-server.js +12 -7
  19. package/dist/agent-farm/servers/open-server.js.map +1 -1
  20. package/dist/agent-farm/servers/tower-server.d.ts +0 -2
  21. package/dist/agent-farm/servers/tower-server.d.ts.map +1 -1
  22. package/dist/agent-farm/servers/tower-server.js +16 -4
  23. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  24. package/dist/agent-farm/types.d.ts +1 -0
  25. package/dist/agent-farm/types.d.ts.map +1 -1
  26. package/dist/agent-farm/utils/shell.d.ts +2 -1
  27. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  28. package/dist/agent-farm/utils/shell.js +35 -2
  29. package/dist/agent-farm/utils/shell.js.map +1 -1
  30. package/dist/cli.d.ts.map +1 -1
  31. package/dist/cli.js +26 -17
  32. package/dist/cli.js.map +1 -1
  33. package/dist/commands/adopt.d.ts.map +1 -1
  34. package/dist/commands/adopt.js +27 -2
  35. package/dist/commands/adopt.js.map +1 -1
  36. package/dist/commands/consult/index.d.ts.map +1 -1
  37. package/dist/commands/consult/index.js +23 -7
  38. package/dist/commands/consult/index.js.map +1 -1
  39. package/dist/commands/doctor.d.ts.map +1 -1
  40. package/dist/commands/doctor.js +51 -0
  41. package/dist/commands/doctor.js.map +1 -1
  42. package/dist/commands/generate-image.d.ts +13 -0
  43. package/dist/commands/generate-image.d.ts.map +1 -0
  44. package/dist/commands/generate-image.js +155 -0
  45. package/dist/commands/generate-image.js.map +1 -0
  46. package/dist/commands/init.d.ts.map +1 -1
  47. package/dist/commands/init.js +23 -2
  48. package/dist/commands/init.js.map +1 -1
  49. package/dist/version.d.ts +3 -0
  50. package/dist/version.d.ts.map +1 -0
  51. package/dist/version.js +23 -0
  52. package/dist/version.js.map +1 -0
  53. package/package.json +5 -3
  54. package/skeleton/DEPENDENCIES.md +3 -3
  55. package/skeleton/protocols/maintain/protocol.md +2 -2
  56. package/skeleton/{docs → resources}/commands/codev.md +0 -39
  57. package/skeleton/{docs → resources}/commands/consult.md +12 -2
  58. package/skeleton/{docs → resources}/commands/overview.md +0 -1
  59. package/skeleton/roles/architect.md +22 -0
  60. package/skeleton/roles/builder.md +22 -0
  61. package/skeleton/templates/arch.md +56 -0
  62. package/skeleton/templates/pr-overview.md +73 -0
  63. package/templates/dashboard-split.html +781 -39
  64. package/templates/open.html +278 -0
  65. package/templates/tower.html +71 -12
  66. package/dist/agent-farm/index.d.ts +0 -7
  67. package/dist/agent-farm/index.d.ts.map +0 -1
  68. package/dist/agent-farm/index.js +0 -373
  69. package/dist/agent-farm/index.js.map +0 -1
  70. package/skeleton/bin/agent-farm +0 -7
  71. package/skeleton/bin/codev-doctor +0 -335
  72. package/skeleton/resources/lessons-learned.md +0 -30
  73. /package/skeleton/{roles/review-types → consult-types}/impl-review.md +0 -0
  74. /package/skeleton/{roles/review-types → consult-types}/integration-review.md +0 -0
  75. /package/skeleton/{roles/review-types → consult-types}/plan-review.md +0 -0
  76. /package/skeleton/{roles/review-types → consult-types}/pr-ready.md +0 -0
  77. /package/skeleton/{roles/review-types → consult-types}/spec-review.md +0 -0
  78. /package/skeleton/{docs → resources}/commands/agent-farm.md +0 -0
  79. /package/skeleton/{AGENTS.md.template → templates/AGENTS.md} +0 -0
  80. /package/skeleton/{CLAUDE.md.template → templates/CLAUDE.md} +0 -0
@@ -199,6 +199,78 @@
199
199
  outline: none;
200
200
  }
201
201
 
202
+ /* Search bar */
203
+ .search-bar {
204
+ display: none;
205
+ position: fixed;
206
+ top: 10px;
207
+ right: 20px;
208
+ background: #2a2a2a;
209
+ border: 1px solid #444;
210
+ border-radius: 6px;
211
+ padding: 8px 12px;
212
+ z-index: 250;
213
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
214
+ }
215
+ .search-bar.active {
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 8px;
219
+ }
220
+ .search-bar input {
221
+ background: #1a1a1a;
222
+ border: 1px solid #444;
223
+ border-radius: 4px;
224
+ color: #fff;
225
+ padding: 6px 10px;
226
+ font-size: 14px;
227
+ width: 250px;
228
+ outline: none;
229
+ }
230
+ .search-bar input:focus {
231
+ border-color: #3b82f6;
232
+ }
233
+ .search-bar .search-nav {
234
+ display: flex;
235
+ align-items: center;
236
+ gap: 4px;
237
+ }
238
+ .search-bar .search-nav button {
239
+ background: #444;
240
+ border: none;
241
+ color: #fff;
242
+ padding: 4px 8px;
243
+ border-radius: 4px;
244
+ cursor: pointer;
245
+ font-size: 14px;
246
+ }
247
+ .search-bar .search-nav button:hover {
248
+ background: #555;
249
+ }
250
+ .search-bar .search-count {
251
+ color: #888;
252
+ font-size: 12px;
253
+ min-width: 60px;
254
+ }
255
+ .search-bar .search-close {
256
+ background: none;
257
+ border: none;
258
+ color: #888;
259
+ cursor: pointer;
260
+ font-size: 18px;
261
+ padding: 0 4px;
262
+ }
263
+ .search-bar .search-close:hover {
264
+ color: #fff;
265
+ }
266
+ .search-highlight {
267
+ background: rgba(250, 204, 21, 0.4);
268
+ border-radius: 2px;
269
+ }
270
+ .search-highlight.current {
271
+ background: rgba(59, 130, 246, 0.6);
272
+ }
273
+
202
274
  /* Notification toast */
203
275
  .notification {
204
276
  position: fixed;
@@ -401,6 +473,17 @@
401
473
  <!-- Editor mode -->
402
474
  <textarea id="editor" spellcheck="false"></textarea>
403
475
 
476
+ <!-- Search Bar -->
477
+ <div class="search-bar" id="searchBar">
478
+ <input type="text" id="searchInput" placeholder="Search..." autocomplete="off" />
479
+ <div class="search-nav">
480
+ <span class="search-count" id="searchCount"></span>
481
+ <button onclick="searchPrev()" title="Previous (Shift+Enter)">↑</button>
482
+ <button onclick="searchNext()" title="Next (Enter)">↓</button>
483
+ </div>
484
+ <button class="search-close" onclick="closeSearch()" title="Close (Esc)">×</button>
485
+ </div>
486
+
404
487
  <!-- Comment Dialog -->
405
488
  <div class="overlay" id="overlay">
406
489
  <div class="dialog">
@@ -1377,6 +1460,201 @@
1377
1460
  }
1378
1461
  });
1379
1462
 
1463
+ // ==================== Search Functions ====================
1464
+
1465
+ let searchMatches = [];
1466
+ let currentMatchIndex = -1;
1467
+ let searchActive = false;
1468
+
1469
+ function openSearch() {
1470
+ const searchBar = document.getElementById('searchBar');
1471
+ const searchInput = document.getElementById('searchInput');
1472
+ searchBar.classList.add('active');
1473
+ searchInput.focus();
1474
+ searchInput.select();
1475
+ searchActive = true;
1476
+ }
1477
+
1478
+ function closeSearch() {
1479
+ const searchBar = document.getElementById('searchBar');
1480
+ searchBar.classList.remove('active');
1481
+ clearSearchHighlights();
1482
+ searchMatches = [];
1483
+ currentMatchIndex = -1;
1484
+ searchActive = false;
1485
+ document.getElementById('searchCount').textContent = '';
1486
+ }
1487
+
1488
+ function clearSearchHighlights() {
1489
+ document.querySelectorAll('.search-highlight').forEach(el => {
1490
+ const parent = el.parentNode;
1491
+ parent.replaceChild(document.createTextNode(el.textContent), el);
1492
+ parent.normalize();
1493
+ });
1494
+ }
1495
+
1496
+ function performSearch(query) {
1497
+ clearSearchHighlights();
1498
+ searchMatches = [];
1499
+ currentMatchIndex = -1;
1500
+
1501
+ if (!query) {
1502
+ document.getElementById('searchCount').textContent = '';
1503
+ return;
1504
+ }
1505
+
1506
+ const viewMode = document.getElementById('viewMode');
1507
+ const codeLines = viewMode.querySelectorAll('.code-line');
1508
+ const queryLower = query.toLowerCase();
1509
+
1510
+ codeLines.forEach((codeLine, lineIndex) => {
1511
+ const walker = document.createTreeWalker(codeLine, NodeFilter.SHOW_TEXT, null, false);
1512
+ const textNodes = [];
1513
+ while (walker.nextNode()) {
1514
+ textNodes.push(walker.currentNode);
1515
+ }
1516
+
1517
+ textNodes.forEach(textNode => {
1518
+ const text = textNode.textContent;
1519
+ const textLower = text.toLowerCase();
1520
+ let startIndex = 0;
1521
+ let index;
1522
+
1523
+ while ((index = textLower.indexOf(queryLower, startIndex)) !== -1) {
1524
+ searchMatches.push({
1525
+ node: textNode,
1526
+ index: index,
1527
+ length: query.length,
1528
+ lineIndex: lineIndex
1529
+ });
1530
+ startIndex = index + 1;
1531
+ }
1532
+ });
1533
+ });
1534
+
1535
+ // Highlight matches (in reverse to preserve indices)
1536
+ const processedNodes = new Set();
1537
+ searchMatches.slice().reverse().forEach((match, reverseIdx) => {
1538
+ const idx = searchMatches.length - 1 - reverseIdx;
1539
+ if (processedNodes.has(match.node)) return;
1540
+
1541
+ const text = match.node.textContent;
1542
+ const parent = match.node.parentNode;
1543
+
1544
+ // Find all matches in this node
1545
+ const nodeMatches = searchMatches.filter(m => m.node === match.node);
1546
+ processedNodes.add(match.node);
1547
+
1548
+ // Build new content with highlights
1549
+ let lastEnd = 0;
1550
+ const fragment = document.createDocumentFragment();
1551
+
1552
+ nodeMatches.forEach((m, i) => {
1553
+ // Text before match
1554
+ if (m.index > lastEnd) {
1555
+ fragment.appendChild(document.createTextNode(text.substring(lastEnd, m.index)));
1556
+ }
1557
+ // Highlighted match
1558
+ const span = document.createElement('span');
1559
+ span.className = 'search-highlight';
1560
+ span.textContent = text.substring(m.index, m.index + m.length);
1561
+ span.dataset.matchIndex = searchMatches.indexOf(m);
1562
+ fragment.appendChild(span);
1563
+ lastEnd = m.index + m.length;
1564
+ });
1565
+
1566
+ // Text after last match
1567
+ if (lastEnd < text.length) {
1568
+ fragment.appendChild(document.createTextNode(text.substring(lastEnd)));
1569
+ }
1570
+
1571
+ parent.replaceChild(fragment, match.node);
1572
+ });
1573
+
1574
+ updateSearchCount();
1575
+
1576
+ // Jump to first match
1577
+ if (searchMatches.length > 0) {
1578
+ currentMatchIndex = 0;
1579
+ highlightCurrentMatch();
1580
+ }
1581
+ }
1582
+
1583
+ function updateSearchCount() {
1584
+ const countEl = document.getElementById('searchCount');
1585
+ if (searchMatches.length === 0) {
1586
+ countEl.textContent = 'No results';
1587
+ } else {
1588
+ countEl.textContent = `${currentMatchIndex + 1} of ${searchMatches.length}`;
1589
+ }
1590
+ }
1591
+
1592
+ function highlightCurrentMatch() {
1593
+ // Remove current class from all
1594
+ document.querySelectorAll('.search-highlight.current').forEach(el => {
1595
+ el.classList.remove('current');
1596
+ });
1597
+
1598
+ // Add current class to current match
1599
+ const currentHighlight = document.querySelector(`.search-highlight[data-match-index="${currentMatchIndex}"]`);
1600
+ if (currentHighlight) {
1601
+ currentHighlight.classList.add('current');
1602
+ currentHighlight.scrollIntoView({ behavior: 'smooth', block: 'center' });
1603
+ }
1604
+
1605
+ updateSearchCount();
1606
+ }
1607
+
1608
+ function searchNext() {
1609
+ if (searchMatches.length === 0) return;
1610
+ currentMatchIndex = (currentMatchIndex + 1) % searchMatches.length;
1611
+ highlightCurrentMatch();
1612
+ }
1613
+
1614
+ function searchPrev() {
1615
+ if (searchMatches.length === 0) return;
1616
+ currentMatchIndex = (currentMatchIndex - 1 + searchMatches.length) % searchMatches.length;
1617
+ highlightCurrentMatch();
1618
+ }
1619
+
1620
+ // Search input handlers
1621
+ document.getElementById('searchInput').addEventListener('input', (e) => {
1622
+ performSearch(e.target.value);
1623
+ });
1624
+
1625
+ document.getElementById('searchInput').addEventListener('keydown', (e) => {
1626
+ if (e.key === 'Enter') {
1627
+ e.preventDefault();
1628
+ if (e.shiftKey) {
1629
+ searchPrev();
1630
+ } else {
1631
+ searchNext();
1632
+ }
1633
+ } else if (e.key === 'Escape') {
1634
+ closeSearch();
1635
+ }
1636
+ });
1637
+
1638
+ // Cmd/Ctrl+F to open search, Cmd/Ctrl+G for next match
1639
+ document.addEventListener('keydown', (e) => {
1640
+ if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
1641
+ e.preventDefault();
1642
+ openSearch();
1643
+ }
1644
+ if ((e.ctrlKey || e.metaKey) && e.key === 'g') {
1645
+ e.preventDefault();
1646
+ if (searchActive) {
1647
+ if (e.shiftKey) {
1648
+ searchPrev();
1649
+ } else {
1650
+ searchNext();
1651
+ }
1652
+ } else {
1653
+ openSearch();
1654
+ }
1655
+ }
1656
+ });
1657
+
1380
1658
  // FILE_CONTENT will be injected by the server
1381
1659
  </script>
1382
1660
  </body>
@@ -152,28 +152,42 @@
152
152
  font-size: 15px;
153
153
  }
154
154
 
155
- /* Instance cards - 4 column grid */
155
+ /* Instance cards - dynamic grid based on count */
156
156
  .instances {
157
157
  display: grid;
158
- grid-template-columns: repeat(4, 1fr);
159
158
  gap: 16px;
160
159
  margin-bottom: 32px;
161
160
  }
162
161
 
163
- @media (max-width: 1600px) {
164
- .instances {
165
- grid-template-columns: repeat(3, 1fr);
166
- }
162
+ .instances-1 {
163
+ grid-template-columns: 1fr;
164
+ }
165
+
166
+ .instances-2 {
167
+ grid-template-columns: repeat(2, 1fr);
168
+ }
169
+
170
+ .instances-3 {
171
+ grid-template-columns: repeat(3, 1fr);
172
+ }
173
+
174
+ .instances-many {
175
+ grid-template-columns: repeat(3, 1fr);
167
176
  }
168
177
 
169
178
  @media (max-width: 1200px) {
170
- .instances {
179
+ .instances-2,
180
+ .instances-3,
181
+ .instances-many {
171
182
  grid-template-columns: repeat(2, 1fr);
172
183
  }
173
184
  }
174
185
 
175
186
  @media (max-width: 800px) {
176
- .instances {
187
+ .instances-1,
188
+ .instances-2,
189
+ .instances-3,
190
+ .instances-many {
177
191
  grid-template-columns: 1fr;
178
192
  }
179
193
  }
@@ -204,10 +218,28 @@
204
218
  }
205
219
 
206
220
  .instance-path-row {
207
- padding: 0 20px 16px;
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 8px;
224
+ padding: 8px 20px;
208
225
  border-bottom: 1px solid var(--border);
209
226
  }
210
227
 
228
+ .copy-btn {
229
+ background: none;
230
+ border: none;
231
+ color: var(--text-muted);
232
+ cursor: pointer;
233
+ padding: 4px;
234
+ font-size: 12px;
235
+ opacity: 0.6;
236
+ transition: opacity 0.15s;
237
+ }
238
+
239
+ .copy-btn:hover {
240
+ opacity: 1;
241
+ }
242
+
211
243
  .instance-name {
212
244
  font-size: 20px;
213
245
  font-weight: 600;
@@ -254,9 +286,14 @@
254
286
  }
255
287
 
256
288
  .instance-path {
257
- font-size: 14px;
289
+ font-size: 12px;
258
290
  color: var(--text-muted);
259
291
  font-family: monospace;
292
+ overflow: hidden;
293
+ text-overflow: ellipsis;
294
+ white-space: nowrap;
295
+ flex: 1;
296
+ min-width: 0;
260
297
  }
261
298
 
262
299
  .instance-body {
@@ -282,11 +319,14 @@
282
319
  display: flex;
283
320
  align-items: center;
284
321
  gap: 12px;
322
+ min-width: 0;
323
+ flex: 1;
285
324
  }
286
325
 
287
326
  .port-type {
288
327
  font-size: 15px;
289
328
  color: var(--text-secondary);
329
+ flex-shrink: 0;
290
330
  min-width: 100px;
291
331
  }
292
332
 
@@ -294,6 +334,9 @@
294
334
  font-size: 15px;
295
335
  font-family: monospace;
296
336
  color: var(--text-muted);
337
+ overflow: hidden;
338
+ text-overflow: ellipsis;
339
+ white-space: nowrap;
297
340
  }
298
341
 
299
342
  .port-status {
@@ -313,6 +356,7 @@
313
356
  .port-actions {
314
357
  display: flex;
315
358
  gap: 8px;
359
+ flex-shrink: 0;
316
360
  }
317
361
 
318
362
  .port-actions a {
@@ -643,9 +687,13 @@
643
687
  </div>
644
688
  `;
645
689
  } else {
690
+ const countClass = runningInstances.length === 1 ? 'instances-1'
691
+ : runningInstances.length === 2 ? 'instances-2'
692
+ : runningInstances.length === 3 ? 'instances-3'
693
+ : 'instances-many';
646
694
  content.innerHTML = `
647
695
  <h2 class="section-header">Running Instances</h2>
648
- <div class="instances">
696
+ <div class="instances ${countClass}">
649
697
  ${runningInstances.map(renderInstance).join('')}
650
698
  </div>
651
699
  `;
@@ -677,7 +725,7 @@
677
725
  <div class="port-info">
678
726
  <span class="port-status ${port.active ? 'active' : 'inactive'}"></span>
679
727
  <span class="port-type">${escapeHtml(port.type)}</span>
680
- <span class="port-url">${escapeHtml(port.url)}</span>
728
+ <span class="port-url">Port ${port.port}</span>
681
729
  </div>
682
730
  <div class="port-actions">
683
731
  <a href="${escapeHtml(port.url)}" target="_blank" class="${port.active ? '' : 'disabled'}">
@@ -714,6 +762,7 @@
714
762
  <span class="instance-path" title="${escapeHtml(instance.projectPath)}">
715
763
  ${escapeHtml(instance.projectPath)}
716
764
  </span>
765
+ <button class="copy-btn" onclick="copyPath('${escapeHtml(instance.projectPath)}')" title="Copy path">📋</button>
717
766
  </div>
718
767
  <div class="instance-body">
719
768
  <div class="ports-list">
@@ -1001,6 +1050,16 @@
1001
1050
  return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
1002
1051
  }
1003
1052
 
1053
+ // Copy path to clipboard
1054
+ async function copyPath(path) {
1055
+ try {
1056
+ await navigator.clipboard.writeText(path);
1057
+ showToast('Path copied', 'success');
1058
+ } catch (err) {
1059
+ showToast('Failed to copy', 'error');
1060
+ }
1061
+ }
1062
+
1004
1063
  // HTML escape
1005
1064
  function escapeHtml(str) {
1006
1065
  if (!str) return '';
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Agent Farm CLI
4
- * A multi-agent orchestration tool for software development
5
- */
6
- export {};
7
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-farm/index.ts"],"names":[],"mappings":";AAEA;;;GAGG"}