@tekyzinc/gsd-t 2.73.23 → 2.73.25
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
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [2.73.25] - 2026-04-09
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Undo remove** — excluded elements show as struck-through with a green ↩ restore button instead of disappearing. Click to undo before submitting.
|
|
9
|
+
- **Contract deletion on submit** — when excluded elements are submitted, their contract files and source files are deleted from the project. The `/review/api/exclude` endpoint handles cleanup.
|
|
10
|
+
|
|
11
|
+
## [2.73.24] - 2026-04-09
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Remove element button** — hover over any component in the list to reveal a red × button. Clicking it excludes the element from review and auto-comments "EXCLUDED — not in Figma design". Excluded count shown in submit stats.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Comment validation removed** — all comments are now accepted (questions, exclusions, feedback). The "don't suggest specific changes" popup no longer blocks submission.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Submit stats** show removed count alongside changed/commented.
|
|
21
|
+
|
|
5
22
|
## [2.73.23] - 2026-04-09
|
|
6
23
|
|
|
7
24
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "2.73.
|
|
3
|
+
"version": "2.73.25",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 56 slash commands with headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -620,6 +620,48 @@ const server = http.createServer((req, res) => {
|
|
|
620
620
|
return;
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
+
if (pathname === "/review/api/exclude" && req.method === "POST") {
|
|
624
|
+
// Remove excluded elements: delete contract files and remove from queue
|
|
625
|
+
let body = "";
|
|
626
|
+
req.on("data", (chunk) => { body += chunk; });
|
|
627
|
+
req.on("end", () => {
|
|
628
|
+
try {
|
|
629
|
+
const { excludedIds } = JSON.parse(body);
|
|
630
|
+
const removed = [];
|
|
631
|
+
for (const id of excludedIds) {
|
|
632
|
+
const item = reviewQueue.find(q => q.id === id);
|
|
633
|
+
if (!item || !item.sourcePath) continue;
|
|
634
|
+
const match = item.sourcePath.match(/src\/components\/(\w+)\/(\w+)\.vue$/);
|
|
635
|
+
if (!match) continue;
|
|
636
|
+
const [, tier, name] = match;
|
|
637
|
+
const kebab = name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
638
|
+
const contractPath = path.join(PROJECT_DIR, ".gsd-t", "contracts", "design", tier, `${kebab}.contract.md`);
|
|
639
|
+
if (fs.existsSync(contractPath)) {
|
|
640
|
+
fs.unlinkSync(contractPath);
|
|
641
|
+
removed.push({ id, contract: contractPath });
|
|
642
|
+
}
|
|
643
|
+
// Remove source file too
|
|
644
|
+
const srcPath = path.join(PROJECT_DIR, item.sourcePath);
|
|
645
|
+
if (fs.existsSync(srcPath)) {
|
|
646
|
+
fs.unlinkSync(srcPath);
|
|
647
|
+
removed.push({ id, source: srcPath });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// Remove from queue
|
|
651
|
+
for (let i = reviewQueue.length - 1; i >= 0; i--) {
|
|
652
|
+
if (excludedIds.includes(reviewQueue[i].id)) reviewQueue.splice(i, 1);
|
|
653
|
+
}
|
|
654
|
+
broadcast("queue-update", reviewQueue);
|
|
655
|
+
res.writeHead(200, { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" });
|
|
656
|
+
res.end(JSON.stringify({ ok: true, removed }));
|
|
657
|
+
} catch (err) {
|
|
658
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
659
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
|
|
623
665
|
if (pathname === "/review/api/write-source" && req.method === "POST") {
|
|
624
666
|
// Apply CSS property changes back to source files
|
|
625
667
|
let body = "";
|
|
@@ -195,6 +195,14 @@
|
|
|
195
195
|
|
|
196
196
|
.component-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
197
197
|
.component-type { font-size: 10px; color: #94a3b8; }
|
|
198
|
+
.remove-btn, .undo-remove-btn {
|
|
199
|
+
display: none; background: none; border: none; font-size: 14px;
|
|
200
|
+
cursor: pointer; padding: 0 4px; line-height: 1; opacity: 0.6;
|
|
201
|
+
}
|
|
202
|
+
.remove-btn { color: #ef4444; }
|
|
203
|
+
.undo-remove-btn { color: #22c55e; display: block; }
|
|
204
|
+
.remove-btn:hover, .undo-remove-btn:hover { opacity: 1; }
|
|
205
|
+
.component-item:hover .remove-btn { display: block; }
|
|
198
206
|
|
|
199
207
|
/* ── Submit bar ─────────────────────────────────── */
|
|
200
208
|
.submit-bar {
|
|
@@ -801,6 +809,7 @@
|
|
|
801
809
|
let inspectActive = false;
|
|
802
810
|
const changes = new Map(); // componentId → [{path, property, oldValue, newValue}]
|
|
803
811
|
const comments = new Map(); // componentId → string
|
|
812
|
+
const excluded = new Set(); // componentIds removed from review
|
|
804
813
|
let currentElementPath = null;
|
|
805
814
|
let currentStyles = null;
|
|
806
815
|
|
|
@@ -952,14 +961,45 @@
|
|
|
952
961
|
statusClass = "rejected";
|
|
953
962
|
}
|
|
954
963
|
|
|
964
|
+
const isExcluded = excluded.has(item.id);
|
|
965
|
+
|
|
955
966
|
const div = document.createElement("div");
|
|
956
|
-
div.className = `component-item${idx === selectedIdx ? " selected" : ""}`;
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
967
|
+
div.className = `component-item${idx === selectedIdx ? " selected" : ""}${isExcluded ? " excluded" : ""}`;
|
|
968
|
+
|
|
969
|
+
if (isExcluded) {
|
|
970
|
+
div.innerHTML = `
|
|
971
|
+
<div class="component-status" style="background:#ef4444"></div>
|
|
972
|
+
<div class="component-name" style="text-decoration:line-through;opacity:0.5">${item.name || item.id}</div>
|
|
973
|
+
<button class="undo-remove-btn" title="Restore element">↩</button>
|
|
974
|
+
`;
|
|
975
|
+
div.querySelector(".undo-remove-btn").addEventListener("click", (e) => {
|
|
976
|
+
e.stopPropagation();
|
|
977
|
+
excluded.delete(item.id);
|
|
978
|
+
comments.delete(item.id);
|
|
979
|
+
renderComponentList();
|
|
980
|
+
updateSubmitStats();
|
|
981
|
+
});
|
|
982
|
+
} else {
|
|
983
|
+
div.innerHTML = `
|
|
984
|
+
<div class="component-status ${statusClass}"></div>
|
|
985
|
+
<div class="component-name">${item.name || item.id}</div>
|
|
986
|
+
<div class="component-type">${item.type || ""}</div>
|
|
987
|
+
<button class="remove-btn" title="Remove from review">×</button>
|
|
988
|
+
`;
|
|
989
|
+
div.querySelector(".component-name").addEventListener("click", () => selectComponent(idx));
|
|
990
|
+
div.querySelector(".component-status").addEventListener("click", () => selectComponent(idx));
|
|
991
|
+
div.querySelector(".remove-btn").addEventListener("click", (e) => {
|
|
992
|
+
e.stopPropagation();
|
|
993
|
+
excluded.add(item.id);
|
|
994
|
+
comments.set(item.id, "EXCLUDED — not in Figma design");
|
|
995
|
+
if (selectedIdx === idx) {
|
|
996
|
+
const remaining = filteredQueue.filter(q => !excluded.has(q.id));
|
|
997
|
+
selectedIdx = remaining.length > 0 ? filteredQueue.indexOf(remaining[0]) : -1;
|
|
998
|
+
}
|
|
999
|
+
renderComponentList();
|
|
1000
|
+
updateSubmitStats();
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
963
1003
|
componentList.appendChild(div);
|
|
964
1004
|
});
|
|
965
1005
|
}
|
|
@@ -2110,21 +2150,20 @@
|
|
|
2110
2150
|
// ── Submit ────────────────────────────────────────
|
|
2111
2151
|
function updateSubmitStats() {
|
|
2112
2152
|
const total = queue.length;
|
|
2153
|
+
const excludedCount = excluded.size;
|
|
2113
2154
|
const changed = Array.from(changes.keys()).filter(id => (changes.get(id) || []).length > 0).length;
|
|
2114
|
-
const commented = comments.
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
submitAll.textContent = changed > 0 || commented > 0
|
|
2127
|
-
? `Submit (${changed} changes, ${commented} comments)`
|
|
2155
|
+
const commented = Array.from(comments.keys()).filter(id => !excluded.has(id)).length;
|
|
2156
|
+
|
|
2157
|
+
const parts = [];
|
|
2158
|
+
if (changed > 0) parts.push(`<span><span class="component-status changed" style="display:inline-block"></span> ${changed} changed</span>`);
|
|
2159
|
+
if (commented > 0) parts.push(`<span><span class="component-status rejected" style="display:inline-block"></span> ${commented} commented</span>`);
|
|
2160
|
+
if (excludedCount > 0) parts.push(`<span style="color:#ef4444">${excludedCount} removed</span>`);
|
|
2161
|
+
parts.push(`<span style="color:var(--text-dim)">${total - excludedCount} of ${total}</span>`);
|
|
2162
|
+
submitStats.innerHTML = parts.join(" ");
|
|
2163
|
+
|
|
2164
|
+
const actionCount = changed + commented + excludedCount;
|
|
2165
|
+
submitAll.textContent = actionCount > 0
|
|
2166
|
+
? `Submit (${changed} changes, ${commented} comments, ${excludedCount} removed)`
|
|
2128
2167
|
: "Submit — Approve All";
|
|
2129
2168
|
}
|
|
2130
2169
|
|
|
@@ -2136,28 +2175,7 @@
|
|
|
2136
2175
|
else comments.delete(filteredQueue[selectedIdx].id);
|
|
2137
2176
|
}
|
|
2138
2177
|
|
|
2139
|
-
//
|
|
2140
|
-
const actionWords = /change|make|set|move|add|remove|reduce|increase|fix|use|switch|replace|adjust|align|center|should be|needs to|too |bigger|smaller|wider|narrower|thicker|thinner|lighter|darker|bolder|px|rem|%|#[0-9a-f]/i;
|
|
2141
|
-
const docComments = [];
|
|
2142
|
-
for (const [compId, comment] of comments) {
|
|
2143
|
-
if (!actionWords.test(comment)) {
|
|
2144
|
-
const item = queue.find(q => q.id === compId);
|
|
2145
|
-
docComments.push(item ? item.name : compId);
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
if (docComments.length > 0) {
|
|
2149
|
-
const proceed = confirm(
|
|
2150
|
-
"These comments don't suggest specific changes:\n\n" +
|
|
2151
|
-
docComments.map(n => " \u2022 " + n).join("\n") +
|
|
2152
|
-
"\n\nComments should describe what to change, e.g.:\n" +
|
|
2153
|
-
' "make padding 8px"\n "use darker blue"\n "reduce gap between title and chart"\n\n' +
|
|
2154
|
-
"Non-actionable comments will be discarded.\n\nSubmit anyway?"
|
|
2155
|
-
);
|
|
2156
|
-
if (!proceed) return;
|
|
2157
|
-
for (const [compId, comment] of comments) {
|
|
2158
|
-
if (!actionWords.test(comment)) comments.delete(compId);
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2178
|
+
// All comments are accepted — questions, exclusions, and change requests alike
|
|
2161
2179
|
|
|
2162
2180
|
// Build feedback: each element gets its changes and comments
|
|
2163
2181
|
const feedback = queue.map(item => ({
|
|
@@ -2193,6 +2211,15 @@
|
|
|
2193
2211
|
body: JSON.stringify({ changes: allChanges }),
|
|
2194
2212
|
});
|
|
2195
2213
|
}
|
|
2214
|
+
|
|
2215
|
+
// Delete contracts and source files for excluded elements
|
|
2216
|
+
if (excluded.size > 0) {
|
|
2217
|
+
await fetch("/review/api/exclude", {
|
|
2218
|
+
method: "POST",
|
|
2219
|
+
headers: { "Content-Type": "application/json" },
|
|
2220
|
+
body: JSON.stringify({ excludedIds: Array.from(excluded) }),
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2196
2223
|
}
|
|
2197
2224
|
} catch (err) {
|
|
2198
2225
|
submitAll.textContent = "Error — retry";
|