@tenonhq/dovetail-dashboard 0.0.28 → 0.0.29
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/package.json +1 -1
- package/public/claude-plans.css +15 -0
- package/public/claude-plans.html +4 -2
- package/public/claude-plans.js +46 -5
- package/server.js +7 -1
package/package.json
CHANGED
package/public/claude-plans.css
CHANGED
|
@@ -23,6 +23,21 @@
|
|
|
23
23
|
border-color: var(--brand);
|
|
24
24
|
color: var(--brand);
|
|
25
25
|
}
|
|
26
|
+
.cp-lints-badge {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
min-width: 18px;
|
|
31
|
+
height: 18px;
|
|
32
|
+
margin: 0 2px;
|
|
33
|
+
padding: 0 5px;
|
|
34
|
+
background: var(--danger);
|
|
35
|
+
color: var(--fg-on-dark);
|
|
36
|
+
border-radius: var(--radius-pill);
|
|
37
|
+
font-size: 11px;
|
|
38
|
+
font-weight: var(--weight-bold);
|
|
39
|
+
line-height: 1;
|
|
40
|
+
}
|
|
26
41
|
.cp-storage {
|
|
27
42
|
font-size: 11px;
|
|
28
43
|
font-family: var(--font-mono);
|
package/public/claude-plans.html
CHANGED
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
<div class="header-right">
|
|
32
32
|
<a class="cp-nav-link" href="/">← Update Sets</a>
|
|
33
|
-
<a class="cp-nav-link" href="/prompt-lints">Prompt Lints
|
|
33
|
+
<a class="cp-nav-link" href="/prompt-lints">Prompt Lints
|
|
34
|
+
<span class="cp-lints-badge" id="cp-lints-badge" title="Prompts needing review" hidden>0</span>
|
|
35
|
+
→</a>
|
|
34
36
|
<button class="cp-theme-toggle" id="cp-theme-toggle" type="button"
|
|
35
37
|
aria-label="Toggle dark mode" title="Toggle dark mode"></button>
|
|
36
38
|
<span class="cp-storage" id="cp-storage" title="storage root"></span>
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
<span class="cp-rail-title">Plans</span>
|
|
44
46
|
<span class="cp-count" id="cp-count">0</span>
|
|
45
47
|
</div>
|
|
46
|
-
<details class="cp-topics" id="cp-topics"
|
|
48
|
+
<details class="cp-topics" id="cp-topics">
|
|
47
49
|
<summary class="cp-topics-summary">
|
|
48
50
|
<span class="cp-topics-caret" aria-hidden="true">▾</span>
|
|
49
51
|
<span class="cp-topics-title">Topics</span>
|
package/public/claude-plans.js
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
var els = {
|
|
44
44
|
storage: document.getElementById("cp-storage"),
|
|
45
|
+
lintsBadge: document.getElementById("cp-lints-badge"),
|
|
45
46
|
list: document.getElementById("cp-list"),
|
|
46
47
|
count: document.getElementById("cp-count"),
|
|
47
48
|
railEmpty: document.getElementById("cp-rail-empty"),
|
|
@@ -219,7 +220,7 @@
|
|
|
219
220
|
if (els.topics) {
|
|
220
221
|
try {
|
|
221
222
|
var stored = localStorage.getItem("cp-topics-collapsed");
|
|
222
|
-
|
|
223
|
+
els.topics.open = stored === "0";
|
|
223
224
|
} catch (_) {}
|
|
224
225
|
els.topics.addEventListener("toggle", function () {
|
|
225
226
|
try {
|
|
@@ -709,6 +710,10 @@
|
|
|
709
710
|
|
|
710
711
|
function selectPlan(slug) {
|
|
711
712
|
state.selectedSlug = slug;
|
|
713
|
+
if (window.history && window.history.replaceState) {
|
|
714
|
+
var url = window.location.pathname + "?plan=" + encodeURIComponent(slug);
|
|
715
|
+
window.history.replaceState(null, "", url);
|
|
716
|
+
}
|
|
712
717
|
renderRail();
|
|
713
718
|
renderDetail();
|
|
714
719
|
}
|
|
@@ -831,6 +836,42 @@
|
|
|
831
836
|
if (state.selectedSlug === planSlug) renderDetail();
|
|
832
837
|
}
|
|
833
838
|
|
|
839
|
+
// A lint event "needs attention" if its score fell below the recording
|
|
840
|
+
// threshold (default 50 when no threshold is supplied) or it flagged any
|
|
841
|
+
// missing tags, antipatterns, or ceremony. The badge surfaces that count.
|
|
842
|
+
function lintNeedsAttention(e) {
|
|
843
|
+
if (!e) return false;
|
|
844
|
+
var threshold = typeof e.threshold === "number" ? e.threshold : 50;
|
|
845
|
+
if (typeof e.score === "number" && e.score < threshold) return true;
|
|
846
|
+
if (Array.isArray(e.missing) && e.missing.length) return true;
|
|
847
|
+
if (Array.isArray(e.antipatterns) && e.antipatterns.length) return true;
|
|
848
|
+
if (Array.isArray(e.ceremony) && e.ceremony.length) return true;
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function setLintsBadge(count) {
|
|
853
|
+
if (!els.lintsBadge) return;
|
|
854
|
+
if (count > 0) {
|
|
855
|
+
els.lintsBadge.textContent = String(count);
|
|
856
|
+
els.lintsBadge.hidden = false;
|
|
857
|
+
} else {
|
|
858
|
+
els.lintsBadge.hidden = true;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function loadLintsBadge() {
|
|
863
|
+
return fetch("/api/prompt-lints")
|
|
864
|
+
.then(function (r) { return r.json(); })
|
|
865
|
+
.then(function (data) {
|
|
866
|
+
var events = data && Array.isArray(data.events) ? data.events : [];
|
|
867
|
+
setLintsBadge(events.filter(lintNeedsAttention).length);
|
|
868
|
+
})
|
|
869
|
+
.catch(function (err) {
|
|
870
|
+
console.warn("[claude-plans] failed to load prompt lints:", err);
|
|
871
|
+
setLintsBadge(0);
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
|
|
834
875
|
async function loadInitial() {
|
|
835
876
|
try {
|
|
836
877
|
var res = await fetch("/api/claude-plans");
|
|
@@ -863,11 +904,10 @@
|
|
|
863
904
|
}));
|
|
864
905
|
renderRail();
|
|
865
906
|
if (!state.selectedSlug) {
|
|
866
|
-
var
|
|
867
|
-
var pathSlug = pathMatch && pathMatch[1];
|
|
907
|
+
var paramSlug = new URLSearchParams(window.location.search).get("plan");
|
|
868
908
|
var sorted = sortedPlans();
|
|
869
|
-
var target =
|
|
870
|
-
?
|
|
909
|
+
var target = paramSlug && state.plans.has(paramSlug)
|
|
910
|
+
? paramSlug
|
|
871
911
|
: (sorted.length > 0 ? sorted[0].slug : null);
|
|
872
912
|
if (target) selectPlan(target);
|
|
873
913
|
}
|
|
@@ -941,6 +981,7 @@
|
|
|
941
981
|
setupSearch();
|
|
942
982
|
setupTopics();
|
|
943
983
|
setActiveTab("plan");
|
|
984
|
+
loadLintsBadge();
|
|
944
985
|
loadInitial().then(startStream);
|
|
945
986
|
});
|
|
946
987
|
})();
|
package/server.js
CHANGED
|
@@ -1038,10 +1038,16 @@ function startClaudePlanWatcher() {
|
|
|
1038
1038
|
claudePlanWatcher.on("unlink", function (p) { handleWatcherChange("unlink", p); });
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
-
app.get(
|
|
1041
|
+
app.get("/claude-plans", function (req, res) {
|
|
1042
1042
|
res.sendFile(path.join(__dirname, "public", "claude-plans.html"));
|
|
1043
1043
|
});
|
|
1044
1044
|
|
|
1045
|
+
// Legacy path-segment deep links (/claude-plans/:slug) redirect to the
|
|
1046
|
+
// query-param form so relative assets resolve against /claude-plans.
|
|
1047
|
+
app.get("/claude-plans/:slug", function (req, res) {
|
|
1048
|
+
res.redirect(301, "/claude-plans?plan=" + encodeURIComponent(req.params.slug));
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1045
1051
|
app.get("/prompt-lints", function (req, res) {
|
|
1046
1052
|
res.sendFile(path.join(__dirname, "public", "prompt-lints.html"));
|
|
1047
1053
|
});
|