@yemi33/minions 0.1.10 → 0.1.12
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 +32 -0
- package/dashboard.html +338 -100
- package/dashboard.js +281 -119
- package/engine/ado.js +14 -0
- package/engine/cli.js +11 -0
- package/engine/github.js +14 -0
- package/engine/lifecycle.js +47 -36
- package/engine/scheduler.js +30 -39
- package/engine.js +117 -28
- package/package.json +1 -1
- package/routing.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.12 (2026-03-26)
|
|
4
|
+
|
|
5
|
+
### Engine
|
|
6
|
+
- engine.js
|
|
7
|
+
- engine/ado.js
|
|
8
|
+
- engine/cli.js
|
|
9
|
+
- engine/github.js
|
|
10
|
+
- engine/lifecycle.js
|
|
11
|
+
|
|
12
|
+
### Dashboard
|
|
13
|
+
- dashboard.html
|
|
14
|
+
- dashboard.js
|
|
15
|
+
|
|
16
|
+
### Other
|
|
17
|
+
- TODO.md
|
|
18
|
+
- routing.md
|
|
19
|
+
- test/unit.test.js
|
|
20
|
+
|
|
21
|
+
## 0.1.11 (2026-03-26)
|
|
22
|
+
|
|
23
|
+
### Engine
|
|
24
|
+
- engine.js
|
|
25
|
+
- engine/lifecycle.js
|
|
26
|
+
- engine/scheduler.js
|
|
27
|
+
|
|
28
|
+
### Dashboard
|
|
29
|
+
- dashboard.html
|
|
30
|
+
- dashboard.js
|
|
31
|
+
|
|
32
|
+
### Other
|
|
33
|
+
- test/unit.test.js
|
|
34
|
+
|
|
3
35
|
## 0.1.9 (2026-03-26)
|
|
4
36
|
|
|
5
37
|
### Engine
|
package/dashboard.html
CHANGED
|
@@ -45,9 +45,19 @@
|
|
|
45
45
|
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
|
|
46
46
|
.timestamp { color: var(--muted); font-size: var(--text-md); font-variant-numeric: tabular-nums; }
|
|
47
47
|
|
|
48
|
-
.layout { display:
|
|
48
|
+
.layout { display: none; } /* Replaced by page-layout */
|
|
49
49
|
section { padding: var(--space-8) var(--space-9); border-bottom: 1px solid var(--border); overflow: hidden; min-width: 0; }
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
/* Sidebar navigation */
|
|
52
|
+
.page-layout { display: flex; height: calc(100vh - 44px); overflow: hidden; }
|
|
53
|
+
.sidebar { width: 150px; min-width: 150px; background: var(--surface); border-right: 1px solid var(--border); padding: var(--space-4) 0; overflow-y: auto; position: sticky; top: 0; }
|
|
54
|
+
.sidebar-link { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; color: var(--muted); text-decoration: none; font-size: var(--text-sm); border-left: 3px solid transparent; transition: all var(--transition-fast); cursor: pointer; }
|
|
55
|
+
.sidebar-link:hover { color: var(--text); background: var(--surface2); }
|
|
56
|
+
.sidebar-link.active { color: var(--blue); border-left-color: var(--blue); background: var(--surface2); font-weight: 600; }
|
|
57
|
+
.sidebar-count { font-size: 9px; color: var(--muted); background: var(--surface2); padding: 1px 5px; border-radius: 8px; min-width: 16px; text-align: center; }
|
|
58
|
+
.page-content { flex: 1; overflow-y: auto; min-width: 0; }
|
|
59
|
+
.page { display: none; }
|
|
60
|
+
.page.active { display: block; }
|
|
51
61
|
section h2 { font-size: var(--text-base); font-weight: 600; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 14px; display: flex; align-items: center; gap: var(--space-4); }
|
|
52
62
|
section h2 .count { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-xl); padding: var(--space-1) 7px; font-size: var(--text-base); color: var(--text); }
|
|
53
63
|
|
|
@@ -189,7 +199,7 @@
|
|
|
189
199
|
.inbox-name { font-weight: 500; font-size: var(--text-md); color: var(--purple); margin-bottom: var(--space-2); display: flex; justify-content: space-between; }
|
|
190
200
|
.inbox-preview { font-size: var(--text-base); color: var(--muted); line-height: 1.5; max-height: 60px; overflow: hidden; }
|
|
191
201
|
|
|
192
|
-
.prd-panel, .pr-panel {
|
|
202
|
+
.prd-panel, .pr-panel { border-bottom: 1px solid var(--border); overflow: visible; min-width: 0; }
|
|
193
203
|
.prd-inner { display: flex; gap: 16px; align-items: flex-start; }
|
|
194
204
|
.prd-stats { display: flex; gap: 16px; }
|
|
195
205
|
.prd-stat { text-align: center; background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--space-6) var(--space-8); }
|
|
@@ -331,7 +341,7 @@
|
|
|
331
341
|
.empty { color: var(--muted); font-style: italic; font-size: var(--text-md); padding: var(--space-4) 0; }
|
|
332
342
|
|
|
333
343
|
/* Command Center — Unified Input */
|
|
334
|
-
.cmd-center {
|
|
344
|
+
.cmd-center { overflow: visible !important; }
|
|
335
345
|
.cmd-input-wrap {
|
|
336
346
|
position: relative; display: flex; align-items: flex-start; gap: 0;
|
|
337
347
|
background: var(--bg); border: 2px solid var(--border); border-radius: var(--radius-xl);
|
|
@@ -642,104 +652,134 @@
|
|
|
642
652
|
<code style="background:var(--bg);padding:6px 16px;border-radius:4px;font-size:14px;color:var(--blue);border:1px solid var(--border)">minions init</code>
|
|
643
653
|
</div>
|
|
644
654
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
655
|
+
<!-- Layout replaced by page-layout sidebar navigation -->
|
|
656
|
+
|
|
657
|
+
<div class="page-layout">
|
|
658
|
+
<nav class="sidebar" id="sidebar">
|
|
659
|
+
<a class="sidebar-link" data-page="home" href="/">Home</a>
|
|
660
|
+
<a class="sidebar-link" data-page="work" href="/work">Work Items <span class="sidebar-count" id="sidebar-wi"></span></a>
|
|
661
|
+
<a class="sidebar-link" data-page="prd" href="/prd">PRD</a>
|
|
662
|
+
<a class="sidebar-link" data-page="prs" href="/prs">Pull Requests <span class="sidebar-count" id="sidebar-pr"></span></a>
|
|
663
|
+
<a class="sidebar-link" data-page="plans" href="/plans">Plans</a>
|
|
664
|
+
<a class="sidebar-link" data-page="inbox" href="/inbox">Notes & KB</a>
|
|
665
|
+
<a class="sidebar-link" data-page="schedule" href="/schedule">Schedules</a>
|
|
666
|
+
<a class="sidebar-link" data-page="engine" href="/engine">Engine</a>
|
|
667
|
+
</nav>
|
|
668
|
+
<div class="page-content" id="page-content">
|
|
669
|
+
|
|
670
|
+
<div class="page active" id="page-home">
|
|
671
|
+
<section class="cmd-center">
|
|
672
|
+
<h2>Command Center</h2>
|
|
673
|
+
<div class="cmd-input-wrap" id="cmd-input-wrap">
|
|
674
|
+
<div class="cmd-highlight-layer" id="cmd-highlight" aria-hidden="true"></div>
|
|
675
|
+
<textarea id="cmd-input" rows="1" placeholder='What do you need? e.g. "Fix the auth bug @dallas", "explain the dispatch flow", or "/note always use feature flags"'
|
|
676
|
+
oninput="cmdInputChanged()" onkeydown="cmdKeyDown(event)" onscroll="syncHighlightScroll()"></textarea>
|
|
677
|
+
<button class="cmd-send-btn" id="cmd-send-btn" onclick="cmdSubmit()">Send <kbd>Ctrl+Enter</kbd></button>
|
|
678
|
+
</div>
|
|
679
|
+
<div class="cmd-mention-popup" id="cmd-mention-popup"></div>
|
|
680
|
+
<div class="cmd-meta" id="cmd-meta" style="display:none"></div>
|
|
681
|
+
<div class="cmd-hints">
|
|
682
|
+
<span style="color:var(--blue);font-weight:600">Command Center</span>
|
|
683
|
+
<span>Ask anything, dispatch work, manage plans — powered by Sonnet</span>
|
|
684
|
+
<button class="cmd-history-btn" onclick="cmdShowHistory()">Past Commands</button>
|
|
685
|
+
</div>
|
|
686
|
+
<div class="cmd-toast" id="cmd-toast"></div>
|
|
687
|
+
</section>
|
|
688
|
+
<section>
|
|
689
|
+
<h2>Minions Members <span style="font-size:10px;color:var(--border);font-weight:400;text-transform:none;letter-spacing:0">click for details</span></h2>
|
|
690
|
+
<div class="agents" id="agents-grid">Loading...</div>
|
|
691
|
+
</section>
|
|
692
|
+
<section>
|
|
693
|
+
<h2>Dispatch Queue</h2>
|
|
694
|
+
<div class="dispatch-stats" id="dispatch-stats"></div>
|
|
695
|
+
<div id="dispatch-active"></div>
|
|
696
|
+
<div id="dispatch-pending"></div>
|
|
697
|
+
</section>
|
|
698
|
+
<section class="pr-panel" id="completed-section">
|
|
699
|
+
<h2>Recent Completions <span class="count" id="completed-count">0</span></h2>
|
|
700
|
+
<div id="completed-content"><p class="empty">No completed dispatches yet.</p></div>
|
|
701
|
+
</section>
|
|
702
|
+
</div>
|
|
703
|
+
|
|
704
|
+
<div class="page" id="page-work">
|
|
705
|
+
<section id="work-items-section" style="overflow:visible">
|
|
706
|
+
<h2>Work Items <span class="count" id="wi-count">0</span> <button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;margin-left:8px" onclick="toggleWorkItemArchive()">See Archive</button></h2>
|
|
707
|
+
<div id="work-items-content"><p class="empty">No work items. Add tasks via Command Center above.</p></div>
|
|
708
|
+
<div id="work-items-archive" style="display:none;margin-top:12px"></div>
|
|
709
|
+
</section>
|
|
710
|
+
</div>
|
|
711
|
+
|
|
712
|
+
<div class="page" id="page-prd">
|
|
713
|
+
<section class="prd-panel" id="prd-section">
|
|
714
|
+
<h2>PRD <span class="count" id="prd-progress-count">0%</span> <span id="prd-badge"></span> <span id="archive-btns"></span></h2>
|
|
715
|
+
<div id="prd-content"><p class="prd-pending">No PRD found.</p></div>
|
|
716
|
+
<div id="prd-progress-content" style="margin-top:12px"></div>
|
|
717
|
+
</section>
|
|
718
|
+
</div>
|
|
719
|
+
|
|
720
|
+
<div class="page" id="page-prs">
|
|
721
|
+
<section class="pr-panel" id="pr-section">
|
|
722
|
+
<h2>Pull Requests <span class="count" id="pr-count">0</span></h2>
|
|
723
|
+
<div id="pr-content"><p class="pr-empty">No pull requests yet.</p></div>
|
|
724
|
+
</section>
|
|
725
|
+
</div>
|
|
726
|
+
|
|
727
|
+
<div class="page" id="page-plans">
|
|
728
|
+
<section>
|
|
729
|
+
<h2>Plans <span class="count" id="plans-count">0</span></h2>
|
|
730
|
+
<div id="plans-list"><p class="empty">No plans yet. Use /plan in the command center to create one.</p></div>
|
|
731
|
+
</section>
|
|
732
|
+
</div>
|
|
733
|
+
|
|
734
|
+
<div class="page" id="page-inbox">
|
|
735
|
+
<section>
|
|
736
|
+
<h2>Notes Inbox <span class="count" id="inbox-count">0</span> <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">auto-consolidates at 3 notes</span></h2>
|
|
737
|
+
<div class="inbox-list" id="inbox-list">Loading...</div>
|
|
738
|
+
</section>
|
|
739
|
+
<section>
|
|
740
|
+
<h2 data-file="notes.md" style="position:relative">Team Notes <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0" id="notes-updated"></span></h2>
|
|
741
|
+
<div id="notes-list">Loading...</div>
|
|
742
|
+
</section>
|
|
743
|
+
<section>
|
|
744
|
+
<h2>Knowledge Base <span class="count" id="kb-count">0</span> <button id="kb-sweep-btn" onclick="kbSweep()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);color:var(--muted);border-radius:4px;cursor:pointer;margin-left:8px;vertical-align:middle">sweep</button><span id="kb-swept-time" style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0;margin-left:8px"></span></h2>
|
|
745
|
+
<div class="kb-tabs" id="kb-tabs"></div>
|
|
746
|
+
<div class="kb-list" id="kb-list"><p class="empty">No knowledge entries yet. Notes are classified here after consolidation.</p></div>
|
|
747
|
+
</section>
|
|
748
|
+
<section>
|
|
749
|
+
<h2>Minions Skills <span class="count" id="skills-count">0</span></h2>
|
|
750
|
+
<div id="skills-list"><p class="empty">No skills yet. Agents create these when they discover repeatable workflows.</p></div>
|
|
751
|
+
</section>
|
|
653
752
|
</div>
|
|
654
|
-
|
|
655
|
-
<div class="
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
753
|
+
|
|
754
|
+
<div class="page" id="page-schedule">
|
|
755
|
+
<section id="scheduled-section">
|
|
756
|
+
<h2>Scheduled Tasks <span class="count" id="scheduled-count">0</span>
|
|
757
|
+
<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--green);border-color:var(--green);margin-left:8px" onclick="openCreateScheduleModal()">+ New</button>
|
|
758
|
+
</h2>
|
|
759
|
+
<div id="scheduled-content"><p class="empty">No scheduled tasks. Add one to automate recurring work.</p></div>
|
|
760
|
+
</section>
|
|
761
|
+
<section>
|
|
762
|
+
<h2>MCP Servers <span class="count" id="mcp-count">0</span></h2>
|
|
763
|
+
<div id="mcp-list"><p class="empty">No MCP servers synced.</p></div>
|
|
764
|
+
</section>
|
|
660
765
|
</div>
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
<div id="prd-progress-content" style="margin-top:12px"></div>
|
|
679
|
-
</section>
|
|
680
|
-
|
|
681
|
-
<section class="pr-panel" id="pr-section">
|
|
682
|
-
<h2>Pull Requests <span class="count" id="pr-count">0</span></h2>
|
|
683
|
-
<div id="pr-content"><p class="pr-empty">No pull requests yet.</p></div>
|
|
684
|
-
</section>
|
|
685
|
-
|
|
686
|
-
<section>
|
|
687
|
-
<h2>Plans <span class="count" id="plans-count">0</span></h2>
|
|
688
|
-
<div id="plans-list"><p class="empty">No plans yet. Use /plan in the command center to create one.</p></div>
|
|
689
|
-
</section>
|
|
690
|
-
|
|
691
|
-
<section>
|
|
692
|
-
<h2>Notes Inbox <span class="count" id="inbox-count">0</span> <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">auto-consolidates at 3 notes</span></h2>
|
|
693
|
-
<div class="inbox-list" id="inbox-list">Loading...</div>
|
|
694
|
-
</section>
|
|
695
|
-
|
|
696
|
-
<section>
|
|
697
|
-
<h2 data-file="notes.md" style="position:relative">Team Notes <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0" id="notes-updated"></span></h2>
|
|
698
|
-
<div id="notes-list">Loading...</div>
|
|
699
|
-
</section>
|
|
700
|
-
|
|
701
|
-
<section>
|
|
702
|
-
<h2>Knowledge Base <span class="count" id="kb-count">0</span> <button id="kb-sweep-btn" onclick="kbSweep()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);color:var(--muted);border-radius:4px;cursor:pointer;margin-left:8px;vertical-align:middle">sweep</button><span id="kb-swept-time" style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0;margin-left:8px"></span></h2>
|
|
703
|
-
<div class="kb-tabs" id="kb-tabs"></div>
|
|
704
|
-
<div class="kb-list" id="kb-list"><p class="empty">No knowledge entries yet. Notes are classified here after consolidation.</p></div>
|
|
705
|
-
</section>
|
|
706
|
-
|
|
707
|
-
<section>
|
|
708
|
-
<h2>Minions Skills <span class="count" id="skills-count">0</span></h2>
|
|
709
|
-
<div id="skills-list"><p class="empty">No skills yet. Agents create these when they discover repeatable workflows.</p></div>
|
|
710
|
-
</section>
|
|
711
|
-
|
|
712
|
-
<section>
|
|
713
|
-
<h2>MCP Servers <span class="count" id="mcp-count">0</span></h2>
|
|
714
|
-
<div id="mcp-list"><p class="empty">No MCP servers synced.</p></div>
|
|
715
|
-
</section>
|
|
716
|
-
|
|
717
|
-
<section>
|
|
718
|
-
<h2>Dispatch Queue</h2>
|
|
719
|
-
<div class="dispatch-stats" id="dispatch-stats"></div>
|
|
720
|
-
<div id="dispatch-active"></div>
|
|
721
|
-
<div id="dispatch-pending"></div>
|
|
722
|
-
</section>
|
|
723
|
-
|
|
724
|
-
<section>
|
|
725
|
-
<h2>Engine Log</h2>
|
|
726
|
-
<div class="log-list" id="engine-log">No log entries yet.</div>
|
|
727
|
-
</section>
|
|
728
|
-
|
|
729
|
-
<section>
|
|
730
|
-
<h2>Agent Metrics</h2>
|
|
731
|
-
<div id="metrics-content"><p class="empty">No metrics yet. Metrics appear after agents complete tasks.</p></div>
|
|
732
|
-
</section>
|
|
733
|
-
|
|
734
|
-
<section>
|
|
735
|
-
<h2>Token Usage</h2>
|
|
736
|
-
<div id="token-usage-content"><p class="empty">No usage data yet.</p></div>
|
|
737
|
-
</section>
|
|
738
|
-
|
|
739
|
-
<section class="pr-panel" id="completed-section">
|
|
740
|
-
<h2>Recent Completions <span class="count" id="completed-count">0</span></h2>
|
|
741
|
-
<div id="completed-content"><p class="empty">No completed dispatches yet.</p></div>
|
|
742
|
-
</section>
|
|
766
|
+
|
|
767
|
+
<div class="page" id="page-engine">
|
|
768
|
+
<section>
|
|
769
|
+
<h2>Engine Log</h2>
|
|
770
|
+
<div class="log-list" id="engine-log">No log entries yet.</div>
|
|
771
|
+
</section>
|
|
772
|
+
<section>
|
|
773
|
+
<h2>Agent Metrics</h2>
|
|
774
|
+
<div id="metrics-content"><p class="empty">No metrics yet. Metrics appear after agents complete tasks.</p></div>
|
|
775
|
+
</section>
|
|
776
|
+
<section>
|
|
777
|
+
<h2>Token Usage</h2>
|
|
778
|
+
<div id="token-usage-content"><p class="empty">No usage data yet.</p></div>
|
|
779
|
+
</section>
|
|
780
|
+
</div>
|
|
781
|
+
|
|
782
|
+
</div>
|
|
743
783
|
</div>
|
|
744
784
|
|
|
745
785
|
<!-- Agent Detail Panel -->
|
|
@@ -796,6 +836,34 @@ let inboxData = [];
|
|
|
796
836
|
let agentData = [];
|
|
797
837
|
let currentAgentId = null;
|
|
798
838
|
let currentTab = 'thought-process';
|
|
839
|
+
|
|
840
|
+
// Sidebar page navigation — URL-routed
|
|
841
|
+
function getPageFromUrl() {
|
|
842
|
+
const path = window.location.pathname.replace(/^\//, '') || 'home';
|
|
843
|
+
if (document.querySelector('.sidebar-link[data-page="' + path + '"]')) return path;
|
|
844
|
+
return 'home';
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
let currentPage = getPageFromUrl();
|
|
848
|
+
|
|
849
|
+
function switchPage(page, pushState) {
|
|
850
|
+
currentPage = page;
|
|
851
|
+
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
|
852
|
+
const target = document.getElementById('page-' + page);
|
|
853
|
+
if (target) target.classList.add('active');
|
|
854
|
+
document.querySelectorAll('.sidebar-link').forEach(l => l.classList.remove('active'));
|
|
855
|
+
const link = document.querySelector('.sidebar-link[data-page="' + page + '"]');
|
|
856
|
+
if (link) link.classList.add('active');
|
|
857
|
+
if (pushState !== false) {
|
|
858
|
+
const url = page === 'home' ? '/' : '/' + page;
|
|
859
|
+
history.pushState({ page }, '', url);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Browser back/forward navigation
|
|
864
|
+
window.addEventListener('popstate', (e) => {
|
|
865
|
+
switchPage(e.state?.page || getPageFromUrl(), false);
|
|
866
|
+
});
|
|
799
867
|
window._prdRequeueUi = window._prdRequeueUi || {};
|
|
800
868
|
|
|
801
869
|
function getPrdRequeueState(workItemId) {
|
|
@@ -2380,6 +2448,12 @@ async function refresh() {
|
|
|
2380
2448
|
renderWorkItems(data.workItems || []);
|
|
2381
2449
|
renderSkills(data.skills || []);
|
|
2382
2450
|
renderMcpServers(data.mcpServers || []);
|
|
2451
|
+
renderSchedules(data.schedules || []);
|
|
2452
|
+
// Update sidebar counts
|
|
2453
|
+
const swi = document.getElementById('sidebar-wi');
|
|
2454
|
+
if (swi) swi.textContent = (data.workItems || []).length || '';
|
|
2455
|
+
const spr = document.getElementById('sidebar-pr');
|
|
2456
|
+
if (spr) spr.textContent = (data.pullRequests || []).length || '';
|
|
2383
2457
|
// Refresh KB and plans less frequently (every 3rd cycle = ~12s)
|
|
2384
2458
|
if (!window._kbRefreshCount) window._kbRefreshCount = 0;
|
|
2385
2459
|
if (window._kbRefreshCount++ % 3 === 0) { refreshKnowledgeBase(); refreshPlans(); }
|
|
@@ -2389,6 +2463,12 @@ async function refresh() {
|
|
|
2389
2463
|
refresh();
|
|
2390
2464
|
setInterval(refresh, 4000);
|
|
2391
2465
|
|
|
2466
|
+
// Wire sidebar navigation
|
|
2467
|
+
document.querySelectorAll('.sidebar-link').forEach(link => {
|
|
2468
|
+
link.addEventListener('click', e => { e.preventDefault(); switchPage(link.dataset.page); });
|
|
2469
|
+
});
|
|
2470
|
+
switchPage(currentPage);
|
|
2471
|
+
|
|
2392
2472
|
// -- Projects --
|
|
2393
2473
|
function renderProjects(projects) {
|
|
2394
2474
|
const header = document.getElementById('header-projects');
|
|
@@ -2448,6 +2528,164 @@ function renderMcpServers(servers) {
|
|
|
2448
2528
|
'<p style="font-size:10px;color:var(--muted);margin:0">Synced from <code style="color:var(--blue)">~/.claude.json</code> — add MCP servers there to make them available to all agents.</p>';
|
|
2449
2529
|
}
|
|
2450
2530
|
|
|
2531
|
+
// -- Scheduled Tasks --
|
|
2532
|
+
function renderSchedules(schedules) {
|
|
2533
|
+
const el = document.getElementById('scheduled-content');
|
|
2534
|
+
const countEl = document.getElementById('scheduled-count');
|
|
2535
|
+
countEl.textContent = schedules.length;
|
|
2536
|
+
if (!schedules.length) {
|
|
2537
|
+
el.innerHTML = '<p class="empty">No scheduled tasks. Add one to automate recurring work.</p>';
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
let html = '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>ID</th><th>Title</th><th>Cron</th><th>Type</th><th>Project</th><th>Agent</th><th>Enabled</th><th>Last Run</th><th></th></tr></thead><tbody>';
|
|
2541
|
+
for (const s of schedules) {
|
|
2542
|
+
const enabledBadge = s.enabled
|
|
2543
|
+
? '<span class="pr-badge approved">enabled</span>'
|
|
2544
|
+
: '<span class="pr-badge rejected">disabled</span>';
|
|
2545
|
+
const lastRun = s._lastRun ? timeAgo(s._lastRun) : 'never';
|
|
2546
|
+
const typeBadge = '<span class="dispatch-type ' + escHtml(s.type || 'implement') + '">' + escHtml(s.type || 'implement') + '</span>';
|
|
2547
|
+
html += '<tr>' +
|
|
2548
|
+
'<td><span class="pr-id">' + escHtml(s.id || '') + '</span></td>' +
|
|
2549
|
+
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escHtml(s.title || '') + '">' + escHtml(s.title || '') + '</td>' +
|
|
2550
|
+
'<td><code style="font-size:10px;color:var(--blue)">' + escHtml(s.cron || '') + '</code></td>' +
|
|
2551
|
+
'<td>' + typeBadge + '</td>' +
|
|
2552
|
+
'<td><span style="font-size:10px;color:var(--muted)">' + escHtml(s.project || '') + '</span></td>' +
|
|
2553
|
+
'<td><span class="pr-agent">' + escHtml(s.agent || 'auto') + '</span></td>' +
|
|
2554
|
+
'<td>' + enabledBadge + '</td>' +
|
|
2555
|
+
'<td><span class="pr-date">' + escHtml(lastRun) + '</span></td>' +
|
|
2556
|
+
'<td style="white-space:nowrap">' +
|
|
2557
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';border-color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';margin-right:4px" onclick="event.stopPropagation();toggleScheduleEnabled(\'' + escHtml(s.id) + '\',' + !s.enabled + ')" title="' + (s.enabled ? 'Disable' : 'Enable') + '">' + (s.enabled ? '⏸' : '▶') + '</button>' +
|
|
2558
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--blue);border-color:var(--blue);margin-right:4px" onclick="event.stopPropagation();openEditScheduleModal(\'' + escHtml(s.id) + '\')" title="Edit">✎</button>' +
|
|
2559
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--red);border-color:var(--red)" onclick="event.stopPropagation();deleteSchedule(\'' + escHtml(s.id) + '\')" title="Delete">✕</button>' +
|
|
2560
|
+
'</td>' +
|
|
2561
|
+
'</tr>';
|
|
2562
|
+
}
|
|
2563
|
+
html += '</tbody></table></div>';
|
|
2564
|
+
el.innerHTML = html;
|
|
2565
|
+
window._lastSchedules = schedules;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
function _scheduleFormHtml(sched, isEdit) {
|
|
2569
|
+
const types = ['implement', 'test', 'explore', 'ask', 'review', 'fix'];
|
|
2570
|
+
const priorities = ['high', 'medium', 'low'];
|
|
2571
|
+
const typeOpts = types.map(t => '<option value="' + t + '"' + ((sched.type || 'implement') === t ? ' selected' : '') + '>' + t + '</option>').join('');
|
|
2572
|
+
const priOpts = priorities.map(p => '<option value="' + p + '"' + ((sched.priority || 'medium') === p ? ' selected' : '') + '>' + p + '</option>').join('');
|
|
2573
|
+
const projOpts = '<option value="">Any</option>' + cmdProjects.map(p => '<option value="' + escHtml(p.name) + '"' + (sched.project === p.name ? ' selected' : '') + '>' + escHtml(p.name) + '</option>').join('');
|
|
2574
|
+
const agentOpts = '<option value="">Auto</option>' + cmdAgents.map(a => '<option value="' + escHtml(a.id) + '"' + (sched.agent === a.id ? ' selected' : '') + '>' + escHtml(a.name) + '</option>').join('');
|
|
2575
|
+
|
|
2576
|
+
const inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit';
|
|
2577
|
+
|
|
2578
|
+
return '<div style="display:flex;flex-direction:column;gap:12px;font-family:inherit">' +
|
|
2579
|
+
(isEdit ? '' :
|
|
2580
|
+
'<label style="color:var(--text);font-size:var(--text-md)">ID (unique slug)' +
|
|
2581
|
+
'<input id="sched-edit-id" value="' + escHtml(sched.id || '') + '" placeholder="e.g. nightly-tests" style="' + inputStyle + '">' +
|
|
2582
|
+
'</label>') +
|
|
2583
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Title' +
|
|
2584
|
+
'<input id="sched-edit-title" value="' + escHtml(sched.title || '') + '" style="' + inputStyle + '">' +
|
|
2585
|
+
'</label>' +
|
|
2586
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Cron <span style="font-size:10px;color:var(--muted)">(minute hour dayOfWeek)</span>' +
|
|
2587
|
+
'<input id="sched-edit-cron" value="' + escHtml(sched.cron || '') + '" placeholder="0 2 *" style="' + inputStyle + '">' +
|
|
2588
|
+
'</label>' +
|
|
2589
|
+
'<div style="display:flex;gap:12px">' +
|
|
2590
|
+
'<label style="color:var(--text);font-size:var(--text-md);flex:1">Type' +
|
|
2591
|
+
'<select id="sched-edit-type" style="' + inputStyle + '">' + typeOpts + '</select>' +
|
|
2592
|
+
'</label>' +
|
|
2593
|
+
'<label style="color:var(--text);font-size:var(--text-md);flex:1">Priority' +
|
|
2594
|
+
'<select id="sched-edit-priority" style="' + inputStyle + '">' + priOpts + '</select>' +
|
|
2595
|
+
'</label>' +
|
|
2596
|
+
'</div>' +
|
|
2597
|
+
'<div style="display:flex;gap:12px">' +
|
|
2598
|
+
'<label style="color:var(--text);font-size:var(--text-md);flex:1">Project' +
|
|
2599
|
+
'<select id="sched-edit-project" style="' + inputStyle + '">' + projOpts + '</select>' +
|
|
2600
|
+
'</label>' +
|
|
2601
|
+
'<label style="color:var(--text);font-size:var(--text-md);flex:1">Agent' +
|
|
2602
|
+
'<select id="sched-edit-agent" style="' + inputStyle + '">' + agentOpts + '</select>' +
|
|
2603
|
+
'</label>' +
|
|
2604
|
+
'</div>' +
|
|
2605
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Description' +
|
|
2606
|
+
'<textarea id="sched-edit-desc" rows="3" style="' + inputStyle + ';resize:vertical">' + escHtml(sched.description || '') + '</textarea>' +
|
|
2607
|
+
'</label>' +
|
|
2608
|
+
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">' +
|
|
2609
|
+
'<button onclick="closeModal()" class="pr-pager-btn" style="padding:6px 16px;font-size:var(--text-md)">Cancel</button>' +
|
|
2610
|
+
'<button onclick="submitSchedule(' + isEdit + ')" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">' + (isEdit ? 'Save' : 'Create') + '</button>' +
|
|
2611
|
+
'</div>' +
|
|
2612
|
+
'</div>';
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
function openCreateScheduleModal() {
|
|
2616
|
+
document.getElementById('modal-title').textContent = 'New Scheduled Task';
|
|
2617
|
+
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
2618
|
+
document.getElementById('modal-body').style.fontFamily = '';
|
|
2619
|
+
document.getElementById('modal-body').innerHTML = _scheduleFormHtml({}, false);
|
|
2620
|
+
document.getElementById('modal').classList.add('open');
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
function openEditScheduleModal(id) {
|
|
2624
|
+
const sched = (window._lastSchedules || []).find(s => s.id === id);
|
|
2625
|
+
if (!sched) return;
|
|
2626
|
+
document.getElementById('modal-title').textContent = 'Edit Schedule: ' + id;
|
|
2627
|
+
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
2628
|
+
document.getElementById('modal-body').style.fontFamily = '';
|
|
2629
|
+
document.getElementById('modal-body').innerHTML = _scheduleFormHtml(sched, true);
|
|
2630
|
+
window._editScheduleId = id;
|
|
2631
|
+
document.getElementById('modal').classList.add('open');
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
async function submitSchedule(isEdit) {
|
|
2635
|
+
const title = document.getElementById('sched-edit-title').value.trim();
|
|
2636
|
+
const cron = document.getElementById('sched-edit-cron').value.trim();
|
|
2637
|
+
const type = document.getElementById('sched-edit-type').value;
|
|
2638
|
+
const priority = document.getElementById('sched-edit-priority').value;
|
|
2639
|
+
const project = document.getElementById('sched-edit-project').value;
|
|
2640
|
+
const agent = document.getElementById('sched-edit-agent').value;
|
|
2641
|
+
const description = document.getElementById('sched-edit-desc').value;
|
|
2642
|
+
const id = isEdit ? window._editScheduleId : (document.getElementById('sched-edit-id') ? document.getElementById('sched-edit-id').value.trim() : '');
|
|
2643
|
+
|
|
2644
|
+
if (!id) { alert('ID is required'); return; }
|
|
2645
|
+
if (!title) { alert('Title is required'); return; }
|
|
2646
|
+
if (!cron) { alert('Cron expression is required'); return; }
|
|
2647
|
+
|
|
2648
|
+
const payload = { id, title, cron, type, priority, project: project || undefined, agent: agent || undefined, description: description || undefined, enabled: true };
|
|
2649
|
+
const url = isEdit ? '/api/schedules/update' : '/api/schedules';
|
|
2650
|
+
try {
|
|
2651
|
+
const res = await fetch(url, {
|
|
2652
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
2653
|
+
body: JSON.stringify(payload)
|
|
2654
|
+
});
|
|
2655
|
+
if (res.ok) { closeModal(); refresh(); showToast('cmd-toast', isEdit ? 'Schedule updated' : 'Schedule created', true); } else {
|
|
2656
|
+
const d = await res.json().catch(() => ({}));
|
|
2657
|
+
alert((isEdit ? 'Update' : 'Create') + ' failed: ' + (d.error || 'unknown'));
|
|
2658
|
+
}
|
|
2659
|
+
} catch (e) { alert('Error: ' + e.message); }
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
async function toggleScheduleEnabled(id, enabled) {
|
|
2663
|
+
try {
|
|
2664
|
+
const res = await fetch('/api/schedules/update', {
|
|
2665
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
2666
|
+
body: JSON.stringify({ id, enabled })
|
|
2667
|
+
});
|
|
2668
|
+
if (res.ok) { refresh(); } else {
|
|
2669
|
+
const d = await res.json().catch(() => ({}));
|
|
2670
|
+
alert('Toggle failed: ' + (d.error || 'unknown'));
|
|
2671
|
+
}
|
|
2672
|
+
} catch (e) { alert('Toggle error: ' + e.message); }
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
async function deleteSchedule(id) {
|
|
2676
|
+
if (!confirm('Delete scheduled task "' + id + '"?')) return;
|
|
2677
|
+
try {
|
|
2678
|
+
const res = await fetch('/api/schedules/delete', {
|
|
2679
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
2680
|
+
body: JSON.stringify({ id })
|
|
2681
|
+
});
|
|
2682
|
+
if (res.ok) { refresh(); showToast('cmd-toast', 'Schedule deleted', true); } else {
|
|
2683
|
+
const d = await res.json().catch(() => ({}));
|
|
2684
|
+
alert('Delete failed: ' + (d.error || 'unknown'));
|
|
2685
|
+
}
|
|
2686
|
+
} catch (e) { alert('Delete error: ' + e.message); }
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2451
2689
|
// -- Minions Skills --
|
|
2452
2690
|
let _skillsTab = 'all';
|
|
2453
2691
|
let _skillsPage = 0;
|